1*c0d07f7cShaesbaert /* $OpenBSD: ichiic.c,v 1.29 2012/10/05 10:51:28 haesbaert Exp $ */ 23b00bf77Sgrange 33b00bf77Sgrange /* 4a547cd46Sgrange * Copyright (c) 2005, 2006 Alexander Yurchenko <grange@openbsd.org> 53b00bf77Sgrange * 63b00bf77Sgrange * Permission to use, copy, modify, and distribute this software for any 73b00bf77Sgrange * purpose with or without fee is hereby granted, provided that the above 83b00bf77Sgrange * copyright notice and this permission notice appear in all copies. 93b00bf77Sgrange * 103b00bf77Sgrange * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 113b00bf77Sgrange * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 123b00bf77Sgrange * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 133b00bf77Sgrange * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 143b00bf77Sgrange * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 153b00bf77Sgrange * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 163b00bf77Sgrange * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 173b00bf77Sgrange */ 183b00bf77Sgrange 193b00bf77Sgrange /* 203b00bf77Sgrange * Intel ICH SMBus controller driver. 213b00bf77Sgrange */ 223b00bf77Sgrange 233b00bf77Sgrange #include <sys/param.h> 243b00bf77Sgrange #include <sys/systm.h> 253b00bf77Sgrange #include <sys/device.h> 263b00bf77Sgrange #include <sys/kernel.h> 2773550447Sdlg #include <sys/rwlock.h> 283b00bf77Sgrange 293b00bf77Sgrange #include <machine/bus.h> 303b00bf77Sgrange 313b00bf77Sgrange #include <dev/pci/pcidevs.h> 323b00bf77Sgrange #include <dev/pci/pcireg.h> 333b00bf77Sgrange #include <dev/pci/pcivar.h> 343b00bf77Sgrange 353b00bf77Sgrange #include <dev/pci/ichreg.h> 363b00bf77Sgrange 373b00bf77Sgrange #include <dev/i2c/i2cvar.h> 383b00bf77Sgrange 393b00bf77Sgrange #ifdef ICHIIC_DEBUG 403b00bf77Sgrange #define DPRINTF(x) printf x 413b00bf77Sgrange #else 423b00bf77Sgrange #define DPRINTF(x) 433b00bf77Sgrange #endif 443b00bf77Sgrange 453b00bf77Sgrange #define ICHIIC_DELAY 100 463b00bf77Sgrange #define ICHIIC_TIMEOUT 1 473b00bf77Sgrange 483b00bf77Sgrange struct ichiic_softc { 493b00bf77Sgrange struct device sc_dev; 503b00bf77Sgrange 513b00bf77Sgrange bus_space_tag_t sc_iot; 523b00bf77Sgrange bus_space_handle_t sc_ioh; 533b00bf77Sgrange void * sc_ih; 54749dbd03Sgrange int sc_poll; 553b00bf77Sgrange 563b00bf77Sgrange struct i2c_controller sc_i2c_tag; 5773550447Sdlg struct rwlock sc_i2c_lock; 58c17198b5Sgrange struct { 593b00bf77Sgrange i2c_op_t op; 603b00bf77Sgrange void * buf; 613b00bf77Sgrange size_t len; 623b00bf77Sgrange int flags; 63c17198b5Sgrange volatile int error; 643b00bf77Sgrange } sc_i2c_xfer; 653b00bf77Sgrange }; 663b00bf77Sgrange 673b00bf77Sgrange int ichiic_match(struct device *, void *, void *); 683b00bf77Sgrange void ichiic_attach(struct device *, struct device *, void *); 693b00bf77Sgrange 703b00bf77Sgrange int ichiic_i2c_acquire_bus(void *, int); 713b00bf77Sgrange void ichiic_i2c_release_bus(void *, int); 723b00bf77Sgrange int ichiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 733b00bf77Sgrange void *, size_t, int); 743b00bf77Sgrange 753b00bf77Sgrange int ichiic_intr(void *); 763b00bf77Sgrange 773b00bf77Sgrange struct cfattach ichiic_ca = { 783b00bf77Sgrange sizeof(struct ichiic_softc), 793b00bf77Sgrange ichiic_match, 803b00bf77Sgrange ichiic_attach 813b00bf77Sgrange }; 823b00bf77Sgrange 833b00bf77Sgrange struct cfdriver ichiic_cd = { 843b00bf77Sgrange NULL, "ichiic", DV_DULL 853b00bf77Sgrange }; 863b00bf77Sgrange 873b00bf77Sgrange const struct pci_matchid ichiic_ids[] = { 888e056978Ssthen { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_3400_SMB }, 89d22bb502Sjsg { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6SERIES_SMB }, 909eb33f7aSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_SMB }, 91ae7cc0c1Sbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_SMB }, 92c9849cecSjasper { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_7SERIES_SMB }, 933b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_SMB }, 943b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_SMB }, 953b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_SMB }, 963b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_SMB }, 973b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_SMB }, 989eb33f7aSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_SMB }, 993b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_SMB }, 1003b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_SMB }, 101e182382cSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_SMB }, 102a2cd58f7Sbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_SMB }, 103a54030c0Sbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801I_SMB }, 1048e056978Ssthen { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JD_SMB }, 10554d12d52Sjsg { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JI_SMB }, 10654d12d52Sjsg { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_C600_SMB } 1073b00bf77Sgrange }; 1083b00bf77Sgrange 1093b00bf77Sgrange int 1103b00bf77Sgrange ichiic_match(struct device *parent, void *match, void *aux) 1113b00bf77Sgrange { 1123b00bf77Sgrange return (pci_matchbyid(aux, ichiic_ids, 1133b00bf77Sgrange sizeof(ichiic_ids) / sizeof(ichiic_ids[0]))); 1143b00bf77Sgrange } 1153b00bf77Sgrange 1163b00bf77Sgrange void 1173b00bf77Sgrange ichiic_attach(struct device *parent, struct device *self, void *aux) 1183b00bf77Sgrange { 1193b00bf77Sgrange struct ichiic_softc *sc = (struct ichiic_softc *)self; 1203b00bf77Sgrange struct pci_attach_args *pa = aux; 1213b00bf77Sgrange struct i2cbus_attach_args iba; 122749dbd03Sgrange pcireg_t conf; 1233b00bf77Sgrange bus_size_t iosize; 1243b00bf77Sgrange pci_intr_handle_t ih; 1253b00bf77Sgrange const char *intrstr = NULL; 1263b00bf77Sgrange 127d204c798Sgrange /* Read configuration */ 128d204c798Sgrange conf = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_SMB_HOSTC); 1296f23e57aSgrange DPRINTF((": conf 0x%08x", conf)); 130d204c798Sgrange 131d204c798Sgrange if ((conf & ICH_SMB_HOSTC_HSTEN) == 0) { 132d204c798Sgrange printf(": SMBus disabled\n"); 133d204c798Sgrange return; 134d204c798Sgrange } 135d204c798Sgrange 1363b00bf77Sgrange /* Map I/O space */ 1373b00bf77Sgrange if (pci_mapreg_map(pa, ICH_SMB_BASE, PCI_MAPREG_TYPE_IO, 0, 1383b00bf77Sgrange &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) { 139e10c952fSsthen printf(": can't map i/o space\n"); 1403b00bf77Sgrange return; 1413b00bf77Sgrange } 1423b00bf77Sgrange 143749dbd03Sgrange sc->sc_poll = 1; 144e3269dfcSgrange if (conf & ICH_SMB_HOSTC_SMIEN) { 145e3269dfcSgrange /* No PCI IRQ */ 146e3269dfcSgrange printf(": SMI"); 147749dbd03Sgrange } else { 1483b00bf77Sgrange /* Install interrupt handler */ 149e3269dfcSgrange if (pci_intr_map(pa, &ih) == 0) { 1503b00bf77Sgrange intrstr = pci_intr_string(pa->pa_pc, ih); 151749dbd03Sgrange sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, 152749dbd03Sgrange ichiic_intr, sc, sc->sc_dev.dv_xname); 153e3269dfcSgrange if (sc->sc_ih != NULL) { 1543b00bf77Sgrange printf(": %s", intrstr); 155e3269dfcSgrange sc->sc_poll = 0; 156e3269dfcSgrange } 157e3269dfcSgrange } 158e3269dfcSgrange if (sc->sc_poll) 159e3269dfcSgrange printf(": polling"); 160749dbd03Sgrange } 1613b00bf77Sgrange 1623b00bf77Sgrange printf("\n"); 1633b00bf77Sgrange 1643b00bf77Sgrange /* Attach I2C bus */ 16573550447Sdlg rw_init(&sc->sc_i2c_lock, "iiclk"); 1663b00bf77Sgrange sc->sc_i2c_tag.ic_cookie = sc; 1673b00bf77Sgrange sc->sc_i2c_tag.ic_acquire_bus = ichiic_i2c_acquire_bus; 1683b00bf77Sgrange sc->sc_i2c_tag.ic_release_bus = ichiic_i2c_release_bus; 1693b00bf77Sgrange sc->sc_i2c_tag.ic_exec = ichiic_i2c_exec; 170a5a63700Sderaadt 171f57d9f10Sgrange bzero(&iba, sizeof(iba)); 1723b00bf77Sgrange iba.iba_name = "iic"; 1733b00bf77Sgrange iba.iba_tag = &sc->sc_i2c_tag; 1743b00bf77Sgrange config_found(self, &iba, iicbus_print); 1753b00bf77Sgrange 1763b00bf77Sgrange return; 1773b00bf77Sgrange } 1783b00bf77Sgrange 1793b00bf77Sgrange int 1803b00bf77Sgrange ichiic_i2c_acquire_bus(void *cookie, int flags) 1813b00bf77Sgrange { 1823b00bf77Sgrange struct ichiic_softc *sc = cookie; 1833b00bf77Sgrange 1847ecfa26eSgrange if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 1853b00bf77Sgrange return (0); 1863b00bf77Sgrange 18773550447Sdlg return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR)); 1883b00bf77Sgrange } 1893b00bf77Sgrange 1903b00bf77Sgrange void 1913b00bf77Sgrange ichiic_i2c_release_bus(void *cookie, int flags) 1923b00bf77Sgrange { 1933b00bf77Sgrange struct ichiic_softc *sc = cookie; 1943b00bf77Sgrange 1957ecfa26eSgrange if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 1963b00bf77Sgrange return; 1973b00bf77Sgrange 19873550447Sdlg rw_exit(&sc->sc_i2c_lock); 1993b00bf77Sgrange } 2003b00bf77Sgrange 2013b00bf77Sgrange int 2023b00bf77Sgrange ichiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 2033b00bf77Sgrange const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 2043b00bf77Sgrange { 2053b00bf77Sgrange struct ichiic_softc *sc = cookie; 2063b00bf77Sgrange u_int8_t *b; 2073b00bf77Sgrange u_int8_t ctl, st; 2083b00bf77Sgrange int retries; 2093b00bf77Sgrange 2106f23e57aSgrange DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, " 2116f23e57aSgrange "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen, 2126f23e57aSgrange len, flags)); 213d204c798Sgrange 214c90a9223Sgrange /* Wait for bus to be idle */ 215c90a9223Sgrange for (retries = 100; retries > 0; retries--) { 216d204c798Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS); 217c90a9223Sgrange if (!(st & ICH_SMB_HS_BUSY)) 218c90a9223Sgrange break; 219c90a9223Sgrange DELAY(ICHIIC_DELAY); 220c90a9223Sgrange } 221d204c798Sgrange DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st, 222d204c798Sgrange ICH_SMB_HS_BITS)); 223d204c798Sgrange if (st & ICH_SMB_HS_BUSY) 224c5eaf0b6Sgrange return (1); 225749dbd03Sgrange 2267ecfa26eSgrange if (cold || sc->sc_poll) 227749dbd03Sgrange flags |= I2C_F_POLL; 2283b00bf77Sgrange 2293b00bf77Sgrange if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2) 230c5eaf0b6Sgrange return (1); 2313b00bf77Sgrange 2323b00bf77Sgrange /* Setup transfer */ 2333b00bf77Sgrange sc->sc_i2c_xfer.op = op; 2343b00bf77Sgrange sc->sc_i2c_xfer.buf = buf; 2353b00bf77Sgrange sc->sc_i2c_xfer.len = len; 2363b00bf77Sgrange sc->sc_i2c_xfer.flags = flags; 2373b00bf77Sgrange sc->sc_i2c_xfer.error = 0; 2383b00bf77Sgrange 2393b00bf77Sgrange /* Set slave address and transfer direction */ 2403b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA, 2413b00bf77Sgrange ICH_SMB_TXSLVA_ADDR(addr) | 2423b00bf77Sgrange (I2C_OP_READ_P(op) ? ICH_SMB_TXSLVA_READ : 0)); 2433b00bf77Sgrange 2443b00bf77Sgrange b = (void *)cmdbuf; 2453b00bf77Sgrange if (cmdlen > 0) 2463b00bf77Sgrange /* Set command byte */ 2473b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HCMD, b[0]); 2483b00bf77Sgrange 2493b00bf77Sgrange if (I2C_OP_WRITE_P(op)) { 2503b00bf77Sgrange /* Write data */ 2513b00bf77Sgrange b = buf; 2523b00bf77Sgrange if (len > 0) 2533b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, 2543b00bf77Sgrange ICH_SMB_HD0, b[0]); 2553b00bf77Sgrange if (len > 1) 2563b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, 2573b00bf77Sgrange ICH_SMB_HD1, b[1]); 2583b00bf77Sgrange } 2593b00bf77Sgrange 2603b00bf77Sgrange /* Set SMBus command */ 2613b00bf77Sgrange if (len == 0) 2623b00bf77Sgrange ctl = ICH_SMB_HC_CMD_BYTE; 2633b00bf77Sgrange else if (len == 1) 2643b00bf77Sgrange ctl = ICH_SMB_HC_CMD_BDATA; 2653b00bf77Sgrange else if (len == 2) 2663b00bf77Sgrange ctl = ICH_SMB_HC_CMD_WDATA; 267*c0d07f7cShaesbaert else 268*c0d07f7cShaesbaert panic("%s: unexpected len %zd", __func__, len); 2693b00bf77Sgrange 2703b00bf77Sgrange if ((flags & I2C_F_POLL) == 0) 2713b00bf77Sgrange ctl |= ICH_SMB_HC_INTREN; 2723b00bf77Sgrange 2733b00bf77Sgrange /* Start transaction */ 2743b00bf77Sgrange ctl |= ICH_SMB_HC_START; 2753b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl); 2763b00bf77Sgrange 2773b00bf77Sgrange if (flags & I2C_F_POLL) { 2783b00bf77Sgrange /* Poll for completion */ 2793b00bf77Sgrange DELAY(ICHIIC_DELAY); 2803b00bf77Sgrange for (retries = 1000; retries > 0; retries--) { 2813b00bf77Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 2823b00bf77Sgrange ICH_SMB_HS); 2833b00bf77Sgrange if ((st & ICH_SMB_HS_BUSY) == 0) 2843b00bf77Sgrange break; 2853b00bf77Sgrange DELAY(ICHIIC_DELAY); 2863b00bf77Sgrange } 287d204c798Sgrange if (st & ICH_SMB_HS_BUSY) 288d204c798Sgrange goto timeout; 2893b00bf77Sgrange ichiic_intr(sc); 2903b00bf77Sgrange } else { 291c17198b5Sgrange /* Wait for interrupt */ 292cda39e48Sderaadt if (tsleep(sc, PRIBIO, "ichiic", ICHIIC_TIMEOUT * hz)) 293d204c798Sgrange goto timeout; 2943b00bf77Sgrange } 2953b00bf77Sgrange 2963b00bf77Sgrange if (sc->sc_i2c_xfer.error) 2973b00bf77Sgrange return (1); 2983b00bf77Sgrange 2993b00bf77Sgrange return (0); 300d204c798Sgrange 301d204c798Sgrange timeout: 302d204c798Sgrange /* 303d204c798Sgrange * Transfer timeout. Kill the transaction and clear status bits. 304d204c798Sgrange */ 305d204c798Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, 306d204c798Sgrange ICH_SMB_HC_KILL); 307d204c798Sgrange DELAY(ICHIIC_DELAY); 308d204c798Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS); 309d204c798Sgrange if ((st & ICH_SMB_HS_FAILED) == 0) 3106f23e57aSgrange printf("%s: abort failed, status 0x%b\n", 311d204c798Sgrange sc->sc_dev.dv_xname, st, ICH_SMB_HS_BITS); 312d204c798Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st); 313d204c798Sgrange return (1); 3143b00bf77Sgrange } 3153b00bf77Sgrange 3163b00bf77Sgrange int 3173b00bf77Sgrange ichiic_intr(void *arg) 3183b00bf77Sgrange { 3193b00bf77Sgrange struct ichiic_softc *sc = arg; 3203b00bf77Sgrange u_int8_t st; 3213b00bf77Sgrange u_int8_t *b; 3223b00bf77Sgrange size_t len; 3233b00bf77Sgrange 3243b00bf77Sgrange /* Read status */ 3253b00bf77Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS); 3263b00bf77Sgrange if ((st & ICH_SMB_HS_BUSY) != 0 || (st & (ICH_SMB_HS_INTR | 3273b00bf77Sgrange ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED | 3283b00bf77Sgrange ICH_SMB_HS_SMBAL | ICH_SMB_HS_BDONE)) == 0) 3293b00bf77Sgrange /* Interrupt was not for us */ 3303b00bf77Sgrange return (0); 3313b00bf77Sgrange 3323b00bf77Sgrange DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st, 3333b00bf77Sgrange ICH_SMB_HS_BITS)); 3343b00bf77Sgrange 3353b00bf77Sgrange /* Clear status bits */ 3363b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st); 3373b00bf77Sgrange 3383b00bf77Sgrange /* Check for errors */ 3393b00bf77Sgrange if (st & (ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED)) { 3403b00bf77Sgrange sc->sc_i2c_xfer.error = 1; 3413b00bf77Sgrange goto done; 3423b00bf77Sgrange } 3433b00bf77Sgrange 3443b00bf77Sgrange if (st & ICH_SMB_HS_INTR) { 3453b00bf77Sgrange if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op)) 3463b00bf77Sgrange goto done; 3473b00bf77Sgrange 3483b00bf77Sgrange /* Read data */ 3493b00bf77Sgrange b = sc->sc_i2c_xfer.buf; 3503b00bf77Sgrange len = sc->sc_i2c_xfer.len; 3513b00bf77Sgrange if (len > 0) 3523b00bf77Sgrange b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 3533b00bf77Sgrange ICH_SMB_HD0); 3543b00bf77Sgrange if (len > 1) 3553b00bf77Sgrange b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 3563b00bf77Sgrange ICH_SMB_HD1); 3573b00bf77Sgrange } 3583b00bf77Sgrange 3593b00bf77Sgrange done: 3603b00bf77Sgrange if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) 3613b00bf77Sgrange wakeup(sc); 3623b00bf77Sgrange return (1); 3633b00bf77Sgrange } 364