xref: /netbsd-src/sys/arch/x86/pci/tcpcib.c (revision a536ee5124e62c9a0051a252f7833dc8f50f44c9)
1 /*	$NetBSD: tcpcib.c,v 1.1 2012/12/05 16:19:46 christos Exp $	*/
2 /*	$OpenBSD: tcpcib.c,v 1.4 2012/10/17 22:32:01 deraadt Exp $	*/
3 
4 /*
5  * Copyright (c) 2012 Matt Dainty <matt@bodgit-n-scarper.com>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
16  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * Intel Atom E600 series LPC bridge also containing HPET and watchdog
22  */
23 
24 #include <sys/cdefs.h>
25 __KERNEL_RCSID(0, "$NetBSD: tcpcib.c,v 1.1 2012/12/05 16:19:46 christos Exp $");
26 
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/device.h>
30 #include <sys/timetc.h>
31 #include <sys/bus.h>
32 
33 #include <dev/pci/pcireg.h>
34 #include <dev/pci/pcivar.h>
35 #include <dev/pci/pcidevs.h>
36 
37 #include <dev/ic/i82801lpcvar.h>
38 
39 #include <dev/sysmon/sysmonvar.h>
40 
41 #include "pcibvar.h"
42 
43 #define	E600_LPC_SMBA		0x40		/* SMBus Base Address */
44 #define	E600_LPC_GBA		0x44		/* GPIO Base Address */
45 #define	E600_LPC_WDTBA		0x84		/* WDT Base Address */
46 
47 #define	E600_WDT_SIZE		64		/* I/O region size */
48 #define	E600_WDT_PV1		0x00		/* Preload Value 1 Register */
49 #define	E600_WDT_PV2		0x04		/* Preload Value 2 Register */
50 #define	E600_WDT_RR0		0x0c		/* Reload Register 0 */
51 #define	E600_WDT_RR1		0x0d		/* Reload Register 1 */
52 #define	E600_WDT_RR1_RELOAD	(1 << 0)	/* WDT Reload Flag */
53 #define	E600_WDT_RR1_TIMEOUT	(1 << 1)	/* WDT Timeout Flag */
54 #define	E600_WDT_WDTCR		0x10		/* WDT Configuration Register */
55 #define	E600_WDT_WDTCR_PRE	(1 << 2)	/* WDT Prescalar Select */
56 #define	E600_WDT_WDTCR_RESET	(1 << 3)	/* WDT Reset Select */
57 #define	E600_WDT_WDTCR_ENABLE	(1 << 4)	/* WDT Reset Enable */
58 #define	E600_WDT_WDTCR_TIMEOUT	(1 << 5)	/* WDT Timeout Output Enable */
59 #define	E600_WDT_DCR		0x14		/* Down Counter Register */
60 #define	E600_WDT_WDTLR		0x18		/* WDT Lock Register */
61 #define	E600_WDT_WDTLR_LOCK	(1 << 0)	/* Watchdog Timer Lock */
62 #define	E600_WDT_WDTLR_ENABLE	(1 << 1)	/* Watchdog Timer Enable */
63 #define	E600_WDT_WDTLR_TIMEOUT	(1 << 2)	/* WDT Timeout Configuration */
64 
65 #define	E600_HPET_BASE		0xfed00000	/* HPET register base */
66 #define	E600_HPET_SIZE		0x00000400	/* HPET register size */
67 
68 #define	E600_HPET_GCID		0x000		/* Capabilities and ID */
69 #define	E600_HPET_GCID_WIDTH	(1 << 13)	/* Counter Size */
70 #define	E600_HPET_PERIOD	0x004		/* Counter Tick Period */
71 #define	E600_HPET_GC		0x010		/* General Configuration */
72 #define	E600_HPET_GC_ENABLE	(1 << 0)	/* Overall Enable */
73 #define	E600_HPET_GIS		0x020		/* General Interrupt Status */
74 #define	E600_HPET_MCV		0x0f0		/* Main Counter Value */
75 #define	E600_HPET_T0C		0x100		/* Timer 0 Config and Capabilities */
76 #define	E600_HPET_T0CV		0x108		/* Timer 0 Comparator Value */
77 #define	E600_HPET_T1C		0x120		/* Timer 1 Config and Capabilities */
78 #define	E600_HPET_T1CV		0x128		/* Timer 1 Comparator Value */
79 #define	E600_HPET_T2C		0x140		/* Timer 2 Config and Capabilities */
80 #define	E600_HPET_T2CV		0x148		/* Timer 2 Comparator Value */
81 
82 struct tcpcib_softc {
83 	/* we call pcibattach() which assumes this starts like this: */
84 	struct pcib_softc	sc_pcib;
85 
86 	/* Watchdog interface */
87 	bool sc_wdt_valid;
88 	struct sysmon_wdog sc_wdt_smw;
89 	bus_space_tag_t sc_wdt_iot;
90 	bus_space_handle_t sc_wdt_ioh;
91 
92 	/* High Precision Event Timer */
93 	device_t sc_hpetbus;
94 	bus_space_tag_t sc_hpet_memt;
95 };
96 
97 static int	tcpcib_match(device_t, cfdata_t, void *);
98 static void	tcpcib_attach(device_t, device_t, void *);
99 static int	tcpcib_detach(device_t, int);
100 static int	tcpcib_rescan(device_t, const char *, const int *);
101 static void	tcpcib_childdet(device_t, device_t);
102 static bool	tcpcib_suspend(device_t, const pmf_qual_t *);
103 static bool	tcpcib_resume(device_t, const pmf_qual_t *);
104 
105 static int	tcpcib_wdt_setmode(struct sysmon_wdog *);
106 static int	tcpcib_wdt_tickle(struct sysmon_wdog *);
107 static void	tcpcib_wdt_init(struct tcpcib_softc *, int);
108 static void	tcpcib_wdt_start(struct tcpcib_softc *);
109 static void	tcpcib_wdt_stop(struct tcpcib_softc *);
110 
111 CFATTACH_DECL2_NEW(tcpcib, sizeof(struct tcpcib_softc),
112     tcpcib_match, tcpcib_attach, tcpcib_detach, NULL,
113     tcpcib_rescan, tcpcib_childdet);
114 
115 static struct tcpcib_device {
116 	pcireg_t vendor, product;
117 } tcpcib_devices[] = {
118 	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_E600_LPC }
119 };
120 
121 static void
122 tcpcib_wdt_unlock(struct tcpcib_softc *sc)
123 {
124 	/* Register unlocking sequence */
125 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x80);
126 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR0, 0x86);
127 }
128 
129 static void
130 tcpcib_wdt_init(struct tcpcib_softc *sc, int period)
131 {
132 	uint32_t preload;
133 
134 	/* Set new timeout */
135 	preload = (period * 33000000) >> 15;
136 	preload--;
137 
138 	/*
139 	 * Set watchdog to perform a cold reset toggling the GPIO pin and the
140 	 * prescaler set to 1ms-10m resolution
141 	 */
142 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTCR,
143 	    E600_WDT_WDTCR_ENABLE);
144 	tcpcib_wdt_unlock(sc);
145 	bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV1, 0);
146 	tcpcib_wdt_unlock(sc);
147 	bus_space_write_4(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_PV2,
148 	    preload);
149 	tcpcib_wdt_unlock(sc);
150 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1,
151 	    E600_WDT_RR1_RELOAD);
152 }
153 
154 static void
155 tcpcib_wdt_start(struct tcpcib_softc *sc)
156 {
157 	/* Enable watchdog */
158 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR,
159 	    E600_WDT_WDTLR_ENABLE);
160 }
161 
162 static void
163 tcpcib_wdt_stop(struct tcpcib_softc *sc)
164 {
165 	/* Disable watchdog, with a reload before for safety */
166 	tcpcib_wdt_unlock(sc);
167 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_RR1,
168 	    E600_WDT_RR1_RELOAD);
169 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh, E600_WDT_WDTLR, 0);
170 }
171 
172 static int
173 tcpcib_match(device_t parent, cfdata_t match, void *aux)
174 {
175 	struct pci_attach_args *pa = aux;
176 	unsigned int n;
177 
178 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
179 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA)
180 		return 0;
181 
182 	for (n = 0; n < __arraycount(tcpcib_devices); n++) {
183 		if (PCI_VENDOR(pa->pa_id) == tcpcib_devices[n].vendor &&
184 		    PCI_PRODUCT(pa->pa_id) == tcpcib_devices[n].product)
185 			return 10;	/* beat pcib(4) */
186 	}
187 
188 	return 0;
189 }
190 
191 static void
192 tcpcib_attach(device_t parent, device_t self, void *aux)
193 {
194 	struct tcpcib_softc *sc = device_private(self);
195 	struct pci_attach_args *pa = aux;
196 	uint32_t reg, wdtbase;
197 
198 	pmf_device_register(self, tcpcib_suspend, tcpcib_resume);
199 
200 	/* Provide core pcib(4) functionality */
201 	pcibattach(parent, self, aux);
202 
203 	/* High Precision Event Timer */
204 	sc->sc_hpet_memt = pa->pa_memt;
205 	tcpcib_rescan(self, "hpetichbus", NULL);
206 
207 	/* Map Watchdog I/O space */
208 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA);
209 	wdtbase = reg & 0xffff;
210 	sc->sc_wdt_iot = pa->pa_iot;
211 	if (reg & (1 << 31) && wdtbase) {
212 		if (PCI_MAPREG_IO_ADDR(wdtbase) == 0 ||
213 		    bus_space_map(sc->sc_wdt_iot, PCI_MAPREG_IO_ADDR(wdtbase),
214 		    E600_WDT_SIZE, 0, &sc->sc_wdt_ioh)) {
215 			aprint_error_dev(self, "can't map watchdog I/O space\n");
216 			return;
217 		}
218 		aprint_normal_dev(self, "watchdog");
219 
220 		/* Check for reboot on timeout */
221 		reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
222 		    E600_WDT_RR1);
223 		if (reg & E600_WDT_RR1_TIMEOUT) {
224 			aprint_normal(", reboot on timeout");
225 
226 			/* Clear timeout bit */
227 			tcpcib_wdt_unlock(sc);
228 			bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
229 			    E600_WDT_RR1, E600_WDT_RR1_TIMEOUT);
230 		}
231 
232 		/* Check it's not locked already */
233 		reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
234 		    E600_WDT_WDTLR);
235 		if (reg & E600_WDT_WDTLR_LOCK) {
236 			aprint_error(", locked\n");
237 			return;
238 		}
239 
240 		/* Disable watchdog */
241 		tcpcib_wdt_stop(sc);
242 
243 		/* Register new watchdog */
244 		sc->sc_wdt_smw.smw_name = device_xname(self);
245 		sc->sc_wdt_smw.smw_cookie = sc;
246 		sc->sc_wdt_smw.smw_setmode = tcpcib_wdt_setmode;
247 		sc->sc_wdt_smw.smw_tickle = tcpcib_wdt_tickle;
248 		sc->sc_wdt_smw.smw_period = 600; /* seconds */
249 		if (sysmon_wdog_register(&sc->sc_wdt_smw)) {
250 			aprint_error(", unable to register wdog timer\n");
251 			return;
252 		}
253 
254 		sc->sc_wdt_valid = true;
255 		aprint_normal("\n");
256 	}
257 
258 }
259 
260 static int
261 tcpcib_detach(device_t self, int flags)
262 {
263 	return pcibdetach(self, flags);
264 }
265 
266 static int
267 tcpcib_rescan(device_t self, const char *ifattr, const int *locators)
268 {
269 	struct tcpcib_softc *sc = device_private(self);
270 
271 	if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL) {
272 		struct lpcib_hpet_attach_args hpet_arg;
273 		hpet_arg.hpet_mem_t = sc->sc_hpet_memt;
274 		hpet_arg.hpet_reg = E600_HPET_BASE;
275 		sc->sc_hpetbus = config_found_ia(self, "hpetichbus",
276 		    &hpet_arg, NULL);
277 	}
278 
279 	return pcibrescan(self, ifattr, locators);
280 }
281 
282 static void
283 tcpcib_childdet(device_t self, device_t child)
284 {
285 	struct tcpcib_softc *sc = device_private(self);
286 
287 	if (sc->sc_hpetbus == child) {
288 		sc->sc_hpetbus = NULL;
289 		return;
290 	}
291 
292 	pcibchilddet(self, child);
293 }
294 
295 static bool
296 tcpcib_suspend(device_t self, const pmf_qual_t *qual)
297 {
298 	struct tcpcib_softc *sc = device_private(self);
299 
300 	if (sc->sc_wdt_valid)
301 		tcpcib_wdt_stop(sc);
302 
303 	return true;
304 }
305 
306 static bool
307 tcpcib_resume(device_t self, const pmf_qual_t *qual)
308 {
309 	struct tcpcib_softc *sc = device_private(self);
310 	struct sysmon_wdog *smw = &sc->sc_wdt_smw;
311 
312 	if (sc->sc_wdt_valid) {
313 		if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED &&
314 		    smw->smw_period > 0) {
315 			tcpcib_wdt_init(sc, smw->smw_period);
316 			tcpcib_wdt_start(sc);
317 		} else {
318 			tcpcib_wdt_stop(sc);
319 		}
320 	}
321 
322 	return true;
323 }
324 
325 static int
326 tcpcib_wdt_setmode(struct sysmon_wdog *smw)
327 {
328 	struct tcpcib_softc *sc = smw->smw_cookie;
329 	unsigned int period;
330 
331 	period = smw->smw_period;
332 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
333 		tcpcib_wdt_stop(sc);
334 	} else {
335 		/* 600 seconds is the maximum supported timeout value */
336 		if (period > 600)
337 			return EINVAL;
338 
339 		tcpcib_wdt_stop(sc);
340 		tcpcib_wdt_init(sc, period);
341 		tcpcib_wdt_start(sc);
342 		tcpcib_wdt_tickle(smw);
343 	}
344 
345 	return 0;
346 }
347 
348 static int
349 tcpcib_wdt_tickle(struct sysmon_wdog *smw)
350 {
351 	struct tcpcib_softc *sc = smw->smw_cookie;
352 
353 	/* Reset timer */
354 	tcpcib_wdt_unlock(sc);
355 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
356 	    E600_WDT_RR1, E600_WDT_RR1_RELOAD);
357 
358 	return 0;
359 }
360