root/dev/pci/viapm.c

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

DEFINITIONS

This source file includes following definitions.
  1. viapm_match
  2. viapm_attach
  3. viapm_i2c_acquire_bus
  4. viapm_i2c_release_bus
  5. viapm_i2c_exec
  6. viapm_intr

    1 /*      $OpenBSD: viapm.c,v 1.8 2007/05/03 09:36:26 dlg Exp $   */
    2 
    3 /*
    4  * Copyright (c) 2005 Mark Kettenis <kettenis@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  * VIA VT8237 SMBus 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/i2c/i2cvar.h>
   37 
   38 /*
   39  * VIA VT8237 ISA register definitions.
   40  */
   41 
   42 /* PCI configuration registers */
   43 #define VIAPM_SMB_BASE  0xd0            /* SMBus base address */
   44 #define VIAPM_SMB_HOSTC 0xd2            /* host configuration */
   45 #define VIAPM_SMB_HOSTC_HSTEN   (1 << 0)        /* enable host controller */
   46 #define VIAPM_SMB_HOSTC_INTEN   (1 << 1)        /* enable SCI/SMI */
   47 #define VIAPM_SMB_HOSTC_SCIEN   (1 << 3)        /* interrupt type (SCI/SMI) */
   48 
   49 /* SMBus I/O registers */
   50 #define VIAPM_SMB_HS    0x00            /* host status */
   51 #define VIAPM_SMB_HS_BUSY       (1 << 0)        /* running a command */
   52 #define VIAPM_SMB_HS_INTR       (1 << 1)        /* command completed */
   53 #define VIAPM_SMB_HS_DEVERR     (1 << 2)        /* command error */
   54 #define VIAPM_SMB_HS_BUSERR     (1 << 3)        /* transaction collision */
   55 #define VIAPM_SMB_HS_FAILED     (1 << 4)        /* failed bus transaction */
   56 #define VIAPM_SMB_HS_INUSE      (1 << 6)        /* bus semaphore */
   57 #define VIAPM_SMB_HS_BITS       \
   58   "\020\001BUSY\002INTR\003DEVERR\004BUSERR\005FAILED\007INUSE"
   59 #define VIAPM_SMB_HC    0x02            /* host control */
   60 #define VIAPM_SMB_HC_INTREN     (1 << 0)        /* enable interrupts */
   61 #define VIAPM_SMB_HC_KILL       (1 << 1)        /* kill current transaction */
   62 #define VIAPM_SMB_HC_CMD_QUICK  (0 << 2)        /* QUICK command */
   63 #define VIAPM_SMB_HC_CMD_BYTE   (1 << 2)        /* BYTE command */
   64 #define VIAPM_SMB_HC_CMD_BDATA  (2 << 2)        /* BYTE DATA command */
   65 #define VIAPM_SMB_HC_CMD_WDATA  (3 << 2)        /* WORD DATA command */
   66 #define VIAPM_SMB_HC_CMD_PCALL  (4 << 2)        /* PROCESS CALL command */
   67 #define VIAPM_SMB_HC_CMD_BLOCK  (5 << 2)        /* BLOCK command */
   68 #define VIAPM_SMB_HC_START      (1 << 6)        /* start transaction */
   69 #define VIAPM_SMB_HCMD  0x03            /* host command */
   70 #define VIAPM_SMB_TXSLVA        0x04            /* transmit slave address */
   71 #define VIAPM_SMB_TXSLVA_READ   (1 << 0)        /* read direction */
   72 #define VIAPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
   73 #define VIAPM_SMB_HD0   0x05            /* host data 0 */
   74 #define VIAPM_SMB_HD1   0x06            /* host data 1 */
   75 #define VIAPM_SMB_HBDB  0x07            /* host block data byte */
   76 
   77 #define VIAPM_SMB_SIZE  16
   78 
   79 #ifdef VIAPM_DEBUG
   80 #define DPRINTF(x) printf x
   81 #else
   82 #define DPRINTF(x)
   83 #endif
   84 
   85 #define VIAPM_DELAY     100
   86 #define VIAPM_TIMEOUT   1
   87 
   88 struct viapm_softc {
   89         struct device           sc_dev;
   90 
   91         bus_space_tag_t         sc_iot;
   92         bus_space_handle_t      sc_ioh;
   93         void *                  sc_ih;
   94         int                     sc_poll;
   95 
   96         struct i2c_controller   sc_i2c_tag;
   97         struct rwlock           sc_i2c_lock;
   98         struct {
   99                 i2c_op_t     op;
  100                 void *       buf;
  101                 size_t       len;
  102                 int          flags;
  103                 volatile int error;
  104         }                       sc_i2c_xfer;
  105 };
  106 
  107 int     viapm_match(struct device *, void *, void *);
  108 void    viapm_attach(struct device *, struct device *, void *);
  109 
  110 int     viapm_i2c_acquire_bus(void *, int);
  111 void    viapm_i2c_release_bus(void *, int);
  112 int     viapm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
  113             void *, size_t, int);
  114 
  115 int     viapm_intr(void *);
  116 
  117 struct cfattach viapm_ca = {
  118         sizeof(struct viapm_softc),
  119         viapm_match,
  120         viapm_attach
  121 };
  122 
  123 struct cfdriver viapm_cd = {
  124         NULL, "viapm", DV_DULL
  125 };
  126 
  127 const struct pci_matchid viapm_ids[] = {
  128         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233_ISA },
  129         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233A_ISA },
  130         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8235_ISA },
  131         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237_ISA },
  132         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237A_ISA },
  133         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8251_ISA },
  134         { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_CX700_ISA }
  135 };
  136 
  137 int
  138 viapm_match(struct device *parent, void *match, void *aux)
  139 {
  140         return (pci_matchbyid(aux, viapm_ids,
  141             sizeof(viapm_ids) / sizeof(viapm_ids[0])));
  142 }
  143 
  144 void
  145 viapm_attach(struct device *parent, struct device *self, void *aux)
  146 {
  147         struct viapm_softc *sc = (struct viapm_softc *)self;
  148         struct pci_attach_args *pa = aux;
  149         struct i2cbus_attach_args iba;
  150         pcireg_t conf, iobase;
  151 #if 0
  152         pci_intr_handle_t ih;
  153         const char *intrstr = NULL;
  154 #endif
  155 
  156         /* Map I/O space */
  157         sc->sc_iot = pa->pa_iot;
  158         iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_SMB_BASE);
  159         if (iobase == 0 ||
  160             bus_space_map(sc->sc_iot, iobase & 0xfffe,
  161             VIAPM_SMB_SIZE, 0, &sc->sc_ioh)) {
  162                 printf(": can't map I/O space\n");
  163                 return;
  164         }
  165 
  166         /* Read configuration */
  167         conf = (iobase >> 16);
  168         DPRINTF((": conf 0x%x", conf));
  169 
  170         if ((conf & VIAPM_SMB_HOSTC_HSTEN) == 0) {
  171                 printf(": SMBus host disabled\n");
  172                 goto fail;
  173         }
  174 
  175         if (conf & VIAPM_SMB_HOSTC_INTEN) {
  176                 if (conf & VIAPM_SMB_HOSTC_SCIEN)
  177                         printf(": SCI");
  178                 else
  179                         printf(": SMI");
  180                 sc->sc_poll = 1;
  181         } else {
  182 #if 0
  183                 /* Install interrupt handler */
  184                 if (pci_intr_map(pa, &ih)) {
  185                         printf(": can't map interrupt\n");
  186                         goto fail;
  187                 }
  188                 intrstr = pci_intr_string(pa->pa_pc, ih);
  189                 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
  190                     viapm_intr, sc, sc->sc_dev.dv_xname);
  191                 if (sc->sc_ih == NULL) {
  192                         printf(": can't establish interrupt");
  193                         if (intrstr != NULL)
  194                                 printf(" at %s", intrstr);
  195                         printf("\n");
  196                         goto fail;
  197                 }
  198                 printf(": %s", intrstr);
  199 #endif
  200                 sc->sc_poll = 1;
  201         }
  202 
  203         printf("\n");
  204 
  205         /* Attach I2C bus */
  206         rw_init(&sc->sc_i2c_lock, "iiclk");
  207         sc->sc_i2c_tag.ic_cookie = sc;
  208         sc->sc_i2c_tag.ic_acquire_bus = viapm_i2c_acquire_bus;
  209         sc->sc_i2c_tag.ic_release_bus = viapm_i2c_release_bus;
  210         sc->sc_i2c_tag.ic_exec = viapm_i2c_exec;
  211 
  212         bzero(&iba, sizeof iba);
  213         iba.iba_name = "iic";
  214         iba.iba_tag = &sc->sc_i2c_tag;
  215         config_found(self, &iba, iicbus_print);
  216 
  217         return;
  218 
  219 fail:
  220         bus_space_unmap(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_SIZE);
  221 }
  222 
  223 int
  224 viapm_i2c_acquire_bus(void *cookie, int flags)
  225 {
  226         struct viapm_softc *sc = cookie;
  227 
  228         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
  229                 return (0);
  230 
  231         return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
  232 }
  233 
  234 void
  235 viapm_i2c_release_bus(void *cookie, int flags)
  236 {
  237         struct viapm_softc *sc = cookie;
  238 
  239         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
  240                 return;
  241 
  242         rw_exit(&sc->sc_i2c_lock);
  243 }
  244 
  245 int
  246 viapm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
  247     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
  248 {
  249         struct viapm_softc *sc = cookie;
  250         u_int8_t *b;
  251         u_int8_t ctl, st;
  252         int retries;
  253 
  254         DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
  255             "flags 0x%x, status 0x%b\n", sc->sc_dev.dv_xname, op, addr,
  256             cmdlen, len, flags, bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  257             VIAPM_SMB_HS), VIAPM_SMB_HS_BITS));
  258 
  259         /* Check if there's a transfer already running */
  260         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
  261         DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
  262             VIAPM_SMB_HS_BITS));
  263         if (st & VIAPM_SMB_HS_BUSY)
  264                 return (1);
  265 
  266         if (cold || sc->sc_poll)
  267                 flags |= I2C_F_POLL;
  268 
  269         if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
  270                 return (1);
  271 
  272         /* Setup transfer */
  273         sc->sc_i2c_xfer.op = op;
  274         sc->sc_i2c_xfer.buf = buf;
  275         sc->sc_i2c_xfer.len = len;
  276         sc->sc_i2c_xfer.flags = flags;
  277         sc->sc_i2c_xfer.error = 0;
  278 
  279         /* Set slave address and transfer direction */
  280         bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_TXSLVA,
  281             VIAPM_SMB_TXSLVA_ADDR(addr) |
  282             (I2C_OP_READ_P(op) ? VIAPM_SMB_TXSLVA_READ : 0));
  283 
  284         b = (void *)cmdbuf;
  285         if (cmdlen > 0)
  286                 /* Set command byte */
  287                 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  288                     VIAPM_SMB_HCMD, b[0]);
  289 
  290         if (I2C_OP_WRITE_P(op)) {
  291                 /* Write data */
  292                 b = buf;
  293                 if (len > 0)
  294                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  295                             VIAPM_SMB_HD0, b[0]);
  296                 if (len > 1)
  297                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  298                             VIAPM_SMB_HD1, b[1]);
  299         }
  300 
  301         /* Set SMBus command */
  302         if (len == 0)
  303                 ctl = VIAPM_SMB_HC_CMD_BYTE;
  304         else if (len == 1)
  305                 ctl = VIAPM_SMB_HC_CMD_BDATA;
  306         else if (len == 2)
  307                 ctl = VIAPM_SMB_HC_CMD_WDATA;
  308 
  309         if ((flags & I2C_F_POLL) == 0)
  310                 ctl |= VIAPM_SMB_HC_INTREN;
  311 
  312         /* Start transaction */
  313         ctl |= VIAPM_SMB_HC_START;
  314         bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC, ctl);
  315 
  316         if (flags & I2C_F_POLL) {
  317                 /* Poll for completion */
  318                 DELAY(VIAPM_DELAY);
  319                 for (retries = 1000; retries > 0; retries--) {
  320                         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  321                             VIAPM_SMB_HS);
  322                         if ((st & VIAPM_SMB_HS_BUSY) == 0)
  323                                 break;
  324                         DELAY(VIAPM_DELAY);
  325                 }
  326                 if (st & VIAPM_SMB_HS_BUSY)
  327                         goto timeout;
  328                 viapm_intr(sc);
  329         } else {
  330                 /* Wait for interrupt */
  331                 if (tsleep(sc, PRIBIO, "iicexec", VIAPM_TIMEOUT * hz))
  332                         goto timeout;
  333         }       
  334 
  335         if (sc->sc_i2c_xfer.error)
  336                 return (1);
  337 
  338         return (0);
  339 
  340 timeout:
  341         /*
  342          * Transfer timeout. Kill the transaction and clear status bits.
  343          */
  344         printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st,
  345             VIAPM_SMB_HS_BITS);
  346         bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC,
  347             VIAPM_SMB_HC_KILL);
  348         DELAY(VIAPM_DELAY);
  349         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
  350         if ((st & VIAPM_SMB_HS_FAILED) == 0)
  351                 printf("%s: transaction abort failed, status 0x%b\n",
  352                     sc->sc_dev.dv_xname, st, VIAPM_SMB_HS_BITS);
  353         bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st);
  354         return (1);
  355 }
  356 
  357 int
  358 viapm_intr(void *arg)
  359 {
  360         struct viapm_softc *sc = arg;
  361         u_int8_t st;
  362         u_int8_t *b;
  363         size_t len;
  364 
  365         /* Read status */
  366         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
  367         if ((st & VIAPM_SMB_HS_BUSY) != 0 || (st & (VIAPM_SMB_HS_INTR |
  368             VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
  369             VIAPM_SMB_HS_FAILED)) == 0)
  370                 /* Interrupt was not for us */
  371                 return (0);
  372 
  373         DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
  374             VIAPM_SMB_HS_BITS));
  375 
  376         /* Clear status bits */
  377         bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st);
  378 
  379         /* Check for errors */
  380         if (st & (VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
  381             VIAPM_SMB_HS_FAILED)) {
  382                 sc->sc_i2c_xfer.error = 1;
  383                 goto done;
  384         }
  385 
  386         if (st & VIAPM_SMB_HS_INTR) {
  387                 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
  388                         goto done;
  389 
  390                 /* Read data */
  391                 b = sc->sc_i2c_xfer.buf;
  392                 len = sc->sc_i2c_xfer.len;
  393                 if (len > 0)
  394                         b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  395                             VIAPM_SMB_HD0);
  396                 if (len > 1)
  397                         b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  398                             VIAPM_SMB_HD1);
  399         }
  400 
  401 done:
  402         if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
  403                 wakeup(sc);
  404         return (1);
  405 }

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