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
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 void
tcpcib_wdt_init(struct tcpcib_softc * sc,int period)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
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 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 int
tcpcib_match(struct device * parent,void * match,void * aux)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
tcpcib_attach(struct device * parent,struct device * self,void * aux)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
tcpcib_activate(struct device * self,int act)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
tcpcib_wdt_cb(void * arg,int period)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
tcpcib_hpet_get_timecount(struct timecounter * tc)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