imx7_gpc.c revision 1.1 1 /* $NetBSD: imx7_gpc.c,v 1.1 2020/12/23 14:42:38 skrll Exp $ */
2 /*-
3 * Copyright (c) 2019 Genetec Corporation. All rights reserved.
4 * Written by Hashimoto Kenichi for Genetec Corporation.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: imx7_gpc.c,v 1.1 2020/12/23 14:42:38 skrll Exp $");
29
30 #include "opt_fdt.h"
31
32 #define _INTR_PRIVATE
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/device.h>
37
38 #include <arm/nxp/imx6var.h>
39 #include <arm/nxp/imx6_reg.h>
40 #include <arm/nxp/imx6_gpcreg.h>
41
42 #include <arm/cortex/gic_intr.h>
43
44 #include <dev/fdt/fdtvar.h>
45
46 #define GPC_PCG_CPU_0_1_MAPPING 0xec
47 #define OTG2_A53_DOMAIN __BIT(5)
48 #define OTG1_A53_DOMAIN __BIT(4)
49
50 #define GPC_PU_PGC_SW_PUP_REQ 0xf8
51 #define USB_OTG2_SW_PUP_REQ __BIT(3)
52 #define USB_OTG1_SW_PUP_REQ __BIT(2)
53
54 #define IMXGPC_MAXCPUS 4
55
56 /* Mapping of CPU number to GPC_IMR1_COREx base offset */
57 static const bus_size_t imx7gpc_imr_base[IMXGPC_MAXCPUS] = {
58 0x30,
59 0x40,
60 0x1c0,
61 0x1d0,
62 };
63
64 #define GPC_IMRn_COREx(n,x) (imx7gpc_imr_base[(x)] + (n) * 0x4)
65
66 struct imx7gpc_softc {
67 device_t sc_dev;
68
69 bus_space_tag_t sc_iot;
70 bus_space_handle_t sc_ioh;
71 };
72
73 static int imx7gpc_match(device_t, struct cfdata *, void *);
74 static void imx7gpc_attach(device_t, device_t, void *);
75
76 static void imx7gpc_powerup(struct imx7gpc_softc *, uint32_t, uint32_t);
77 static void imx7gpc_mask(struct imx7gpc_softc *, u_int, bool);
78 static void imx7gpc_unmask(struct imx7gpc_softc *, u_int, bool);
79
80 static void *imx7gpc_establish(device_t, u_int *, int, int,
81 int (*)(void *), void *);
82 static void imx7gpc_disestablish(device_t, void *);
83 static bool imx7gpc_intrstr(device_t, u_int *, char *, size_t);
84
85 struct fdtbus_interrupt_controller_func imx7gpc_funcs = {
86 .establish = imx7gpc_establish,
87 .disestablish = imx7gpc_disestablish,
88 .intrstr = imx7gpc_intrstr
89 };
90
91 CFATTACH_DECL_NEW(imx7gpc, sizeof(struct imx7gpc_softc),
92 imx7gpc_match, imx7gpc_attach, NULL, NULL);
93
94 static int
95 imx7gpc_match(device_t parent, cfdata_t cf, void *aux)
96 {
97 const char * const compatible[] = {
98 "fsl,imx7d-gpc",
99 "fsl,imx8mq-gpc",
100 NULL
101 };
102 struct fdt_attach_args * const faa = aux;
103
104 return of_match_compatible(faa->faa_phandle, compatible);
105 }
106
107 static void
108 imx7gpc_attach(device_t parent, device_t self, void *aux)
109 {
110 struct imx7gpc_softc * const sc = device_private(self);
111 struct fdt_attach_args * const faa = aux;
112 const int phandle = faa->faa_phandle;
113 bus_addr_t gpc_addr;
114 bus_size_t gpc_size;
115 int error;
116
117 KASSERT(ncpu <= IMXGPC_MAXCPUS);
118
119 if (fdtbus_get_reg(phandle, 0, &gpc_addr, &gpc_size) != 0) {
120 aprint_error(": couldn't get gpc registers\n");
121 return;
122 }
123
124 sc->sc_dev = self;
125 sc->sc_iot = faa->faa_bst;
126
127 error = bus_space_map(sc->sc_iot, gpc_addr, gpc_size, 0,
128 &sc->sc_ioh);
129 if (error) {
130 aprint_error(": couldn't map gpc registers: %d\n", error);
131 return;
132 }
133
134 error = fdtbus_register_interrupt_controller(self, faa->faa_phandle,
135 &imx7gpc_funcs);
136 if (error) {
137 aprint_error(": couldn't register with fdtbus: %d\n", error);
138 return;
139 }
140
141 aprint_naive("\n");
142 aprint_normal(": General Power Controller\n");
143
144 /* XXX enable OTG power domains */
145 imx7gpc_powerup(sc, USB_OTG2_SW_PUP_REQ | USB_OTG1_SW_PUP_REQ,
146 OTG2_A53_DOMAIN | OTG1_A53_DOMAIN);
147 }
148
149 static void
150 imx7gpc_powerup(struct imx7gpc_softc *sc, uint32_t req, uint32_t map)
151 {
152 uint32_t val;
153
154 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPC_PCG_CPU_0_1_MAPPING);
155 val |= map;
156 bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPC_PCG_CPU_0_1_MAPPING, val);
157
158 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPC_PU_PGC_SW_PUP_REQ);
159 val |= req;
160 bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPC_PU_PGC_SW_PUP_REQ, val);
161
162 delay(5000);
163
164 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPC_PCG_CPU_0_1_MAPPING);
165 val &= ~map;
166 bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPC_PCG_CPU_0_1_MAPPING, val);
167 }
168
169 static void
170 imx7gpc_mask(struct imx7gpc_softc *sc, u_int irq, bool mpsafe)
171 {
172 const u_int reg = irq / 32;
173 const u_int bit = irq % 32;
174 uint32_t val;
175
176 for (u_int cpu = 0; cpu < (mpsafe ? ncpu : 1); cpu++) {
177 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
178 GPC_IMRn_COREx(reg, cpu));
179 val |= __BIT(bit);
180 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
181 GPC_IMRn_COREx(reg, cpu), val);
182 }
183 }
184
185 static void
186 imx7gpc_unmask(struct imx7gpc_softc *sc, u_int irq, bool mpsafe)
187 {
188 const u_int reg = irq / 32;
189 const u_int bit = irq % 32;
190 uint32_t val;
191
192 for (u_int cpu = 0; cpu < (mpsafe ? ncpu : 1); cpu++) {
193 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
194 GPC_IMRn_COREx(reg, cpu));
195 val &= ~__BIT(bit);
196 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
197 GPC_IMRn_COREx(reg, cpu), val);
198 }
199 }
200
201
202 static void *
203 imx7gpc_establish(device_t dev, u_int *specifier, int ipl, int flags,
204 int (*func)(void *), void *arg)
205 {
206 struct imx7gpc_softc * const sc = device_private(dev);
207 void *ih;
208
209 /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
210 /* 2nd cell is the interrupt number */
211 /* 3rd cell is flags */
212
213 const u_int type = be32toh(specifier[0]);
214 const u_int intr = be32toh(specifier[1]);
215 const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
216 const u_int trig = be32toh(specifier[2]) & 0xf;
217 const u_int level = (trig & 0x3) ? IST_EDGE : IST_LEVEL;
218
219 const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
220
221 if (type != 0)
222 return NULL; /* Only SPIs are supported */
223
224 KASSERT(irq >= 32);
225
226 aprint_debug_dev(dev, "intr establish irq %d, level %d\n", irq, level);
227
228 ih = intr_establish(irq, ipl, level | mpsafe, func, arg);
229 if (ih != NULL)
230 imx7gpc_unmask(sc, irq - 32, mpsafe == IST_MPSAFE);
231
232 return ih;
233 }
234
235 static void
236 imx7gpc_disestablish(device_t dev, void *ih)
237 {
238 struct imx7gpc_softc * const sc = device_private(dev);
239 struct intrsource *is = ih;
240 const u_int irq = is->is_irq;
241 const bool mpsafe = is->is_mpsafe;
242
243 intr_disestablish(ih);
244 imx7gpc_mask(sc, irq - 32, mpsafe);
245 }
246
247 static bool
248 imx7gpc_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
249 {
250 /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
251 /* 2nd cell is the interrupt number */
252 /* 3rd cell is flags */
253
254 if (!specifier)
255 return false;
256
257 const u_int type = be32toh(specifier[0]);
258 const u_int intr = be32toh(specifier[1]);
259 const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
260
261 snprintf(buf, buflen, "irq %d", irq);
262
263 return true;
264 }
265