1 /* $OpenBSD: voyager.c,v 1.7 2022/02/21 12:46:59 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2010 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Silicon Motion SM501/SM502 (VoyagerGX) master driver.
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/gpio.h>
27
28 #include <machine/bus.h>
29 #include <machine/cpu.h>
30 #include <machine/intr.h>
31
32 #include <dev/gpio/gpiovar.h>
33
34 #include <dev/pci/pcireg.h>
35 #include <dev/pci/pcivar.h>
36 #include <dev/pci/pcidevs.h>
37
38 #include <loongson/dev/bonito_irq.h> /* for BONITO_NINTS */
39 #include <loongson/dev/voyagerreg.h>
40 #include <loongson/dev/voyagervar.h>
41
42 struct voyager_softc {
43 struct device sc_dev;
44
45 bus_space_tag_t sc_fbt;
46 bus_space_handle_t sc_fbh;
47 bus_size_t sc_fbsize;
48
49 bus_space_tag_t sc_mmiot;
50 bus_space_handle_t sc_mmioh;
51 bus_size_t sc_mmiosize;
52
53 struct gpio_chipset_tag sc_gpio_tag;
54 gpio_pin_t sc_gpio_pins[32 + 32];
55
56 void *sc_ih;
57 struct intrhand *sc_intr[32];
58 };
59
60 int voyager_match(struct device *, void *, void *);
61 void voyager_attach(struct device *, struct device *, void *);
62
63 const struct cfattach voyager_ca = {
64 sizeof(struct voyager_softc), voyager_match, voyager_attach
65 };
66
67 struct cfdriver voyager_cd = {
68 NULL, "voyager", DV_DULL
69 };
70
71 void voyager_attach_gpio(struct voyager_softc *);
72 int voyager_intr(void *);
73 int voyager_print(void *, const char *);
74 int voyager_search(struct device *, void *, void *);
75
76 const struct pci_matchid voyager_devices[] = {
77 /*
78 * 502 shares the same device ID as 501, but uses a different
79 * revision number.
80 */
81 { PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM501 }
82 };
83
84 int
voyager_match(struct device * parent,void * vcf,void * aux)85 voyager_match(struct device *parent, void *vcf, void *aux)
86 {
87 struct pci_attach_args *pa = (struct pci_attach_args *)aux;
88
89 return pci_matchbyid(pa, voyager_devices, nitems(voyager_devices));
90 }
91
92 void
voyager_attach(struct device * parent,struct device * self,void * aux)93 voyager_attach(struct device *parent, struct device *self, void *aux)
94 {
95 struct voyager_softc *sc = (struct voyager_softc *)self;
96 struct pci_attach_args *pa = (struct pci_attach_args *)aux;
97 pci_intr_handle_t ih;
98 const char *intrstr;
99
100 printf(": ");
101
102 /*
103 * Map registers.
104 */
105
106 if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM,
107 BUS_SPACE_MAP_LINEAR, &sc->sc_fbt, &sc->sc_fbh,
108 NULL, &sc->sc_fbsize, 0) != 0) {
109 printf("can't map frame buffer\n");
110 return;
111 }
112
113 if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x04, PCI_MAPREG_TYPE_MEM,
114 BUS_SPACE_MAP_LINEAR, &sc->sc_mmiot, &sc->sc_mmioh,
115 NULL, &sc->sc_mmiosize, 0) != 0) {
116 printf("can't map mmio\n");
117 goto fail1;
118 }
119
120 /*
121 * Setup interrupt handling.
122 */
123
124 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_RAW_ICR,
125 0xffffffff);
126 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR, 0);
127 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR);
128
129 if (pci_intr_map(pa, &ih) != 0) {
130 printf("can't map interrupt\n");
131 goto fail2;
132 }
133 intrstr = pci_intr_string(pa->pa_pc, ih);
134 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY, voyager_intr,
135 sc, self->dv_xname);
136 if (sc->sc_ih == NULL) {
137 printf("can't establish interrupt");
138 if (intrstr != NULL)
139 printf(" at %s", intrstr);
140 printf("\n");
141 goto fail2;
142 }
143
144 printf("%s\n", intrstr);
145
146 /*
147 * Attach GPIO devices.
148 */
149 voyager_attach_gpio(sc);
150
151 /*
152 * Attach child devices.
153 */
154 config_search(voyager_search, self, pa);
155
156 return;
157 fail2:
158 bus_space_unmap(sc->sc_mmiot, sc->sc_mmioh, sc->sc_mmiosize);
159 fail1:
160 bus_space_unmap(sc->sc_fbt, sc->sc_fbh, sc->sc_fbsize);
161 }
162
163 int
voyager_print(void * args,const char * parentname)164 voyager_print(void *args, const char *parentname)
165 {
166 struct voyager_attach_args *vaa = (struct voyager_attach_args *)args;
167
168 if (parentname != NULL)
169 printf("%s at %s", vaa->vaa_name, parentname);
170
171 return UNCONF;
172 }
173
174 int
voyager_search(struct device * parent,void * vcf,void * args)175 voyager_search(struct device *parent, void *vcf, void *args)
176 {
177 struct voyager_softc *sc = (struct voyager_softc *)parent;
178 struct cfdata *cf = (struct cfdata *)vcf;
179 struct pci_attach_args *pa = (struct pci_attach_args *)args;
180 struct voyager_attach_args vaa;
181
182 /*
183 * Caller should have attached gpio already. If it didn't, bail
184 * out here.
185 */
186 if (strcmp(cf->cf_driver->cd_name, "gpio") == 0)
187 return 0;
188
189 vaa.vaa_name = cf->cf_driver->cd_name;
190 vaa.vaa_pa = pa;
191 vaa.vaa_fbt = sc->sc_fbt;
192 vaa.vaa_fbh = sc->sc_fbh;
193 vaa.vaa_mmiot = sc->sc_mmiot;
194 vaa.vaa_mmioh = sc->sc_mmioh;
195
196 if (cf->cf_attach->ca_match(parent, cf, &vaa) == 0)
197 return 0;
198
199 config_attach(parent, cf, &vaa, voyager_print);
200 return 1;
201 }
202
203 /*
204 * Interrupt dispatcher
205 */
206
207 int
voyager_intr(void * vsc)208 voyager_intr(void *vsc)
209 {
210 struct voyager_softc *sc = (struct voyager_softc *)vsc;
211 uint32_t isr, imr, mask, bitno;
212 struct intrhand *ih;
213
214 isr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_ISR);
215 imr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR);
216
217 isr &= imr;
218 if (isr == 0)
219 return 0;
220
221 for (bitno = 0, mask = 1 << 0; isr != 0; bitno++, mask <<= 1) {
222 if ((isr & mask) == 0)
223 continue;
224 isr ^= mask;
225 for (ih = sc->sc_intr[bitno]; ih != NULL; ih = ih->ih_next) {
226 if ((*ih->ih_fun)(ih->ih_arg) != 0)
227 ih->ih_count.ec_count++;
228 }
229 }
230
231 return 1;
232 }
233
234 void *
voyager_intr_establish(void * cookie,int irq,int level,int (* fun)(void *),void * arg,const char * name)235 voyager_intr_establish(void *cookie, int irq, int level, int (*fun)(void *),
236 void *arg, const char *name)
237 {
238 struct voyager_softc *sc = (struct voyager_softc *)cookie;
239 struct intrhand *prevh, *nh;
240 uint32_t imr;
241
242 #ifdef DIAGNOSTIC
243 if (irq < 0 || irq >= nitems(sc->sc_intr))
244 return NULL;
245 #endif
246
247 level &= ~IPL_MPSAFE;
248
249 nh = (struct intrhand *)malloc(sizeof *nh, M_DEVBUF, M_NOWAIT | M_ZERO);
250 if (nh == NULL)
251 return NULL;
252
253 nh->ih_fun = fun;
254 nh->ih_arg = arg;
255 nh->ih_level = level;
256 nh->ih_irq = irq + BONITO_NINTS;
257 evcount_attach(&nh->ih_count, name, &nh->ih_irq);
258
259 if (sc->sc_intr[irq] == NULL)
260 sc->sc_intr[irq] = nh;
261 else {
262 /* insert at tail */
263 for (prevh = sc->sc_intr[irq]; prevh->ih_next != NULL;
264 prevh = prevh->ih_next) ;
265 prevh->ih_next = nh;
266 }
267
268 /* enable interrupt source */
269 imr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR);
270 imr |= 1 << irq;
271 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR, imr);
272 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR);
273
274 return nh;
275 }
276
277 const char *
voyager_intr_string(void * vih)278 voyager_intr_string(void *vih)
279 {
280 struct intrhand *ih = (struct intrhand *)vih;
281 static char intrstr[1 + 32];
282
283 snprintf(intrstr, sizeof intrstr, "voyager irq %d",
284 ih->ih_irq - BONITO_NINTS);
285 return intrstr;
286 }
287
288 /*
289 * GPIO support code
290 */
291
292 int voyager_gpio_pin_read(void *, int);
293 void voyager_gpio_pin_write(void *, int, int);
294 void voyager_gpio_pin_ctl(void *, int, int);
295
296 static const struct gpio_chipset_tag voyager_gpio_tag = {
297 .gp_pin_read = voyager_gpio_pin_read,
298 .gp_pin_write = voyager_gpio_pin_write,
299 .gp_pin_ctl = voyager_gpio_pin_ctl
300 };
301
302 int
voyager_gpio_pin_read(void * cookie,int pin)303 voyager_gpio_pin_read(void *cookie, int pin)
304 {
305 struct voyager_softc *sc = (struct voyager_softc *)cookie;
306 bus_addr_t reg;
307 int32_t data, mask;
308
309 if (pin >= 32) {
310 pin -= 32;
311 reg = VOYAGER_GPIO_DATA_HIGH;
312 } else {
313 reg = VOYAGER_GPIO_DATA_LOW;
314 }
315 mask = 1 << pin;
316
317 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
318 return data & mask ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
319 }
320
321 void
voyager_gpio_pin_write(void * cookie,int pin,int val)322 voyager_gpio_pin_write(void *cookie, int pin, int val)
323 {
324 struct voyager_softc *sc = (struct voyager_softc *)cookie;
325 bus_addr_t reg;
326 int32_t data, mask;
327
328 if (pin >= 32) {
329 pin -= 32;
330 reg = VOYAGER_GPIO_DATA_HIGH;
331 } else {
332 reg = VOYAGER_GPIO_DATA_LOW;
333 }
334 mask = 1 << pin;
335 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
336 if (val)
337 data |= mask;
338 else
339 data &= ~mask;
340 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data);
341 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
342 }
343
344 void
voyager_gpio_pin_ctl(void * cookie,int pin,int flags)345 voyager_gpio_pin_ctl(void *cookie, int pin, int flags)
346 {
347 struct voyager_softc *sc = (struct voyager_softc *)cookie;
348 bus_addr_t reg;
349 int32_t data, mask;
350
351 if (pin >= 32) {
352 pin -= 32;
353 reg = VOYAGER_GPIO_DIR_HIGH;
354 } else {
355 reg = VOYAGER_GPIO_DIR_LOW;
356 }
357 mask = 1 << pin;
358 data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
359 if (ISSET(flags, GPIO_PIN_OUTPUT))
360 data |= mask;
361 else
362 data &= ~mask;
363 bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data);
364 (void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
365 }
366
367 void
voyager_attach_gpio(struct voyager_softc * sc)368 voyager_attach_gpio(struct voyager_softc *sc)
369 {
370 struct gpiobus_attach_args gba;
371 int pin;
372 uint32_t control, value;
373
374 bcopy(&voyager_gpio_tag, &sc->sc_gpio_tag, sizeof voyager_gpio_tag);
375 sc->sc_gpio_tag.gp_cookie = sc;
376
377 control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
378 VOYAGER_GPIOL_CONTROL);
379 value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
380 VOYAGER_GPIO_DATA_LOW);
381 for (pin = 0; pin < 32; pin++) {
382 sc->sc_gpio_pins[pin].pin_num = pin;
383 if ((control & 1) == 0) {
384 sc->sc_gpio_pins[pin].pin_caps =
385 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
386 sc->sc_gpio_pins[pin].pin_state = value & 1;
387 } else {
388 /* disable control of taken over pins */
389 sc->sc_gpio_pins[pin].pin_caps = 0;
390 sc->sc_gpio_pins[pin].pin_state = 0;
391 }
392 }
393
394 control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
395 VOYAGER_GPIOH_CONTROL);
396 value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
397 VOYAGER_GPIO_DATA_HIGH);
398 for (pin = 32 + 0; pin < 32 + 32; pin++) {
399 sc->sc_gpio_pins[pin].pin_num = pin;
400 if ((control & 1) == 0) {
401 sc->sc_gpio_pins[pin].pin_caps =
402 GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
403 sc->sc_gpio_pins[pin].pin_state = value & 1;
404 } else {
405 /* disable control of taken over pins */
406 sc->sc_gpio_pins[pin].pin_caps = 0;
407 sc->sc_gpio_pins[pin].pin_state = 0;
408 }
409 }
410
411 gba.gba_name = "gpio";
412 gba.gba_gc = &sc->sc_gpio_tag;
413 gba.gba_pins = sc->sc_gpio_pins;
414 gba.gba_npins = nitems(sc->sc_gpio_pins);
415
416 config_found(&sc->sc_dev, &gba, voyager_print);
417 }
418