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