xref: /netbsd-src/sys/arch/x86/pci/tcpcib.c (revision 76c7fc5f6b13ed0b1508e6b313e88e59977ed78e)
1 /*	$NetBSD: tcpcib.c,v 1.2 2016/07/11 11:31:50 msaitoh 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.2 2016/07/11 11:31:50 msaitoh 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,
216 			    "can't map watchdog I/O space\n");
217 			return;
218 		}
219 		aprint_normal_dev(self, "watchdog");
220 
221 		/* Check for reboot on timeout */
222 		reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
223 		    E600_WDT_RR1);
224 		if (reg & E600_WDT_RR1_TIMEOUT) {
225 			aprint_normal(", reboot on timeout");
226 
227 			/* Clear timeout bit */
228 			tcpcib_wdt_unlock(sc);
229 			bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
230 			    E600_WDT_RR1, E600_WDT_RR1_TIMEOUT);
231 		}
232 
233 		/* Check it's not locked already */
234 		reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
235 		    E600_WDT_WDTLR);
236 		if (reg & E600_WDT_WDTLR_LOCK) {
237 			aprint_error(", locked\n");
238 			return;
239 		}
240 
241 		/* Disable watchdog */
242 		tcpcib_wdt_stop(sc);
243 
244 		/* Register new watchdog */
245 		sc->sc_wdt_smw.smw_name = device_xname(self);
246 		sc->sc_wdt_smw.smw_cookie = sc;
247 		sc->sc_wdt_smw.smw_setmode = tcpcib_wdt_setmode;
248 		sc->sc_wdt_smw.smw_tickle = tcpcib_wdt_tickle;
249 		sc->sc_wdt_smw.smw_period = 600; /* seconds */
250 		if (sysmon_wdog_register(&sc->sc_wdt_smw)) {
251 			aprint_error(", unable to register wdog timer\n");
252 			return;
253 		}
254 
255 		sc->sc_wdt_valid = true;
256 		aprint_normal("\n");
257 	}
258 
259 }
260 
261 static int
262 tcpcib_detach(device_t self, int flags)
263 {
264 	return pcibdetach(self, flags);
265 }
266 
267 static int
268 tcpcib_rescan(device_t self, const char *ifattr, const int *locators)
269 {
270 	struct tcpcib_softc *sc = device_private(self);
271 
272 	if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL) {
273 		struct lpcib_hpet_attach_args hpet_arg;
274 		hpet_arg.hpet_mem_t = sc->sc_hpet_memt;
275 		hpet_arg.hpet_reg = E600_HPET_BASE;
276 		sc->sc_hpetbus = config_found_ia(self, "hpetichbus",
277 		    &hpet_arg, NULL);
278 	}
279 
280 	return pcibrescan(self, ifattr, locators);
281 }
282 
283 static void
284 tcpcib_childdet(device_t self, device_t child)
285 {
286 	struct tcpcib_softc *sc = device_private(self);
287 
288 	if (sc->sc_hpetbus == child) {
289 		sc->sc_hpetbus = NULL;
290 		return;
291 	}
292 
293 	pcibchilddet(self, child);
294 }
295 
296 static bool
297 tcpcib_suspend(device_t self, const pmf_qual_t *qual)
298 {
299 	struct tcpcib_softc *sc = device_private(self);
300 
301 	if (sc->sc_wdt_valid)
302 		tcpcib_wdt_stop(sc);
303 
304 	return true;
305 }
306 
307 static bool
308 tcpcib_resume(device_t self, const pmf_qual_t *qual)
309 {
310 	struct tcpcib_softc *sc = device_private(self);
311 	struct sysmon_wdog *smw = &sc->sc_wdt_smw;
312 
313 	if (sc->sc_wdt_valid) {
314 		if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED &&
315 		    smw->smw_period > 0) {
316 			tcpcib_wdt_init(sc, smw->smw_period);
317 			tcpcib_wdt_start(sc);
318 		} else {
319 			tcpcib_wdt_stop(sc);
320 		}
321 	}
322 
323 	return true;
324 }
325 
326 static int
327 tcpcib_wdt_setmode(struct sysmon_wdog *smw)
328 {
329 	struct tcpcib_softc *sc = smw->smw_cookie;
330 	unsigned int period;
331 
332 	period = smw->smw_period;
333 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
334 		tcpcib_wdt_stop(sc);
335 	} else {
336 		/* 600 seconds is the maximum supported timeout value */
337 		if (period > 600)
338 			return EINVAL;
339 
340 		tcpcib_wdt_stop(sc);
341 		tcpcib_wdt_init(sc, period);
342 		tcpcib_wdt_start(sc);
343 		tcpcib_wdt_tickle(smw);
344 	}
345 
346 	return 0;
347 }
348 
349 static int
350 tcpcib_wdt_tickle(struct sysmon_wdog *smw)
351 {
352 	struct tcpcib_softc *sc = smw->smw_cookie;
353 
354 	/* Reset timer */
355 	tcpcib_wdt_unlock(sc);
356 	bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
357 	    E600_WDT_RR1, E600_WDT_RR1_RELOAD);
358 
359 	return 0;
360 }
361