xref: /openbsd/sys/arch/loongson/dev/voyager.c (revision c622896b)
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