1 /*        $NetBSD: tcpcib.c,v 1.4 2021/08/07 16:19:08 thorpej 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.4 2021/08/07 16:19:08 thorpej 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
tcpcib_wdt_unlock(struct tcpcib_softc * sc)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
tcpcib_wdt_init(struct tcpcib_softc * sc,int period)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
tcpcib_wdt_start(struct tcpcib_softc * sc)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
tcpcib_wdt_stop(struct tcpcib_softc * sc)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
tcpcib_match(device_t parent,cfdata_t match,void * aux)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
tcpcib_attach(device_t parent,device_t self,void * aux)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
tcpcib_detach(device_t self,int flags)262 tcpcib_detach(device_t self, int flags)
263 {
264           return pcibdetach(self, flags);
265 }
266 
267 static int
tcpcib_rescan(device_t self,const char * ifattr,const int * locators)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 =
277                         config_found(self, &hpet_arg, NULL,
278                                          CFARGS(.iattr = "hpetichbus"));
279           }
280 
281           return pcibrescan(self, ifattr, locators);
282 }
283 
284 static void
tcpcib_childdet(device_t self,device_t child)285 tcpcib_childdet(device_t self, device_t child)
286 {
287           struct tcpcib_softc *sc = device_private(self);
288 
289           if (sc->sc_hpetbus == child) {
290                     sc->sc_hpetbus = NULL;
291                     return;
292           }
293 
294           pcibchilddet(self, child);
295 }
296 
297 static bool
tcpcib_suspend(device_t self,const pmf_qual_t * qual)298 tcpcib_suspend(device_t self, const pmf_qual_t *qual)
299 {
300           struct tcpcib_softc *sc = device_private(self);
301 
302           if (sc->sc_wdt_valid)
303                     tcpcib_wdt_stop(sc);
304 
305           return true;
306 }
307 
308 static bool
tcpcib_resume(device_t self,const pmf_qual_t * qual)309 tcpcib_resume(device_t self, const pmf_qual_t *qual)
310 {
311           struct tcpcib_softc *sc = device_private(self);
312           struct sysmon_wdog *smw = &sc->sc_wdt_smw;
313 
314           if (sc->sc_wdt_valid) {
315                     if ((smw->smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED &&
316                         smw->smw_period > 0) {
317                               tcpcib_wdt_init(sc, smw->smw_period);
318                               tcpcib_wdt_start(sc);
319                     } else {
320                               tcpcib_wdt_stop(sc);
321                     }
322           }
323 
324           return true;
325 }
326 
327 static int
tcpcib_wdt_setmode(struct sysmon_wdog * smw)328 tcpcib_wdt_setmode(struct sysmon_wdog *smw)
329 {
330           struct tcpcib_softc *sc = smw->smw_cookie;
331           unsigned int period;
332 
333           period = smw->smw_period;
334           if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
335                     tcpcib_wdt_stop(sc);
336           } else {
337                     /* 600 seconds is the maximum supported timeout value */
338                     if (period > 600)
339                               return EINVAL;
340 
341                     tcpcib_wdt_stop(sc);
342                     tcpcib_wdt_init(sc, period);
343                     tcpcib_wdt_start(sc);
344                     tcpcib_wdt_tickle(smw);
345           }
346 
347           return 0;
348 }
349 
350 static int
tcpcib_wdt_tickle(struct sysmon_wdog * smw)351 tcpcib_wdt_tickle(struct sysmon_wdog *smw)
352 {
353           struct tcpcib_softc *sc = smw->smw_cookie;
354 
355           /* Reset timer */
356           tcpcib_wdt_unlock(sc);
357           bus_space_write_1(sc->sc_wdt_iot, sc->sc_wdt_ioh,
358               E600_WDT_RR1, E600_WDT_RR1_RELOAD);
359 
360           return 0;
361 }
362