xref: /openbsd/sys/dev/pci/glxpcib.c (revision 3d8817e4)
1 /*      $OpenBSD: glxpcib.c,v 1.3 2010/10/23 17:42:57 pirofti Exp $	*/
2 
3 /*
4  * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
5  * Copyright (c) 2007 Michael Shalayeff
6  * All rights reserved.
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
17  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
18  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 /*
22  * AMD CS5536 series LPC bridge also containing timer, watchdog, and GPIO.
23  */
24 
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/proc.h>
28 #include <sys/device.h>
29 #include <sys/gpio.h>
30 #include <sys/timetc.h>
31 
32 #include <machine/bus.h>
33 #ifdef __i386__
34 #include <machine/cpufunc.h>
35 #endif
36 
37 #include <dev/gpio/gpiovar.h>
38 #include <dev/pci/pcireg.h>
39 #include <dev/pci/pcivar.h>
40 #include <dev/pci/pcidevs.h>
41 
42 #include <dev/pci/glxreg.h>
43 #include <dev/pci/glxvar.h>
44 
45 #include "gpio.h"
46 
47 #define	AMD5536_REV		GLCP_CHIP_REV_ID
48 #define	AMD5536_REV_MASK	0xff
49 #define	AMD5536_TMC		PMC_LTMR
50 
51 #define	MSR_LBAR_ENABLE		0x100000000ULL
52 
53 /* Multi-Functional General Purpose Timer */
54 #define	MSR_LBAR_MFGPT		DIVIL_LBAR_MFGPT
55 #define	MSR_MFGPT_SIZE		0x40
56 #define	MSR_MFGPT_ADDR_MASK	0xffc0
57 #define	AMD5536_MFGPT0_CMP1	0x00000000
58 #define	AMD5536_MFGPT0_CMP2	0x00000002
59 #define	AMD5536_MFGPT0_CNT	0x00000004
60 #define	AMD5536_MFGPT0_SETUP	0x00000006
61 #define	AMD5536_MFGPT_DIV_MASK	0x000f	/* div = 1 << mask */
62 #define	AMD5536_MFGPT_CLKSEL	0x0010
63 #define	AMD5536_MFGPT_REV_EN	0x0020
64 #define	AMD5536_MFGPT_CMP1DIS	0x0000
65 #define	AMD5536_MFGPT_CMP1EQ	0x0040
66 #define	AMD5536_MFGPT_CMP1GE	0x0080
67 #define	AMD5536_MFGPT_CMP1EV	0x00c0
68 #define	AMD5536_MFGPT_CMP2DIS	0x0000
69 #define	AMD5536_MFGPT_CMP2EQ	0x0100
70 #define	AMD5536_MFGPT_CMP2GE	0x0200
71 #define	AMD5536_MFGPT_CMP2EV	0x0300
72 #define	AMD5536_MFGPT_STOP_EN	0x0800
73 #define	AMD5536_MFGPT_SET	0x1000
74 #define	AMD5536_MFGPT_CMP1	0x2000
75 #define	AMD5536_MFGPT_CMP2	0x4000
76 #define	AMD5536_MFGPT_CNT_EN	0x8000
77 #define	AMD5536_MFGPT_IRQ	MFGPT_IRQ
78 #define	AMD5536_MFGPT0_C1_IRQM	0x00000001
79 #define	AMD5536_MFGPT1_C1_IRQM	0x00000002
80 #define	AMD5536_MFGPT2_C1_IRQM	0x00000004
81 #define	AMD5536_MFGPT3_C1_IRQM	0x00000008
82 #define	AMD5536_MFGPT4_C1_IRQM	0x00000010
83 #define	AMD5536_MFGPT5_C1_IRQM	0x00000020
84 #define	AMD5536_MFGPT6_C1_IRQM	0x00000040
85 #define	AMD5536_MFGPT7_C1_IRQM	0x00000080
86 #define	AMD5536_MFGPT0_C2_IRQM	0x00000100
87 #define	AMD5536_MFGPT1_C2_IRQM	0x00000200
88 #define	AMD5536_MFGPT2_C2_IRQM	0x00000400
89 #define	AMD5536_MFGPT3_C2_IRQM	0x00000800
90 #define	AMD5536_MFGPT4_C2_IRQM	0x00001000
91 #define	AMD5536_MFGPT5_C2_IRQM	0x00002000
92 #define	AMD5536_MFGPT6_C2_IRQM	0x00004000
93 #define	AMD5536_MFGPT7_C2_IRQM	0x00008000
94 #define	AMD5536_MFGPT_NR	MFGPT_NR
95 #define	AMD5536_MFGPT0_C1_NMIM	0x00000001
96 #define	AMD5536_MFGPT1_C1_NMIM	0x00000002
97 #define	AMD5536_MFGPT2_C1_NMIM	0x00000004
98 #define	AMD5536_MFGPT3_C1_NMIM	0x00000008
99 #define	AMD5536_MFGPT4_C1_NMIM	0x00000010
100 #define	AMD5536_MFGPT5_C1_NMIM	0x00000020
101 #define	AMD5536_MFGPT6_C1_NMIM	0x00000040
102 #define	AMD5536_MFGPT7_C1_NMIM	0x00000080
103 #define	AMD5536_MFGPT0_C2_NMIM	0x00000100
104 #define	AMD5536_MFGPT1_C2_NMIM	0x00000200
105 #define	AMD5536_MFGPT2_C2_NMIM	0x00000400
106 #define	AMD5536_MFGPT3_C2_NMIM	0x00000800
107 #define	AMD5536_MFGPT4_C2_NMIM	0x00001000
108 #define	AMD5536_MFGPT5_C2_NMIM	0x00002000
109 #define	AMD5536_MFGPT6_C2_NMIM	0x00004000
110 #define	AMD5536_MFGPT7_C2_NMIM	0x00008000
111 #define	AMD5536_NMI_LEG		0x00010000
112 #define	AMD5536_MFGPT0_C2_RSTEN	0x01000000
113 #define	AMD5536_MFGPT1_C2_RSTEN	0x02000000
114 #define	AMD5536_MFGPT2_C2_RSTEN	0x04000000
115 #define	AMD5536_MFGPT3_C2_RSTEN	0x08000000
116 #define	AMD5536_MFGPT4_C2_RSTEN	0x10000000
117 #define	AMD5536_MFGPT5_C2_RSTEN	0x20000000
118 #define	AMD5536_MFGPT_SETUP	MFGPT_SETUP
119 
120 /* GPIO */
121 #define	MSR_LBAR_GPIO		DIVIL_LBAR_GPIO
122 #define	MSR_GPIO_SIZE		0x100
123 #define	MSR_GPIO_ADDR_MASK	0xff00
124 #define	AMD5536_GPIO_NPINS	32
125 #define	AMD5536_GPIOH_OFFSET	0x80	/* high bank register offset */
126 #define	AMD5536_GPIO_OUT_VAL	0x00	/* output value */
127 #define	AMD5536_GPIO_OUT_EN	0x04	/* output enable */
128 #define	AMD5536_GPIO_OD_EN	0x08	/* open-drain enable */
129 #define AMD5536_GPIO_OUT_INVRT_EN 0x0c	/* invert output */
130 #define	AMD5536_GPIO_PU_EN	0x18	/* pull-up enable */
131 #define	AMD5536_GPIO_PD_EN	0x1c	/* pull-down enable */
132 #define	AMD5536_GPIO_IN_EN	0x20	/* input enable */
133 #define AMD5536_GPIO_IN_INVRT_EN 0x24	/* invert input */
134 #define	AMD5536_GPIO_READ_BACK	0x30	/* read back value */
135 
136 /*
137  * MSR registers we want to preserve accross suspend/resume
138  */
139 const uint32_t glxpcib_msrlist[] = {
140 	GLIU_PAE,
141 	GLCP_GLD_MSR_PM,
142 	DIVIL_BALL_OPTS
143 };
144 
145 struct glxpcib_softc {
146 	struct device		sc_dev;
147 
148 	struct timecounter	sc_timecounter;
149 	bus_space_tag_t		sc_iot;
150 	bus_space_handle_t	sc_ioh;
151 
152 	uint64_t 		sc_msrsave[nitems(glxpcib_msrlist)];
153 
154 #if !defined(SMALL_KERNEL) && NGPIO > 0
155 	/* GPIO interface */
156 	bus_space_tag_t		sc_gpio_iot;
157 	bus_space_handle_t	sc_gpio_ioh;
158 	struct gpio_chipset_tag	sc_gpio_gc;
159 	gpio_pin_t		sc_gpio_pins[AMD5536_GPIO_NPINS];
160 	int			sc_wdog;
161 	int			sc_wdog_period;
162 #endif
163 };
164 
165 struct cfdriver glxpcib_cd = {
166 	NULL, "glxpcib", DV_DULL
167 };
168 
169 int	glxpcib_match(struct device *, void *, void *);
170 void	glxpcib_attach(struct device *, struct device *, void *);
171 int	glxpcib_activate(struct device *, int);
172 
173 struct cfattach glxpcib_ca = {
174 	sizeof(struct glxpcib_softc), glxpcib_match, glxpcib_attach,
175 	NULL, glxpcib_activate
176 };
177 
178 /* from arch/<*>/pci/pcib.c */
179 void	pcibattach(struct device *parent, struct device *self, void *aux);
180 
181 u_int	glxpcib_get_timecount(struct timecounter *tc);
182 
183 #if !defined(SMALL_KERNEL) && NGPIO > 0
184 void	glxpcib_gpio_pin_ctl(void *, int, int);
185 int	glxpcib_gpio_pin_read(void *, int);
186 void	glxpcib_gpio_pin_write(void *, int, int);
187 int     glxpcib_wdogctl_cb(void *, int);
188 #endif
189 
190 const struct pci_matchid glxpcib_devices[] = {
191 	{ PCI_VENDOR_AMD,	PCI_PRODUCT_AMD_CS5536_PCIB }
192 };
193 
194 int
195 glxpcib_match(struct device *parent, void *match, void *aux)
196 {
197 	if (pci_matchbyid((struct pci_attach_args *)aux, glxpcib_devices,
198 	    nitems(glxpcib_devices))) {
199 		/* needs to win over pcib */
200 		return 2;
201 	}
202 
203 	return 0;
204 }
205 
206 void
207 glxpcib_attach(struct device *parent, struct device *self, void *aux)
208 {
209 	struct glxpcib_softc *sc = (struct glxpcib_softc *)self;
210 	struct timecounter *tc = &sc->sc_timecounter;
211 #if !defined(SMALL_KERNEL) && NGPIO > 0
212 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
213 	u_int64_t wa, ga;
214 	struct gpiobus_attach_args gba;
215 	int i, gpio = 0;
216 #endif
217 	tc->tc_get_timecount = glxpcib_get_timecount;
218 	tc->tc_counter_mask = 0xffffffff;
219 	tc->tc_frequency = 3579545;
220 	tc->tc_name = "CS5536";
221 #ifdef __loongson__
222 	tc->tc_quality = 0;
223 #else
224 	tc->tc_quality = 1000;
225 #endif
226 	tc->tc_priv = sc;
227 	tc_init(tc);
228 
229 	printf(": rev %d, 32-bit %lluHz timer",
230 	    (int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK,
231 	    tc->tc_frequency);
232 
233 #if !defined(SMALL_KERNEL) && NGPIO > 0
234 	/* Attach the watchdog timer */
235 	sc->sc_iot = pa->pa_iot;
236 	wa = rdmsr(MSR_LBAR_MFGPT);
237 	if (wa & MSR_LBAR_ENABLE &&
238 	    !bus_space_map(sc->sc_iot, wa & MSR_MFGPT_ADDR_MASK,
239 	    MSR_MFGPT_SIZE, 0, &sc->sc_ioh)) {
240 		/* count in seconds (as upper level desires) */
241 		bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_SETUP,
242 		    AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2EV |
243 		    AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_DIV_MASK);
244 		wdog_register(sc, glxpcib_wdogctl_cb);
245 		sc->sc_wdog = 1;
246 		printf(", watchdog");
247 	}
248 
249 	/* map GPIO I/O space */
250 	sc->sc_gpio_iot = pa->pa_iot;
251 	ga = rdmsr(MSR_LBAR_GPIO);
252 	if (ga & MSR_LBAR_ENABLE &&
253 	    !bus_space_map(sc->sc_gpio_iot, ga & MSR_GPIO_ADDR_MASK,
254 	    MSR_GPIO_SIZE, 0, &sc->sc_gpio_ioh)) {
255 		printf(", gpio");
256 
257 		/* initialize pin array */
258 		for (i = 0; i < AMD5536_GPIO_NPINS; i++) {
259 			sc->sc_gpio_pins[i].pin_num = i;
260 			sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
261 			    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
262 			    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN |
263 			    GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
264 
265 			/* read initial state */
266 			sc->sc_gpio_pins[i].pin_state =
267 			    glxpcib_gpio_pin_read(sc, i);
268 		}
269 
270 		/* create controller tag */
271 		sc->sc_gpio_gc.gp_cookie = sc;
272 		sc->sc_gpio_gc.gp_pin_read = glxpcib_gpio_pin_read;
273 		sc->sc_gpio_gc.gp_pin_write = glxpcib_gpio_pin_write;
274 		sc->sc_gpio_gc.gp_pin_ctl = glxpcib_gpio_pin_ctl;
275 
276 		gba.gba_name = "gpio";
277 		gba.gba_gc = &sc->sc_gpio_gc;
278 		gba.gba_pins = sc->sc_gpio_pins;
279 		gba.gba_npins = AMD5536_GPIO_NPINS;
280 		gpio = 1;
281 
282 	}
283 #endif
284 	pcibattach(parent, self, aux);
285 
286 #if !defined(SMALL_KERNEL) && NGPIO > 0
287 	if (gpio)
288 		config_found(&sc->sc_dev, &gba, gpiobus_print);
289 #endif
290 }
291 
292 int
293 glxpcib_activate(struct device *self, int act)
294 {
295 #ifndef SMALL_KERNEL
296 	struct glxpcib_softc *sc = (struct glxpcib_softc *)self;
297 	uint i;
298 #endif
299 	int rv = 0;
300 
301 	switch (act) {
302 	case DVACT_QUIESCE:
303 		rv = config_activate_children(self, act);
304 		break;
305 	case DVACT_SUSPEND:
306 #ifndef SMALL_KERNEL
307 		if (sc->sc_wdog) {
308 			sc->sc_wdog_period = bus_space_read_2(sc->sc_iot,
309 			    sc->sc_ioh, AMD5536_MFGPT0_CMP2);
310 			glxpcib_wdogctl_cb(sc, 0);
311 		}
312 #endif
313 		rv = config_activate_children(self, act);
314 #ifndef SMALL_KERNEL
315 		for (i = 0; i < nitems(glxpcib_msrlist); i++)
316 			sc->sc_msrsave[i] = rdmsr(glxpcib_msrlist[i]);
317 #endif
318 
319 		break;
320 	case DVACT_RESUME:
321 #ifndef SMALL_KERNEL
322 		if (sc->sc_wdog)
323 			glxpcib_wdogctl_cb(sc, sc->sc_wdog_period);
324 		for (i = 0; i < nitems(glxpcib_msrlist); i++)
325 			wrmsr(glxpcib_msrlist[i], sc->sc_msrsave[i]);
326 #endif
327 		rv = config_activate_children(self, act);
328 		break;
329 	}
330 	return (rv);
331 }
332 
333 u_int
334 glxpcib_get_timecount(struct timecounter *tc)
335 {
336         return rdmsr(AMD5536_TMC);
337 }
338 
339 #if !defined(SMALL_KERNEL) && NGPIO > 0
340 int
341 glxpcib_wdogctl_cb(void *v, int period)
342 {
343 	struct glxpcib_softc *sc = v;
344 
345 	if (period > 0xffff)
346 		period = 0xffff;
347 
348 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_SETUP,
349 	    AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
350 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_CNT, 0);
351 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, AMD5536_MFGPT0_CMP2, period);
352 
353 	if (period)
354 		wrmsr(AMD5536_MFGPT_NR,
355 		    rdmsr(AMD5536_MFGPT_NR) | AMD5536_MFGPT0_C2_RSTEN);
356 	else
357 		wrmsr(AMD5536_MFGPT_NR,
358 		    rdmsr(AMD5536_MFGPT_NR) & ~AMD5536_MFGPT0_C2_RSTEN);
359 
360 	return period;
361 }
362 
363 int
364 glxpcib_gpio_pin_read(void *arg, int pin)
365 {
366 	struct glxpcib_softc *sc = arg;
367 	u_int32_t data;
368 	int reg, off = 0;
369 
370 	reg = AMD5536_GPIO_IN_EN;
371 	if (pin > 15) {
372 		pin &= 0x0f;
373 		off = AMD5536_GPIOH_OFFSET;
374 	}
375 	reg += off;
376 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
377 
378 	if (data & (1 << pin))
379 		reg = AMD5536_GPIO_READ_BACK + off;
380 	else
381 		reg = AMD5536_GPIO_OUT_VAL + off;
382 
383 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
384 
385 	return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
386 }
387 
388 void
389 glxpcib_gpio_pin_write(void *arg, int pin, int value)
390 {
391 	struct glxpcib_softc *sc = arg;
392 	u_int32_t data;
393 	int reg;
394 
395 	reg = AMD5536_GPIO_OUT_VAL;
396 	if (pin > 15) {
397 		pin &= 0x0f;
398 		reg += AMD5536_GPIOH_OFFSET;
399 	}
400 	if (value == 1)
401 		data = 1 << pin;
402 	else
403 		data = 1 << (pin + 16);
404 
405 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
406 }
407 
408 void
409 glxpcib_gpio_pin_ctl(void *arg, int pin, int flags)
410 {
411 	struct glxpcib_softc *sc = arg;
412 	int n, reg[7], val[7], nreg = 0, off = 0;
413 
414 	if (pin > 15) {
415 		pin &= 0x0f;
416 		off = AMD5536_GPIOH_OFFSET;
417 	}
418 
419 	reg[nreg] = AMD5536_GPIO_IN_EN + off;
420 	if (flags & GPIO_PIN_INPUT)
421 		val[nreg++] = 1 << pin;
422 	else
423 		val[nreg++] = 1 << (pin + 16);
424 
425 	reg[nreg] = AMD5536_GPIO_OUT_EN + off;
426 	if (flags & GPIO_PIN_OUTPUT)
427 		val[nreg++] = 1 << pin;
428 	else
429 		val[nreg++] = 1 << (pin + 16);
430 
431 	reg[nreg] = AMD5536_GPIO_OD_EN + off;
432 	if (flags & GPIO_PIN_OPENDRAIN)
433 		val[nreg++] = 1 << pin;
434 	else
435 		val[nreg++] = 1 << (pin + 16);
436 
437 	reg[nreg] = AMD5536_GPIO_PU_EN + off;
438 	if (flags & GPIO_PIN_PULLUP)
439 		val[nreg++] = 1 << pin;
440 	else
441 		val[nreg++] = 1 << (pin + 16);
442 
443 	reg[nreg] = AMD5536_GPIO_PD_EN + off;
444 	if (flags & GPIO_PIN_PULLDOWN)
445 		val[nreg++] = 1 << pin;
446 	else
447 		val[nreg++] = 1 << (pin + 16);
448 
449 	reg[nreg] = AMD5536_GPIO_IN_INVRT_EN + off;
450 	if (flags & GPIO_PIN_INVIN)
451 		val[nreg++] = 1 << pin;
452 	else
453 		val[nreg++] = 1 << (pin + 16);
454 
455 	reg[nreg] = AMD5536_GPIO_OUT_INVRT_EN + off;
456 	if (flags & GPIO_PIN_INVOUT)
457 		val[nreg++] = 1 << pin;
458 	else
459 		val[nreg++] = 1 << (pin + 16);
460 
461 	/* set flags */
462 	for (n = 0; n < nreg; n++)
463 		bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg[n],
464 		    val[n]);
465 }
466 
467 #endif
468