xref: /openbsd/sys/dev/pci/tcpcib.c (revision d415bd75)
1 /*	$OpenBSD: tcpcib.c,v 1.9 2022/03/11 18:00:52 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Matt Dainty <matt@bodgit-n-scarper.com>
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 MIND, USE, DATA OR PROFITS, WHETHER IN
15  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Intel Atom E600 series LPC bridge also containing HPET and watchdog
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/timetc.h>
27 
28 #include <machine/bus.h>
29 
30 #include <dev/pci/pcireg.h>
31 #include <dev/pci/pcivar.h>
32 #include <dev/pci/pcidevs.h>
33 
34 #define	E600_LPC_SMBA		0x40		/* SMBus Base Address */
35 #define	E600_LPC_GBA		0x44		/* GPIO Base Address */
36 #define	E600_LPC_WDTBA		0x84		/* WDT Base Address */
37 
38 #define	E600_WDT_SIZE		64		/* I/O region size */
39 #define	E600_WDT_PV1		0x00		/* Preload Value 1 Register */
40 #define	E600_WDT_PV2		0x04		/* Preload Value 2 Register */
41 #define	E600_WDT_RR0		0x0c		/* Reload Register 0 */
42 #define	E600_WDT_RR1		0x0d		/* Reload Register 1 */
43 #define	E600_WDT_RR1_RELOAD	(1 << 0)	/* WDT Reload Flag */
44 #define	E600_WDT_RR1_TIMEOUT	(1 << 1)	/* WDT Timeout Flag */
45 #define	E600_WDT_WDTCR		0x10		/* WDT Configuration Register */
46 #define	E600_WDT_WDTCR_PRE	(1 << 2)	/* WDT Prescalar Select */
47 #define	E600_WDT_WDTCR_RESET	(1 << 3)	/* WDT Reset Select */
48 #define	E600_WDT_WDTCR_ENABLE	(1 << 4)	/* WDT Reset Enable */
49 #define	E600_WDT_WDTCR_TIMEOUT	(1 << 5)	/* WDT Timeout Output Enable */
50 #define	E600_WDT_DCR		0x14		/* Down Counter Register */
51 #define	E600_WDT_WDTLR		0x18		/* WDT Lock Register */
52 #define	E600_WDT_WDTLR_LOCK	(1 << 0)	/* Watchdog Timer Lock */
53 #define	E600_WDT_WDTLR_ENABLE	(1 << 1)	/* Watchdog Timer Enable */
54 #define	E600_WDT_WDTLR_TIMEOUT	(1 << 2)	/* WDT Timeout Configuration */
55 
56 #define	E600_HPET_BASE		0xfed00000	/* HPET register base */
57 #define	E600_HPET_SIZE		0x00000400	/* HPET register size */
58 
59 #define	E600_HPET_GCID		0x000		/* Capabilities and ID */
60 #define	E600_HPET_GCID_WIDTH	(1 << 13)	/* Counter Size */
61 #define	E600_HPET_PERIOD	0x004		/* Counter Tick Period */
62 #define	E600_HPET_GC		0x010		/* General Configuration */
63 #define	E600_HPET_GC_ENABLE	(1 << 0)	/* Overall Enable */
64 #define	E600_HPET_GIS		0x020		/* General Interrupt Status */
65 #define	E600_HPET_MCV		0x0f0		/* Main Counter Value */
66 #define	E600_HPET_T0C		0x100		/* Timer 0 Config and Capabilities */
67 #define	E600_HPET_T0CV		0x108		/* Timer 0 Comparator Value */
68 #define	E600_HPET_T1C		0x120		/* Timer 1 Config and Capabilities */
69 #define	E600_HPET_T1CV		0x128		/* Timer 1 Comparator Value */
70 #define	E600_HPET_T2C		0x140		/* Timer 2 Config and Capabilities */
71 #define	E600_HPET_T2CV		0x148		/* Timer 2 Comparator Value */
72 
73 struct tcpcib_softc {
74 	struct device sc_dev;
75 
76 	/* Keep track of which parts of the hardware are active */
77 	int sc_active;
78 #define	E600_WDT_ACTIVE		(1 << 0)
79 #define	E600_HPET_ACTIVE	(1 << 1)
80 
81 	/* Watchdog interface */
82 	bus_space_tag_t sc_wdt_iot;
83 	bus_space_handle_t sc_wdt_ioh;
84 
85 	int sc_wdt_period;
86 
87 	/* High Precision Event Timer */
88 	bus_space_tag_t sc_hpet_iot;
89 	bus_space_handle_t sc_hpet_ioh;
90 
91 	struct timecounter sc_hpet_timecounter;
92 };
93 
94 struct cfdriver tcpcib_cd = {
95 	NULL, "tcpcib", DV_DULL
96 };
97 
98 int	 tcpcib_match(struct device *, void *, void *);
99 void	 tcpcib_attach(struct device *, struct device *, void *);
100 int	 tcpcib_activate(struct device *, int);
101 
102 int	 tcpcib_wdt_cb(void *, int);
103 void	 tcpcib_wdt_init(struct tcpcib_softc *, int);
104 void	 tcpcib_wdt_start(struct tcpcib_softc *);
105 void	 tcpcib_wdt_stop(struct tcpcib_softc *);
106 
107 u_int	 tcpcib_hpet_get_timecount(struct timecounter *tc);
108 
109 const struct cfattach tcpcib_ca = {
110 	sizeof(struct tcpcib_softc), tcpcib_match, tcpcib_attach,
111 	NULL, tcpcib_activate
112 };
113 
114 /* from arch/<*>/pci/pcib.c */
115 void	pcibattach(struct device *parent, struct device *self, void *aux);
116 
117 const struct pci_matchid tcpcib_devices[] = {
118 	{ PCI_VENDOR_INTEL,	PCI_PRODUCT_INTEL_E600_LPC }
119 };
120 
121 static __inline 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 void
130 tcpcib_wdt_init(struct tcpcib_softc *sc, int period)
131 {
132 	u_int32_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 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 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 int
173 tcpcib_match(struct device *parent, void *match, void *aux)
174 {
175 	if (pci_matchbyid((struct pci_attach_args *)aux, tcpcib_devices,
176 	    sizeof(tcpcib_devices) / sizeof(tcpcib_devices[0])))
177 		return (2);
178 
179 	return (0);
180 }
181 
182 void
183 tcpcib_attach(struct device *parent, struct device *self, void *aux)
184 {
185 	struct tcpcib_softc *sc = (struct tcpcib_softc *)self;
186 	struct pci_attach_args *pa = aux;
187 	struct timecounter *tc = &sc->sc_hpet_timecounter;
188 	u_int32_t reg, wdtbase;
189 
190 	sc->sc_active = 0;
191 
192 	/* High Precision Event Timer */
193 	sc->sc_hpet_iot = pa->pa_memt;
194 	if (bus_space_map(sc->sc_hpet_iot, E600_HPET_BASE, E600_HPET_SIZE, 0,
195 	    &sc->sc_hpet_ioh) == 0) {
196 		tc->tc_get_timecount = tcpcib_hpet_get_timecount;
197 		/* XXX 64-bit counter is not supported! */
198 		tc->tc_counter_mask = 0xffffffff;
199 
200 		reg = bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
201 		    E600_HPET_PERIOD);
202 		/* femtosecs -> Hz */
203 		tc->tc_frequency = 1000000000000000ULL / reg;
204 
205 		tc->tc_name = sc->sc_dev.dv_xname;
206 		tc->tc_quality = 2000;
207 		tc->tc_priv = sc;
208 		tc_init(tc);
209 
210 		/* Enable counting */
211 		bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
212 		    E600_HPET_GC, E600_HPET_GC_ENABLE);
213 
214 		sc->sc_active |= E600_HPET_ACTIVE;
215 
216 		printf(": %llu Hz timer", tc->tc_frequency);
217 	}
218 
219 	/* Map Watchdog I/O space */
220 	reg = pci_conf_read(pa->pa_pc, pa->pa_tag, E600_LPC_WDTBA);
221 	wdtbase = reg & 0xffff;
222 	sc->sc_wdt_iot = pa->pa_iot;
223 	if (reg & (1U << 31) && wdtbase) {
224 		if (PCI_MAPREG_IO_ADDR(wdtbase) == 0 ||
225 		    bus_space_map(sc->sc_wdt_iot, PCI_MAPREG_IO_ADDR(wdtbase),
226 		    E600_WDT_SIZE, 0, &sc->sc_wdt_ioh)) {
227 			printf("%c can't map watchdog I/O space",
228 			    sc->sc_active ? ',' : ':');
229 			goto corepcib;
230 		}
231 		printf("%c watchdog", sc->sc_active ? ',' : ':');
232 
233 		/* Check for reboot on timeout */
234 		reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
235 		    E600_WDT_RR1);
236 		if (reg & E600_WDT_RR1_TIMEOUT) {
237 			printf(", reboot on timeout");
238 
239 			/* Clear timeout bit */
240 			tcpcib_wdt_unlock(sc);
241 			bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
242 			    E600_WDT_RR1, E600_WDT_RR1_TIMEOUT);
243 		}
244 
245 		/* Check it's not locked already */
246 		reg = bus_space_read_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
247 		    E600_WDT_WDTLR);
248 		if (reg & E600_WDT_WDTLR_LOCK) {
249 			printf(", locked");
250 			goto corepcib;
251 		}
252 
253 		/* Disable watchdog */
254 		tcpcib_wdt_stop(sc);
255 		sc->sc_wdt_period = 0;
256 
257 		sc->sc_active |= E600_WDT_ACTIVE;
258 
259 		/* Register new watchdog */
260 		wdog_register(tcpcib_wdt_cb, sc);
261 	}
262 
263 corepcib:
264 	/* Provide core pcib(4) functionality */
265 	pcibattach(parent, self, aux);
266 }
267 
268 int
269 tcpcib_activate(struct device *self, int act)
270 {
271 	struct tcpcib_softc *sc = (struct tcpcib_softc *)self;
272 	int rv = 0;
273 
274 	switch (act) {
275 	case DVACT_SUSPEND:
276 		rv = config_activate_children(self, act);
277 		/* Watchdog is running, disable it */
278 		if (sc->sc_active & E600_WDT_ACTIVE && sc->sc_wdt_period != 0)
279 			tcpcib_wdt_stop(sc);
280 		break;
281 	case DVACT_RESUME:
282 		if (sc->sc_active & E600_WDT_ACTIVE) {
283 			/*
284 			 * Watchdog was running prior to suspend so reenable
285 			 * it, otherwise make sure it stays disabled
286 			 */
287 			if (sc->sc_wdt_period != 0) {
288 				tcpcib_wdt_init(sc, sc->sc_wdt_period);
289 				tcpcib_wdt_start(sc);
290 			} else
291 				tcpcib_wdt_stop(sc);
292 		}
293 		if (sc->sc_active & E600_HPET_ACTIVE)
294 			bus_space_write_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
295 			    E600_HPET_GC, E600_HPET_GC_ENABLE);
296 		rv = config_activate_children(self, act);
297 		break;
298 	case DVACT_POWERDOWN:
299 		if (sc->sc_active & E600_WDT_ACTIVE)
300 			wdog_shutdown(self);
301 		rv = config_activate_children(self, act);
302 		break;
303 	default:
304 		rv = config_activate_children(self, act);
305 		break;
306 	}
307 	return (rv);
308 }
309 
310 int
311 tcpcib_wdt_cb(void *arg, int period)
312 {
313 	struct tcpcib_softc *sc = arg;
314 
315 	if (period == 0) {
316 		if (sc->sc_wdt_period != 0)
317 			tcpcib_wdt_stop(sc);
318 	} else {
319 		/* 600 seconds is the maximum supported timeout value */
320 		if (period > 600)
321 			period = 600;
322 		if (sc->sc_wdt_period != period)
323 			tcpcib_wdt_init(sc, period);
324 		if (sc->sc_wdt_period == 0) {
325 			tcpcib_wdt_start(sc);
326 		} else {
327 			/* Reset timer */
328 			tcpcib_wdt_unlock(sc);
329 			bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
330 			    E600_WDT_RR1, E600_WDT_RR1_RELOAD);
331 		}
332 	}
333 	sc->sc_wdt_period = period;
334 
335 	return (period);
336 }
337 
338 u_int
339 tcpcib_hpet_get_timecount(struct timecounter *tc)
340 {
341 	struct tcpcib_softc *sc = tc->tc_priv;
342 
343 	/* XXX 64-bit counter is not supported! */
344 	return bus_space_read_4(sc->sc_hpet_iot, sc->sc_hpet_ioh,
345 	    E600_HPET_MCV);
346 }
347