xref: /netbsd-src/sys/arch/x86/pci/ichlpcib.c (revision 0136eacd6d6a135ac761a4a7ddf70b69547e10ca)
1*0136eacdShans /*	$NetBSD: ichlpcib.c,v 1.62 2024/12/18 18:18:30 hans Exp $	*/
2031bec6dSxtraeme 
3031bec6dSxtraeme /*-
4031bec6dSxtraeme  * Copyright (c) 2004 The NetBSD Foundation, Inc.
5031bec6dSxtraeme  * All rights reserved.
6031bec6dSxtraeme  *
7031bec6dSxtraeme  * This code is derived from software contributed to The NetBSD Foundation
8031bec6dSxtraeme  * by Minoura Makoto and Matthew R. Green.
9031bec6dSxtraeme  *
10031bec6dSxtraeme  * Redistribution and use in source and binary forms, with or without
11031bec6dSxtraeme  * modification, are permitted provided that the following conditions
12031bec6dSxtraeme  * are met:
13031bec6dSxtraeme  * 1. Redistributions of source code must retain the above copyright
14031bec6dSxtraeme  *    notice, this list of conditions and the following disclaimer.
15031bec6dSxtraeme  * 2. Redistributions in binary form must reproduce the above copyright
16031bec6dSxtraeme  *    notice, this list of conditions and the following disclaimer in the
17031bec6dSxtraeme  *    documentation and/or other materials provided with the distribution.
18031bec6dSxtraeme  *
19031bec6dSxtraeme  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20031bec6dSxtraeme  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21031bec6dSxtraeme  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22031bec6dSxtraeme  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23031bec6dSxtraeme  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24031bec6dSxtraeme  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25031bec6dSxtraeme  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26031bec6dSxtraeme  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27031bec6dSxtraeme  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28031bec6dSxtraeme  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29031bec6dSxtraeme  * POSSIBILITY OF SUCH DAMAGE.
30031bec6dSxtraeme  */
31031bec6dSxtraeme 
32031bec6dSxtraeme /*
33031bec6dSxtraeme  * Intel I/O Controller Hub (ICHn) LPC Interface Bridge driver
34031bec6dSxtraeme  *
35031bec6dSxtraeme  *  LPC Interface Bridge is basically a pcib (PCI-ISA Bridge), but has
36031bec6dSxtraeme  *  some power management and monitoring functions.
37ed77961aSpgoyette  *  Currently we support the watchdog timer, SpeedStep (on some systems),
38ed77961aSpgoyette  *  the gpio interface, hpet timer, hardware random number generator,
39031bec6dSxtraeme  *  and the power management timer.
40031bec6dSxtraeme  */
41031bec6dSxtraeme 
42031bec6dSxtraeme #include <sys/cdefs.h>
43*0136eacdShans __KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.62 2024/12/18 18:18:30 hans Exp $");
44031bec6dSxtraeme 
45031bec6dSxtraeme #include <sys/types.h>
46031bec6dSxtraeme #include <sys/param.h>
47031bec6dSxtraeme #include <sys/systm.h>
48031bec6dSxtraeme #include <sys/device.h>
49031bec6dSxtraeme #include <sys/sysctl.h>
504c1d81b2Sjmcneill #include <sys/timetc.h>
51bd5cb774Sjakllsch #include <sys/gpio.h>
52391925c7Sdyoung #include <sys/bus.h>
53031bec6dSxtraeme 
54031bec6dSxtraeme #include <dev/pci/pcivar.h>
55031bec6dSxtraeme #include <dev/pci/pcireg.h>
56031bec6dSxtraeme #include <dev/pci/pcidevs.h>
57031bec6dSxtraeme 
58bd5cb774Sjakllsch #include <dev/gpio/gpiovar.h>
59031bec6dSxtraeme 
60031bec6dSxtraeme #include <dev/ic/acpipmtimer.h>
614c1d81b2Sjmcneill #include <dev/ic/i82801lpcreg.h>
62d7620599Sjruoho #include <dev/ic/i82801lpcvar.h>
634c1d81b2Sjmcneill #include <dev/ic/hpetreg.h>
644c1d81b2Sjmcneill #include <dev/ic/hpetvar.h>
654c1d81b2Sjmcneill 
66ed77961aSpgoyette #include <arch/x86/pci/tco.h>
67ed77961aSpgoyette 
684956be1fSmartin #include "pcibvar.h"
69bd5cb774Sjakllsch #include "gpio.h"
70897b6b7dSjakllsch #include "fwhrng.h"
71bd5cb774Sjakllsch 
72bd5cb774Sjakllsch #define LPCIB_GPIO_NPINS 64
73031bec6dSxtraeme 
74031bec6dSxtraeme struct lpcib_softc {
754956be1fSmartin 	/* we call pcibattach() which assumes this starts like this: */
764956be1fSmartin 	struct pcib_softc	sc_pcib;
77031bec6dSxtraeme 
784c1d81b2Sjmcneill 	struct pci_attach_args	sc_pa;
794c1d81b2Sjmcneill 	int			sc_has_rcba;
804c1d81b2Sjmcneill 	int			sc_has_ich5_hpet;
814c1d81b2Sjmcneill 
824c1d81b2Sjmcneill 	/* RCBA */
834c1d81b2Sjmcneill 	bus_space_tag_t		sc_rcbat;
844c1d81b2Sjmcneill 	bus_space_handle_t	sc_rcbah;
854c1d81b2Sjmcneill 	pcireg_t		sc_rcba_reg;
864c1d81b2Sjmcneill 
87ed77961aSpgoyette 	/* Power management variables. */
8844da6efcSriastradh 	bus_space_tag_t		sc_pmt;
8944da6efcSriastradh 	bus_space_handle_t	sc_pmh;
905a395386Sdyoung 	bus_size_t		sc_iosize;
91031bec6dSxtraeme 
921d6f2ebdSriastradh 	/* TCO variables. */
931d6f2ebdSriastradh 	bus_space_tag_t		sc_tcot;
941d6f2ebdSriastradh 	bus_space_handle_t	sc_tcoh;
951d6f2ebdSriastradh 	bus_size_t		sc_tcosz;
961d6f2ebdSriastradh 
974c1d81b2Sjmcneill 	/* HPET variables. */
984c1d81b2Sjmcneill 	uint32_t		sc_hpet_reg;
994c1d81b2Sjmcneill 
100bd5cb774Sjakllsch #if NGPIO > 0
101bd5cb774Sjakllsch 	device_t		sc_gpiobus;
102bd5cb774Sjakllsch 	kmutex_t		sc_gpio_mtx;
103bd5cb774Sjakllsch 	bus_space_tag_t		sc_gpio_iot;
104bd5cb774Sjakllsch 	bus_space_handle_t	sc_gpio_ioh;
105bd5cb774Sjakllsch 	bus_size_t		sc_gpio_ios;
106bd5cb774Sjakllsch 	struct gpio_chipset_tag	sc_gpio_gc;
107bd5cb774Sjakllsch 	gpio_pin_t		sc_gpio_pins[LPCIB_GPIO_NPINS];
108bd5cb774Sjakllsch #endif
109bd5cb774Sjakllsch 
110897b6b7dSjakllsch #if NFWHRNG > 0
111897b6b7dSjakllsch 	device_t		sc_fwhbus;
112897b6b7dSjakllsch #endif
113897b6b7dSjakllsch 
114ace0f41fSjoerg 	/* Speedstep */
115ace0f41fSjoerg 	pcireg_t		sc_pmcon_orig;
116ace0f41fSjoerg 
117031bec6dSxtraeme 	/* Power management */
11898da2f9cSdrochner 	pcireg_t		sc_pirq[2];
1194c1d81b2Sjmcneill 	pcireg_t		sc_pmcon;
1204c1d81b2Sjmcneill 	pcireg_t		sc_fwhsel2;
1215a395386Sdyoung 
1225a395386Sdyoung 	/* Child devices */
123ed77961aSpgoyette 	device_t		sc_tco;
1245a395386Sdyoung 	device_t		sc_hpetbus;
1255a395386Sdyoung 	acpipmtimer_t		sc_pmtimer;
1265a395386Sdyoung 	pcireg_t		sc_acpi_cntl;
1275a395386Sdyoung 
1285a395386Sdyoung 	struct sysctllog	*sc_log;
129031bec6dSxtraeme };
130031bec6dSxtraeme 
131d749da91Sxtraeme static int lpcibmatch(device_t, cfdata_t, void *);
132d749da91Sxtraeme static void lpcibattach(device_t, device_t, void *);
1335a395386Sdyoung static int lpcibdetach(device_t, int);
1345a395386Sdyoung static void lpcibchilddet(device_t, device_t);
1355a395386Sdyoung static int lpcibrescan(device_t, const char *, const int *);
136c1b390d4Sdyoung static bool lpcib_suspend(device_t, const pmf_qual_t *);
137c1b390d4Sdyoung static bool lpcib_resume(device_t, const pmf_qual_t *);
138ace0f41fSjoerg static bool lpcib_shutdown(device_t, int);
139031bec6dSxtraeme 
140d749da91Sxtraeme static void pmtimer_configure(device_t);
141f653f102Sriastradh static void pmtimer_unconfigure(device_t, int);
142031bec6dSxtraeme 
143d749da91Sxtraeme static void tcotimer_configure(device_t);
144031bec6dSxtraeme 
145d749da91Sxtraeme static void speedstep_configure(device_t);
1465a395386Sdyoung static void speedstep_unconfigure(device_t);
147031bec6dSxtraeme static int speedstep_sysctl_helper(SYSCTLFN_ARGS);
148031bec6dSxtraeme 
149d749da91Sxtraeme static void lpcib_hpet_configure(device_t);
1504c1d81b2Sjmcneill 
151bd5cb774Sjakllsch #if NGPIO > 0
152bd5cb774Sjakllsch static void lpcib_gpio_configure(device_t);
153f653f102Sriastradh static void lpcib_gpio_unconfigure(device_t);
154bd5cb774Sjakllsch static int lpcib_gpio_pin_read(void *, int);
155bd5cb774Sjakllsch static void lpcib_gpio_pin_write(void *, int, int);
156bd5cb774Sjakllsch static void lpcib_gpio_pin_ctl(void *, int, int);
157bd5cb774Sjakllsch #endif
158bd5cb774Sjakllsch 
159897b6b7dSjakllsch #if NFWHRNG > 0
160897b6b7dSjakllsch static void lpcib_fwh_configure(device_t);
161897b6b7dSjakllsch #endif
162897b6b7dSjakllsch 
163031bec6dSxtraeme struct lpcib_softc *speedstep_cookie;	/* XXX */
164031bec6dSxtraeme 
1655a395386Sdyoung CFATTACH_DECL2_NEW(ichlpcib, sizeof(struct lpcib_softc),
1665a395386Sdyoung     lpcibmatch, lpcibattach, lpcibdetach, NULL, lpcibrescan, lpcibchilddet);
167031bec6dSxtraeme 
168de7002fcSmaxv static const struct lpcib_device {
1694c1d81b2Sjmcneill 	pcireg_t vendor, product;
1704c1d81b2Sjmcneill 	int has_rcba;
1714c1d81b2Sjmcneill 	int has_ich5_hpet;
1724c1d81b2Sjmcneill } lpcib_devices[] = {
1734ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3400_LPC, 1, 0 },
1744ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3420_LPC, 1, 0 },
1754ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3450_LPC, 1, 0 },
1764ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC, 1, 0 },
177beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_63XXESB_LPC, 1, 0 },
1784c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC, 0, 0 },
1793e117bf4Sjakllsch 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC, 0, 0 },
1804c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC, 0, 0 },
1814c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC, 0, 0 },
1824c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC, 0, 0 },
1834c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC, 0, 0 },
1844c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC, 0, 0 },
1854ad86a18Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC, 0, 0 },
1864ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC, 0, 1 },
1874c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC, 0, 1 },
1884c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC, 1, 0 },
1894c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC, 1, 0 },
1904c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801G_LPC, 1, 0 },
1914c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC, 1, 0 },
1924ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC, 1, 0 },
1934c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC, 1, 0 },
1944c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC, 1, 0 },
1954c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HEM_LPC, 1, 0 },
1964c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HH_LPC, 1, 0 },
1974c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HO_LPC, 1, 0 },
1984c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC, 1, 0 },
199beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC, 1, 0 },
2004c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC, 1, 0 },
201beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IM_LPC, 1, 0 },
2024c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC, 1, 0 },
2034c1d81b2Sjmcneill 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC, 1, 0 },
2047e5810aaSnjoly 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IEM_LPC, 1, 0 },
205beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JD_LPC, 1, 0 },
206beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JDO_LPC, 1, 0 },
207beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JIB_LPC, 1, 0 },
208beb6fb74Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JIR_LPC, 1, 0 },
209abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C202_LPC, 1, 0 },
210abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C204_LPC, 1, 0 },
211abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C206_LPC, 1, 0 },
2124ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C216_LPC, 1, 0 },
2134ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_NM10_LPC, 1, 0 },
2144ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H55_LPC, 1, 0 },
2154ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H57_LPC, 1, 0 },
2164ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM55_LPC, 1, 0 },
2174ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM57_LPC, 1, 0 },
2184ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_P55_LPC, 1, 0 },
2194ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PM55_LPC, 1, 0 },
2204ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q57_LPC, 1, 0 },
2214ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QM57_LPC, 1, 0 },
2224ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QS57_LPC, 1, 0 },
2234ebf4680Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_B65_LPC, 1, 0 },
224abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H61_LPC, 1, 0 },
225abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H67_LPC, 1, 0 },
226abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM65_LPC, 1, 0 },
227abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM67_LPC, 1, 0 },
228abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_P67_LPC, 1, 0 },
229abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q65_LPC, 1, 0 },
230abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q67_LPC, 1, 0 },
231abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QM67_LPC, 1, 0 },
232abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QS67_LPC, 1, 0 },
233abdd14beSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_UM67_LPC, 1, 0 },
2343c10ad53Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z68_LPC, 1, 0 },
235749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_B75_LPC, 1, 0 },
236749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H77_LPC, 1, 0 },
237749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM70_LPC, 1, 0 },
238749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM75_LPC, 1, 0 },
239749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM76_LPC, 1, 0 },
240749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_HM77_LPC, 1, 0 },
241749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_QM77_LPC, 1, 0 },
242749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_QS77_LPC, 1, 0 },
243749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_MOBILE_UM77_LPC, 1, 0 },
244749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_NM70_LPC, 1, 0 },
245749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q75_LPC, 1, 0 },
246749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q77_LPC, 1, 0 },
247749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z75_LPC, 1, 0 },
248749b4364Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z77_LPC, 1, 0 },
24923a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z87_LPC, 1, 0 },
25023a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z85_LPC, 1, 0 },
25123a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM86_LPC, 1, 0 },
25223a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H87_LPC, 1, 0 },
25323a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_HM87_LPC, 1, 0 },
25423a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q85_LPC, 1, 0 },
25523a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Q87_LPC, 1, 0 },
25623a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_QM87_LPC, 1, 0 },
25723a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_B85_LPC, 1, 0 },
258521394bfSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H97_LPC, 1, 0 },
259521394bfSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_Z97_LPC, 1, 0 },
260d58067dcSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X99_LPC, 1, 0 },
261d58067dcSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X99_LPC_2, 1, 0 },
2628d7cf22eSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_CORE5G_M_LPC_4, 1, 0 },
2638d7cf22eSmsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_CORE5G_M_LPC_7, 1, 0 },
26423a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C222_LPC, 1, 0 },
26523a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C224_LPC, 1, 0 },
26623a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C226_LPC, 1, 0 },
26723a617d6Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_H81_LPC, 1, 0 },
2684ac55101Sriastradh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C600_LPC, 1, 0 },
2691f3e38b7Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_DH89XXCC_LPC, 1, 0 },
2701f3e38b7Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_DH89XXCL_LPC, 1, 0 },
271*0136eacdShans 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_BSW_PCU_LPC, 0, 1 },
2724c6bef97Smsaitoh #if 0
273f8748bc5Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_1, 1, 0 },
274f8748bc5Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_2, 1, 0 },
275f8748bc5Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_3, 1, 0 },
276f8748bc5Smsaitoh 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C2000_PCU_4, 1, 0 },
2774c6bef97Smsaitoh #endif
278019af4c0Sjoerg 
2794c1d81b2Sjmcneill 	{ 0, 0, 0, 0 },
2804c1d81b2Sjmcneill };
2814c1d81b2Sjmcneill 
282031bec6dSxtraeme /*
2835b9bdcf5Sjakllsch  * Allow user to enable GPIO functionality if they really need it.  The
2845b9bdcf5Sjakllsch  * vast majority of systems with an ICH should not expose GPIO to the
2855b9bdcf5Sjakllsch  * kernel or user.  In at least one instance the gpio_resume() handler
2865b9bdcf5Sjakllsch  * on ICH GPIO was found to sabotage S3 suspend/resume.
2875b9bdcf5Sjakllsch  */
2885b9bdcf5Sjakllsch int	ichlpcib_gpio_disable = 1;
2895b9bdcf5Sjakllsch 
2905b9bdcf5Sjakllsch /*
291031bec6dSxtraeme  * Autoconf callbacks.
292031bec6dSxtraeme  */
293031bec6dSxtraeme static int
294d749da91Sxtraeme lpcibmatch(device_t parent, cfdata_t match, void *aux)
295031bec6dSxtraeme {
296031bec6dSxtraeme 	struct pci_attach_args *pa = aux;
297de7002fcSmaxv 	const struct lpcib_device *lpcib_dev;
298031bec6dSxtraeme 
299031bec6dSxtraeme 	/* We are ISA bridge, of course */
300031bec6dSxtraeme 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
301031bec6dSxtraeme 	    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA)
302031bec6dSxtraeme 		return 0;
303031bec6dSxtraeme 
3044c1d81b2Sjmcneill 	for (lpcib_dev = lpcib_devices; lpcib_dev->vendor; ++lpcib_dev) {
3054c1d81b2Sjmcneill 		if (PCI_VENDOR(pa->pa_id) == lpcib_dev->vendor &&
3064c1d81b2Sjmcneill 		    PCI_PRODUCT(pa->pa_id) == lpcib_dev->product)
307031bec6dSxtraeme 			return 10;
308031bec6dSxtraeme 	}
309031bec6dSxtraeme 
310031bec6dSxtraeme 	return 0;
311031bec6dSxtraeme }
312031bec6dSxtraeme 
313031bec6dSxtraeme static void
314d749da91Sxtraeme lpcibattach(device_t parent, device_t self, void *aux)
315031bec6dSxtraeme {
316031bec6dSxtraeme 	struct pci_attach_args *pa = aux;
3174c1d81b2Sjmcneill 	struct lpcib_softc *sc = device_private(self);
318de7002fcSmaxv 	const struct lpcib_device *lpcib_dev;
319453034ecSmsaitoh 	pcireg_t pmbase;
320031bec6dSxtraeme 
3214c1d81b2Sjmcneill 	sc->sc_pa = *pa;
3224c1d81b2Sjmcneill 
3234c1d81b2Sjmcneill 	for (lpcib_dev = lpcib_devices; lpcib_dev->vendor; ++lpcib_dev) {
3244c1d81b2Sjmcneill 		if (PCI_VENDOR(pa->pa_id) != lpcib_dev->vendor ||
3254c1d81b2Sjmcneill 		    PCI_PRODUCT(pa->pa_id) != lpcib_dev->product)
3264c1d81b2Sjmcneill 			continue;
3274c1d81b2Sjmcneill 		sc->sc_has_rcba = lpcib_dev->has_rcba;
3284c1d81b2Sjmcneill 		sc->sc_has_ich5_hpet = lpcib_dev->has_ich5_hpet;
3294c1d81b2Sjmcneill 		break;
3304c1d81b2Sjmcneill 	}
331031bec6dSxtraeme 
332031bec6dSxtraeme 	pcibattach(parent, self, aux);
333031bec6dSxtraeme 
334031bec6dSxtraeme 	/*
335031bec6dSxtraeme 	 * Part of our I/O registers are used as ACPI PM regs.
336031bec6dSxtraeme 	 * Since our ACPI subsystem accesses the I/O space directly so far,
337031bec6dSxtraeme 	 * we do not have to bother bus_space I/O map confliction.
33883c4f0e1Smsaitoh 	 *
33983c4f0e1Smsaitoh 	 * The PMBASE register is alike PCI BAR but not completely compatible
34083c4f0e1Smsaitoh 	 * with it. The PMBASE define the base address and the type but
341453034ecSmsaitoh 	 * not describe the size. The value of the register may be lower
342453034ecSmsaitoh 	 * than LPCIB_PCI_PM_SIZE. It makes impossible to use
343453034ecSmsaitoh 	 * pci_mapreg_submap() because the function does range check.
344031bec6dSxtraeme 	 */
34544da6efcSriastradh 	sc->sc_pmt = pa->pa_iot;
346453034ecSmsaitoh 	pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, LPCIB_PCI_PMBASE);
34744da6efcSriastradh 	if (bus_space_map(sc->sc_pmt, PCI_MAPREG_IO_ADDR(pmbase),
34844da6efcSriastradh 		LPCIB_PCI_PM_SIZE, 0, &sc->sc_pmh) != 0) {
349453034ecSmsaitoh 		aprint_error_dev(self,
350453034ecSmsaitoh 		    "can't map power management i/o space\n");
351031bec6dSxtraeme 		return;
352031bec6dSxtraeme 	}
353031bec6dSxtraeme 
3541d6f2ebdSriastradh 	if (bus_space_subregion(sc->sc_pmt, sc->sc_pmh, PMC_TCO_BASE,
3551d6f2ebdSriastradh 		TCO_REGSIZE, &sc->sc_tcoh)) {
3561d6f2ebdSriastradh 		aprint_error_dev(self, "can't map TCO space\n");
3571d6f2ebdSriastradh 	} else {
3581d6f2ebdSriastradh 		sc->sc_tcot = sc->sc_pmt;
3591d6f2ebdSriastradh 	}
3601d6f2ebdSriastradh 
361ace0f41fSjoerg 	sc->sc_pmcon_orig = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
362ace0f41fSjoerg 	    LPCIB_PCI_GEN_PMCON_1);
363ace0f41fSjoerg 
3644c1d81b2Sjmcneill 	/* For ICH6 and later, always enable RCBA */
3654c1d81b2Sjmcneill 	if (sc->sc_has_rcba) {
3664c1d81b2Sjmcneill 		pcireg_t rcba;
367031bec6dSxtraeme 
3684c1d81b2Sjmcneill 		sc->sc_rcbat = sc->sc_pa.pa_memt;
369031bec6dSxtraeme 
3704956be1fSmartin 		rcba = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
3714956be1fSmartin 		    LPCIB_RCBA);
3724c1d81b2Sjmcneill 		if ((rcba & LPCIB_RCBA_EN) == 0) {
3739463b023Sjakllsch 			aprint_error_dev(self, "RCBA is not enabled\n");
3744c1d81b2Sjmcneill 			return;
3754c1d81b2Sjmcneill 		}
3764c1d81b2Sjmcneill 		rcba &= ~LPCIB_RCBA_EN;
3774c1d81b2Sjmcneill 
3784c1d81b2Sjmcneill 		if (bus_space_map(sc->sc_rcbat, rcba, LPCIB_RCBA_SIZE, 0,
3794c1d81b2Sjmcneill 			&sc->sc_rcbah)) {
3809463b023Sjakllsch 			aprint_error_dev(self, "RCBA could not be mapped\n");
3814c1d81b2Sjmcneill 			return;
3824c1d81b2Sjmcneill 		}
383031bec6dSxtraeme 	}
384031bec6dSxtraeme 
3854c1d81b2Sjmcneill 	/* Set up the power management timer. */
386d749da91Sxtraeme 	pmtimer_configure(self);
3874c1d81b2Sjmcneill 
3884c1d81b2Sjmcneill 	/* Set up the TCO (watchdog). */
389d749da91Sxtraeme 	tcotimer_configure(self);
3904c1d81b2Sjmcneill 
3914c1d81b2Sjmcneill 	/* Set up SpeedStep. */
392d749da91Sxtraeme 	speedstep_configure(self);
3934c1d81b2Sjmcneill 
3944c1d81b2Sjmcneill 	/* Set up HPET. */
395d749da91Sxtraeme 	lpcib_hpet_configure(self);
3964c1d81b2Sjmcneill 
397bd5cb774Sjakllsch #if NGPIO > 0
398bd5cb774Sjakllsch 	/* Set up GPIO */
399bd5cb774Sjakllsch 	lpcib_gpio_configure(self);
400bd5cb774Sjakllsch #endif
401bd5cb774Sjakllsch 
402897b6b7dSjakllsch #if NFWHRNG > 0
403897b6b7dSjakllsch 	lpcib_fwh_configure(self);
404897b6b7dSjakllsch #endif
405897b6b7dSjakllsch 
4064c1d81b2Sjmcneill 	/* Install power handler */
407ace0f41fSjoerg 	if (!pmf_device_register1(self, lpcib_suspend, lpcib_resume,
408ace0f41fSjoerg 		lpcib_shutdown))
4094c1d81b2Sjmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
4104c1d81b2Sjmcneill }
4114c1d81b2Sjmcneill 
4125a395386Sdyoung static void
4135a395386Sdyoung lpcibchilddet(device_t self, device_t child)
4145a395386Sdyoung {
4155a395386Sdyoung 	struct lpcib_softc *sc = device_private(self);
4165a395386Sdyoung 	uint32_t val;
4175a395386Sdyoung 
418897b6b7dSjakllsch #if NFWHRNG > 0
419897b6b7dSjakllsch 	if (sc->sc_fwhbus == child) {
420897b6b7dSjakllsch 		sc->sc_fwhbus = NULL;
421897b6b7dSjakllsch 		return;
422897b6b7dSjakllsch 	}
423897b6b7dSjakllsch #endif
424ff243da7Sjakllsch #if NGPIO > 0
425bd5cb774Sjakllsch 	if (sc->sc_gpiobus == child) {
426bd5cb774Sjakllsch 		sc->sc_gpiobus = NULL;
427bd5cb774Sjakllsch 		return;
428bd5cb774Sjakllsch 	}
429ff243da7Sjakllsch #endif
430ed77961aSpgoyette 	if (sc->sc_tco == child) {
431ed77961aSpgoyette 		sc->sc_tco = NULL;
432ed77961aSpgoyette 		return;
433ed77961aSpgoyette 	}
434ed77961aSpgoyette 
4355a395386Sdyoung 	if (sc->sc_hpetbus != child) {
4365a395386Sdyoung 		pcibchilddet(self, child);
4375a395386Sdyoung 		return;
4385a395386Sdyoung 	}
4395a395386Sdyoung 	sc->sc_hpetbus = NULL;
4405a395386Sdyoung 	if (sc->sc_has_ich5_hpet) {
4415a395386Sdyoung 		val = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
4425a395386Sdyoung 		    LPCIB_PCI_GEN_CNTL);
4435a395386Sdyoung 		switch (val & LPCIB_ICH5_HPTC_WIN_MASK) {
4445a395386Sdyoung 		case LPCIB_ICH5_HPTC_0000:
4455a395386Sdyoung 		case LPCIB_ICH5_HPTC_1000:
4465a395386Sdyoung 		case LPCIB_ICH5_HPTC_2000:
4475a395386Sdyoung 		case LPCIB_ICH5_HPTC_3000:
4485a395386Sdyoung 			break;
4495a395386Sdyoung 		default:
4505a395386Sdyoung 			return;
4515a395386Sdyoung 		}
4525a395386Sdyoung 		val &= ~LPCIB_ICH5_HPTC_EN;
4535a395386Sdyoung 		pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
4545a395386Sdyoung 		    LPCIB_PCI_GEN_CNTL, val);
4555a395386Sdyoung 	} else if (sc->sc_has_rcba) {
4565a395386Sdyoung 		val = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
4575a395386Sdyoung 		    LPCIB_RCBA_HPTC);
4585a395386Sdyoung 		switch (val & LPCIB_RCBA_HPTC_WIN_MASK) {
4595a395386Sdyoung 		case LPCIB_RCBA_HPTC_0000:
4605a395386Sdyoung 		case LPCIB_RCBA_HPTC_1000:
4615a395386Sdyoung 		case LPCIB_RCBA_HPTC_2000:
4625a395386Sdyoung 		case LPCIB_RCBA_HPTC_3000:
4635a395386Sdyoung 			break;
4645a395386Sdyoung 		default:
4655a395386Sdyoung 			return;
4665a395386Sdyoung 		}
4675a395386Sdyoung 		val &= ~LPCIB_RCBA_HPTC_EN;
4685a395386Sdyoung 		bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_HPTC,
4695a395386Sdyoung 		    val);
4705a395386Sdyoung 	}
4715a395386Sdyoung }
4725a395386Sdyoung 
4735a395386Sdyoung static int
4745a395386Sdyoung lpcibrescan(device_t self, const char *ifattr, const int *locators)
4755a395386Sdyoung {
4765a395386Sdyoung 	struct lpcib_softc *sc = device_private(self);
4775a395386Sdyoung 
478ed77961aSpgoyette 	if (ifattr_match(ifattr, "tcoichbus") && sc->sc_tco == NULL)
479ed77961aSpgoyette 		tcotimer_configure(self);
480ed77961aSpgoyette 
481897b6b7dSjakllsch #if NFWHRNG > 0
482897b6b7dSjakllsch 	if (ifattr_match(ifattr, "fwhichbus") && sc->sc_fwhbus == NULL)
483897b6b7dSjakllsch 		lpcib_fwh_configure(self);
484897b6b7dSjakllsch #endif
485897b6b7dSjakllsch 
4865a395386Sdyoung 	if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL)
4875a395386Sdyoung 		lpcib_hpet_configure(self);
4885a395386Sdyoung 
489bd5cb774Sjakllsch #if NGPIO > 0
490bd5cb774Sjakllsch 	if (ifattr_match(ifattr, "gpiobus") && sc->sc_gpiobus == NULL)
491bd5cb774Sjakllsch 		lpcib_gpio_configure(self);
492bd5cb774Sjakllsch #endif
493bd5cb774Sjakllsch 
4945a395386Sdyoung 	return pcibrescan(self, ifattr, locators);
4955a395386Sdyoung }
4965a395386Sdyoung 
4975a395386Sdyoung static int
4985a395386Sdyoung lpcibdetach(device_t self, int flags)
4995a395386Sdyoung {
5005a395386Sdyoung 	struct lpcib_softc *sc = device_private(self);
501f653f102Sriastradh 	int error;
502f653f102Sriastradh 
503f653f102Sriastradh 	error = config_detach_children(self, flags);
504f653f102Sriastradh 	if (error)
505f653f102Sriastradh 		return error;
5065a395386Sdyoung 
5075a395386Sdyoung 	pmf_device_deregister(self);
5085a395386Sdyoung 
509bd5cb774Sjakllsch #if NGPIO > 0
510f653f102Sriastradh 	lpcib_gpio_unconfigure(self);
511bd5cb774Sjakllsch #endif
512bd5cb774Sjakllsch 
5135a395386Sdyoung 	/* Set up SpeedStep. */
5145a395386Sdyoung 	speedstep_unconfigure(self);
5155a395386Sdyoung 
516f653f102Sriastradh 	pmtimer_unconfigure(self, flags);
5175a395386Sdyoung 
5185a395386Sdyoung 	if (sc->sc_has_rcba)
5195a395386Sdyoung 		bus_space_unmap(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_SIZE);
5205a395386Sdyoung 
52144da6efcSriastradh 	bus_space_unmap(sc->sc_pmt, sc->sc_pmh, sc->sc_iosize);
5225a395386Sdyoung 
523f653f102Sriastradh 	error = pcibdetach(self, flags);
524f653f102Sriastradh 	KASSERTMSG(error == 0, "error=%d", error);
525f653f102Sriastradh 
526f653f102Sriastradh 	return 0;
5275a395386Sdyoung }
5285a395386Sdyoung 
5294c1d81b2Sjmcneill static bool
530ace0f41fSjoerg lpcib_shutdown(device_t dv, int howto)
531ace0f41fSjoerg {
532ace0f41fSjoerg 	struct lpcib_softc *sc = device_private(dv);
533ace0f41fSjoerg 
534ace0f41fSjoerg 	pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
535ace0f41fSjoerg 	    LPCIB_PCI_GEN_PMCON_1, sc->sc_pmcon_orig);
536ace0f41fSjoerg 
537ace0f41fSjoerg 	return true;
538ace0f41fSjoerg }
539ace0f41fSjoerg 
540ace0f41fSjoerg static bool
541c1b390d4Sdyoung lpcib_suspend(device_t dv, const pmf_qual_t *qual)
542031bec6dSxtraeme {
5434c1d81b2Sjmcneill 	struct lpcib_softc *sc = device_private(dv);
5444956be1fSmartin 	pci_chipset_tag_t pc = sc->sc_pcib.sc_pc;
5454956be1fSmartin 	pcitag_t tag = sc->sc_pcib.sc_tag;
546031bec6dSxtraeme 
547031bec6dSxtraeme 	/* capture PIRQ routing control registers */
548031bec6dSxtraeme 	sc->sc_pirq[0] = pci_conf_read(pc, tag, LPCIB_PCI_PIRQA_ROUT);
54998da2f9cSdrochner 	sc->sc_pirq[1] = pci_conf_read(pc, tag, LPCIB_PCI_PIRQE_ROUT);
550031bec6dSxtraeme 
5514c1d81b2Sjmcneill 	sc->sc_pmcon = pci_conf_read(pc, tag, LPCIB_PCI_GEN_PMCON_1);
5524c1d81b2Sjmcneill 	sc->sc_fwhsel2 = pci_conf_read(pc, tag, LPCIB_PCI_GEN_STA);
553031bec6dSxtraeme 
5544c1d81b2Sjmcneill 	if (sc->sc_has_rcba) {
5554c1d81b2Sjmcneill 		sc->sc_rcba_reg = pci_conf_read(pc, tag, LPCIB_RCBA);
5564c1d81b2Sjmcneill 		sc->sc_hpet_reg = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
5574c1d81b2Sjmcneill 		    LPCIB_RCBA_HPTC);
5584c1d81b2Sjmcneill 	} else if (sc->sc_has_ich5_hpet) {
5594c1d81b2Sjmcneill 		sc->sc_hpet_reg = pci_conf_read(pc, tag, LPCIB_PCI_GEN_CNTL);
5604c1d81b2Sjmcneill 	}
5614c1d81b2Sjmcneill 
5624c1d81b2Sjmcneill 	return true;
5634c1d81b2Sjmcneill }
5644c1d81b2Sjmcneill 
5654c1d81b2Sjmcneill static bool
566c1b390d4Sdyoung lpcib_resume(device_t dv, const pmf_qual_t *qual)
5674c1d81b2Sjmcneill {
5684c1d81b2Sjmcneill 	struct lpcib_softc *sc = device_private(dv);
5694956be1fSmartin 	pci_chipset_tag_t pc = sc->sc_pcib.sc_pc;
5704956be1fSmartin 	pcitag_t tag = sc->sc_pcib.sc_tag;
571031bec6dSxtraeme 
572031bec6dSxtraeme 	/* restore PIRQ routing control registers */
573031bec6dSxtraeme 	pci_conf_write(pc, tag, LPCIB_PCI_PIRQA_ROUT, sc->sc_pirq[0]);
57498da2f9cSdrochner 	pci_conf_write(pc, tag, LPCIB_PCI_PIRQE_ROUT, sc->sc_pirq[1]);
575031bec6dSxtraeme 
5764c1d81b2Sjmcneill 	pci_conf_write(pc, tag, LPCIB_PCI_GEN_PMCON_1, sc->sc_pmcon);
5774c1d81b2Sjmcneill 	pci_conf_write(pc, tag, LPCIB_PCI_GEN_STA, sc->sc_fwhsel2);
5784c1d81b2Sjmcneill 
5794c1d81b2Sjmcneill 	if (sc->sc_has_rcba) {
5804c1d81b2Sjmcneill 		pci_conf_write(pc, tag, LPCIB_RCBA, sc->sc_rcba_reg);
5814c1d81b2Sjmcneill 		bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_HPTC,
5824c1d81b2Sjmcneill 		    sc->sc_hpet_reg);
5834c1d81b2Sjmcneill 	} else if (sc->sc_has_ich5_hpet) {
5844c1d81b2Sjmcneill 		pci_conf_write(pc, tag, LPCIB_PCI_GEN_CNTL, sc->sc_hpet_reg);
585031bec6dSxtraeme 	}
5864c1d81b2Sjmcneill 
5874c1d81b2Sjmcneill 	return true;
588031bec6dSxtraeme }
589031bec6dSxtraeme 
590031bec6dSxtraeme /*
591031bec6dSxtraeme  * Initialize the power management timer.
592031bec6dSxtraeme  */
593031bec6dSxtraeme static void
594d749da91Sxtraeme pmtimer_configure(device_t self)
595031bec6dSxtraeme {
596d749da91Sxtraeme 	struct lpcib_softc *sc = device_private(self);
597031bec6dSxtraeme 	pcireg_t control;
598031bec6dSxtraeme 
599031bec6dSxtraeme 	/*
600031bec6dSxtraeme 	 * Check if power management I/O space is enabled and enable the ACPI_EN
601031bec6dSxtraeme 	 * bit if it's disabled.
602031bec6dSxtraeme 	 */
6034956be1fSmartin 	control = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
6044956be1fSmartin 	    LPCIB_PCI_ACPI_CNTL);
6055a395386Sdyoung 	sc->sc_acpi_cntl = control;
606031bec6dSxtraeme 	if ((control & LPCIB_PCI_ACPI_CNTL_EN) == 0) {
607031bec6dSxtraeme 		control |= LPCIB_PCI_ACPI_CNTL_EN;
6084956be1fSmartin 		pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
6094956be1fSmartin 		    LPCIB_PCI_ACPI_CNTL, control);
610031bec6dSxtraeme 	}
611031bec6dSxtraeme 
612031bec6dSxtraeme 	/* Attach our PM timer with the generic acpipmtimer function */
61344da6efcSriastradh 	sc->sc_pmtimer = acpipmtimer_attach(self, sc->sc_pmt, sc->sc_pmh,
614a350b83aSriastradh 	    PMC_PM1_TMR, 0);
615031bec6dSxtraeme }
616031bec6dSxtraeme 
617f653f102Sriastradh static void
6185a395386Sdyoung pmtimer_unconfigure(device_t self, int flags)
6195a395386Sdyoung {
6205a395386Sdyoung 	struct lpcib_softc *sc = device_private(self);
621f653f102Sriastradh 	int error __diagused;
6225a395386Sdyoung 
623f653f102Sriastradh 	if (sc->sc_pmtimer != NULL) {
624f653f102Sriastradh 		error = acpipmtimer_detach(sc->sc_pmtimer, flags);
625f653f102Sriastradh 		KASSERTMSG(error == 0, "error=%d", error);
626f653f102Sriastradh 	}
6275a395386Sdyoung 
6285a395386Sdyoung 	pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
6295a395386Sdyoung 	    LPCIB_PCI_ACPI_CNTL, sc->sc_acpi_cntl);
6305a395386Sdyoung }
6315a395386Sdyoung 
632031bec6dSxtraeme /*
633ed77961aSpgoyette  * Configure the watchdog timer.
634031bec6dSxtraeme  */
635031bec6dSxtraeme static void
636d749da91Sxtraeme tcotimer_configure(device_t self)
637031bec6dSxtraeme {
638d749da91Sxtraeme 	struct lpcib_softc *sc = device_private(self);
6394fd73fd7Sriastradh 	struct tco_attach_args arg;
640031bec6dSxtraeme 
6414679bb91Sriastradh 	if (sc->sc_has_rcba)
6424679bb91Sriastradh 		arg.ta_version = TCO_VERSION_RCBA;
6434679bb91Sriastradh 	else
6444679bb91Sriastradh 		arg.ta_version = TCO_VERSION_PCIB;
64544da6efcSriastradh 	arg.ta_pmt = sc->sc_pmt;
64644da6efcSriastradh 	arg.ta_pmh = sc->sc_pmh;
647ed77961aSpgoyette 	arg.ta_rcbat = sc->sc_rcbat;
648ed77961aSpgoyette 	arg.ta_rcbah = sc->sc_rcbah;
6494679bb91Sriastradh 	arg.ta_pcib = &sc->sc_pcib;
6501d6f2ebdSriastradh 	arg.ta_tcot = sc->sc_tcot;
6511d6f2ebdSriastradh 	arg.ta_tcoh = sc->sc_tcoh;
652ff5b821dSyamt 
6532685996bSthorpej 	sc->sc_tco = config_found(self, &arg, NULL,
654c7fb772bSthorpej 	    CFARGS(.iattr = "tcoichbus"));
655031bec6dSxtraeme }
656031bec6dSxtraeme 
6575a395386Sdyoung 
658031bec6dSxtraeme /*
659031bec6dSxtraeme  * Intel ICH SpeedStep support.
660031bec6dSxtraeme  */
661031bec6dSxtraeme #define SS_READ(sc, reg) \
66244da6efcSriastradh 	bus_space_read_1((sc)->sc_pmt, (sc)->sc_pmh, (reg))
663031bec6dSxtraeme #define SS_WRITE(sc, reg, val) \
66444da6efcSriastradh 	bus_space_write_1((sc)->sc_pmt, (sc)->sc_pmh, (reg), (val))
665031bec6dSxtraeme 
666031bec6dSxtraeme /*
667031bec6dSxtraeme  * Linux driver says that SpeedStep on older chipsets cause
668031bec6dSxtraeme  * lockups on Dell Inspiron 8000 and 8100.
669826af062Smrg  * It should also not be enabled on systems with the 82855GM
670826af062Smrg  * Hub, which typically have an EST-enabled CPU.
671031bec6dSxtraeme  */
672031bec6dSxtraeme static int
673d3e53912Sdyoung speedstep_bad_hb_check(const struct pci_attach_args *pa)
674031bec6dSxtraeme {
675031bec6dSxtraeme 
676031bec6dSxtraeme 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82815_FULL_HUB &&
677031bec6dSxtraeme 	    PCI_REVISION(pa->pa_class) < 5)
678031bec6dSxtraeme 		return 1;
679031bec6dSxtraeme 
680826af062Smrg 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82855GM_MCH)
681826af062Smrg 		return 1;
682826af062Smrg 
683031bec6dSxtraeme 	return 0;
684031bec6dSxtraeme }
685031bec6dSxtraeme 
686031bec6dSxtraeme static void
687d749da91Sxtraeme speedstep_configure(device_t self)
688031bec6dSxtraeme {
689d749da91Sxtraeme 	struct lpcib_softc *sc = device_private(self);
690031bec6dSxtraeme 	const struct sysctlnode	*node, *ssnode;
691031bec6dSxtraeme 	int rv;
692031bec6dSxtraeme 
693031bec6dSxtraeme 	/* Supported on ICH2-M, ICH3-M and ICH4-M.  */
6944ad86a18Smsaitoh 	if (PCI_PRODUCT(sc->sc_pa.pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
6954c1d81b2Sjmcneill 	    PCI_PRODUCT(sc->sc_pa.pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC ||
6964c1d81b2Sjmcneill 	    (PCI_PRODUCT(sc->sc_pa.pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC &&
6974c1d81b2Sjmcneill 		pci_find_device(&sc->sc_pa, speedstep_bad_hb_check) == 0)) {
6985a395386Sdyoung 		pcireg_t pmcon;
699031bec6dSxtraeme 
700031bec6dSxtraeme 		/* Enable SpeedStep if it isn't already enabled. */
7014956be1fSmartin 		pmcon = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
702031bec6dSxtraeme 		    LPCIB_PCI_GEN_PMCON_1);
703031bec6dSxtraeme 		if ((pmcon & LPCIB_PCI_GEN_PMCON_1_SS_EN) == 0)
7044956be1fSmartin 			pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
705031bec6dSxtraeme 			    LPCIB_PCI_GEN_PMCON_1,
706031bec6dSxtraeme 			    pmcon | LPCIB_PCI_GEN_PMCON_1_SS_EN);
707031bec6dSxtraeme 
708031bec6dSxtraeme 		/* Put in machdep.speedstep_state (0 for low, 1 for high). */
7095a395386Sdyoung 		if ((rv = sysctl_createv(&sc->sc_log, 0, NULL, &node,
710031bec6dSxtraeme 		    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
711031bec6dSxtraeme 		    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0)
712031bec6dSxtraeme 			goto err;
713031bec6dSxtraeme 
714031bec6dSxtraeme 		/* CTLFLAG_ANYWRITE? kernel option like EST? */
7155a395386Sdyoung 		if ((rv = sysctl_createv(&sc->sc_log, 0, &node, &ssnode,
716031bec6dSxtraeme 		    CTLFLAG_READWRITE, CTLTYPE_INT, "speedstep_state", NULL,
717031bec6dSxtraeme 		    speedstep_sysctl_helper, 0, NULL, 0, CTL_CREATE,
718031bec6dSxtraeme 		    CTL_EOL)) != 0)
719031bec6dSxtraeme 			goto err;
720031bec6dSxtraeme 
721031bec6dSxtraeme 		/* XXX save the sc for IO tag/handle */
722031bec6dSxtraeme 		speedstep_cookie = sc;
723d749da91Sxtraeme 		aprint_verbose_dev(self, "SpeedStep enabled\n");
724031bec6dSxtraeme 	}
725031bec6dSxtraeme 
726031bec6dSxtraeme 	return;
727031bec6dSxtraeme 
728031bec6dSxtraeme err:
729031bec6dSxtraeme 	aprint_normal("%s: sysctl_createv failed (rv = %d)\n", __func__, rv);
730031bec6dSxtraeme }
731031bec6dSxtraeme 
7325a395386Sdyoung static void
7335a395386Sdyoung speedstep_unconfigure(device_t self)
7345a395386Sdyoung {
7355a395386Sdyoung 	struct lpcib_softc *sc = device_private(self);
7365a395386Sdyoung 
7375a395386Sdyoung 	sysctl_teardown(&sc->sc_log);
7385a395386Sdyoung 	pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
7395a395386Sdyoung 	    LPCIB_PCI_GEN_PMCON_1, sc->sc_pmcon_orig);
7405a395386Sdyoung 
7415a395386Sdyoung 	speedstep_cookie = NULL;
7425a395386Sdyoung }
7435a395386Sdyoung 
744031bec6dSxtraeme /*
745031bec6dSxtraeme  * get/set the SpeedStep state: 0 == low power, 1 == high power.
746031bec6dSxtraeme  */
747031bec6dSxtraeme static int
748031bec6dSxtraeme speedstep_sysctl_helper(SYSCTLFN_ARGS)
749031bec6dSxtraeme {
750031bec6dSxtraeme 	struct sysctlnode	node;
751031bec6dSxtraeme 	struct lpcib_softc 	*sc = speedstep_cookie;
752031bec6dSxtraeme 	uint8_t			state, state2;
753031bec6dSxtraeme 	int			ostate, nstate, s, error = 0;
754031bec6dSxtraeme 
755031bec6dSxtraeme 	/*
756031bec6dSxtraeme 	 * We do the dance with spl's to avoid being at high ipl during
757031bec6dSxtraeme 	 * sysctl_lookup() which can both copyin and copyout.
758031bec6dSxtraeme 	 */
759031bec6dSxtraeme 	s = splserial();
760a350b83aSriastradh 	state = SS_READ(sc, PMC_PM_SS_CNTL);
761031bec6dSxtraeme 	splx(s);
762a350b83aSriastradh 	if ((state & PMC_PM_SS_STATE_LOW) == 0)
763031bec6dSxtraeme 		ostate = 1;
764031bec6dSxtraeme 	else
765031bec6dSxtraeme 		ostate = 0;
766031bec6dSxtraeme 	nstate = ostate;
767031bec6dSxtraeme 
768031bec6dSxtraeme 	node = *rnode;
769031bec6dSxtraeme 	node.sysctl_data = &nstate;
770031bec6dSxtraeme 
771031bec6dSxtraeme 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
772031bec6dSxtraeme 	if (error || newp == NULL)
773031bec6dSxtraeme 		goto out;
774031bec6dSxtraeme 
775031bec6dSxtraeme 	/* Only two states are available */
776031bec6dSxtraeme 	if (nstate != 0 && nstate != 1) {
777031bec6dSxtraeme 		error = EINVAL;
778031bec6dSxtraeme 		goto out;
779031bec6dSxtraeme 	}
780031bec6dSxtraeme 
781031bec6dSxtraeme 	s = splserial();
782a350b83aSriastradh 	state2 = SS_READ(sc, PMC_PM_SS_CNTL);
783a350b83aSriastradh 	if ((state2 & PMC_PM_SS_STATE_LOW) == 0)
784031bec6dSxtraeme 		ostate = 1;
785031bec6dSxtraeme 	else
786031bec6dSxtraeme 		ostate = 0;
787031bec6dSxtraeme 
788031bec6dSxtraeme 	if (ostate != nstate) {
789031bec6dSxtraeme 		uint8_t cntl;
790031bec6dSxtraeme 
791031bec6dSxtraeme 		if (nstate == 0)
792a350b83aSriastradh 			state2 |= PMC_PM_SS_STATE_LOW;
793031bec6dSxtraeme 		else
794a350b83aSriastradh 			state2 &= ~PMC_PM_SS_STATE_LOW;
795031bec6dSxtraeme 
796031bec6dSxtraeme 		/*
797031bec6dSxtraeme 		 * Must disable bus master arbitration during the change.
798031bec6dSxtraeme 		 */
799a350b83aSriastradh 		cntl = SS_READ(sc, PMC_PM_CTRL);
800a350b83aSriastradh 		SS_WRITE(sc, PMC_PM_CTRL, cntl | PMC_PM_SS_CNTL_ARB_DIS);
801a350b83aSriastradh 		SS_WRITE(sc, PMC_PM_SS_CNTL, state2);
802a350b83aSriastradh 		SS_WRITE(sc, PMC_PM_CTRL, cntl);
803031bec6dSxtraeme 	}
804031bec6dSxtraeme 	splx(s);
805031bec6dSxtraeme out:
806031bec6dSxtraeme 	return error;
807031bec6dSxtraeme }
8084c1d81b2Sjmcneill 
8094c1d81b2Sjmcneill static void
810d749da91Sxtraeme lpcib_hpet_configure(device_t self)
8114c1d81b2Sjmcneill {
812d749da91Sxtraeme 	struct lpcib_softc *sc = device_private(self);
813d7620599Sjruoho 	struct lpcib_hpet_attach_args arg;
8144c1d81b2Sjmcneill 	uint32_t hpet_reg, val;
8154c1d81b2Sjmcneill 
8164c1d81b2Sjmcneill 	if (sc->sc_has_ich5_hpet) {
8174956be1fSmartin 		val = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
818d749da91Sxtraeme 		    LPCIB_PCI_GEN_CNTL);
8194c1d81b2Sjmcneill 		switch (val & LPCIB_ICH5_HPTC_WIN_MASK) {
8204c1d81b2Sjmcneill 		case LPCIB_ICH5_HPTC_0000:
8214c1d81b2Sjmcneill 			hpet_reg = LPCIB_ICH5_HPTC_0000_BASE;
8224c1d81b2Sjmcneill 			break;
8234c1d81b2Sjmcneill 		case LPCIB_ICH5_HPTC_1000:
8244c1d81b2Sjmcneill 			hpet_reg = LPCIB_ICH5_HPTC_1000_BASE;
8254c1d81b2Sjmcneill 			break;
8264c1d81b2Sjmcneill 		case LPCIB_ICH5_HPTC_2000:
8274c1d81b2Sjmcneill 			hpet_reg = LPCIB_ICH5_HPTC_2000_BASE;
8284c1d81b2Sjmcneill 			break;
8294c1d81b2Sjmcneill 		case LPCIB_ICH5_HPTC_3000:
8304c1d81b2Sjmcneill 			hpet_reg = LPCIB_ICH5_HPTC_3000_BASE;
8314c1d81b2Sjmcneill 			break;
8324c1d81b2Sjmcneill 		default:
8334c1d81b2Sjmcneill 			return;
8344c1d81b2Sjmcneill 		}
8354c1d81b2Sjmcneill 		val |= sc->sc_hpet_reg | LPCIB_ICH5_HPTC_EN;
8364956be1fSmartin 		pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
837d749da91Sxtraeme 		    LPCIB_PCI_GEN_CNTL, val);
8384c1d81b2Sjmcneill 	} else if (sc->sc_has_rcba) {
8394c1d81b2Sjmcneill 		val = bus_space_read_4(sc->sc_rcbat, sc->sc_rcbah,
8404c1d81b2Sjmcneill 		    LPCIB_RCBA_HPTC);
8414c1d81b2Sjmcneill 		switch (val & LPCIB_RCBA_HPTC_WIN_MASK) {
8424c1d81b2Sjmcneill 		case LPCIB_RCBA_HPTC_0000:
8434c1d81b2Sjmcneill 			hpet_reg = LPCIB_RCBA_HPTC_0000_BASE;
8444c1d81b2Sjmcneill 			break;
8454c1d81b2Sjmcneill 		case LPCIB_RCBA_HPTC_1000:
8464c1d81b2Sjmcneill 			hpet_reg = LPCIB_RCBA_HPTC_1000_BASE;
8474c1d81b2Sjmcneill 			break;
8484c1d81b2Sjmcneill 		case LPCIB_RCBA_HPTC_2000:
8494c1d81b2Sjmcneill 			hpet_reg = LPCIB_RCBA_HPTC_2000_BASE;
8504c1d81b2Sjmcneill 			break;
8514c1d81b2Sjmcneill 		case LPCIB_RCBA_HPTC_3000:
8524c1d81b2Sjmcneill 			hpet_reg = LPCIB_RCBA_HPTC_3000_BASE;
8534c1d81b2Sjmcneill 			break;
8544c1d81b2Sjmcneill 		default:
8554c1d81b2Sjmcneill 			return;
8564c1d81b2Sjmcneill 		}
8574c1d81b2Sjmcneill 		val |= LPCIB_RCBA_HPTC_EN;
8584c1d81b2Sjmcneill 		bus_space_write_4(sc->sc_rcbat, sc->sc_rcbah, LPCIB_RCBA_HPTC,
8594c1d81b2Sjmcneill 		    val);
8604c1d81b2Sjmcneill 	} else {
8614c1d81b2Sjmcneill 		/* No HPET here */
8624c1d81b2Sjmcneill 		return;
8634c1d81b2Sjmcneill 	}
8644c1d81b2Sjmcneill 
8654c1d81b2Sjmcneill 	arg.hpet_mem_t = sc->sc_pa.pa_memt;
8664c1d81b2Sjmcneill 	arg.hpet_reg = hpet_reg;
8674c1d81b2Sjmcneill 
8682685996bSthorpej 	sc->sc_hpetbus = config_found(self, &arg, NULL,
869c7fb772bSthorpej 	    CFARGS(.iattr = "hpetichbus"));
8705a395386Sdyoung }
8715a395386Sdyoung 
872bd5cb774Sjakllsch #if NGPIO > 0
873bd5cb774Sjakllsch static void
874bd5cb774Sjakllsch lpcib_gpio_configure(device_t self)
875bd5cb774Sjakllsch {
876bd5cb774Sjakllsch 	struct lpcib_softc *sc = device_private(self);
877bd5cb774Sjakllsch 	struct gpiobus_attach_args gba;
878bd5cb774Sjakllsch 	pcireg_t gpio_cntl;
879bd5cb774Sjakllsch 	uint32_t use, io, bit;
880bd5cb774Sjakllsch 	int pin, shift, base_reg, cntl_reg, reg;
88183c4f0e1Smsaitoh 	int rv;
882bd5cb774Sjakllsch 
8835b9bdcf5Sjakllsch 	if (ichlpcib_gpio_disable != 0)
8845b9bdcf5Sjakllsch 		return;
8855b9bdcf5Sjakllsch 
886bd5cb774Sjakllsch 	/* this implies ICH >= 6, and thus different mapreg */
887bd5cb774Sjakllsch 	if (sc->sc_has_rcba) {
888bd5cb774Sjakllsch 		base_reg = LPCIB_PCI_GPIO_BASE_ICH6;
889bd5cb774Sjakllsch 		cntl_reg = LPCIB_PCI_GPIO_CNTL_ICH6;
890bd5cb774Sjakllsch 	} else {
891bd5cb774Sjakllsch 		base_reg = LPCIB_PCI_GPIO_BASE;
892bd5cb774Sjakllsch 		cntl_reg = LPCIB_PCI_GPIO_CNTL;
893bd5cb774Sjakllsch 	}
894bd5cb774Sjakllsch 
895bd5cb774Sjakllsch 	gpio_cntl = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
896bd5cb774Sjakllsch 	    cntl_reg);
897bd5cb774Sjakllsch 
898bd5cb774Sjakllsch 	/* Is GPIO enabled? */
899bd5cb774Sjakllsch 	if ((gpio_cntl & LPCIB_PCI_GPIO_CNTL_EN) == 0)
900bd5cb774Sjakllsch 		return;
90183c4f0e1Smsaitoh 	/*
90283c4f0e1Smsaitoh 	 * The GPIO_BASE register is alike PCI BAR but not completely
90383c4f0e1Smsaitoh 	 * compatible with it. The PMBASE define the base address and the type
904453034ecSmsaitoh 	 * but not describe the size. The value of the register may be lower
905453034ecSmsaitoh 	 * than LPCIB_PCI_GPIO_SIZE. It makes impossible to use
906453034ecSmsaitoh 	 * pci_mapreg_submap() because the function does range check.
90783c4f0e1Smsaitoh 	 */
908453034ecSmsaitoh 	sc->sc_gpio_iot = sc->sc_pa.pa_iot;
909453034ecSmsaitoh 	reg = pci_conf_read(sc->sc_pa.pa_pc, sc->sc_pa.pa_tag, base_reg);
910453034ecSmsaitoh 	rv = bus_space_map(sc->sc_gpio_iot, PCI_MAPREG_IO_ADDR(reg),
911453034ecSmsaitoh 	    LPCIB_PCI_GPIO_SIZE, 0, &sc->sc_gpio_ioh);
91283c4f0e1Smsaitoh 	if (rv != 0) {
91383c4f0e1Smsaitoh 		aprint_error_dev(self, "can't map general purpose i/o space(rv = %d)\n", rv);
914bd5cb774Sjakllsch 		return;
915bd5cb774Sjakllsch 	}
916bd5cb774Sjakllsch 
917bd5cb774Sjakllsch 	mutex_init(&sc->sc_gpio_mtx, MUTEX_DEFAULT, IPL_NONE);
918bd5cb774Sjakllsch 
919bd5cb774Sjakllsch 	for (pin = 0; pin < LPCIB_GPIO_NPINS; pin++) {
920bd5cb774Sjakllsch 		sc->sc_gpio_pins[pin].pin_num = pin;
921bd5cb774Sjakllsch 
922bd5cb774Sjakllsch 		/* Read initial state */
923bd5cb774Sjakllsch 		reg = (pin < 32) ? LPCIB_GPIO_GPIO_USE_SEL : LPCIB_GPIO_GPIO_USE_SEL2;
924bd5cb774Sjakllsch 		use = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
925bd5cb774Sjakllsch 		reg = (pin < 32) ? LPCIB_GPIO_GP_IO_SEL : LPCIB_GPIO_GP_IO_SEL;
926bd5cb774Sjakllsch 		io = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, 4);
927bd5cb774Sjakllsch 		shift = pin % 32;
928bd5cb774Sjakllsch 		bit = __BIT(shift);
929bd5cb774Sjakllsch 
930bd5cb774Sjakllsch 		if ((use & bit) != 0) {
931bd5cb774Sjakllsch 			sc->sc_gpio_pins[pin].pin_caps =
932bd5cb774Sjakllsch 			    GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
933bd5cb774Sjakllsch 			if (pin < 32)
934bd5cb774Sjakllsch 				sc->sc_gpio_pins[pin].pin_caps |=
935bd5cb774Sjakllsch 				    GPIO_PIN_PULSATE;
936bd5cb774Sjakllsch 			if ((io & bit) != 0)
937bd5cb774Sjakllsch 				sc->sc_gpio_pins[pin].pin_flags =
938bd5cb774Sjakllsch 				    GPIO_PIN_INPUT;
939bd5cb774Sjakllsch 			else
940bd5cb774Sjakllsch 				sc->sc_gpio_pins[pin].pin_flags =
941bd5cb774Sjakllsch 				    GPIO_PIN_OUTPUT;
942bd5cb774Sjakllsch 		} else
943bd5cb774Sjakllsch 			sc->sc_gpio_pins[pin].pin_caps = 0;
944bd5cb774Sjakllsch 
945bd5cb774Sjakllsch 		if (lpcib_gpio_pin_read(sc, pin) == 0)
946bd5cb774Sjakllsch 			sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW;
947bd5cb774Sjakllsch 		else
948bd5cb774Sjakllsch 			sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH;
949bd5cb774Sjakllsch 
950bd5cb774Sjakllsch 	}
951bd5cb774Sjakllsch 
952bd5cb774Sjakllsch 	/* Create controller tag */
953bd5cb774Sjakllsch 	sc->sc_gpio_gc.gp_cookie = sc;
954bd5cb774Sjakllsch 	sc->sc_gpio_gc.gp_pin_read = lpcib_gpio_pin_read;
955bd5cb774Sjakllsch 	sc->sc_gpio_gc.gp_pin_write = lpcib_gpio_pin_write;
956bd5cb774Sjakllsch 	sc->sc_gpio_gc.gp_pin_ctl = lpcib_gpio_pin_ctl;
957bd5cb774Sjakllsch 
958bd5cb774Sjakllsch 	memset(&gba, 0, sizeof(gba));
959bd5cb774Sjakllsch 
960bd5cb774Sjakllsch 	gba.gba_gc = &sc->sc_gpio_gc;
961bd5cb774Sjakllsch 	gba.gba_pins = sc->sc_gpio_pins;
962bd5cb774Sjakllsch 	gba.gba_npins = LPCIB_GPIO_NPINS;
963bd5cb774Sjakllsch 
9642685996bSthorpej 	sc->sc_gpiobus = config_found(self, &gba, gpiobus_print,
965c7fb772bSthorpej 	    CFARGS(.iattr = "gpiobus"));
966bd5cb774Sjakllsch }
967bd5cb774Sjakllsch 
968f653f102Sriastradh static void
969f653f102Sriastradh lpcib_gpio_unconfigure(device_t self)
970bd5cb774Sjakllsch {
971bd5cb774Sjakllsch 	struct lpcib_softc *sc = device_private(self);
972bd5cb774Sjakllsch 
973bd5cb774Sjakllsch 	mutex_destroy(&sc->sc_gpio_mtx);
974bd5cb774Sjakllsch 
975bd5cb774Sjakllsch 	bus_space_unmap(sc->sc_gpio_iot, sc->sc_gpio_ioh, sc->sc_gpio_ios);
976bd5cb774Sjakllsch }
977bd5cb774Sjakllsch 
978bd5cb774Sjakllsch static int
979bd5cb774Sjakllsch lpcib_gpio_pin_read(void *arg, int pin)
980bd5cb774Sjakllsch {
981bd5cb774Sjakllsch 	struct lpcib_softc *sc = arg;
982bd5cb774Sjakllsch 	uint32_t data;
983bd5cb774Sjakllsch 	int reg, shift;
984bd5cb774Sjakllsch 
985bd5cb774Sjakllsch 	reg = (pin < 32) ? LPCIB_GPIO_GP_LVL : LPCIB_GPIO_GP_LVL2;
986bd5cb774Sjakllsch 	shift = pin % 32;
987bd5cb774Sjakllsch 
988bd5cb774Sjakllsch 	mutex_enter(&sc->sc_gpio_mtx);
989bd5cb774Sjakllsch 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
990bd5cb774Sjakllsch 	mutex_exit(&sc->sc_gpio_mtx);
991bd5cb774Sjakllsch 
992bd5cb774Sjakllsch 	return (__SHIFTOUT(data, __BIT(shift)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
993bd5cb774Sjakllsch }
994bd5cb774Sjakllsch 
995bd5cb774Sjakllsch static void
996bd5cb774Sjakllsch lpcib_gpio_pin_write(void *arg, int pin, int value)
997bd5cb774Sjakllsch {
998bd5cb774Sjakllsch 	struct lpcib_softc *sc = arg;
999bd5cb774Sjakllsch 	uint32_t data;
1000bd5cb774Sjakllsch 	int reg, shift;
1001bd5cb774Sjakllsch 
1002bd5cb774Sjakllsch 	reg = (pin < 32) ? LPCIB_GPIO_GP_LVL : LPCIB_GPIO_GP_LVL2;
1003bd5cb774Sjakllsch 	shift = pin % 32;
1004bd5cb774Sjakllsch 
1005bd5cb774Sjakllsch 	mutex_enter(&sc->sc_gpio_mtx);
1006bd5cb774Sjakllsch 
1007bd5cb774Sjakllsch 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
1008bd5cb774Sjakllsch 
1009bd5cb774Sjakllsch 	if (value)
1010bd5cb774Sjakllsch 		data |= __BIT(shift);
1011bd5cb774Sjakllsch 	else
1012bd5cb774Sjakllsch 		data &= ~__BIT(shift);
1013bd5cb774Sjakllsch 
1014bd5cb774Sjakllsch 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
1015bd5cb774Sjakllsch 
1016bd5cb774Sjakllsch 	mutex_exit(&sc->sc_gpio_mtx);
1017bd5cb774Sjakllsch }
1018bd5cb774Sjakllsch 
1019bd5cb774Sjakllsch static void
1020bd5cb774Sjakllsch lpcib_gpio_pin_ctl(void *arg, int pin, int flags)
1021bd5cb774Sjakllsch {
1022bd5cb774Sjakllsch 	struct lpcib_softc *sc = arg;
1023bd5cb774Sjakllsch 	uint32_t data;
1024bd5cb774Sjakllsch 	int reg, shift;
1025bd5cb774Sjakllsch 
1026bd5cb774Sjakllsch 	shift = pin % 32;
1027bd5cb774Sjakllsch 	reg = (pin < 32) ? LPCIB_GPIO_GP_IO_SEL : LPCIB_GPIO_GP_IO_SEL2;
1028bd5cb774Sjakllsch 
1029bd5cb774Sjakllsch 	mutex_enter(&sc->sc_gpio_mtx);
1030bd5cb774Sjakllsch 
1031bd5cb774Sjakllsch 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
1032bd5cb774Sjakllsch 
1033bd5cb774Sjakllsch 	if (flags & GPIO_PIN_OUTPUT)
1034bd5cb774Sjakllsch 		data &= ~__BIT(shift);
1035bd5cb774Sjakllsch 
1036bd5cb774Sjakllsch 	if (flags & GPIO_PIN_INPUT)
1037bd5cb774Sjakllsch 		data |= __BIT(shift);
1038bd5cb774Sjakllsch 
1039bd5cb774Sjakllsch 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
1040bd5cb774Sjakllsch 
1041bd5cb774Sjakllsch 
1042bd5cb774Sjakllsch 	if (pin < 32) {
1043bd5cb774Sjakllsch 		reg = LPCIB_GPIO_GPO_BLINK;
1044bd5cb774Sjakllsch 		data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
1045bd5cb774Sjakllsch 
1046bd5cb774Sjakllsch 		if (flags & GPIO_PIN_PULSATE)
1047bd5cb774Sjakllsch 			data |= __BIT(shift);
1048bd5cb774Sjakllsch 		else
1049bd5cb774Sjakllsch 			data &= ~__BIT(shift);
1050bd5cb774Sjakllsch 
1051bd5cb774Sjakllsch 		bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
1052bd5cb774Sjakllsch 	}
1053bd5cb774Sjakllsch 
1054bd5cb774Sjakllsch 	mutex_exit(&sc->sc_gpio_mtx);
1055bd5cb774Sjakllsch }
1056bd5cb774Sjakllsch #endif
1057897b6b7dSjakllsch 
1058897b6b7dSjakllsch #if NFWHRNG > 0
1059897b6b7dSjakllsch static void
1060897b6b7dSjakllsch lpcib_fwh_configure(device_t self)
1061897b6b7dSjakllsch {
10621bccaf8fSjakllsch 	struct lpcib_softc *sc;
1063897b6b7dSjakllsch 	pcireg_t pr;
1064897b6b7dSjakllsch 
10651bccaf8fSjakllsch 	sc = device_private(self);
10661bccaf8fSjakllsch 
1067897b6b7dSjakllsch 	if (sc->sc_has_rcba) {
1068897b6b7dSjakllsch 		/*
1069897b6b7dSjakllsch 		 * Very unlikely to find a 82802 on a ICH6 or newer.
1070897b6b7dSjakllsch 		 * Also the write enable register moved at that point.
1071897b6b7dSjakllsch 		 */
1072897b6b7dSjakllsch 		return;
1073897b6b7dSjakllsch 	} else {
1074897b6b7dSjakllsch 		/* Enable FWH write to identify FWH. */
1075897b6b7dSjakllsch 		pr = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
10761bccaf8fSjakllsch 		    LPCIB_PCI_BIOS_CNTL);
1077897b6b7dSjakllsch 		pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
10781bccaf8fSjakllsch 		    LPCIB_PCI_BIOS_CNTL, pr|LPCIB_PCI_BIOS_CNTL_BWE);
1079897b6b7dSjakllsch 	}
1080897b6b7dSjakllsch 
10812685996bSthorpej 	sc->sc_fwhbus = config_found(self, NULL, NULL,
1082c7fb772bSthorpej 	    CFARGS(.iattr = "fwhichbus"));
1083897b6b7dSjakllsch 
10841bccaf8fSjakllsch 	/* restore previous write enable setting */
10851bccaf8fSjakllsch 	pci_conf_write(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
10861bccaf8fSjakllsch 	    LPCIB_PCI_BIOS_CNTL, pr);
1087897b6b7dSjakllsch }
1088897b6b7dSjakllsch #endif
1089