1 | /* $NetBSD: ichlpcib.c,v 1.51 2016/08/06 21:57:04 jakllsch Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Minoura Makoto and Matthew R. Green. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Intel I/O Controller Hub (ICHn) LPC Interface Bridge driver |
34 | * |
35 | * LPC Interface Bridge is basically a pcib (PCI-ISA Bridge), but has |
36 | * some power management and monitoring functions. |
37 | * Currently we support the watchdog timer, SpeedStep (on some systems), |
38 | * the gpio interface, hpet timer, hardware random number generator, |
39 | * and the power management timer. |
40 | */ |
41 | |
42 | #include <sys/cdefs.h> |
43 | __KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.51 2016/08/06 21:57:04 jakllsch Exp $" ); |
44 | |
45 | #include <sys/types.h> |
46 | #include <sys/param.h> |
47 | #include <sys/systm.h> |
48 | #include <sys/device.h> |
49 | #include <sys/sysctl.h> |
50 | #include <sys/timetc.h> |
51 | #include <sys/gpio.h> |
52 | #include <sys/bus.h> |
53 | |
54 | #include <dev/pci/pcivar.h> |
55 | #include <dev/pci/pcireg.h> |
56 | #include <dev/pci/pcidevs.h> |
57 | |
58 | #include <dev/gpio/gpiovar.h> |
59 | |
60 | #include <dev/ic/acpipmtimer.h> |
61 | #include <dev/ic/i82801lpcreg.h> |
62 | #include <dev/ic/i82801lpcvar.h> |
63 | #include <dev/ic/hpetreg.h> |
64 | #include <dev/ic/hpetvar.h> |
65 | |
66 | #include <arch/x86/pci/tco.h> |
67 | |
68 | #include "pcibvar.h" |
69 | #include "gpio.h" |
70 | #include "fwhrng.h" |
71 | |
72 | #define LPCIB_GPIO_NPINS 64 |
73 | |
74 | struct lpcib_softc { |
75 | /* we call pcibattach() which assumes this starts like this: */ |
76 | struct pcib_softc sc_pcib; |
77 | |
78 | struct pci_attach_args sc_pa; |
79 | int sc_has_rcba; |
80 | int sc_has_ich5_hpet; |
81 | |
82 | /* RCBA */ |
83 | bus_space_tag_t sc_rcbat; |
84 | bus_space_handle_t sc_rcbah; |
85 | pcireg_t sc_rcba_reg; |
86 | |
87 | /* Power management variables. */ |
88 | bus_space_tag_t sc_iot; |
89 | bus_space_handle_t sc_ioh; |
90 | bus_size_t sc_iosize; |
91 | |
92 | /* HPET variables. */ |
93 | uint32_t sc_hpet_reg; |
94 | |
95 | #if NGPIO > 0 |
96 | device_t sc_gpiobus; |
97 | kmutex_t sc_gpio_mtx; |
98 | bus_space_tag_t sc_gpio_iot; |
99 | bus_space_handle_t sc_gpio_ioh; |
100 | bus_size_t sc_gpio_ios; |
101 | struct gpio_chipset_tag sc_gpio_gc; |
102 | gpio_pin_t sc_gpio_pins[LPCIB_GPIO_NPINS]; |
103 | #endif |
104 | |
105 | #if NFWHRNG > 0 |
106 | device_t sc_fwhbus; |
107 | #endif |
108 | |
109 | /* Speedstep */ |
110 | pcireg_t sc_pmcon_orig; |
111 | |
112 | /* Power management */ |
113 | pcireg_t sc_pirq[2]; |
114 | pcireg_t sc_pmcon; |
115 | pcireg_t sc_fwhsel2; |
116 | |
117 | /* Child devices */ |
118 | device_t sc_tco; |
119 | device_t sc_hpetbus; |
120 | acpipmtimer_t sc_pmtimer; |
121 | pcireg_t sc_acpi_cntl; |
122 | |
123 | struct sysctllog *sc_log; |
124 | }; |
125 | |
126 | static int lpcibmatch(device_t, cfdata_t, void *); |
127 | static void lpcibattach(device_t, device_t, void *); |
128 | static int lpcibdetach(device_t, int); |
129 | static void lpcibchilddet(device_t, device_t); |
130 | static int lpcibrescan(device_t, const char *, const int *); |
131 | static bool lpcib_suspend(device_t, const pmf_qual_t *); |
132 | static bool lpcib_resume(device_t, const pmf_qual_t *); |
133 | static bool lpcib_shutdown(device_t, int); |
134 | |
135 | static void pmtimer_configure(device_t); |
136 | static int pmtimer_unconfigure(device_t, int); |
137 | |
138 | static void tcotimer_configure(device_t); |
139 | static int tcotimer_unconfigure(device_t, int); |
140 | |
141 | static void speedstep_configure(device_t); |
142 | static void speedstep_unconfigure(device_t); |
143 | static int speedstep_sysctl_helper(SYSCTLFN_ARGS); |
144 | |
145 | static void lpcib_hpet_configure(device_t); |
146 | static int lpcib_hpet_unconfigure(device_t, int); |
147 | |
148 | #if NGPIO > 0 |
149 | static void lpcib_gpio_configure(device_t); |
150 | static int lpcib_gpio_unconfigure(device_t, int); |
151 | static int lpcib_gpio_pin_read(void *, int); |
152 | static void lpcib_gpio_pin_write(void *, int, int); |
153 | static void lpcib_gpio_pin_ctl(void *, int, int); |
154 | #endif |
155 | |
156 | #if NFWHRNG > 0 |
157 | static void lpcib_fwh_configure(device_t); |
158 | static int lpcib_fwh_unconfigure(device_t, int); |
159 | #endif |
160 | |
161 | struct lpcib_softc *speedstep_cookie; /* XXX */ |
162 | |
163 | CFATTACH_DECL2_NEW(ichlpcib, sizeof(struct lpcib_softc), |
164 | lpcibmatch, lpcibattach, lpcibdetach, NULL, lpcibrescan, lpcibchilddet); |
165 | |
166 | static struct lpcib_device { |
167 | pcireg_t vendor, product; |
168 | int has_rcba; |
169 | int has_ich5_hpet; |
170 | } lpcib_devices[] = { |
171 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3400_LPC, 1, 0 }, |
172 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3420_LPC, 1, 0 }, |
173 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3450_LPC, 1, 0 }, |
174 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC, 1, 0 }, |
175 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_63XXESB_LPC, 1, 0 }, |
176 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC, 0, 0 }, |
177 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC, 0, 0 }, |
178 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC, 0, 0 }, |
179 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC, 0, 0 }, |
180 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC, 0, 0 }, |
181 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC, 0, 0 }, |
182 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC, 0, 0 }, |
183 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC, 0, 0 }, |
184 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC, 0, 1 }, |
185 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC, 0, 1 }, |
186 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC, 1, 0 }, |
187 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC, 1, 0 }, |
188 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801G_LPC, 1, 0 }, |
189 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC, 1, 0 }, |
190 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC, 1, 0 }, |
191 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC, 1, 0 }, |
192 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC, 1, 0 }, |
193 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HEM_LPC, 1, 0 }, |
194 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HH_LPC, 1, 0 }, |
195 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HO_LPC, 1, 0 }, |
196 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC, 1, 0 }, |
197 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC, 1, 0 }, |
198 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC, 1, 0 }, |
199 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IM_LPC, 1, 0 }, |
200 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC, 1, 0 }, |
201 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC, 1, 0 }, |
202 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IEM_LPC, 1, 0 }, |
203 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JD_LPC, 1, 0 }, |
204 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JDO_LPC, 1, 0 }, |
205 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JIB_LPC, 1, 0 }, |
206 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JIR_LPC, 1, 0 }, |
207 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C202_LPC, 1, 0 }, |
208 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C204_LPC, 1, 0 }, |
209 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C206_LPC, 1, 0 }, |
210 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C216_LPC, 1, 0 }, |
211 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_NM10_LPC, 1, 0 }, |
212 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H55_LPC, 1, 0 }, |
213 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H57_LPC, 1, 0 }, |
214 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM55_LPC, 1, 0 }, |
215 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM57_LPC, 1, 0 }, |
216 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_P55_LPC, 1, 0 }, |
217 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PM55_LPC, 1, 0 }, |
218 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q57_LPC, 1, 0 }, |
219 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QM57_LPC, 1, 0 }, |
220 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QS57_LPC, 1, 0 }, |
221 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_B65_LPC, 1, 0 }, |
222 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H61_LPC, 1, 0 }, |
223 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H67_LPC, 1, 0 }, |
224 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM65_LPC, 1, 0 }, |
225 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM67_LPC, 1, 0 }, |
226 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_P67_LPC, 1, 0 }, |
227 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q65_LPC, 1, 0 }, |
228 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q67_LPC, 1, 0 }, |
229 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QM67_LPC, 1, 0 }, |
230 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QS67_LPC, 1, 0 }, |
231 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_UM67_LPC, 1, 0 }, |
232 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z68_LPC, 1, 0 }, |
233 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_B75_LPC, 1, 0 }, |
234 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H77_LPC, 1, 0 }, |
235 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM70_LPC, 1, 0 }, |
236 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM75_LPC, 1, 0 }, |
237 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM76_LPC, 1, 0 }, |
238 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM77_LPC, 1, 0 }, |
239 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_QM77_LPC, 1, 0 }, |
240 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_QS77_LPC, 1, 0 }, |
241 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_UM77_LPC, 1, 0 }, |
242 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_NM70_LPC, 1, 0 }, |
243 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q75_LPC, 1, 0 }, |
244 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q77_LPC, 1, 0 }, |
245 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z75_LPC, 1, 0 }, |
246 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z77_LPC, 1, 0 }, |
247 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z87_LPC, 1, 0 }, |
248 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z85_LPC, 1, 0 }, |
249 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM86_LPC, 1, 0 }, |
250 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H87_LPC, 1, 0 }, |
251 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM87_LPC, 1, 0 }, |
252 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q85_LPC, 1, 0 }, |
253 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q87_LPC, 1, 0 }, |
254 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QM87_LPC, 1, 0 }, |
255 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_B85_LPC, 1, 0 }, |
256 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H97_LPC, 1, 0 }, |
257 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z97_LPC, 1, 0 }, |
258 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X99_LPC, 1, 0 }, |
259 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X99_LPC_2, 1, 0 }, |
260 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_CORE5G_M_LPC_4, 1, 0 }, |
261 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_CORE5G_M_LPC_7, 1, 0 }, |
262 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C222_LPC, 1, 0 }, |
263 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C224_LPC, 1, 0 }, |
264 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C226_LPC, 1, 0 }, |
265 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H81_LPC, 1, 0 }, |
266 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C600_LPC, 1, 0 }, |
267 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_DH89XXCC_LPC, 1, 0 }, |
268 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_DH89XXCL_LPC, 1, 0 }, |
269 | #if 0 |
270 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_1, 1, 0 }, |
271 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_2, 1, 0 }, |
272 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_3, 1, 0 }, |
273 | { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_4, 1, 0 }, |
274 | #endif |
275 | |
276 | { 0, 0, 0, 0 }, |
277 | }; |
278 | |
279 | /* |
280 | * Allow user to enable GPIO functionality if they really need it. The |
281 | * vast majority of systems with an ICH should not expose GPIO to the |
282 | * kernel or user. In at least one instance the gpio_resume() handler |
283 | * on ICH GPIO was found to sabotage S3 suspend/resume. |
284 | */ |
285 | int ichlpcib_gpio_disable = 1; |
286 | |
287 | /* |
288 | * Autoconf callbacks. |
289 | */ |
290 | static int |
291 | lpcibmatch(device_t parent, cfdata_t match, void *aux) |
292 | { |
293 | struct pci_attach_args *pa = aux; |
294 | struct lpcib_device *lpcib_dev; |
295 | |
296 | /* We are ISA bridge, of course */ |
297 | if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE || |
298 | PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA) |
299 | return 0; |
300 | |
301 | for (lpcib_dev = lpcib_devices; lpcib_dev->vendor; ++lpcib_dev) { |
302 | if (PCI_VENDOR(pa->pa_id) == lpcib_dev->vendor && |
303 | PCI_PRODUCT(pa->pa_id) == lpcib_dev->product) |
304 | return 10; |
305 | } |
306 | |
307 | return 0; |
308 | } |
309 | |
310 | static void |
311 | lpcibattach(device_t parent, device_t self, void *aux) |
312 | { |
313 | struct pci_attach_args *pa = aux; |
314 | struct lpcib_softc *sc = device_private(self); |
315 | struct lpcib_device *lpcib_dev; |
316 | pcireg_t pmbase; |
317 | |
318 | sc->sc_pa = *pa; |
319 | |
320 | for (lpcib_dev = lpcib_devices; lpcib_dev->vendor; ++lpcib_dev) { |
321 | if (PCI_VENDOR(pa->pa_id) != lpcib_dev->vendor || |
322 | PCI_PRODUCT(pa->pa_id) != lpcib_dev->product) |
323 | continue; |
324 | sc->sc_has_rcba = lpcib_dev->has_rcba; |
325 | sc->sc_has_ich5_hpet = lpcib_dev->has_ich5_hpet; |
326 | break; |
327 | } |
328 | |
329 | pcibattach(parent, self, aux); |
330 | |
331 | /* |
332 | * Part of our I/O registers are used as ACPI PM regs. |
333 | * Since our ACPI subsystem accesses the I/O space directly so far, |
334 | * we do not have to bother bus_space I/O map confliction. |
335 | * |
336 | * The PMBASE register is alike PCI BAR but not completely compatible |
337 | * with it. The PMBASE define the base address and the type but |
338 | * not describe the size. The value of the register may be lower |
339 | * than LPCIB_PCI_PM_SIZE. It makes impossible to use |
340 | * pci_mapreg_submap() because the function does range check. |
341 | */ |
342 | sc->sc_iot = pa->pa_iot; |
343 | pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, LPCIB_PCI_PMBASE); |
344 | if (bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(pmbase), |
345 | LPCIB_PCI_PM_SIZE, 0, &sc->sc_ioh) != 0) { |
346 | aprint_error_dev(self, |
347 | "can't map power management i/o space\n" ); |
348 | return; |
349 | } |
350 | |
351 | sc->sc_pmcon_orig = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
352 | LPCIB_PCI_GEN_PMCON_1); |
353 | |
354 | /* For ICH6 and later, always enable RCBA */ |
355 | if (sc->sc_has_rcba) { |
356 | pcireg_t rcba; |
357 | |
358 | sc->sc_rcbat = sc->sc_pa.pa_memt; |
359 | |
360 | rcba = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
361 | LPCIB_RCBA); |
362 | if ((rcba & LPCIB_RCBA_EN) == 0) { |
363 | aprint_error_dev(self, "RCBA is not enabled\n" ); |
364 | return; |
365 | } |
366 | rcba &= ~LPCIB_RCBA_EN; |
367 | |
368 | if (bus_space_map(sc->sc_rcbat, rcba, LPCIB_RCBA_SIZE, 0, |
369 | &sc->sc_rcbah)) { |
370 | aprint_error_dev(self, "RCBA could not be mapped\n" ); |
371 | return; |
372 | } |
373 | } |
374 | |
375 | /* Set up the power management timer. */ |
376 | pmtimer_configure(self); |
377 | |
378 | /* Set up the TCO (watchdog). */ |
379 | tcotimer_configure(self); |
380 | |
381 | /* Set up SpeedStep. */ |
382 | speedstep_configure(self); |
383 | |
384 | /* Set up HPET. */ |
385 | lpcib_hpet_configure(self); |
386 | |
387 | #if NGPIO > 0 |
388 | /* Set up GPIO */ |
389 | lpcib_gpio_configure(self); |
390 | #endif |
391 | |
392 | #if NFWHRNG > 0 |
393 | lpcib_fwh_configure(self); |
394 | #endif |
395 | |
396 | /* Install power handler */ |
397 | if (!pmf_device_register1(self, lpcib_suspend, lpcib_resume, |
398 | lpcib_shutdown)) |
399 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
400 | } |
401 | |
402 | static void |
403 | lpcibchilddet(device_t self, device_t child) |
404 | { |
405 | struct lpcib_softc *sc = device_private(self); |
406 | uint32_t val; |
407 | |
408 | #if NFWHRNG > 0 |
409 | if (sc->sc_fwhbus == child) { |
410 | sc->sc_fwhbus = NULL; |
411 | return; |
412 | } |
413 | #endif |
414 | #if NGPIO > 0 |
415 | if (sc->sc_gpiobus == child) { |
416 | sc->sc_gpiobus = NULL; |
417 | return; |
418 | } |
419 | #endif |
420 | if (sc->sc_tco == child) { |
421 | sc->sc_tco = NULL; |
422 | return; |
423 | } |
424 | |
425 | if (sc->sc_hpetbus != child) { |
426 | pcibchilddet(self, child); |
427 | return; |
428 | } |
429 | sc->sc_hpetbus = NULL; |
430 | if (sc->sc_has_ich5_hpet) { |
431 | val = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
432 | LPCIB_PCI_GEN_CNTL); |
433 | switch (val & LPCIB_ICH5_HPTC_WIN_MASK) { |
434 | case LPCIB_ICH5_HPTC_0000: |
435 | case LPCIB_ICH5_HPTC_1000: |
436 | case LPCIB_ICH5_HPTC_2000: |
437 | case LPCIB_ICH5_HPTC_3000: |
438 | break; |
439 | default: |
440 | return; |
441 | } |
442 | val &= ~LPCIB_ICH5_HPTC_EN; |
443 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
444 | LPCIB_PCI_GEN_CNTL, val); |
445 | } else if (sc->sc_has_rcba) { |
446 | val = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah, |
447 | LPCIB_RCBA_HPTC); |
448 | switch (val & LPCIB_RCBA_HPTC_WIN_MASK) { |
449 | case LPCIB_RCBA_HPTC_0000: |
450 | case LPCIB_RCBA_HPTC_1000: |
451 | case LPCIB_RCBA_HPTC_2000: |
452 | case LPCIB_RCBA_HPTC_3000: |
453 | break; |
454 | default: |
455 | return; |
456 | } |
457 | val &= ~LPCIB_RCBA_HPTC_EN; |
458 | bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_HPTC, |
459 | val); |
460 | } |
461 | } |
462 | |
463 | static int |
464 | lpcibrescan(device_t self, const char *ifattr, const int *locators) |
465 | { |
466 | struct lpcib_softc *sc = device_private(self); |
467 | |
468 | if(ifattr_match(ifattr, "tcoichbus" ) && sc->sc_tco == NULL) |
469 | tcotimer_configure(self); |
470 | |
471 | #if NFWHRNG > 0 |
472 | if (ifattr_match(ifattr, "fwhichbus" ) && sc->sc_fwhbus == NULL) |
473 | lpcib_fwh_configure(self); |
474 | #endif |
475 | |
476 | if (ifattr_match(ifattr, "hpetichbus" ) && sc->sc_hpetbus == NULL) |
477 | lpcib_hpet_configure(self); |
478 | |
479 | #if NGPIO > 0 |
480 | if (ifattr_match(ifattr, "gpiobus" ) && sc->sc_gpiobus == NULL) |
481 | lpcib_gpio_configure(self); |
482 | #endif |
483 | |
484 | return pcibrescan(self, ifattr, locators); |
485 | } |
486 | |
487 | static int |
488 | lpcibdetach(device_t self, int flags) |
489 | { |
490 | struct lpcib_softc *sc = device_private(self); |
491 | int rc; |
492 | |
493 | pmf_device_deregister(self); |
494 | |
495 | #if NFWHRNG > 0 |
496 | if ((rc = lpcib_fwh_unconfigure(self, flags)) != 0) |
497 | return rc; |
498 | #endif |
499 | |
500 | if ((rc = lpcib_hpet_unconfigure(self, flags)) != 0) |
501 | return rc; |
502 | |
503 | #if NGPIO > 0 |
504 | if ((rc = lpcib_gpio_unconfigure(self, flags)) != 0) |
505 | return rc; |
506 | #endif |
507 | |
508 | /* Set up SpeedStep. */ |
509 | speedstep_unconfigure(self); |
510 | |
511 | if ((rc = tcotimer_unconfigure(self, flags)) != 0) |
512 | return rc; |
513 | |
514 | if ((rc = pmtimer_unconfigure(self, flags)) != 0) |
515 | return rc; |
516 | |
517 | if (sc->sc_has_rcba) |
518 | bus_space_unmap(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_SIZE); |
519 | |
520 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); |
521 | |
522 | return pcibdetach(self, flags); |
523 | } |
524 | |
525 | static bool |
526 | lpcib_shutdown(device_t dv, int howto) |
527 | { |
528 | struct lpcib_softc *sc = device_private(dv); |
529 | |
530 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
531 | LPCIB_PCI_GEN_PMCON_1, sc->sc_pmcon_orig); |
532 | |
533 | return true; |
534 | } |
535 | |
536 | static bool |
537 | lpcib_suspend(device_t dv, const pmf_qual_t *qual) |
538 | { |
539 | struct lpcib_softc *sc = device_private(dv); |
540 | pci_chipset_tag_t pc = sc->sc_pcib.sc_pc; |
541 | pcitag_t tag = sc->sc_pcib.sc_tag; |
542 | |
543 | /* capture PIRQ routing control registers */ |
544 | sc->sc_pirq[0] = pci_conf_read(pc, tag, LPCIB_PCI_PIRQA_ROUT); |
545 | sc->sc_pirq[1] = pci_conf_read(pc, tag, LPCIB_PCI_PIRQE_ROUT); |
546 | |
547 | sc->sc_pmcon = pci_conf_read(pc, tag, LPCIB_PCI_GEN_PMCON_1); |
548 | sc->sc_fwhsel2 = pci_conf_read(pc, tag, LPCIB_PCI_GEN_STA); |
549 | |
550 | if (sc->sc_has_rcba) { |
551 | sc->sc_rcba_reg = pci_conf_read(pc, tag, LPCIB_RCBA); |
552 | sc->sc_hpet_reg = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah, |
553 | LPCIB_RCBA_HPTC); |
554 | } else if (sc->sc_has_ich5_hpet) { |
555 | sc->sc_hpet_reg = pci_conf_read(pc, tag, LPCIB_PCI_GEN_CNTL); |
556 | } |
557 | |
558 | return true; |
559 | } |
560 | |
561 | static bool |
562 | lpcib_resume(device_t dv, const pmf_qual_t *qual) |
563 | { |
564 | struct lpcib_softc *sc = device_private(dv); |
565 | pci_chipset_tag_t pc = sc->sc_pcib.sc_pc; |
566 | pcitag_t tag = sc->sc_pcib.sc_tag; |
567 | |
568 | /* restore PIRQ routing control registers */ |
569 | pci_conf_write(pc, tag, LPCIB_PCI_PIRQA_ROUT, sc->sc_pirq[0]); |
570 | pci_conf_write(pc, tag, LPCIB_PCI_PIRQE_ROUT, sc->sc_pirq[1]); |
571 | |
572 | pci_conf_write(pc, tag, LPCIB_PCI_GEN_PMCON_1, sc->sc_pmcon); |
573 | pci_conf_write(pc, tag, LPCIB_PCI_GEN_STA, sc->sc_fwhsel2); |
574 | |
575 | if (sc->sc_has_rcba) { |
576 | pci_conf_write(pc, tag, LPCIB_RCBA, sc->sc_rcba_reg); |
577 | bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_HPTC, |
578 | sc->sc_hpet_reg); |
579 | } else if (sc->sc_has_ich5_hpet) { |
580 | pci_conf_write(pc, tag, LPCIB_PCI_GEN_CNTL, sc->sc_hpet_reg); |
581 | } |
582 | |
583 | return true; |
584 | } |
585 | |
586 | /* |
587 | * Initialize the power management timer. |
588 | */ |
589 | static void |
590 | pmtimer_configure(device_t self) |
591 | { |
592 | struct lpcib_softc *sc = device_private(self); |
593 | pcireg_t control; |
594 | |
595 | /* |
596 | * Check if power management I/O space is enabled and enable the ACPI_EN |
597 | * bit if it's disabled. |
598 | */ |
599 | control = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
600 | LPCIB_PCI_ACPI_CNTL); |
601 | sc->sc_acpi_cntl = control; |
602 | if ((control & LPCIB_PCI_ACPI_CNTL_EN) == 0) { |
603 | control |= LPCIB_PCI_ACPI_CNTL_EN; |
604 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
605 | LPCIB_PCI_ACPI_CNTL, control); |
606 | } |
607 | |
608 | /* Attach our PM timer with the generic acpipmtimer function */ |
609 | sc->sc_pmtimer = acpipmtimer_attach(self, sc->sc_iot, sc->sc_ioh, |
610 | LPCIB_PM1_TMR, 0); |
611 | } |
612 | |
613 | static int |
614 | pmtimer_unconfigure(device_t self, int flags) |
615 | { |
616 | struct lpcib_softc *sc = device_private(self); |
617 | int rc; |
618 | |
619 | if (sc->sc_pmtimer != NULL && |
620 | (rc = acpipmtimer_detach(sc->sc_pmtimer, flags)) != 0) |
621 | return rc; |
622 | |
623 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
624 | LPCIB_PCI_ACPI_CNTL, sc->sc_acpi_cntl); |
625 | |
626 | return 0; |
627 | } |
628 | |
629 | /* |
630 | * Configure the watchdog timer. |
631 | */ |
632 | static void |
633 | tcotimer_configure(device_t self) |
634 | { |
635 | struct lpcib_softc *sc = device_private(self); |
636 | struct lpcib_tco_attach_args arg; |
637 | |
638 | arg.ta_iot = sc->sc_iot; |
639 | arg.ta_ioh = sc->sc_ioh; |
640 | arg.ta_rcbat = sc->sc_rcbat; |
641 | arg.ta_rcbah = sc->sc_rcbah; |
642 | arg.ta_has_rcba = sc->sc_has_rcba; |
643 | arg.ta_pcib = &(sc->sc_pcib); |
644 | |
645 | sc->sc_tco = config_found_ia(self, "tcoichbus" , &arg, NULL); |
646 | } |
647 | |
648 | static int |
649 | tcotimer_unconfigure(device_t self, int flags) |
650 | { |
651 | struct lpcib_softc *sc = device_private(self); |
652 | int rc; |
653 | |
654 | if (sc->sc_tco != NULL && |
655 | (rc = config_detach(sc->sc_tco, flags)) != 0) |
656 | return rc; |
657 | |
658 | return 0; |
659 | } |
660 | |
661 | |
662 | /* |
663 | * Intel ICH SpeedStep support. |
664 | */ |
665 | #define SS_READ(sc, reg) \ |
666 | bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (reg)) |
667 | #define SS_WRITE(sc, reg, val) \ |
668 | bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) |
669 | |
670 | /* |
671 | * Linux driver says that SpeedStep on older chipsets cause |
672 | * lockups on Dell Inspiron 8000 and 8100. |
673 | * It should also not be enabled on systems with the 82855GM |
674 | * Hub, which typically have an EST-enabled CPU. |
675 | */ |
676 | static int |
677 | speedstep_bad_hb_check(const struct pci_attach_args *pa) |
678 | { |
679 | |
680 | if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82815_FULL_HUB && |
681 | PCI_REVISION(pa->pa_class) < 5) |
682 | return 1; |
683 | |
684 | if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82855GM_MCH) |
685 | return 1; |
686 | |
687 | return 0; |
688 | } |
689 | |
690 | static void |
691 | speedstep_configure(device_t self) |
692 | { |
693 | struct lpcib_softc *sc = device_private(self); |
694 | const struct sysctlnode *node, *ssnode; |
695 | int rv; |
696 | |
697 | /* Supported on ICH2-M, ICH3-M and ICH4-M. */ |
698 | if (PCI_PRODUCT(sc->sc_pa.pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC || |
699 | PCI_PRODUCT(sc->sc_pa.pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC || |
700 | (PCI_PRODUCT(sc->sc_pa.pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC && |
701 | pci_find_device(&sc->sc_pa, speedstep_bad_hb_check) == 0)) { |
702 | pcireg_t pmcon; |
703 | |
704 | /* Enable SpeedStep if it isn't already enabled. */ |
705 | pmcon = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
706 | LPCIB_PCI_GEN_PMCON_1); |
707 | if ((pmcon & LPCIB_PCI_GEN_PMCON_1_SS_EN) == 0) |
708 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
709 | LPCIB_PCI_GEN_PMCON_1, |
710 | pmcon | LPCIB_PCI_GEN_PMCON_1_SS_EN); |
711 | |
712 | /* Put in machdep.speedstep_state (0 for low, 1 for high). */ |
713 | if ((rv = sysctl_createv(&sc->sc_log, 0, NULL, &node, |
714 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep" , NULL, |
715 | NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0) |
716 | goto err; |
717 | |
718 | /* CTLFLAG_ANYWRITE? kernel option like EST? */ |
719 | if ((rv = sysctl_createv(&sc->sc_log, 0, &node, &ssnode, |
720 | CTLFLAG_READWRITE, CTLTYPE_INT, "speedstep_state" , NULL, |
721 | speedstep_sysctl_helper, 0, NULL, 0, CTL_CREATE, |
722 | CTL_EOL)) != 0) |
723 | goto err; |
724 | |
725 | /* XXX save the sc for IO tag/handle */ |
726 | speedstep_cookie = sc; |
727 | aprint_verbose_dev(self, "SpeedStep enabled\n" ); |
728 | } |
729 | |
730 | return; |
731 | |
732 | err: |
733 | aprint_normal("%s: sysctl_createv failed (rv = %d)\n" , __func__, rv); |
734 | } |
735 | |
736 | static void |
737 | speedstep_unconfigure(device_t self) |
738 | { |
739 | struct lpcib_softc *sc = device_private(self); |
740 | |
741 | sysctl_teardown(&sc->sc_log); |
742 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
743 | LPCIB_PCI_GEN_PMCON_1, sc->sc_pmcon_orig); |
744 | |
745 | speedstep_cookie = NULL; |
746 | } |
747 | |
748 | /* |
749 | * get/set the SpeedStep state: 0 == low power, 1 == high power. |
750 | */ |
751 | static int |
752 | speedstep_sysctl_helper(SYSCTLFN_ARGS) |
753 | { |
754 | struct sysctlnode node; |
755 | struct lpcib_softc *sc = speedstep_cookie; |
756 | uint8_t state, state2; |
757 | int ostate, nstate, s, error = 0; |
758 | |
759 | /* |
760 | * We do the dance with spl's to avoid being at high ipl during |
761 | * sysctl_lookup() which can both copyin and copyout. |
762 | */ |
763 | s = splserial(); |
764 | state = SS_READ(sc, LPCIB_PM_SS_CNTL); |
765 | splx(s); |
766 | if ((state & LPCIB_PM_SS_STATE_LOW) == 0) |
767 | ostate = 1; |
768 | else |
769 | ostate = 0; |
770 | nstate = ostate; |
771 | |
772 | node = *rnode; |
773 | node.sysctl_data = &nstate; |
774 | |
775 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
776 | if (error || newp == NULL) |
777 | goto out; |
778 | |
779 | /* Only two states are available */ |
780 | if (nstate != 0 && nstate != 1) { |
781 | error = EINVAL; |
782 | goto out; |
783 | } |
784 | |
785 | s = splserial(); |
786 | state2 = SS_READ(sc, LPCIB_PM_SS_CNTL); |
787 | if ((state2 & LPCIB_PM_SS_STATE_LOW) == 0) |
788 | ostate = 1; |
789 | else |
790 | ostate = 0; |
791 | |
792 | if (ostate != nstate) { |
793 | uint8_t cntl; |
794 | |
795 | if (nstate == 0) |
796 | state2 |= LPCIB_PM_SS_STATE_LOW; |
797 | else |
798 | state2 &= ~LPCIB_PM_SS_STATE_LOW; |
799 | |
800 | /* |
801 | * Must disable bus master arbitration during the change. |
802 | */ |
803 | cntl = SS_READ(sc, LPCIB_PM_CTRL); |
804 | SS_WRITE(sc, LPCIB_PM_CTRL, cntl | LPCIB_PM_SS_CNTL_ARB_DIS); |
805 | SS_WRITE(sc, LPCIB_PM_SS_CNTL, state2); |
806 | SS_WRITE(sc, LPCIB_PM_CTRL, cntl); |
807 | } |
808 | splx(s); |
809 | out: |
810 | return error; |
811 | } |
812 | |
813 | static void |
814 | lpcib_hpet_configure(device_t self) |
815 | { |
816 | struct lpcib_softc *sc = device_private(self); |
817 | struct lpcib_hpet_attach_args arg; |
818 | uint32_t hpet_reg, val; |
819 | |
820 | if (sc->sc_has_ich5_hpet) { |
821 | val = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
822 | LPCIB_PCI_GEN_CNTL); |
823 | switch (val & LPCIB_ICH5_HPTC_WIN_MASK) { |
824 | case LPCIB_ICH5_HPTC_0000: |
825 | hpet_reg = LPCIB_ICH5_HPTC_0000_BASE; |
826 | break; |
827 | case LPCIB_ICH5_HPTC_1000: |
828 | hpet_reg = LPCIB_ICH5_HPTC_1000_BASE; |
829 | break; |
830 | case LPCIB_ICH5_HPTC_2000: |
831 | hpet_reg = LPCIB_ICH5_HPTC_2000_BASE; |
832 | break; |
833 | case LPCIB_ICH5_HPTC_3000: |
834 | hpet_reg = LPCIB_ICH5_HPTC_3000_BASE; |
835 | break; |
836 | default: |
837 | return; |
838 | } |
839 | val |= sc->sc_hpet_reg | LPCIB_ICH5_HPTC_EN; |
840 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
841 | LPCIB_PCI_GEN_CNTL, val); |
842 | } else if (sc->sc_has_rcba) { |
843 | val = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah, |
844 | LPCIB_RCBA_HPTC); |
845 | switch (val & LPCIB_RCBA_HPTC_WIN_MASK) { |
846 | case LPCIB_RCBA_HPTC_0000: |
847 | hpet_reg = LPCIB_RCBA_HPTC_0000_BASE; |
848 | break; |
849 | case LPCIB_RCBA_HPTC_1000: |
850 | hpet_reg = LPCIB_RCBA_HPTC_1000_BASE; |
851 | break; |
852 | case LPCIB_RCBA_HPTC_2000: |
853 | hpet_reg = LPCIB_RCBA_HPTC_2000_BASE; |
854 | break; |
855 | case LPCIB_RCBA_HPTC_3000: |
856 | hpet_reg = LPCIB_RCBA_HPTC_3000_BASE; |
857 | break; |
858 | default: |
859 | return; |
860 | } |
861 | val |= LPCIB_RCBA_HPTC_EN; |
862 | bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_HPTC, |
863 | val); |
864 | } else { |
865 | /* No HPET here */ |
866 | return; |
867 | } |
868 | |
869 | arg.hpet_mem_t = sc->sc_pa.pa_memt; |
870 | arg.hpet_reg = hpet_reg; |
871 | |
872 | sc->sc_hpetbus = config_found_ia(self, "hpetichbus" , &arg, NULL); |
873 | } |
874 | |
875 | static int |
876 | lpcib_hpet_unconfigure(device_t self, int flags) |
877 | { |
878 | struct lpcib_softc *sc = device_private(self); |
879 | int rc; |
880 | |
881 | if (sc->sc_hpetbus != NULL && |
882 | (rc = config_detach(sc->sc_hpetbus, flags)) != 0) |
883 | return rc; |
884 | |
885 | return 0; |
886 | } |
887 | |
888 | #if NGPIO > 0 |
889 | static void |
890 | lpcib_gpio_configure(device_t self) |
891 | { |
892 | struct lpcib_softc *sc = device_private(self); |
893 | struct gpiobus_attach_args gba; |
894 | pcireg_t gpio_cntl; |
895 | uint32_t use, io, bit; |
896 | int pin, shift, base_reg, cntl_reg, reg; |
897 | int rv; |
898 | |
899 | if (ichlpcib_gpio_disable != 0) |
900 | return; |
901 | |
902 | /* this implies ICH >= 6, and thus different mapreg */ |
903 | if (sc->sc_has_rcba) { |
904 | base_reg = LPCIB_PCI_GPIO_BASE_ICH6; |
905 | cntl_reg = LPCIB_PCI_GPIO_CNTL_ICH6; |
906 | } else { |
907 | base_reg = LPCIB_PCI_GPIO_BASE; |
908 | cntl_reg = LPCIB_PCI_GPIO_CNTL; |
909 | } |
910 | |
911 | gpio_cntl = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
912 | cntl_reg); |
913 | |
914 | /* Is GPIO enabled? */ |
915 | if ((gpio_cntl & LPCIB_PCI_GPIO_CNTL_EN) == 0) |
916 | return; |
917 | /* |
918 | * The GPIO_BASE register is alike PCI BAR but not completely |
919 | * compatible with it. The PMBASE define the base address and the type |
920 | * but not describe the size. The value of the register may be lower |
921 | * than LPCIB_PCI_GPIO_SIZE. It makes impossible to use |
922 | * pci_mapreg_submap() because the function does range check. |
923 | */ |
924 | sc->sc_gpio_iot = sc->sc_pa.pa_iot; |
925 | reg = pci_conf_read(sc->sc_pa.pa_pc, sc->sc_pa.pa_tag, base_reg); |
926 | rv = bus_space_map(sc->sc_gpio_iot, PCI_MAPREG_IO_ADDR(reg), |
927 | LPCIB_PCI_GPIO_SIZE, 0, &sc->sc_gpio_ioh); |
928 | if (rv != 0) { |
929 | aprint_error_dev(self, "can't map general purpose i/o space(rv = %d)\n" , rv); |
930 | return; |
931 | } |
932 | |
933 | mutex_init(&sc->sc_gpio_mtx, MUTEX_DEFAULT, IPL_NONE); |
934 | |
935 | for (pin = 0; pin < LPCIB_GPIO_NPINS; pin++) { |
936 | sc->sc_gpio_pins[pin].pin_num = pin; |
937 | |
938 | /* Read initial state */ |
939 | reg = (pin < 32) ? LPCIB_GPIO_GPIO_USE_SEL : LPCIB_GPIO_GPIO_USE_SEL2; |
940 | use = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); |
941 | reg = (pin < 32) ? LPCIB_GPIO_GP_IO_SEL : LPCIB_GPIO_GP_IO_SEL; |
942 | io = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, 4); |
943 | shift = pin % 32; |
944 | bit = __BIT(shift); |
945 | |
946 | if ((use & bit) != 0) { |
947 | sc->sc_gpio_pins[pin].pin_caps = |
948 | GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; |
949 | if (pin < 32) |
950 | sc->sc_gpio_pins[pin].pin_caps |= |
951 | GPIO_PIN_PULSATE; |
952 | if ((io & bit) != 0) |
953 | sc->sc_gpio_pins[pin].pin_flags = |
954 | GPIO_PIN_INPUT; |
955 | else |
956 | sc->sc_gpio_pins[pin].pin_flags = |
957 | GPIO_PIN_OUTPUT; |
958 | } else |
959 | sc->sc_gpio_pins[pin].pin_caps = 0; |
960 | |
961 | if (lpcib_gpio_pin_read(sc, pin) == 0) |
962 | sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW; |
963 | else |
964 | sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH; |
965 | |
966 | } |
967 | |
968 | /* Create controller tag */ |
969 | sc->sc_gpio_gc.gp_cookie = sc; |
970 | sc->sc_gpio_gc.gp_pin_read = lpcib_gpio_pin_read; |
971 | sc->sc_gpio_gc.gp_pin_write = lpcib_gpio_pin_write; |
972 | sc->sc_gpio_gc.gp_pin_ctl = lpcib_gpio_pin_ctl; |
973 | |
974 | memset(&gba, 0, sizeof(gba)); |
975 | |
976 | gba.gba_gc = &sc->sc_gpio_gc; |
977 | gba.gba_pins = sc->sc_gpio_pins; |
978 | gba.gba_npins = LPCIB_GPIO_NPINS; |
979 | |
980 | sc->sc_gpiobus = config_found_ia(self, "gpiobus" , &gba, gpiobus_print); |
981 | } |
982 | |
983 | static int |
984 | lpcib_gpio_unconfigure(device_t self, int flags) |
985 | { |
986 | struct lpcib_softc *sc = device_private(self); |
987 | int rc; |
988 | |
989 | if (sc->sc_gpiobus != NULL && |
990 | (rc = config_detach(sc->sc_gpiobus, flags)) != 0) |
991 | return rc; |
992 | |
993 | mutex_destroy(&sc->sc_gpio_mtx); |
994 | |
995 | bus_space_unmap(sc->sc_gpio_iot, sc->sc_gpio_ioh, sc->sc_gpio_ios); |
996 | |
997 | return 0; |
998 | } |
999 | |
1000 | static int |
1001 | lpcib_gpio_pin_read(void *arg, int pin) |
1002 | { |
1003 | struct lpcib_softc *sc = arg; |
1004 | uint32_t data; |
1005 | int reg, shift; |
1006 | |
1007 | reg = (pin < 32) ? LPCIB_GPIO_GP_LVL : LPCIB_GPIO_GP_LVL2; |
1008 | shift = pin % 32; |
1009 | |
1010 | mutex_enter(&sc->sc_gpio_mtx); |
1011 | data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); |
1012 | mutex_exit(&sc->sc_gpio_mtx); |
1013 | |
1014 | return (__SHIFTOUT(data, __BIT(shift)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW); |
1015 | } |
1016 | |
1017 | static void |
1018 | lpcib_gpio_pin_write(void *arg, int pin, int value) |
1019 | { |
1020 | struct lpcib_softc *sc = arg; |
1021 | uint32_t data; |
1022 | int reg, shift; |
1023 | |
1024 | reg = (pin < 32) ? LPCIB_GPIO_GP_LVL : LPCIB_GPIO_GP_LVL2; |
1025 | shift = pin % 32; |
1026 | |
1027 | mutex_enter(&sc->sc_gpio_mtx); |
1028 | |
1029 | data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); |
1030 | |
1031 | if(value) |
1032 | data |= __BIT(shift); |
1033 | else |
1034 | data &= ~__BIT(shift); |
1035 | |
1036 | bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data); |
1037 | |
1038 | mutex_exit(&sc->sc_gpio_mtx); |
1039 | } |
1040 | |
1041 | static void |
1042 | lpcib_gpio_pin_ctl(void *arg, int pin, int flags) |
1043 | { |
1044 | struct lpcib_softc *sc = arg; |
1045 | uint32_t data; |
1046 | int reg, shift; |
1047 | |
1048 | shift = pin % 32; |
1049 | reg = (pin < 32) ? LPCIB_GPIO_GP_IO_SEL : LPCIB_GPIO_GP_IO_SEL2; |
1050 | |
1051 | mutex_enter(&sc->sc_gpio_mtx); |
1052 | |
1053 | data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); |
1054 | |
1055 | if (flags & GPIO_PIN_OUTPUT) |
1056 | data &= ~__BIT(shift); |
1057 | |
1058 | if (flags & GPIO_PIN_INPUT) |
1059 | data |= __BIT(shift); |
1060 | |
1061 | bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data); |
1062 | |
1063 | |
1064 | if (pin < 32) { |
1065 | reg = LPCIB_GPIO_GPO_BLINK; |
1066 | data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg); |
1067 | |
1068 | if (flags & GPIO_PIN_PULSATE) |
1069 | data |= __BIT(shift); |
1070 | else |
1071 | data &= ~__BIT(shift); |
1072 | |
1073 | bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data); |
1074 | } |
1075 | |
1076 | mutex_exit(&sc->sc_gpio_mtx); |
1077 | } |
1078 | #endif |
1079 | |
1080 | #if NFWHRNG > 0 |
1081 | static void |
1082 | lpcib_fwh_configure(device_t self) |
1083 | { |
1084 | struct lpcib_softc *sc; |
1085 | pcireg_t pr; |
1086 | |
1087 | sc = device_private(self); |
1088 | |
1089 | if (sc->sc_has_rcba) { |
1090 | /* |
1091 | * Very unlikely to find a 82802 on a ICH6 or newer. |
1092 | * Also the write enable register moved at that point. |
1093 | */ |
1094 | return; |
1095 | } else { |
1096 | /* Enable FWH write to identify FWH. */ |
1097 | pr = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
1098 | LPCIB_PCI_BIOS_CNTL); |
1099 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
1100 | LPCIB_PCI_BIOS_CNTL, pr|LPCIB_PCI_BIOS_CNTL_BWE); |
1101 | } |
1102 | |
1103 | sc->sc_fwhbus = config_found_ia(self, "fwhichbus" , NULL, NULL); |
1104 | |
1105 | /* restore previous write enable setting */ |
1106 | pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag, |
1107 | LPCIB_PCI_BIOS_CNTL, pr); |
1108 | } |
1109 | |
1110 | static int |
1111 | lpcib_fwh_unconfigure(device_t self, int flags) |
1112 | { |
1113 | struct lpcib_softc *sc = device_private(self); |
1114 | int rc; |
1115 | |
1116 | if (sc->sc_fwhbus != NULL && |
1117 | (rc = config_detach(sc->sc_fwhbus, flags)) != 0) |
1118 | return rc; |
1119 | |
1120 | return 0; |
1121 | } |
1122 | #endif |
1123 | |