xref: /openbsd/sys/arch/loongson/dev/voyager.c (revision 5af055cd)
1 /*	$OpenBSD: voyager.c,v 1.4 2010/09/20 06:33:48 matthew 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
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
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 interrut 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
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
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 disatcher
205  */
206 
207 int
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 *
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 	nh = (struct intrhand *)malloc(sizeof *nh, M_DEVBUF, M_NOWAIT | M_ZERO);
248 	if (nh == NULL)
249 		return NULL;
250 
251 	nh->ih_fun = fun;
252 	nh->ih_arg = arg;
253 	nh->ih_level = level;
254 	nh->ih_irq = irq + BONITO_NINTS;
255 	evcount_attach(&nh->ih_count, name, &nh->ih_irq);
256 
257 	if (sc->sc_intr[irq] == NULL)
258 		sc->sc_intr[irq] = nh;
259 	else {
260 		/* insert at tail */
261 		for (prevh = sc->sc_intr[irq]; prevh->ih_next != NULL;
262 		    prevh = prevh->ih_next) ;
263 		prevh->ih_next = nh;
264 	}
265 
266 	/* enable interrupt source */
267 	imr = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR);
268 	imr |= 1 << irq;
269 	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR, imr);
270 	(void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, VOYAGER_IMR);
271 
272 	return nh;
273 }
274 
275 const char *
276 voyager_intr_string(void *vih)
277 {
278 	struct intrhand *ih = (struct intrhand *)vih;
279 	static char intrstr[1 + 32];
280 
281 	snprintf(intrstr, sizeof intrstr, "voyager irq %d",
282 	    ih->ih_irq - BONITO_NINTS);
283 	return intrstr;
284 }
285 
286 /*
287  * GPIO support code
288  */
289 
290 int	voyager_gpio_pin_read(void *, int);
291 void	voyager_gpio_pin_write(void *, int, int);
292 void	voyager_gpio_pin_ctl(void *, int, int);
293 
294 static const struct gpio_chipset_tag voyager_gpio_tag = {
295 	.gp_pin_read = voyager_gpio_pin_read,
296 	.gp_pin_write = voyager_gpio_pin_write,
297 	.gp_pin_ctl = voyager_gpio_pin_ctl
298 };
299 
300 int
301 voyager_gpio_pin_read(void *cookie, int pin)
302 {
303 	struct voyager_softc *sc = (struct voyager_softc *)cookie;
304 	bus_addr_t reg;
305 	int32_t data, mask;
306 
307 	if (pin >= 32) {
308 		pin -= 32;
309 		reg = VOYAGER_GPIO_DATA_HIGH;
310 	} else {
311 		reg = VOYAGER_GPIO_DATA_LOW;
312 	}
313 	mask = 1 << pin;
314 
315 	data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
316 	return data & mask ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
317 }
318 
319 void
320 voyager_gpio_pin_write(void *cookie, int pin, int val)
321 {
322 	struct voyager_softc *sc = (struct voyager_softc *)cookie;
323 	bus_addr_t reg;
324 	int32_t data, mask;
325 
326 	if (pin >= 32) {
327 		pin -= 32;
328 		reg = VOYAGER_GPIO_DATA_HIGH;
329 	} else {
330 		reg = VOYAGER_GPIO_DATA_LOW;
331 	}
332 	mask = 1 << pin;
333 	data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
334 	if (val)
335 		data |= mask;
336 	else
337 		data &= ~mask;
338 	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data);
339 	(void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
340 }
341 
342 void
343 voyager_gpio_pin_ctl(void *cookie, int pin, int flags)
344 {
345 	struct voyager_softc *sc = (struct voyager_softc *)cookie;
346 	bus_addr_t reg;
347 	int32_t data, mask;
348 
349 	if (pin >= 32) {
350 		pin -= 32;
351 		reg = VOYAGER_GPIO_DIR_HIGH;
352 	} else {
353 		reg = VOYAGER_GPIO_DIR_LOW;
354 	}
355 	mask = 1 << pin;
356 	data = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
357 	if (ISSET(flags, GPIO_PIN_OUTPUT))
358 		data |= mask;
359 	else
360 		data &= ~mask;
361 	bus_space_write_4(sc->sc_mmiot, sc->sc_mmioh, reg, data);
362 	(void)bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh, reg);
363 }
364 
365 void
366 voyager_attach_gpio(struct voyager_softc *sc)
367 {
368 	struct gpiobus_attach_args gba;
369 	int pin;
370 	uint32_t control, value;
371 
372 	bcopy(&voyager_gpio_tag, &sc->sc_gpio_tag, sizeof voyager_gpio_tag);
373 	sc->sc_gpio_tag.gp_cookie = sc;
374 
375 	control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
376 	    VOYAGER_GPIOL_CONTROL);
377 	value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
378 	    VOYAGER_GPIO_DATA_LOW);
379 	for (pin = 0; pin < 32; pin++) {
380 		sc->sc_gpio_pins[pin].pin_num = pin;
381 		if ((control & 1) == 0) {
382 			sc->sc_gpio_pins[pin].pin_caps =
383 			    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
384 			sc->sc_gpio_pins[pin].pin_state = value & 1;
385 		} else {
386 			/* disable control of taken over pins */
387 			sc->sc_gpio_pins[pin].pin_caps = 0;
388 			sc->sc_gpio_pins[pin].pin_state = 0;
389 		}
390 	}
391 
392 	control = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
393 	    VOYAGER_GPIOH_CONTROL);
394 	value = bus_space_read_4(sc->sc_mmiot, sc->sc_mmioh,
395 	    VOYAGER_GPIO_DATA_HIGH);
396 	for (pin = 32 + 0; pin < 32 + 32; pin++) {
397 		sc->sc_gpio_pins[pin].pin_num = pin;
398 		if ((control & 1) == 0) {
399 			sc->sc_gpio_pins[pin].pin_caps =
400 			    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
401 			sc->sc_gpio_pins[pin].pin_state = value & 1;
402 		} else {
403 			/* disable control of taken over pins */
404 			sc->sc_gpio_pins[pin].pin_caps = 0;
405 			sc->sc_gpio_pins[pin].pin_state = 0;
406 		}
407 	}
408 
409 	gba.gba_name = "gpio";
410 	gba.gba_gc = &sc->sc_gpio_tag;
411 	gba.gba_pins = sc->sc_gpio_pins;
412 	gba.gba_npins = nitems(sc->sc_gpio_pins);
413 
414 	config_found(&sc->sc_dev, &gba, voyager_print);
415 }
416