1f495ec29SRui Paulo /*- 2f495ec29SRui Paulo * Copyright (c) 1998, 1999 Takanori Watanabe 3f495ec29SRui Paulo * All rights reserved. 4f495ec29SRui Paulo * 5f495ec29SRui Paulo * Redistribution and use in source and binary forms, with or without 6f495ec29SRui Paulo * modification, are permitted provided that the following conditions 7f495ec29SRui Paulo * are met: 8f495ec29SRui Paulo * 1. Redistributions of source code must retain the above copyright 9f495ec29SRui Paulo * notice, this list of conditions and the following disclaimer. 10f495ec29SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 11f495ec29SRui Paulo * notice, this list of conditions and the following disclaimer in the 12f495ec29SRui Paulo * documentation and/or other materials provided with the distribution. 13f495ec29SRui Paulo * 14f495ec29SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15f495ec29SRui Paulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16f495ec29SRui Paulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17f495ec29SRui Paulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18f495ec29SRui Paulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19f495ec29SRui Paulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20f495ec29SRui Paulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21f495ec29SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22f495ec29SRui Paulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23f495ec29SRui Paulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24f495ec29SRui Paulo * SUCH DAMAGE. 25f495ec29SRui Paulo */ 26f495ec29SRui Paulo 27f495ec29SRui Paulo #include <sys/param.h> 28f495ec29SRui Paulo #include <sys/systm.h> 29f495ec29SRui Paulo #include <sys/bus.h> 30f495ec29SRui Paulo #include <sys/kernel.h> 31f495ec29SRui Paulo #include <sys/lock.h> 32f495ec29SRui Paulo #include <sys/module.h> 33f495ec29SRui Paulo #include <sys/mutex.h> 34f495ec29SRui Paulo #include <sys/rman.h> 35f495ec29SRui Paulo #include <machine/bus.h> 36f495ec29SRui Paulo #include <dev/smbus/smbconf.h> 37f495ec29SRui Paulo 38f495ec29SRui Paulo #include "smbus_if.h" 39f495ec29SRui Paulo 40f495ec29SRui Paulo #include <dev/pci/pcireg.h> 41f495ec29SRui Paulo #include <dev/pci/pcivar.h> 42f495ec29SRui Paulo #include <dev/intpm/intpmreg.h> 433673f713SAndriy Gapon #include <dev/amdsbwd/amd_chipset.h> 44f495ec29SRui Paulo 45f495ec29SRui Paulo #include "opt_intpm.h" 46f495ec29SRui Paulo 47f495ec29SRui Paulo struct intsmb_softc { 48f495ec29SRui Paulo device_t dev; 49f495ec29SRui Paulo struct resource *io_res; 50f495ec29SRui Paulo struct resource *irq_res; 51f495ec29SRui Paulo void *irq_hand; 52f495ec29SRui Paulo device_t smbus; 53a48ec78eSAndriy Gapon int io_rid; 54f495ec29SRui Paulo int isbusy; 55f495ec29SRui Paulo int cfg_irq9; 56a48ec78eSAndriy Gapon int sb8xx; 57f495ec29SRui Paulo int poll; 58*b2a49e88SBrian Poole int type; 59f495ec29SRui Paulo struct mtx lock; 60f495ec29SRui Paulo }; 61f495ec29SRui Paulo 62f495ec29SRui Paulo #define INTSMB_LOCK(sc) mtx_lock(&(sc)->lock) 63f495ec29SRui Paulo #define INTSMB_UNLOCK(sc) mtx_unlock(&(sc)->lock) 64f495ec29SRui Paulo #define INTSMB_LOCK_ASSERT(sc) mtx_assert(&(sc)->lock, MA_OWNED) 65f495ec29SRui Paulo 66f495ec29SRui Paulo static int intsmb_probe(device_t); 67f495ec29SRui Paulo static int intsmb_attach(device_t); 68f495ec29SRui Paulo static int intsmb_detach(device_t); 69f495ec29SRui Paulo static int intsmb_intr(struct intsmb_softc *sc); 70f495ec29SRui Paulo static int intsmb_slvintr(struct intsmb_softc *sc); 71f495ec29SRui Paulo static void intsmb_alrintr(struct intsmb_softc *sc); 72f495ec29SRui Paulo static int intsmb_callback(device_t dev, int index, void *data); 73f495ec29SRui Paulo static int intsmb_quick(device_t dev, u_char slave, int how); 74f495ec29SRui Paulo static int intsmb_sendb(device_t dev, u_char slave, char byte); 75f495ec29SRui Paulo static int intsmb_recvb(device_t dev, u_char slave, char *byte); 76f495ec29SRui Paulo static int intsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 77f495ec29SRui Paulo static int intsmb_writew(device_t dev, u_char slave, char cmd, short word); 78f495ec29SRui Paulo static int intsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 79f495ec29SRui Paulo static int intsmb_readw(device_t dev, u_char slave, char cmd, short *word); 80f495ec29SRui Paulo static int intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 81f495ec29SRui Paulo static int intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 82f495ec29SRui Paulo static int intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 83f495ec29SRui Paulo static void intsmb_start(struct intsmb_softc *sc, u_char cmd, int nointr); 84f495ec29SRui Paulo static int intsmb_stop(struct intsmb_softc *sc); 85f495ec29SRui Paulo static int intsmb_stop_poll(struct intsmb_softc *sc); 86f495ec29SRui Paulo static int intsmb_free(struct intsmb_softc *sc); 87f495ec29SRui Paulo static void intsmb_rawintr(void *arg); 88f495ec29SRui Paulo 89a64bf59cSConrad Meyer const struct intsmb_device { 90a64bf59cSConrad Meyer uint32_t devid; 91a64bf59cSConrad Meyer const char *description; 92a64bf59cSConrad Meyer } intsmb_products[] = { 93a64bf59cSConrad Meyer { 0x71138086, "Intel PIIX4 SMBUS Interface" }, 94a64bf59cSConrad Meyer { 0x719b8086, "Intel PIIX4 SMBUS Interface" }, 95a64bf59cSConrad Meyer #if 0 96a64bf59cSConrad Meyer /* Not a good idea yet, this stops isab0 functioning */ 97a64bf59cSConrad Meyer { 0x02001166, "ServerWorks OSB4" }, 98a64bf59cSConrad Meyer #endif 99a64bf59cSConrad Meyer { 0x43721002, "ATI IXP400 SMBus Controller" }, 100a64bf59cSConrad Meyer { AMDSB_SMBUS_DEVID, "AMD SB600/7xx/8xx/9xx SMBus Controller" }, 101a64bf59cSConrad Meyer { AMDFCH_SMBUS_DEVID, "AMD FCH SMBus Controller" }, 102a64bf59cSConrad Meyer { AMDCZ_SMBUS_DEVID, "AMD FCH SMBus Controller" }, 103decf9c5fSKonstantin Belousov { HYGONCZ_SMBUS_DEVID, "Hygon FCH SMBus Controller" }, 104a64bf59cSConrad Meyer }; 105a64bf59cSConrad Meyer 106f495ec29SRui Paulo static int 107f495ec29SRui Paulo intsmb_probe(device_t dev) 108f495ec29SRui Paulo { 109a64bf59cSConrad Meyer const struct intsmb_device *isd; 110a64bf59cSConrad Meyer uint32_t devid; 111a64bf59cSConrad Meyer size_t i; 112f495ec29SRui Paulo 113a64bf59cSConrad Meyer devid = pci_get_devid(dev); 114a64bf59cSConrad Meyer for (i = 0; i < nitems(intsmb_products); i++) { 115a64bf59cSConrad Meyer isd = &intsmb_products[i]; 116a64bf59cSConrad Meyer if (isd->devid == devid) { 117a64bf59cSConrad Meyer device_set_desc(dev, isd->description); 118f495ec29SRui Paulo return (BUS_PROBE_DEFAULT); 119f495ec29SRui Paulo } 120a64bf59cSConrad Meyer } 121a64bf59cSConrad Meyer return (ENXIO); 122a64bf59cSConrad Meyer } 123f495ec29SRui Paulo 124a48ec78eSAndriy Gapon static uint8_t 1253673f713SAndriy Gapon amd_pmio_read(struct resource *res, uint8_t reg) 126a48ec78eSAndriy Gapon { 127a48ec78eSAndriy Gapon bus_write_1(res, 0, reg); /* Index */ 128a48ec78eSAndriy Gapon return (bus_read_1(res, 1)); /* Data */ 129a48ec78eSAndriy Gapon } 130a48ec78eSAndriy Gapon 131a48ec78eSAndriy Gapon static int 132a48ec78eSAndriy Gapon sb8xx_attach(device_t dev) 133a48ec78eSAndriy Gapon { 134e5dc78afSConrad Meyer static const int AMDSB_SMBIO_WIDTH = 0x10; 135a48ec78eSAndriy Gapon struct intsmb_softc *sc; 136a48ec78eSAndriy Gapon struct resource *res; 1373673f713SAndriy Gapon uint32_t devid; 1383673f713SAndriy Gapon uint8_t revid; 139*b2a49e88SBrian Poole uint32_t addr; 140a48ec78eSAndriy Gapon int rid; 141a48ec78eSAndriy Gapon int rc; 1423673f713SAndriy Gapon bool enabled; 143a48ec78eSAndriy Gapon 144a48ec78eSAndriy Gapon sc = device_get_softc(dev); 145*b2a49e88SBrian Poole devid = pci_get_devid(dev); 146*b2a49e88SBrian Poole revid = pci_get_revid(dev); 147*b2a49e88SBrian Poole 148*b2a49e88SBrian Poole /* 149*b2a49e88SBrian Poole * Comment from Linux i2c-piix4.c: 150*b2a49e88SBrian Poole * 151*b2a49e88SBrian Poole * cd6h/cd7h port I/O accesses can be disabled on AMD processors 152*b2a49e88SBrian Poole * w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on 153*b2a49e88SBrian Poole * the same processors and is the recommended access method. 154*b2a49e88SBrian Poole */ 155*b2a49e88SBrian Poole if (devid == AMDCZ_SMBUS_DEVID && revid >= AMDCZ51_SMBUS_REVID) { 156*b2a49e88SBrian Poole sc->type = SYS_RES_MEMORY; 157*b2a49e88SBrian Poole addr = AMDFCH41_MMIO_ADDR + AMDFCH41_MMIO_PM_OFF; 158*b2a49e88SBrian Poole } else { 159*b2a49e88SBrian Poole sc->type = SYS_RES_IOPORT; 160*b2a49e88SBrian Poole addr = AMDSB_PMIO_INDEX; 161*b2a49e88SBrian Poole } 162*b2a49e88SBrian Poole 1633673f713SAndriy Gapon rid = 0; 164*b2a49e88SBrian Poole rc = bus_set_resource(dev, sc->type, rid, addr, 165a48ec78eSAndriy Gapon AMDSB_PMIO_WIDTH); 166a48ec78eSAndriy Gapon if (rc != 0) { 167a48ec78eSAndriy Gapon device_printf(dev, "bus_set_resource for PM IO failed\n"); 168a48ec78eSAndriy Gapon return (ENXIO); 169a48ec78eSAndriy Gapon } 170*b2a49e88SBrian Poole res = bus_alloc_resource_any(dev, sc->type, &rid, 1713673f713SAndriy Gapon RF_ACTIVE); 172a48ec78eSAndriy Gapon if (res == NULL) { 173a48ec78eSAndriy Gapon device_printf(dev, "bus_alloc_resource for PM IO failed\n"); 174a48ec78eSAndriy Gapon return (ENXIO); 175a48ec78eSAndriy Gapon } 176a48ec78eSAndriy Gapon 1773673f713SAndriy Gapon if (devid == AMDSB_SMBUS_DEVID || 1783673f713SAndriy Gapon (devid == AMDFCH_SMBUS_DEVID && revid < AMDFCH41_SMBUS_REVID) || 1793673f713SAndriy Gapon (devid == AMDCZ_SMBUS_DEVID && revid < AMDCZ49_SMBUS_REVID)) { 1803673f713SAndriy Gapon addr = amd_pmio_read(res, AMDSB8_PM_SMBUS_EN + 1); 181a48ec78eSAndriy Gapon addr <<= 8; 1823673f713SAndriy Gapon addr |= amd_pmio_read(res, AMDSB8_PM_SMBUS_EN); 1833673f713SAndriy Gapon enabled = (addr & AMDSB8_SMBUS_EN) != 0; 1843673f713SAndriy Gapon addr &= AMDSB8_SMBUS_ADDR_MASK; 185*b2a49e88SBrian Poole } else if (devid == AMDCZ_SMBUS_DEVID && revid >= AMDCZ51_SMBUS_REVID) { 186*b2a49e88SBrian Poole addr = bus_read_1(res, AMDFCH41_PM_DECODE_EN0); 187*b2a49e88SBrian Poole enabled = (addr & AMDFCH41_SMBUS_EN) != 0; 188*b2a49e88SBrian Poole addr = AMDFCH41_MMIO_ADDR + AMDFCH41_MMIO_SMBUS_OFF; 1893673f713SAndriy Gapon } else { 1903673f713SAndriy Gapon addr = amd_pmio_read(res, AMDFCH41_PM_DECODE_EN0); 1913673f713SAndriy Gapon enabled = (addr & AMDFCH41_SMBUS_EN) != 0; 1923673f713SAndriy Gapon addr = amd_pmio_read(res, AMDFCH41_PM_DECODE_EN1); 1933673f713SAndriy Gapon addr <<= 8; 1943673f713SAndriy Gapon } 195a48ec78eSAndriy Gapon 196*b2a49e88SBrian Poole bus_release_resource(dev, sc->type, rid, res); 197*b2a49e88SBrian Poole bus_delete_resource(dev, sc->type, rid); 198a48ec78eSAndriy Gapon 1993673f713SAndriy Gapon if (!enabled) { 2003673f713SAndriy Gapon device_printf(dev, "SB8xx/SB9xx/FCH SMBus not enabled\n"); 201a48ec78eSAndriy Gapon return (ENXIO); 202a48ec78eSAndriy Gapon } 203a48ec78eSAndriy Gapon 2043673f713SAndriy Gapon sc->io_rid = 0; 205*b2a49e88SBrian Poole rc = bus_set_resource(dev, sc->type, sc->io_rid, addr, 206a48ec78eSAndriy Gapon AMDSB_SMBIO_WIDTH); 207a48ec78eSAndriy Gapon if (rc != 0) { 208a48ec78eSAndriy Gapon device_printf(dev, "bus_set_resource for SMBus IO failed\n"); 209a48ec78eSAndriy Gapon return (ENXIO); 210a48ec78eSAndriy Gapon } 211*b2a49e88SBrian Poole sc->io_res = bus_alloc_resource_any(dev, sc->type, &sc->io_rid, 2123673f713SAndriy Gapon RF_ACTIVE); 21354d89ef1SConrad Meyer if (sc->io_res == NULL) { 21454d89ef1SConrad Meyer device_printf(dev, "Could not allocate I/O space\n"); 21554d89ef1SConrad Meyer return (ENXIO); 21654d89ef1SConrad Meyer } 217a48ec78eSAndriy Gapon sc->poll = 1; 218a48ec78eSAndriy Gapon return (0); 219a48ec78eSAndriy Gapon } 220a48ec78eSAndriy Gapon 221a2f51f57SAndriy Gapon static void 222a2f51f57SAndriy Gapon intsmb_release_resources(device_t dev) 223a2f51f57SAndriy Gapon { 224a2f51f57SAndriy Gapon struct intsmb_softc *sc = device_get_softc(dev); 225a2f51f57SAndriy Gapon 22628f5e880SJohn Baldwin device_delete_children(dev); 227a2f51f57SAndriy Gapon if (sc->irq_hand) 228a2f51f57SAndriy Gapon bus_teardown_intr(dev, sc->irq_res, sc->irq_hand); 229a2f51f57SAndriy Gapon if (sc->irq_res) 230a2f51f57SAndriy Gapon bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 231a2f51f57SAndriy Gapon if (sc->io_res) 232*b2a49e88SBrian Poole bus_release_resource(dev, sc->type, sc->io_rid, 233a2f51f57SAndriy Gapon sc->io_res); 234a2f51f57SAndriy Gapon mtx_destroy(&sc->lock); 235a2f51f57SAndriy Gapon } 236a2f51f57SAndriy Gapon 237f495ec29SRui Paulo static int 238f495ec29SRui Paulo intsmb_attach(device_t dev) 239f495ec29SRui Paulo { 240f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 241f495ec29SRui Paulo int error, rid, value; 242f495ec29SRui Paulo int intr; 243f495ec29SRui Paulo char *str; 244f495ec29SRui Paulo 245f495ec29SRui Paulo sc->dev = dev; 246f495ec29SRui Paulo 247f495ec29SRui Paulo mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF); 248f495ec29SRui Paulo 249f495ec29SRui Paulo sc->cfg_irq9 = 0; 250*b2a49e88SBrian Poole sc->type = SYS_RES_IOPORT; 251f495ec29SRui Paulo switch (pci_get_devid(dev)) { 252a48ec78eSAndriy Gapon #ifndef NO_CHANGE_PCICONF 253f495ec29SRui Paulo case 0x71138086: /* Intel 82371AB */ 254f495ec29SRui Paulo case 0x719b8086: /* Intel 82443MX */ 255f495ec29SRui Paulo /* Changing configuration is allowed. */ 256f495ec29SRui Paulo sc->cfg_irq9 = 1; 257f495ec29SRui Paulo break; 258f495ec29SRui Paulo #endif 2593673f713SAndriy Gapon case AMDSB_SMBUS_DEVID: 2603673f713SAndriy Gapon if (pci_get_revid(dev) >= AMDSB8_SMBUS_REVID) 261a48ec78eSAndriy Gapon sc->sb8xx = 1; 262a48ec78eSAndriy Gapon break; 2633673f713SAndriy Gapon case AMDFCH_SMBUS_DEVID: 2643673f713SAndriy Gapon case AMDCZ_SMBUS_DEVID: 265decf9c5fSKonstantin Belousov case HYGONCZ_SMBUS_DEVID: 2666c29523eSAndriy Gapon sc->sb8xx = 1; 2676c29523eSAndriy Gapon break; 268a48ec78eSAndriy Gapon } 269f495ec29SRui Paulo 270a48ec78eSAndriy Gapon if (sc->sb8xx) { 271a48ec78eSAndriy Gapon error = sb8xx_attach(dev); 272a48ec78eSAndriy Gapon if (error != 0) 273a48ec78eSAndriy Gapon goto fail; 274a48ec78eSAndriy Gapon else 275a48ec78eSAndriy Gapon goto no_intr; 276a48ec78eSAndriy Gapon } 277a48ec78eSAndriy Gapon 278a48ec78eSAndriy Gapon sc->io_rid = PCI_BASE_ADDR_SMB; 279*b2a49e88SBrian Poole sc->io_res = bus_alloc_resource_any(dev, sc->type, &sc->io_rid, 280f495ec29SRui Paulo RF_ACTIVE); 281f495ec29SRui Paulo if (sc->io_res == NULL) { 282f495ec29SRui Paulo device_printf(dev, "Could not allocate I/O space\n"); 283f495ec29SRui Paulo error = ENXIO; 284f495ec29SRui Paulo goto fail; 285f495ec29SRui Paulo } 286f495ec29SRui Paulo 287f495ec29SRui Paulo if (sc->cfg_irq9) { 288f495ec29SRui Paulo pci_write_config(dev, PCIR_INTLINE, 0x9, 1); 289f495ec29SRui Paulo pci_write_config(dev, PCI_HST_CFG_SMB, 290f495ec29SRui Paulo PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1); 291f495ec29SRui Paulo } 292f495ec29SRui Paulo value = pci_read_config(dev, PCI_HST_CFG_SMB, 1); 293f495ec29SRui Paulo sc->poll = (value & PCI_INTR_SMB_ENABLE) == 0; 294f495ec29SRui Paulo intr = value & PCI_INTR_SMB_MASK; 295f495ec29SRui Paulo switch (intr) { 296f495ec29SRui Paulo case PCI_INTR_SMB_SMI: 297f495ec29SRui Paulo str = "SMI"; 298f495ec29SRui Paulo break; 299f495ec29SRui Paulo case PCI_INTR_SMB_IRQ9: 300f495ec29SRui Paulo str = "IRQ 9"; 301f495ec29SRui Paulo break; 302f495ec29SRui Paulo case PCI_INTR_SMB_IRQ_PCI: 303f495ec29SRui Paulo str = "PCI IRQ"; 304f495ec29SRui Paulo break; 305f495ec29SRui Paulo default: 306f495ec29SRui Paulo str = "BOGUS"; 307f495ec29SRui Paulo } 308f495ec29SRui Paulo 309f495ec29SRui Paulo device_printf(dev, "intr %s %s ", str, 310f495ec29SRui Paulo sc->poll == 0 ? "enabled" : "disabled"); 311f495ec29SRui Paulo printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1)); 312f495ec29SRui Paulo 313f495ec29SRui Paulo if (!sc->poll && intr == PCI_INTR_SMB_SMI) { 314f495ec29SRui Paulo device_printf(dev, 315f495ec29SRui Paulo "using polling mode when configured interrupt is SMI\n"); 316f495ec29SRui Paulo sc->poll = 1; 317f495ec29SRui Paulo } 318f495ec29SRui Paulo 319f495ec29SRui Paulo if (sc->poll) 320f495ec29SRui Paulo goto no_intr; 321f495ec29SRui Paulo 322f495ec29SRui Paulo if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_PCI) { 323f495ec29SRui Paulo device_printf(dev, "Unsupported interrupt mode\n"); 324f495ec29SRui Paulo error = ENXIO; 325f495ec29SRui Paulo goto fail; 326f495ec29SRui Paulo } 327f495ec29SRui Paulo 328f495ec29SRui Paulo /* Force IRQ 9. */ 329f495ec29SRui Paulo rid = 0; 330f495ec29SRui Paulo if (sc->cfg_irq9) 331f495ec29SRui Paulo bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1); 332f495ec29SRui Paulo 333f495ec29SRui Paulo sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 334f495ec29SRui Paulo RF_SHAREABLE | RF_ACTIVE); 335f495ec29SRui Paulo if (sc->irq_res == NULL) { 336f495ec29SRui Paulo device_printf(dev, "Could not allocate irq\n"); 337f495ec29SRui Paulo error = ENXIO; 338f495ec29SRui Paulo goto fail; 339f495ec29SRui Paulo } 340f495ec29SRui Paulo 341f495ec29SRui Paulo error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 342f495ec29SRui Paulo NULL, intsmb_rawintr, sc, &sc->irq_hand); 343f495ec29SRui Paulo if (error) { 344f495ec29SRui Paulo device_printf(dev, "Failed to map intr\n"); 345f495ec29SRui Paulo goto fail; 346f495ec29SRui Paulo } 347f495ec29SRui Paulo 348f495ec29SRui Paulo no_intr: 349f495ec29SRui Paulo sc->isbusy = 0; 3505b56413dSWarner Losh sc->smbus = device_add_child(dev, "smbus", DEVICE_UNIT_ANY); 351f495ec29SRui Paulo if (sc->smbus == NULL) { 352a2f51f57SAndriy Gapon device_printf(dev, "failed to add smbus child\n"); 353f495ec29SRui Paulo error = ENXIO; 354f495ec29SRui Paulo goto fail; 355f495ec29SRui Paulo } 356f495ec29SRui Paulo error = device_probe_and_attach(sc->smbus); 357a2f51f57SAndriy Gapon if (error) { 358a2f51f57SAndriy Gapon device_printf(dev, "failed to probe+attach smbus child\n"); 359f495ec29SRui Paulo goto fail; 360a2f51f57SAndriy Gapon } 361f495ec29SRui Paulo 362f495ec29SRui Paulo #ifdef ENABLE_ALART 363f495ec29SRui Paulo /* Enable Arart */ 364f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 365f495ec29SRui Paulo #endif 366f495ec29SRui Paulo return (0); 367f495ec29SRui Paulo 368f495ec29SRui Paulo fail: 369a2f51f57SAndriy Gapon intsmb_release_resources(dev); 370f495ec29SRui Paulo return (error); 371f495ec29SRui Paulo } 372f495ec29SRui Paulo 373f495ec29SRui Paulo static int 374f495ec29SRui Paulo intsmb_detach(device_t dev) 375f495ec29SRui Paulo { 376f495ec29SRui Paulo int error; 377f495ec29SRui Paulo 378f495ec29SRui Paulo error = bus_generic_detach(dev); 379a2f51f57SAndriy Gapon if (error) { 380a2f51f57SAndriy Gapon device_printf(dev, "bus detach failed\n"); 381f495ec29SRui Paulo return (error); 382a2f51f57SAndriy Gapon } 383f495ec29SRui Paulo 384a2f51f57SAndriy Gapon intsmb_release_resources(dev); 385f495ec29SRui Paulo return (0); 386f495ec29SRui Paulo } 387f495ec29SRui Paulo 388f495ec29SRui Paulo static void 389f495ec29SRui Paulo intsmb_rawintr(void *arg) 390f495ec29SRui Paulo { 391f495ec29SRui Paulo struct intsmb_softc *sc = arg; 392f495ec29SRui Paulo 393f495ec29SRui Paulo INTSMB_LOCK(sc); 394f495ec29SRui Paulo intsmb_intr(sc); 395f495ec29SRui Paulo intsmb_slvintr(sc); 396f495ec29SRui Paulo INTSMB_UNLOCK(sc); 397f495ec29SRui Paulo } 398f495ec29SRui Paulo 399f495ec29SRui Paulo static int 400f495ec29SRui Paulo intsmb_callback(device_t dev, int index, void *data) 401f495ec29SRui Paulo { 402f495ec29SRui Paulo int error = 0; 403f495ec29SRui Paulo 404f495ec29SRui Paulo switch (index) { 405f495ec29SRui Paulo case SMB_REQUEST_BUS: 406f495ec29SRui Paulo break; 407f495ec29SRui Paulo case SMB_RELEASE_BUS: 408f495ec29SRui Paulo break; 409f495ec29SRui Paulo default: 410f495ec29SRui Paulo error = SMB_EINVAL; 411f495ec29SRui Paulo } 412f495ec29SRui Paulo 413f495ec29SRui Paulo return (error); 414f495ec29SRui Paulo } 415f495ec29SRui Paulo 416f495ec29SRui Paulo /* Counterpart of smbtx_smb_free(). */ 417f495ec29SRui Paulo static int 418f495ec29SRui Paulo intsmb_free(struct intsmb_softc *sc) 419f495ec29SRui Paulo { 420f495ec29SRui Paulo 421f495ec29SRui Paulo INTSMB_LOCK_ASSERT(sc); 422f495ec29SRui Paulo if ((bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & PIIX4_SMBHSTSTAT_BUSY) || 423f495ec29SRui Paulo #ifdef ENABLE_ALART 424f495ec29SRui Paulo (bus_read_1(sc->io_res, PIIX4_SMBSLVSTS) & PIIX4_SMBSLVSTS_BUSY) || 425f495ec29SRui Paulo #endif 426f495ec29SRui Paulo sc->isbusy) 427f495ec29SRui Paulo return (SMB_EBUSY); 428f495ec29SRui Paulo 429f495ec29SRui Paulo sc->isbusy = 1; 430f495ec29SRui Paulo /* Disable Interrupt in slave part. */ 431f495ec29SRui Paulo #ifndef ENABLE_ALART 432f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 0); 433f495ec29SRui Paulo #endif 434f495ec29SRui Paulo /* Reset INTR Flag to prepare INTR. */ 435f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTSTS, 436f495ec29SRui Paulo PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 437f495ec29SRui Paulo PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL); 438f495ec29SRui Paulo return (0); 439f495ec29SRui Paulo } 440f495ec29SRui Paulo 441f495ec29SRui Paulo static int 442f495ec29SRui Paulo intsmb_intr(struct intsmb_softc *sc) 443f495ec29SRui Paulo { 444f495ec29SRui Paulo int status, tmp; 445f495ec29SRui Paulo 446f495ec29SRui Paulo status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 447f495ec29SRui Paulo if (status & PIIX4_SMBHSTSTAT_BUSY) 448f495ec29SRui Paulo return (1); 449f495ec29SRui Paulo 450f495ec29SRui Paulo if (status & (PIIX4_SMBHSTSTAT_INTR | PIIX4_SMBHSTSTAT_ERR | 451f495ec29SRui Paulo PIIX4_SMBHSTSTAT_BUSC | PIIX4_SMBHSTSTAT_FAIL)) { 452f495ec29SRui Paulo 453f495ec29SRui Paulo tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 454f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, 455f495ec29SRui Paulo tmp & ~PIIX4_SMBHSTCNT_INTREN); 456f495ec29SRui Paulo if (sc->isbusy) { 457f495ec29SRui Paulo sc->isbusy = 0; 458f495ec29SRui Paulo wakeup(sc); 459f495ec29SRui Paulo } 460f495ec29SRui Paulo return (0); 461f495ec29SRui Paulo } 462f495ec29SRui Paulo return (1); /* Not Completed */ 463f495ec29SRui Paulo } 464f495ec29SRui Paulo 465f495ec29SRui Paulo static int 466f495ec29SRui Paulo intsmb_slvintr(struct intsmb_softc *sc) 467f495ec29SRui Paulo { 468f495ec29SRui Paulo int status; 469f495ec29SRui Paulo 470f495ec29SRui Paulo status = bus_read_1(sc->io_res, PIIX4_SMBSLVSTS); 471f495ec29SRui Paulo if (status & PIIX4_SMBSLVSTS_BUSY) 472f495ec29SRui Paulo return (1); 473f495ec29SRui Paulo if (status & PIIX4_SMBSLVSTS_ALART) 474f495ec29SRui Paulo intsmb_alrintr(sc); 475f495ec29SRui Paulo else if (status & ~(PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 476f495ec29SRui Paulo | PIIX4_SMBSLVSTS_SDW1)) { 477f495ec29SRui Paulo } 478f495ec29SRui Paulo 479f495ec29SRui Paulo /* Reset Status Register */ 480f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVSTS, 481f495ec29SRui Paulo PIIX4_SMBSLVSTS_ALART | PIIX4_SMBSLVSTS_SDW2 | 482f495ec29SRui Paulo PIIX4_SMBSLVSTS_SDW1 | PIIX4_SMBSLVSTS_SLV); 483f495ec29SRui Paulo return (0); 484f495ec29SRui Paulo } 485f495ec29SRui Paulo 486f495ec29SRui Paulo static void 487f495ec29SRui Paulo intsmb_alrintr(struct intsmb_softc *sc) 488f495ec29SRui Paulo { 489d483782eSWarner Losh int slvcnt __unused; 490f495ec29SRui Paulo #ifdef ENABLE_ALART 491f495ec29SRui Paulo int error; 492f495ec29SRui Paulo uint8_t addr; 493f495ec29SRui Paulo #endif 494f495ec29SRui Paulo 495f495ec29SRui Paulo /* Stop generating INTR from ALART. */ 496f495ec29SRui Paulo slvcnt = bus_read_1(sc->io_res, PIIX4_SMBSLVCNT); 497f495ec29SRui Paulo #ifdef ENABLE_ALART 498f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 499f495ec29SRui Paulo slvcnt & ~PIIX4_SMBSLVCNT_ALTEN); 500f495ec29SRui Paulo #endif 501f495ec29SRui Paulo DELAY(5); 502f495ec29SRui Paulo 503f495ec29SRui Paulo /* Ask bus who asserted it and then ask it what's the matter. */ 504f495ec29SRui Paulo #ifdef ENABLE_ALART 505f495ec29SRui Paulo error = intsmb_free(sc); 506f495ec29SRui Paulo if (error) 507f495ec29SRui Paulo return; 508f495ec29SRui Paulo 509f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, SMBALTRESP | LSB); 510f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 1); 511f495ec29SRui Paulo error = intsmb_stop_poll(sc); 512f495ec29SRui Paulo if (error) 513f495ec29SRui Paulo device_printf(sc->dev, "ALART: ERROR\n"); 514f495ec29SRui Paulo else { 515f495ec29SRui Paulo addr = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 516f495ec29SRui Paulo device_printf(sc->dev, "ALART_RESPONSE: 0x%x\n", addr); 517f495ec29SRui Paulo } 518f495ec29SRui Paulo 519f495ec29SRui Paulo /* Re-enable INTR from ALART. */ 520f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 521f495ec29SRui Paulo slvcnt | PIIX4_SMBSLVCNT_ALTEN); 522f495ec29SRui Paulo DELAY(5); 523f495ec29SRui Paulo #endif 524f495ec29SRui Paulo } 525f495ec29SRui Paulo 526f495ec29SRui Paulo static void 527f495ec29SRui Paulo intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int nointr) 528f495ec29SRui Paulo { 529f495ec29SRui Paulo unsigned char tmp; 530f495ec29SRui Paulo 531f495ec29SRui Paulo INTSMB_LOCK_ASSERT(sc); 532f495ec29SRui Paulo tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 533f495ec29SRui Paulo tmp &= 0xe0; 534f495ec29SRui Paulo tmp |= cmd; 535f495ec29SRui Paulo tmp |= PIIX4_SMBHSTCNT_START; 536f495ec29SRui Paulo 537f495ec29SRui Paulo /* While not in autoconfiguration enable interrupts. */ 538f495ec29SRui Paulo if (!sc->poll && !cold && !nointr) 539f495ec29SRui Paulo tmp |= PIIX4_SMBHSTCNT_INTREN; 540f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp); 541f495ec29SRui Paulo } 542f495ec29SRui Paulo 543f495ec29SRui Paulo static int 544f495ec29SRui Paulo intsmb_error(device_t dev, int status) 545f495ec29SRui Paulo { 546f495ec29SRui Paulo int error = 0; 547f495ec29SRui Paulo 5484c2fb039SAndriy Gapon /* 5494c2fb039SAndriy Gapon * PIIX4_SMBHSTSTAT_ERR can mean either of 5504c2fb039SAndriy Gapon * - SMB_ENOACK ("Unclaimed cycle"), 5514c2fb039SAndriy Gapon * - SMB_ETIMEOUT ("Host device time-out"), 5524c2fb039SAndriy Gapon * - SMB_EINVAL ("Illegal command field"). 5534c2fb039SAndriy Gapon * SMB_ENOACK seems to be most typical. 5544c2fb039SAndriy Gapon */ 555f495ec29SRui Paulo if (status & PIIX4_SMBHSTSTAT_ERR) 5564c2fb039SAndriy Gapon error |= SMB_ENOACK; 557f495ec29SRui Paulo if (status & PIIX4_SMBHSTSTAT_BUSC) 558f495ec29SRui Paulo error |= SMB_ECOLLI; 559f495ec29SRui Paulo if (status & PIIX4_SMBHSTSTAT_FAIL) 5604c2fb039SAndriy Gapon error |= SMB_EABORT; 561f495ec29SRui Paulo 562f495ec29SRui Paulo if (error != 0 && bootverbose) 563f495ec29SRui Paulo device_printf(dev, "error = %d, status = %#x\n", error, status); 564f495ec29SRui Paulo 565f495ec29SRui Paulo return (error); 566f495ec29SRui Paulo } 567f495ec29SRui Paulo 568f495ec29SRui Paulo /* 569f495ec29SRui Paulo * Polling Code. 570f495ec29SRui Paulo * 571f495ec29SRui Paulo * Polling is not encouraged because it requires waiting for the 572f495ec29SRui Paulo * device if it is busy. 573f495ec29SRui Paulo * (29063505.pdf from Intel) But during boot, interrupt cannot be used, so use 574f495ec29SRui Paulo * polling code then. 575f495ec29SRui Paulo */ 576f495ec29SRui Paulo static int 577f495ec29SRui Paulo intsmb_stop_poll(struct intsmb_softc *sc) 578f495ec29SRui Paulo { 579f495ec29SRui Paulo int error, i, status, tmp; 580f495ec29SRui Paulo 581f495ec29SRui Paulo INTSMB_LOCK_ASSERT(sc); 582f495ec29SRui Paulo 583f495ec29SRui Paulo /* First, wait for busy to be set. */ 584f495ec29SRui Paulo for (i = 0; i < 0x7fff; i++) 585f495ec29SRui Paulo if (bus_read_1(sc->io_res, PIIX4_SMBHSTSTS) & 586f495ec29SRui Paulo PIIX4_SMBHSTSTAT_BUSY) 587f495ec29SRui Paulo break; 588f495ec29SRui Paulo 589f495ec29SRui Paulo /* Wait for busy to clear. */ 590f495ec29SRui Paulo for (i = 0; i < 0x7fff; i++) { 591f495ec29SRui Paulo status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 592f495ec29SRui Paulo if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 593f495ec29SRui Paulo sc->isbusy = 0; 594f495ec29SRui Paulo error = intsmb_error(sc->dev, status); 595f495ec29SRui Paulo return (error); 596f495ec29SRui Paulo } 597f495ec29SRui Paulo } 598f495ec29SRui Paulo 599f495ec29SRui Paulo /* Timed out waiting for busy to clear. */ 600f495ec29SRui Paulo sc->isbusy = 0; 601f495ec29SRui Paulo tmp = bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 602f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp & ~PIIX4_SMBHSTCNT_INTREN); 603f495ec29SRui Paulo return (SMB_ETIMEOUT); 604f495ec29SRui Paulo } 605f495ec29SRui Paulo 606f495ec29SRui Paulo /* 607f495ec29SRui Paulo * Wait for completion and return result. 608f495ec29SRui Paulo */ 609f495ec29SRui Paulo static int 610f495ec29SRui Paulo intsmb_stop(struct intsmb_softc *sc) 611f495ec29SRui Paulo { 612f495ec29SRui Paulo int error, status; 613f495ec29SRui Paulo 614f495ec29SRui Paulo INTSMB_LOCK_ASSERT(sc); 615f495ec29SRui Paulo 616f495ec29SRui Paulo if (sc->poll || cold) 617f495ec29SRui Paulo /* So that it can use device during device probe on SMBus. */ 618f495ec29SRui Paulo return (intsmb_stop_poll(sc)); 619f495ec29SRui Paulo 620f495ec29SRui Paulo error = msleep(sc, &sc->lock, PWAIT | PCATCH, "SMBWAI", hz / 8); 621f495ec29SRui Paulo if (error == 0) { 622f495ec29SRui Paulo status = bus_read_1(sc->io_res, PIIX4_SMBHSTSTS); 623f495ec29SRui Paulo if (!(status & PIIX4_SMBHSTSTAT_BUSY)) { 624f495ec29SRui Paulo error = intsmb_error(sc->dev, status); 625f495ec29SRui Paulo if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR)) 626f495ec29SRui Paulo device_printf(sc->dev, "unknown cause why?\n"); 627f495ec29SRui Paulo #ifdef ENABLE_ALART 628f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, 629f495ec29SRui Paulo PIIX4_SMBSLVCNT_ALTEN); 630f495ec29SRui Paulo #endif 631f495ec29SRui Paulo return (error); 632f495ec29SRui Paulo } 633f495ec29SRui Paulo } 634f495ec29SRui Paulo 635f495ec29SRui Paulo /* Timeout Procedure. */ 636f495ec29SRui Paulo sc->isbusy = 0; 637f495ec29SRui Paulo 638453130d9SPedro F. Giffuni /* Re-enable suppressed interrupt from slave part. */ 639f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBSLVCNT, PIIX4_SMBSLVCNT_ALTEN); 640f495ec29SRui Paulo if (error == EWOULDBLOCK) 641f495ec29SRui Paulo return (SMB_ETIMEOUT); 642f495ec29SRui Paulo else 643f495ec29SRui Paulo return (SMB_EABORT); 644f495ec29SRui Paulo } 645f495ec29SRui Paulo 646f495ec29SRui Paulo static int 647f495ec29SRui Paulo intsmb_quick(device_t dev, u_char slave, int how) 648f495ec29SRui Paulo { 649f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 650f495ec29SRui Paulo int error; 651f495ec29SRui Paulo u_char data; 652f495ec29SRui Paulo 653f495ec29SRui Paulo data = slave; 654f495ec29SRui Paulo 655f495ec29SRui Paulo /* Quick command is part of Address, I think. */ 656f495ec29SRui Paulo switch(how) { 657f495ec29SRui Paulo case SMB_QWRITE: 658f495ec29SRui Paulo data &= ~LSB; 659f495ec29SRui Paulo break; 660f495ec29SRui Paulo case SMB_QREAD: 661f495ec29SRui Paulo data |= LSB; 662f495ec29SRui Paulo break; 663f495ec29SRui Paulo default: 664f495ec29SRui Paulo return (SMB_EINVAL); 665f495ec29SRui Paulo } 666f495ec29SRui Paulo 667f495ec29SRui Paulo INTSMB_LOCK(sc); 668f495ec29SRui Paulo error = intsmb_free(sc); 669f495ec29SRui Paulo if (error) { 670f495ec29SRui Paulo INTSMB_UNLOCK(sc); 671f495ec29SRui Paulo return (error); 672f495ec29SRui Paulo } 673f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, data); 674f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_QUICK, 0); 675f495ec29SRui Paulo error = intsmb_stop(sc); 676f495ec29SRui Paulo INTSMB_UNLOCK(sc); 677f495ec29SRui Paulo return (error); 678f495ec29SRui Paulo } 679f495ec29SRui Paulo 680f495ec29SRui Paulo static int 681f495ec29SRui Paulo intsmb_sendb(device_t dev, u_char slave, char byte) 682f495ec29SRui Paulo { 683f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 684f495ec29SRui Paulo int error; 685f495ec29SRui Paulo 686f495ec29SRui Paulo INTSMB_LOCK(sc); 687f495ec29SRui Paulo error = intsmb_free(sc); 688f495ec29SRui Paulo if (error) { 689f495ec29SRui Paulo INTSMB_UNLOCK(sc); 690f495ec29SRui Paulo return (error); 691f495ec29SRui Paulo } 692f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 693f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, byte); 694f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 695f495ec29SRui Paulo error = intsmb_stop(sc); 696f495ec29SRui Paulo INTSMB_UNLOCK(sc); 697f495ec29SRui Paulo return (error); 698f495ec29SRui Paulo } 699f495ec29SRui Paulo 700f495ec29SRui Paulo static int 701f495ec29SRui Paulo intsmb_recvb(device_t dev, u_char slave, char *byte) 702f495ec29SRui Paulo { 703f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 704f495ec29SRui Paulo int error; 705f495ec29SRui Paulo 706f495ec29SRui Paulo INTSMB_LOCK(sc); 707f495ec29SRui Paulo error = intsmb_free(sc); 708f495ec29SRui Paulo if (error) { 709f495ec29SRui Paulo INTSMB_UNLOCK(sc); 710f495ec29SRui Paulo return (error); 711f495ec29SRui Paulo } 712f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 713f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BYTE, 0); 714f495ec29SRui Paulo error = intsmb_stop(sc); 715f495ec29SRui Paulo if (error == 0) { 716f495ec29SRui Paulo #ifdef RECV_IS_IN_CMD 717f495ec29SRui Paulo /* 718f495ec29SRui Paulo * Linux SMBus stuff also troubles 719f495ec29SRui Paulo * Because Intel's datasheet does not make clear. 720f495ec29SRui Paulo */ 721f495ec29SRui Paulo *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTCMD); 722f495ec29SRui Paulo #else 723f495ec29SRui Paulo *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 724f495ec29SRui Paulo #endif 725f495ec29SRui Paulo } 726f495ec29SRui Paulo INTSMB_UNLOCK(sc); 727f495ec29SRui Paulo return (error); 728f495ec29SRui Paulo } 729f495ec29SRui Paulo 730f495ec29SRui Paulo static int 731f495ec29SRui Paulo intsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 732f495ec29SRui Paulo { 733f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 734f495ec29SRui Paulo int error; 735f495ec29SRui Paulo 736f495ec29SRui Paulo INTSMB_LOCK(sc); 737f495ec29SRui Paulo error = intsmb_free(sc); 738f495ec29SRui Paulo if (error) { 739f495ec29SRui Paulo INTSMB_UNLOCK(sc); 740f495ec29SRui Paulo return (error); 741f495ec29SRui Paulo } 742f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 743f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 744f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, byte); 745f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 746f495ec29SRui Paulo error = intsmb_stop(sc); 747f495ec29SRui Paulo INTSMB_UNLOCK(sc); 748f495ec29SRui Paulo return (error); 749f495ec29SRui Paulo } 750f495ec29SRui Paulo 751f495ec29SRui Paulo static int 752f495ec29SRui Paulo intsmb_writew(device_t dev, u_char slave, char cmd, short word) 753f495ec29SRui Paulo { 754f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 755f495ec29SRui Paulo int error; 756f495ec29SRui Paulo 757f495ec29SRui Paulo INTSMB_LOCK(sc); 758f495ec29SRui Paulo error = intsmb_free(sc); 759f495ec29SRui Paulo if (error) { 760f495ec29SRui Paulo INTSMB_UNLOCK(sc); 761f495ec29SRui Paulo return (error); 762f495ec29SRui Paulo } 763f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 764f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 765f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, word & 0xff); 766f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTDAT1, (word >> 8) & 0xff); 767f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 768f495ec29SRui Paulo error = intsmb_stop(sc); 769f495ec29SRui Paulo INTSMB_UNLOCK(sc); 770f495ec29SRui Paulo return (error); 771f495ec29SRui Paulo } 772f495ec29SRui Paulo 773f495ec29SRui Paulo static int 774f495ec29SRui Paulo intsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 775f495ec29SRui Paulo { 776f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 777f495ec29SRui Paulo int error; 778f495ec29SRui Paulo 779f495ec29SRui Paulo INTSMB_LOCK(sc); 780f495ec29SRui Paulo error = intsmb_free(sc); 781f495ec29SRui Paulo if (error) { 782f495ec29SRui Paulo INTSMB_UNLOCK(sc); 783f495ec29SRui Paulo return (error); 784f495ec29SRui Paulo } 785f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 786f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 787f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BDATA, 0); 788f495ec29SRui Paulo error = intsmb_stop(sc); 789f495ec29SRui Paulo if (error == 0) 790f495ec29SRui Paulo *byte = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 791f495ec29SRui Paulo INTSMB_UNLOCK(sc); 792f495ec29SRui Paulo return (error); 793f495ec29SRui Paulo } 794f495ec29SRui Paulo 795f495ec29SRui Paulo static int 796f495ec29SRui Paulo intsmb_readw(device_t dev, u_char slave, char cmd, short *word) 797f495ec29SRui Paulo { 798f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 799f495ec29SRui Paulo int error; 800f495ec29SRui Paulo 801f495ec29SRui Paulo INTSMB_LOCK(sc); 802f495ec29SRui Paulo error = intsmb_free(sc); 803f495ec29SRui Paulo if (error) { 804f495ec29SRui Paulo INTSMB_UNLOCK(sc); 805f495ec29SRui Paulo return (error); 806f495ec29SRui Paulo } 807f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 808f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 809f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_WDATA, 0); 810f495ec29SRui Paulo error = intsmb_stop(sc); 811f495ec29SRui Paulo if (error == 0) { 812f495ec29SRui Paulo *word = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 813f495ec29SRui Paulo *word |= bus_read_1(sc->io_res, PIIX4_SMBHSTDAT1) << 8; 814f495ec29SRui Paulo } 815f495ec29SRui Paulo INTSMB_UNLOCK(sc); 816f495ec29SRui Paulo return (error); 817f495ec29SRui Paulo } 818f495ec29SRui Paulo 819f495ec29SRui Paulo static int 820f495ec29SRui Paulo intsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 821f495ec29SRui Paulo { 822f495ec29SRui Paulo 823f495ec29SRui Paulo return (SMB_ENOTSUPP); 824f495ec29SRui Paulo } 825f495ec29SRui Paulo 826f495ec29SRui Paulo static int 827f495ec29SRui Paulo intsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 828f495ec29SRui Paulo { 829f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 830f495ec29SRui Paulo int error, i; 831f495ec29SRui Paulo 832f495ec29SRui Paulo if (count > SMBBLOCKTRANS_MAX || count == 0) 833f495ec29SRui Paulo return (SMB_EINVAL); 834f495ec29SRui Paulo 835f495ec29SRui Paulo INTSMB_LOCK(sc); 836f495ec29SRui Paulo error = intsmb_free(sc); 837f495ec29SRui Paulo if (error) { 838f495ec29SRui Paulo INTSMB_UNLOCK(sc); 839f495ec29SRui Paulo return (error); 840f495ec29SRui Paulo } 841f495ec29SRui Paulo 842f495ec29SRui Paulo /* Reset internal array index. */ 843f495ec29SRui Paulo bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 844f495ec29SRui Paulo 845f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave & ~LSB); 846f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 847f495ec29SRui Paulo for (i = 0; i < count; i++) 848f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBBLKDAT, buf[i]); 849f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTDAT0, count); 850f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 851f495ec29SRui Paulo error = intsmb_stop(sc); 852f495ec29SRui Paulo INTSMB_UNLOCK(sc); 853f495ec29SRui Paulo return (error); 854f495ec29SRui Paulo } 855f495ec29SRui Paulo 856f495ec29SRui Paulo static int 857f495ec29SRui Paulo intsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 858f495ec29SRui Paulo { 859f495ec29SRui Paulo struct intsmb_softc *sc = device_get_softc(dev); 860f495ec29SRui Paulo int error, i; 861d483782eSWarner Losh u_char nread; 862f495ec29SRui Paulo 863f495ec29SRui Paulo INTSMB_LOCK(sc); 864f495ec29SRui Paulo error = intsmb_free(sc); 865f495ec29SRui Paulo if (error) { 866f495ec29SRui Paulo INTSMB_UNLOCK(sc); 867f495ec29SRui Paulo return (error); 868f495ec29SRui Paulo } 869f495ec29SRui Paulo 870f495ec29SRui Paulo /* Reset internal array index. */ 871f495ec29SRui Paulo bus_read_1(sc->io_res, PIIX4_SMBHSTCNT); 872f495ec29SRui Paulo 873f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTADD, slave | LSB); 874f495ec29SRui Paulo bus_write_1(sc->io_res, PIIX4_SMBHSTCMD, cmd); 875f495ec29SRui Paulo intsmb_start(sc, PIIX4_SMBHSTCNT_PROT_BLOCK, 0); 876f495ec29SRui Paulo error = intsmb_stop(sc); 877f495ec29SRui Paulo if (error == 0) { 878f495ec29SRui Paulo nread = bus_read_1(sc->io_res, PIIX4_SMBHSTDAT0); 879f495ec29SRui Paulo if (nread != 0 && nread <= SMBBLOCKTRANS_MAX) { 880f495ec29SRui Paulo *count = nread; 881678d569fSAndriy Gapon for (i = 0; i < nread; i++) 882d483782eSWarner Losh bus_read_1(sc->io_res, PIIX4_SMBBLKDAT); 883f495ec29SRui Paulo } else 884f495ec29SRui Paulo error = SMB_EBUSERR; 885f495ec29SRui Paulo } 886f495ec29SRui Paulo INTSMB_UNLOCK(sc); 887f495ec29SRui Paulo return (error); 888f495ec29SRui Paulo } 889f495ec29SRui Paulo 890f495ec29SRui Paulo static device_method_t intsmb_methods[] = { 891f495ec29SRui Paulo /* Device interface */ 892f495ec29SRui Paulo DEVMETHOD(device_probe, intsmb_probe), 893f495ec29SRui Paulo DEVMETHOD(device_attach, intsmb_attach), 894f495ec29SRui Paulo DEVMETHOD(device_detach, intsmb_detach), 895f495ec29SRui Paulo 896f495ec29SRui Paulo /* SMBus interface */ 897f495ec29SRui Paulo DEVMETHOD(smbus_callback, intsmb_callback), 898f495ec29SRui Paulo DEVMETHOD(smbus_quick, intsmb_quick), 899f495ec29SRui Paulo DEVMETHOD(smbus_sendb, intsmb_sendb), 900f495ec29SRui Paulo DEVMETHOD(smbus_recvb, intsmb_recvb), 901f495ec29SRui Paulo DEVMETHOD(smbus_writeb, intsmb_writeb), 902f495ec29SRui Paulo DEVMETHOD(smbus_writew, intsmb_writew), 903f495ec29SRui Paulo DEVMETHOD(smbus_readb, intsmb_readb), 904f495ec29SRui Paulo DEVMETHOD(smbus_readw, intsmb_readw), 905f495ec29SRui Paulo DEVMETHOD(smbus_pcall, intsmb_pcall), 906f495ec29SRui Paulo DEVMETHOD(smbus_bwrite, intsmb_bwrite), 907f495ec29SRui Paulo DEVMETHOD(smbus_bread, intsmb_bread), 908f495ec29SRui Paulo 909f495ec29SRui Paulo DEVMETHOD_END 910f495ec29SRui Paulo }; 911f495ec29SRui Paulo 912f495ec29SRui Paulo static driver_t intsmb_driver = { 913f495ec29SRui Paulo "intsmb", 914f495ec29SRui Paulo intsmb_methods, 915f495ec29SRui Paulo sizeof(struct intsmb_softc), 916f495ec29SRui Paulo }; 917f495ec29SRui Paulo 918dfee3204SJohn Baldwin DRIVER_MODULE_ORDERED(intsmb, pci, intsmb_driver, 0, 0, SI_ORDER_ANY); 919c6d39765SJohn Baldwin DRIVER_MODULE(smbus, intsmb, smbus_driver, 0, 0); 920f495ec29SRui Paulo MODULE_DEPEND(intsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 921f495ec29SRui Paulo MODULE_VERSION(intsmb, 1); 922d2064cf0SWarner Losh MODULE_PNP_INFO("W32:vendor/device;D:#", pci, intpm, intsmb_products, 923329e817fSWarner Losh nitems(intsmb_products)); 924