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