root/dev/pci/piixpm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. piixpm_match
  2. piixpm_attach
  3. piixpm_i2c_acquire_bus
  4. piixpm_i2c_release_bus
  5. piixpm_i2c_exec
  6. piixpm_intr

    1 /*      $OpenBSD: piixpm.c,v 1.27 2007/05/03 09:36:26 dlg Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2005, 2006 Alexander Yurchenko <grange@openbsd.org>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 /*
   20  * Intel PIIX and compatible Power Management controller driver.
   21  */
   22 
   23 #include <sys/param.h>
   24 #include <sys/systm.h>
   25 #include <sys/device.h>
   26 #include <sys/kernel.h>
   27 #include <sys/rwlock.h>
   28 #include <sys/proc.h>
   29 
   30 #include <machine/bus.h>
   31 
   32 #include <dev/pci/pcidevs.h>
   33 #include <dev/pci/pcireg.h>
   34 #include <dev/pci/pcivar.h>
   35 
   36 #include <dev/pci/piixreg.h>
   37 
   38 #include <dev/i2c/i2cvar.h>
   39 
   40 #ifdef PIIXPM_DEBUG
   41 #define DPRINTF(x) printf x
   42 #else
   43 #define DPRINTF(x)
   44 #endif
   45 
   46 #define PIIXPM_DELAY    200
   47 #define PIIXPM_TIMEOUT  1
   48 
   49 struct piixpm_softc {
   50         struct device           sc_dev;
   51 
   52         bus_space_tag_t         sc_iot;
   53         bus_space_handle_t      sc_ioh;
   54         void *                  sc_ih;
   55         int                     sc_poll;
   56 
   57         struct i2c_controller   sc_i2c_tag;
   58         struct rwlock           sc_i2c_lock;
   59         struct {
   60                 i2c_op_t     op;
   61                 void *       buf;
   62                 size_t       len;
   63                 int          flags;
   64                 volatile int error;
   65         }                       sc_i2c_xfer;
   66 };
   67 
   68 int     piixpm_match(struct device *, void *, void *);
   69 void    piixpm_attach(struct device *, struct device *, void *);
   70 
   71 int     piixpm_i2c_acquire_bus(void *, int);
   72 void    piixpm_i2c_release_bus(void *, int);
   73 int     piixpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
   74             void *, size_t, int);
   75 
   76 int     piixpm_intr(void *);
   77 
   78 struct cfattach piixpm_ca = {
   79         sizeof(struct piixpm_softc),
   80         piixpm_match,
   81         piixpm_attach
   82 };
   83 
   84 struct cfdriver piixpm_cd = {
   85         NULL, "piixpm", DV_DULL
   86 };
   87 
   88 const struct pci_matchid piixpm_ids[] = {
   89         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_PM },
   90         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_PM },
   91         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_OSB4 },
   92         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB5 },
   93         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB6 },
   94         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_HT_1000 },
   95         { PCI_VENDOR_SMSC, PCI_PRODUCT_SMSC_VICTORY66_PM },
   96         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB200_SMB },
   97         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_SMB_300 },
   98         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_SMB_400 },
   99         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_SMB_600 }
  100 };
  101 
  102 int
  103 piixpm_match(struct device *parent, void *match, void *aux)
  104 {
  105         return (pci_matchbyid(aux, piixpm_ids,
  106             sizeof(piixpm_ids) / sizeof(piixpm_ids[0])));
  107 }
  108 
  109 void
  110 piixpm_attach(struct device *parent, struct device *self, void *aux)
  111 {
  112         struct piixpm_softc *sc = (struct piixpm_softc *)self;
  113         struct pci_attach_args *pa = aux;
  114         struct i2cbus_attach_args iba;
  115         pcireg_t base, conf;
  116         pci_intr_handle_t ih;
  117         const char *intrstr = NULL;
  118 
  119         /* Read configuration */
  120         conf = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_HOSTC);
  121         DPRINTF((": conf 0x%08x", conf));
  122 
  123         if ((conf & PIIX_SMB_HOSTC_HSTEN) == 0) {
  124                 printf(": SMBus disabled\n");
  125                 return;
  126         }
  127 
  128         /* Map I/O space */
  129         sc->sc_iot = pa->pa_iot;
  130         base = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_BASE) & 0xffff;
  131         if (PCI_MAPREG_IO_ADDR(base) == 0 ||
  132             bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(base),
  133             PIIX_SMB_SIZE, 0, &sc->sc_ioh)) {
  134                 printf(": can't map I/O space\n");
  135                 return;
  136         }
  137 
  138         sc->sc_poll = 1;
  139         if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_SMI) {
  140                 /* No PCI IRQ */
  141                 printf(": SMI");
  142         } else {
  143                 if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_IRQ) {
  144                         /* Install interrupt handler */
  145                         if (pci_intr_map(pa, &ih) == 0) {
  146                                 intrstr = pci_intr_string(pa->pa_pc, ih);
  147                                 sc->sc_ih = pci_intr_establish(pa->pa_pc,
  148                                     ih, IPL_BIO, piixpm_intr, sc,
  149                                     sc->sc_dev.dv_xname);
  150                                 if (sc->sc_ih != NULL) {
  151                                         printf(": %s", intrstr);
  152                                         sc->sc_poll = 0;
  153                                 }
  154                         }
  155                 }
  156                 if (sc->sc_poll)
  157                         printf(": polling");
  158         }
  159 
  160         printf("\n");
  161 
  162         /* Attach I2C bus */
  163         rw_init(&sc->sc_i2c_lock, "iiclk");
  164         sc->sc_i2c_tag.ic_cookie = sc;
  165         sc->sc_i2c_tag.ic_acquire_bus = piixpm_i2c_acquire_bus;
  166         sc->sc_i2c_tag.ic_release_bus = piixpm_i2c_release_bus;
  167         sc->sc_i2c_tag.ic_exec = piixpm_i2c_exec;
  168 
  169         bzero(&iba, sizeof(iba));
  170         iba.iba_name = "iic";
  171         iba.iba_tag = &sc->sc_i2c_tag;
  172         config_found(self, &iba, iicbus_print);
  173 
  174         return;
  175 }
  176 
  177 int
  178 piixpm_i2c_acquire_bus(void *cookie, int flags)
  179 {
  180         struct piixpm_softc *sc = cookie;
  181 
  182         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
  183                 return (0);
  184 
  185         return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
  186 }
  187 
  188 void
  189 piixpm_i2c_release_bus(void *cookie, int flags)
  190 {
  191         struct piixpm_softc *sc = cookie;
  192 
  193         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
  194                 return;
  195 
  196         rw_exit(&sc->sc_i2c_lock);
  197 }
  198 
  199 int
  200 piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
  201     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
  202 {
  203         struct piixpm_softc *sc = cookie;
  204         u_int8_t *b;
  205         u_int8_t ctl, st;
  206         int retries;
  207 
  208         DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
  209             "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
  210             len, flags));
  211 
  212         /* Wait for bus to be idle */
  213         for (retries = 100; retries > 0; retries--) {
  214                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
  215                 if (!(st & PIIX_SMB_HS_BUSY))
  216                         break;
  217                 DELAY(PIIXPM_DELAY);
  218         }
  219         DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
  220             PIIX_SMB_HS_BITS));
  221         if (st & PIIX_SMB_HS_BUSY)
  222                 return (1);
  223 
  224         if (cold || sc->sc_poll)
  225                 flags |= I2C_F_POLL;
  226 
  227         if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
  228                 return (1);
  229 
  230         /* Setup transfer */
  231         sc->sc_i2c_xfer.op = op;
  232         sc->sc_i2c_xfer.buf = buf;
  233         sc->sc_i2c_xfer.len = len;
  234         sc->sc_i2c_xfer.flags = flags;
  235         sc->sc_i2c_xfer.error = 0;
  236 
  237         /* Set slave address and transfer direction */
  238         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_TXSLVA,
  239             PIIX_SMB_TXSLVA_ADDR(addr) |
  240             (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0));
  241 
  242         b = (void *)cmdbuf;
  243         if (cmdlen > 0)
  244                 /* Set command byte */
  245                 bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HCMD, b[0]);
  246 
  247         if (I2C_OP_WRITE_P(op)) {
  248                 /* Write data */
  249                 b = buf;
  250                 if (len > 0)
  251                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  252                             PIIX_SMB_HD0, b[0]);
  253                 if (len > 1)
  254                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  255                             PIIX_SMB_HD1, b[1]);
  256         }
  257 
  258         /* Set SMBus command */
  259         if (len == 0)
  260                 ctl = PIIX_SMB_HC_CMD_BYTE;
  261         else if (len == 1)
  262                 ctl = PIIX_SMB_HC_CMD_BDATA;
  263         else if (len == 2)
  264                 ctl = PIIX_SMB_HC_CMD_WDATA;
  265 
  266         if ((flags & I2C_F_POLL) == 0)
  267                 ctl |= PIIX_SMB_HC_INTREN;
  268 
  269         /* Start transaction */
  270         ctl |= PIIX_SMB_HC_START;
  271         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC, ctl);
  272 
  273         if (flags & I2C_F_POLL) {
  274                 /* Poll for completion */
  275                 DELAY(PIIXPM_DELAY);
  276                 for (retries = 1000; retries > 0; retries--) {
  277                         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  278                             PIIX_SMB_HS);
  279                         if ((st & PIIX_SMB_HS_BUSY) == 0)
  280                                 break;
  281                         DELAY(PIIXPM_DELAY);
  282                 }
  283                 if (st & PIIX_SMB_HS_BUSY)
  284                         goto timeout;
  285                 piixpm_intr(sc);
  286         } else {
  287                 /* Wait for interrupt */
  288                 if (tsleep(sc, PRIBIO, "iicexec", PIIXPM_TIMEOUT * hz))
  289                         goto timeout;
  290         }
  291 
  292         if (sc->sc_i2c_xfer.error)
  293                 return (1);
  294 
  295         return (0);
  296 
  297 timeout:
  298         /*
  299          * Transfer timeout. Kill the transaction and clear status bits.
  300          */
  301         printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
  302             "flags 0x%02x: timeout, status 0x%b\n",
  303             sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
  304             st, PIIX_SMB_HS_BITS);
  305         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC,
  306             PIIX_SMB_HC_KILL);
  307         DELAY(PIIXPM_DELAY);
  308         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
  309         if ((st & PIIX_SMB_HS_FAILED) == 0)
  310                 printf("%s: abort failed, status 0x%b\n",
  311                     sc->sc_dev.dv_xname, st, PIIX_SMB_HS_BITS);
  312         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
  313         return (1);
  314 }
  315 
  316 int
  317 piixpm_intr(void *arg)
  318 {
  319         struct piixpm_softc *sc = arg;
  320         u_int8_t st;
  321         u_int8_t *b;
  322         size_t len;
  323 
  324         /* Read status */
  325         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
  326         if ((st & PIIX_SMB_HS_BUSY) != 0 || (st & (PIIX_SMB_HS_INTR |
  327             PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
  328             PIIX_SMB_HS_FAILED)) == 0)
  329                 /* Interrupt was not for us */
  330                 return (0);
  331 
  332         DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
  333             PIIX_SMB_HS_BITS));
  334 
  335         /* Clear status bits */
  336         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
  337 
  338         /* Check for errors */
  339         if (st & (PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
  340             PIIX_SMB_HS_FAILED)) {
  341                 sc->sc_i2c_xfer.error = 1;
  342                 goto done;
  343         }
  344 
  345         if (st & PIIX_SMB_HS_INTR) {
  346                 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
  347                         goto done;
  348 
  349                 /* Read data */
  350                 b = sc->sc_i2c_xfer.buf;
  351                 len = sc->sc_i2c_xfer.len;
  352                 if (len > 0)
  353                         b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  354                             PIIX_SMB_HD0);
  355                 if (len > 1)
  356                         b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  357                             PIIX_SMB_HD1);
  358         }
  359 
  360 done:
  361         if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
  362                 wakeup(sc);
  363         return (1);
  364 }

/* [<][>][^][v][top][bottom][index][help] */