1*6f23e57aSgrange /* $OpenBSD: ichiic.c,v 1.17 2006/09/28 18:19:14 grange 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> 273b00bf77Sgrange #include <sys/lock.h> 283b00bf77Sgrange #include <sys/proc.h> 293b00bf77Sgrange 303b00bf77Sgrange #include <machine/bus.h> 313b00bf77Sgrange 323b00bf77Sgrange #include <dev/pci/pcidevs.h> 333b00bf77Sgrange #include <dev/pci/pcireg.h> 343b00bf77Sgrange #include <dev/pci/pcivar.h> 353b00bf77Sgrange 363b00bf77Sgrange #include <dev/pci/ichreg.h> 373b00bf77Sgrange 383b00bf77Sgrange #include <dev/i2c/i2cvar.h> 393b00bf77Sgrange 403b00bf77Sgrange #ifdef ICHIIC_DEBUG 413b00bf77Sgrange #define DPRINTF(x) printf x 423b00bf77Sgrange #else 433b00bf77Sgrange #define DPRINTF(x) 443b00bf77Sgrange #endif 453b00bf77Sgrange 463b00bf77Sgrange #define ICHIIC_DELAY 100 473b00bf77Sgrange #define ICHIIC_TIMEOUT 1 483b00bf77Sgrange 493b00bf77Sgrange struct ichiic_softc { 503b00bf77Sgrange struct device sc_dev; 513b00bf77Sgrange 523b00bf77Sgrange bus_space_tag_t sc_iot; 533b00bf77Sgrange bus_space_handle_t sc_ioh; 543b00bf77Sgrange void * sc_ih; 55749dbd03Sgrange int sc_poll; 563b00bf77Sgrange 573b00bf77Sgrange struct i2c_controller sc_i2c_tag; 583b00bf77Sgrange struct lock sc_i2c_lock; 59c17198b5Sgrange struct { 603b00bf77Sgrange i2c_op_t op; 613b00bf77Sgrange void * buf; 623b00bf77Sgrange size_t len; 633b00bf77Sgrange int flags; 64c17198b5Sgrange volatile int error; 653b00bf77Sgrange } sc_i2c_xfer; 663b00bf77Sgrange }; 673b00bf77Sgrange 683b00bf77Sgrange int ichiic_match(struct device *, void *, void *); 693b00bf77Sgrange void ichiic_attach(struct device *, struct device *, void *); 703b00bf77Sgrange 713b00bf77Sgrange int ichiic_i2c_acquire_bus(void *, int); 723b00bf77Sgrange void ichiic_i2c_release_bus(void *, int); 733b00bf77Sgrange int ichiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 743b00bf77Sgrange void *, size_t, int); 753b00bf77Sgrange 763b00bf77Sgrange int ichiic_intr(void *); 773b00bf77Sgrange 783b00bf77Sgrange struct cfattach ichiic_ca = { 793b00bf77Sgrange sizeof(struct ichiic_softc), 803b00bf77Sgrange ichiic_match, 813b00bf77Sgrange ichiic_attach 823b00bf77Sgrange }; 833b00bf77Sgrange 843b00bf77Sgrange struct cfdriver ichiic_cd = { 853b00bf77Sgrange NULL, "ichiic", DV_DULL 863b00bf77Sgrange }; 873b00bf77Sgrange 883b00bf77Sgrange const struct pci_matchid ichiic_ids[] = { 899eb33f7aSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_SMB }, 90ae7cc0c1Sbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_SMB }, 913b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_SMB }, 923b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_SMB }, 933b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_SMB }, 943b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_SMB }, 953b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_SMB }, 969eb33f7aSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_SMB }, 973b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_SMB }, 983b00bf77Sgrange { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_SMB }, 99e182382cSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_SMB }, 100e182382cSbrad { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_SMB } 1013b00bf77Sgrange }; 1023b00bf77Sgrange 1033b00bf77Sgrange int 1043b00bf77Sgrange ichiic_match(struct device *parent, void *match, void *aux) 1053b00bf77Sgrange { 1063b00bf77Sgrange return (pci_matchbyid(aux, ichiic_ids, 1073b00bf77Sgrange sizeof(ichiic_ids) / sizeof(ichiic_ids[0]))); 1083b00bf77Sgrange } 1093b00bf77Sgrange 1103b00bf77Sgrange void 1113b00bf77Sgrange ichiic_attach(struct device *parent, struct device *self, void *aux) 1123b00bf77Sgrange { 1133b00bf77Sgrange struct ichiic_softc *sc = (struct ichiic_softc *)self; 1143b00bf77Sgrange struct pci_attach_args *pa = aux; 1153b00bf77Sgrange struct i2cbus_attach_args iba; 116749dbd03Sgrange pcireg_t conf; 1173b00bf77Sgrange bus_size_t iosize; 1183b00bf77Sgrange pci_intr_handle_t ih; 1193b00bf77Sgrange const char *intrstr = NULL; 1203b00bf77Sgrange 121d204c798Sgrange /* Read configuration */ 122d204c798Sgrange conf = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_SMB_HOSTC); 123*6f23e57aSgrange DPRINTF((": conf 0x%08x", conf)); 124d204c798Sgrange 125d204c798Sgrange if ((conf & ICH_SMB_HOSTC_HSTEN) == 0) { 126d204c798Sgrange printf(": SMBus disabled\n"); 127d204c798Sgrange return; 128d204c798Sgrange } 129d204c798Sgrange 1303b00bf77Sgrange /* Map I/O space */ 1313b00bf77Sgrange if (pci_mapreg_map(pa, ICH_SMB_BASE, PCI_MAPREG_TYPE_IO, 0, 1323b00bf77Sgrange &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) { 1333b00bf77Sgrange printf(": can't map I/O space\n"); 1343b00bf77Sgrange return; 1353b00bf77Sgrange } 1363b00bf77Sgrange 137749dbd03Sgrange sc->sc_poll = 1; 138e3269dfcSgrange if (conf & ICH_SMB_HOSTC_SMIEN) { 139e3269dfcSgrange /* No PCI IRQ */ 140e3269dfcSgrange printf(": SMI"); 141749dbd03Sgrange } else { 1423b00bf77Sgrange /* Install interrupt handler */ 143e3269dfcSgrange if (pci_intr_map(pa, &ih) == 0) { 1443b00bf77Sgrange intrstr = pci_intr_string(pa->pa_pc, ih); 145749dbd03Sgrange sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, 146749dbd03Sgrange ichiic_intr, sc, sc->sc_dev.dv_xname); 147e3269dfcSgrange if (sc->sc_ih != NULL) { 1483b00bf77Sgrange printf(": %s", intrstr); 149e3269dfcSgrange sc->sc_poll = 0; 150e3269dfcSgrange } 151e3269dfcSgrange } 152e3269dfcSgrange if (sc->sc_poll) 153e3269dfcSgrange printf(": polling"); 154749dbd03Sgrange } 1553b00bf77Sgrange 1563b00bf77Sgrange printf("\n"); 1573b00bf77Sgrange 1583b00bf77Sgrange /* Attach I2C bus */ 1593b00bf77Sgrange lockinit(&sc->sc_i2c_lock, PRIBIO | PCATCH, "iiclk", 0, 0); 1603b00bf77Sgrange sc->sc_i2c_tag.ic_cookie = sc; 1613b00bf77Sgrange sc->sc_i2c_tag.ic_acquire_bus = ichiic_i2c_acquire_bus; 1623b00bf77Sgrange sc->sc_i2c_tag.ic_release_bus = ichiic_i2c_release_bus; 1633b00bf77Sgrange sc->sc_i2c_tag.ic_exec = ichiic_i2c_exec; 164a5a63700Sderaadt 165f57d9f10Sgrange bzero(&iba, sizeof(iba)); 1663b00bf77Sgrange iba.iba_name = "iic"; 1673b00bf77Sgrange iba.iba_tag = &sc->sc_i2c_tag; 1683b00bf77Sgrange config_found(self, &iba, iicbus_print); 1693b00bf77Sgrange 1703b00bf77Sgrange return; 1713b00bf77Sgrange } 1723b00bf77Sgrange 1733b00bf77Sgrange int 1743b00bf77Sgrange ichiic_i2c_acquire_bus(void *cookie, int flags) 1753b00bf77Sgrange { 1763b00bf77Sgrange struct ichiic_softc *sc = cookie; 1773b00bf77Sgrange 1787ecfa26eSgrange if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 1793b00bf77Sgrange return (0); 1803b00bf77Sgrange 1813b00bf77Sgrange return (lockmgr(&sc->sc_i2c_lock, LK_EXCLUSIVE, NULL)); 1823b00bf77Sgrange } 1833b00bf77Sgrange 1843b00bf77Sgrange void 1853b00bf77Sgrange ichiic_i2c_release_bus(void *cookie, int flags) 1863b00bf77Sgrange { 1873b00bf77Sgrange struct ichiic_softc *sc = cookie; 1883b00bf77Sgrange 1897ecfa26eSgrange if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 1903b00bf77Sgrange return; 1913b00bf77Sgrange 1923b00bf77Sgrange lockmgr(&sc->sc_i2c_lock, LK_RELEASE, NULL); 1933b00bf77Sgrange } 1943b00bf77Sgrange 1953b00bf77Sgrange int 1963b00bf77Sgrange ichiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 1973b00bf77Sgrange const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 1983b00bf77Sgrange { 1993b00bf77Sgrange struct ichiic_softc *sc = cookie; 2003b00bf77Sgrange u_int8_t *b; 2013b00bf77Sgrange u_int8_t ctl, st; 2023b00bf77Sgrange int retries; 2033b00bf77Sgrange 204*6f23e57aSgrange DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, " 205*6f23e57aSgrange "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen, 206*6f23e57aSgrange len, flags)); 207d204c798Sgrange 208c90a9223Sgrange /* Wait for bus to be idle */ 209c90a9223Sgrange for (retries = 100; retries > 0; retries--) { 210d204c798Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS); 211c90a9223Sgrange if (!(st & ICH_SMB_HS_BUSY)) 212c90a9223Sgrange break; 213c90a9223Sgrange DELAY(ICHIIC_DELAY); 214c90a9223Sgrange } 215d204c798Sgrange DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st, 216d204c798Sgrange ICH_SMB_HS_BITS)); 217d204c798Sgrange if (st & ICH_SMB_HS_BUSY) 218c5eaf0b6Sgrange return (1); 219749dbd03Sgrange 2207ecfa26eSgrange if (cold || sc->sc_poll) 221749dbd03Sgrange flags |= I2C_F_POLL; 2223b00bf77Sgrange 2233b00bf77Sgrange if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2) 224c5eaf0b6Sgrange return (1); 2253b00bf77Sgrange 2263b00bf77Sgrange /* Setup transfer */ 2273b00bf77Sgrange sc->sc_i2c_xfer.op = op; 2283b00bf77Sgrange sc->sc_i2c_xfer.buf = buf; 2293b00bf77Sgrange sc->sc_i2c_xfer.len = len; 2303b00bf77Sgrange sc->sc_i2c_xfer.flags = flags; 2313b00bf77Sgrange sc->sc_i2c_xfer.error = 0; 2323b00bf77Sgrange 2333b00bf77Sgrange /* Set slave address and transfer direction */ 2343b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA, 2353b00bf77Sgrange ICH_SMB_TXSLVA_ADDR(addr) | 2363b00bf77Sgrange (I2C_OP_READ_P(op) ? ICH_SMB_TXSLVA_READ : 0)); 2373b00bf77Sgrange 2383b00bf77Sgrange b = (void *)cmdbuf; 2393b00bf77Sgrange if (cmdlen > 0) 2403b00bf77Sgrange /* Set command byte */ 2413b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HCMD, b[0]); 2423b00bf77Sgrange 2433b00bf77Sgrange if (I2C_OP_WRITE_P(op)) { 2443b00bf77Sgrange /* Write data */ 2453b00bf77Sgrange b = buf; 2463b00bf77Sgrange if (len > 0) 2473b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, 2483b00bf77Sgrange ICH_SMB_HD0, b[0]); 2493b00bf77Sgrange if (len > 1) 2503b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, 2513b00bf77Sgrange ICH_SMB_HD1, b[1]); 2523b00bf77Sgrange } 2533b00bf77Sgrange 2543b00bf77Sgrange /* Set SMBus command */ 2553b00bf77Sgrange if (len == 0) 2563b00bf77Sgrange ctl = ICH_SMB_HC_CMD_BYTE; 2573b00bf77Sgrange else if (len == 1) 2583b00bf77Sgrange ctl = ICH_SMB_HC_CMD_BDATA; 2593b00bf77Sgrange else if (len == 2) 2603b00bf77Sgrange ctl = ICH_SMB_HC_CMD_WDATA; 2613b00bf77Sgrange 2623b00bf77Sgrange if ((flags & I2C_F_POLL) == 0) 2633b00bf77Sgrange ctl |= ICH_SMB_HC_INTREN; 2643b00bf77Sgrange 2653b00bf77Sgrange /* Start transaction */ 2663b00bf77Sgrange ctl |= ICH_SMB_HC_START; 2673b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl); 2683b00bf77Sgrange 2693b00bf77Sgrange if (flags & I2C_F_POLL) { 2703b00bf77Sgrange /* Poll for completion */ 2713b00bf77Sgrange DELAY(ICHIIC_DELAY); 2723b00bf77Sgrange for (retries = 1000; retries > 0; retries--) { 2733b00bf77Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 2743b00bf77Sgrange ICH_SMB_HS); 2753b00bf77Sgrange if ((st & ICH_SMB_HS_BUSY) == 0) 2763b00bf77Sgrange break; 2773b00bf77Sgrange DELAY(ICHIIC_DELAY); 2783b00bf77Sgrange } 279d204c798Sgrange if (st & ICH_SMB_HS_BUSY) 280d204c798Sgrange goto timeout; 2813b00bf77Sgrange ichiic_intr(sc); 2823b00bf77Sgrange } else { 283c17198b5Sgrange /* Wait for interrupt */ 2843b00bf77Sgrange if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz)) 285d204c798Sgrange goto timeout; 2863b00bf77Sgrange } 2873b00bf77Sgrange 2883b00bf77Sgrange if (sc->sc_i2c_xfer.error) 2893b00bf77Sgrange return (1); 2903b00bf77Sgrange 2913b00bf77Sgrange return (0); 292d204c798Sgrange 293d204c798Sgrange timeout: 294d204c798Sgrange /* 295d204c798Sgrange * Transfer timeout. Kill the transaction and clear status bits. 296d204c798Sgrange */ 297*6f23e57aSgrange printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, " 298*6f23e57aSgrange "flags 0x%02x: timeout, status 0x%b\n", 299*6f23e57aSgrange sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags, 300*6f23e57aSgrange st, ICH_SMB_HS_BITS); 301d204c798Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, 302d204c798Sgrange ICH_SMB_HC_KILL); 303d204c798Sgrange DELAY(ICHIIC_DELAY); 304d204c798Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS); 305d204c798Sgrange if ((st & ICH_SMB_HS_FAILED) == 0) 306*6f23e57aSgrange printf("%s: abort failed, status 0x%b\n", 307d204c798Sgrange sc->sc_dev.dv_xname, st, ICH_SMB_HS_BITS); 308d204c798Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st); 309d204c798Sgrange return (1); 3103b00bf77Sgrange } 3113b00bf77Sgrange 3123b00bf77Sgrange int 3133b00bf77Sgrange ichiic_intr(void *arg) 3143b00bf77Sgrange { 3153b00bf77Sgrange struct ichiic_softc *sc = arg; 3163b00bf77Sgrange u_int8_t st; 3173b00bf77Sgrange u_int8_t *b; 3183b00bf77Sgrange size_t len; 3193b00bf77Sgrange 3203b00bf77Sgrange /* Read status */ 3213b00bf77Sgrange st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS); 3223b00bf77Sgrange if ((st & ICH_SMB_HS_BUSY) != 0 || (st & (ICH_SMB_HS_INTR | 3233b00bf77Sgrange ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED | 3243b00bf77Sgrange ICH_SMB_HS_SMBAL | ICH_SMB_HS_BDONE)) == 0) 3253b00bf77Sgrange /* Interrupt was not for us */ 3263b00bf77Sgrange return (0); 3273b00bf77Sgrange 3283b00bf77Sgrange DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st, 3293b00bf77Sgrange ICH_SMB_HS_BITS)); 3303b00bf77Sgrange 3313b00bf77Sgrange /* Clear status bits */ 3323b00bf77Sgrange bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st); 3333b00bf77Sgrange 3343b00bf77Sgrange /* Check for errors */ 3353b00bf77Sgrange if (st & (ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED)) { 3363b00bf77Sgrange sc->sc_i2c_xfer.error = 1; 3373b00bf77Sgrange goto done; 3383b00bf77Sgrange } 3393b00bf77Sgrange 3403b00bf77Sgrange if (st & ICH_SMB_HS_INTR) { 3413b00bf77Sgrange if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op)) 3423b00bf77Sgrange goto done; 3433b00bf77Sgrange 3443b00bf77Sgrange /* Read data */ 3453b00bf77Sgrange b = sc->sc_i2c_xfer.buf; 3463b00bf77Sgrange len = sc->sc_i2c_xfer.len; 3473b00bf77Sgrange if (len > 0) 3483b00bf77Sgrange b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 3493b00bf77Sgrange ICH_SMB_HD0); 3503b00bf77Sgrange if (len > 1) 3513b00bf77Sgrange b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 3523b00bf77Sgrange ICH_SMB_HD1); 3533b00bf77Sgrange } 3543b00bf77Sgrange 3553b00bf77Sgrange done: 3563b00bf77Sgrange if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) 3573b00bf77Sgrange wakeup(sc); 3583b00bf77Sgrange return (1); 3593b00bf77Sgrange } 360