LCOV - code coverage report
Current view: top level - dev/pci - amdiic.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 156 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: amdiic.c,v 1.11 2013/10/01 20:06:00 sf Exp $  */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2005 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             :  * AMD-8111 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             : 
      29             : #include <machine/bus.h>
      30             : 
      31             : #include <dev/pci/pcidevs.h>
      32             : #include <dev/pci/pcireg.h>
      33             : #include <dev/pci/pcivar.h>
      34             : 
      35             : #include <dev/i2c/i2cvar.h>
      36             : 
      37             : #ifdef AMDIIC_DEBUG
      38             : #define DPRINTF(x) printf x
      39             : #else
      40             : #define DPRINTF(x)
      41             : #endif
      42             : 
      43             : #define AMDIIC_DELAY    100
      44             : #define AMDIIC_TIMEOUT  1
      45             : 
      46             : /* PCI configuration registers */
      47             : #define AMD8111_SMB_BASE        0x10    /* SMBus base address */
      48             : #define AMD8111_SMB_MISC        0x48    /* miscellaneous control */
      49             : #define AMD8111_SMB_MISC_SU     (1 << 0)  /* 16x clock speed-up */
      50             : #define AMD8111_SMB_MISC_INTEN  (1 << 1)  /* PCI IRQ enabled */
      51             : #define AMD8111_SMB_MISC_SCIEN  (1 << 2)  /* SCI enabled */
      52             : 
      53             : /* SMBus I/O registers */
      54             : #define AMD8111_SMB_SC_DATA     0x00    /* data port */
      55             : #define AMD8111_SMB_SC_ST       0x04    /* status */
      56             : #define AMD8111_SMB_SC_ST_OBF   (1 << 0)  /* output buffer full */
      57             : #define AMD8111_SMB_SC_ST_IBF   (1 << 1)  /* input buffer full */
      58             : #define AMD8111_SMB_SC_ST_CMD   (1 << 3)  /* command byte */
      59             : #define AMD8111_SMB_SC_ST_BITS  "\020\001OBF\002IBF\004CMD"
      60             : #define AMD8111_SMB_SC_CMD      0x04    /* command port */
      61             : #define AMD8111_SMB_SC_CMD_RD   0x80            /* read */
      62             : #define AMD8111_SMB_SC_CMD_WR   0x81            /* write */
      63             : #define AMD8111_SMB_SC_IC       0x08    /* interrupt control */
      64             : 
      65             : /* Host controller interface registers */
      66             : #define AMD8111_SMB_PROTO       0x00    /* protocol */
      67             : #define AMD8111_SMB_PROTO_READ  0x01            /* read direction */
      68             : #define AMD8111_SMB_PROTO_QUICK 0x02            /* QUICK command */
      69             : #define AMD8111_SMB_PROTO_BYTE  0x04            /* BYTE command */
      70             : #define AMD8111_SMB_PROTO_BDATA 0x06            /* BYTE DATA command */
      71             : #define AMD8111_SMB_PROTO_WDATA 0x08            /* WORD DATA command */
      72             : #define AMD8111_SMB_STAT        0x01    /* status */
      73             : #define AMD8111_SMB_STAT_MASK   0x1f
      74             : #define AMD8111_SMB_STAT_DONE   (1 << 7)  /* command completion */
      75             : #define AMD8111_SMB_ADDR        0x02    /* address */
      76             : #define AMD8111_SMB_ADDR_SHIFT  1
      77             : #define AMD8111_SMB_CMD         0x03    /* SMBus command */
      78             : #define AMD8111_SMB_DATA(x)     (0x04 + (x)) /* SMBus data */
      79             : 
      80             : struct amdiic_softc {
      81             :         struct device           sc_dev;
      82             : 
      83             :         bus_space_tag_t         sc_iot;
      84             :         bus_space_handle_t      sc_ioh;
      85             :         void *                  sc_ih;
      86             :         int                     sc_poll;
      87             : 
      88             :         struct i2c_controller   sc_i2c_tag;
      89             :         struct rwlock           sc_i2c_lock;
      90             :         struct {
      91             :                 i2c_op_t     op;
      92             :                 void *       buf;
      93             :                 size_t       len;
      94             :                 int          flags;
      95             :                 volatile int error;
      96             :         }                       sc_i2c_xfer;
      97             : };
      98             : 
      99             : int     amdiic_match(struct device *, void *, void *);
     100             : void    amdiic_attach(struct device *, struct device *, void *);
     101             : 
     102             : int     amdiic_read(struct amdiic_softc *, u_int8_t);
     103             : int     amdiic_write(struct amdiic_softc *, u_int8_t, u_int8_t);
     104             : int     amdiic_wait(struct amdiic_softc *, int);
     105             : 
     106             : int     amdiic_i2c_acquire_bus(void *, int);
     107             : void    amdiic_i2c_release_bus(void *, int);
     108             : int     amdiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
     109             :             void *, size_t, int);
     110             : 
     111             : int     amdiic_intr(void *);
     112             : 
     113             : struct cfattach amdiic_ca = {
     114             :         sizeof(struct amdiic_softc),
     115             :         amdiic_match,
     116             :         amdiic_attach
     117             : };
     118             : 
     119             : struct cfdriver amdiic_cd = {
     120             :         NULL, "amdiic", DV_DULL
     121             : };
     122             : 
     123             : const struct pci_matchid amdiic_ids[] = {
     124             :         { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_SMB }
     125             : };
     126             : 
     127             : int
     128           0 : amdiic_match(struct device *parent, void *match, void *aux)
     129             : {
     130           0 :         return (pci_matchbyid(aux, amdiic_ids,
     131             :             sizeof(amdiic_ids) / sizeof(amdiic_ids[0])));
     132             : }
     133             : 
     134             : void
     135           0 : amdiic_attach(struct device *parent, struct device *self, void *aux)
     136             : {
     137           0 :         struct amdiic_softc *sc = (struct amdiic_softc *)self;
     138           0 :         struct pci_attach_args *pa = aux;
     139           0 :         struct i2cbus_attach_args iba;
     140             :         pcireg_t conf;
     141           0 :         bus_size_t iosize;
     142           0 :         pci_intr_handle_t ih;
     143             :         const char *intrstr = NULL;
     144             : 
     145             :         /* Map I/O space */
     146           0 :         if (pci_mapreg_map(pa, AMD8111_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
     147           0 :             &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
     148           0 :                 printf(": can't map i/o space\n");
     149           0 :                 return;
     150             :         }
     151             : 
     152             :         /* Read configuration */
     153           0 :         conf = pci_conf_read(pa->pa_pc, pa->pa_tag, AMD8111_SMB_MISC);
     154             :         DPRINTF((": conf 0x%08x", conf));
     155             : 
     156           0 :         sc->sc_poll = 1;
     157           0 :         if (conf & AMD8111_SMB_MISC_SCIEN) {
     158             :                 /* No PCI IRQ */
     159           0 :                 printf(": SCI");
     160           0 :         } else if (conf & AMD8111_SMB_MISC_INTEN) {
     161             :                 /* Install interrupt handler */
     162           0 :                 if (pci_intr_map(pa, &ih) == 0) {
     163           0 :                         intrstr = pci_intr_string(pa->pa_pc, ih);
     164           0 :                         sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
     165           0 :                             amdiic_intr, sc, sc->sc_dev.dv_xname);
     166           0 :                         if (sc->sc_ih != NULL) {
     167           0 :                                 printf(": %s", intrstr);
     168           0 :                                 sc->sc_poll = 0;
     169           0 :                         }
     170             :                 }
     171           0 :                 if (sc->sc_poll)
     172           0 :                         printf(": polling");
     173             :         }
     174             : 
     175           0 :         printf("\n");
     176             : 
     177             :         /* Attach I2C bus */
     178           0 :         rw_init(&sc->sc_i2c_lock, "iiclk");
     179           0 :         sc->sc_i2c_tag.ic_cookie = sc;
     180           0 :         sc->sc_i2c_tag.ic_acquire_bus = amdiic_i2c_acquire_bus;
     181           0 :         sc->sc_i2c_tag.ic_release_bus = amdiic_i2c_release_bus;
     182           0 :         sc->sc_i2c_tag.ic_exec = amdiic_i2c_exec;
     183             : 
     184           0 :         bzero(&iba, sizeof(iba));
     185           0 :         iba.iba_name = "iic";
     186           0 :         iba.iba_tag = &sc->sc_i2c_tag;
     187           0 :         config_found(self, &iba, iicbus_print);
     188             : 
     189           0 :         return;
     190           0 : }
     191             : 
     192             : int
     193           0 : amdiic_read(struct amdiic_softc *sc, u_int8_t reg)
     194             : {
     195           0 :         if (amdiic_wait(sc, 0))
     196           0 :                 return (-1);
     197           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
     198             :             AMD8111_SMB_SC_CMD_RD);
     199           0 :         if (amdiic_wait(sc, 0))
     200           0 :                 return (-1);
     201           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
     202           0 :         if (amdiic_wait(sc, 1))
     203           0 :                 return (-1);
     204             : 
     205           0 :         return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA));
     206           0 : }
     207             : 
     208             : int
     209           0 : amdiic_write(struct amdiic_softc *sc, u_int8_t reg, u_int8_t val)
     210             : {
     211           0 :         if (amdiic_wait(sc, 0))
     212           0 :                 return (-1);
     213           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
     214             :             AMD8111_SMB_SC_CMD_WR);
     215           0 :         if (amdiic_wait(sc, 0))
     216           0 :                 return (-1);
     217           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
     218           0 :         if (amdiic_wait(sc, 0))
     219           0 :                 return (-1);
     220           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, val);
     221             : 
     222           0 :         return (0);
     223           0 : }
     224             : 
     225             : int
     226           0 : amdiic_wait(struct amdiic_softc *sc, int output)
     227             : {
     228             :         int retries;
     229             :         u_int8_t st;
     230             : 
     231           0 :         for (retries = 100; retries > 0; retries--) {
     232           0 :                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
     233             :                     AMD8111_SMB_SC_ST);
     234           0 :                 if (output && (st & AMD8111_SMB_SC_ST_OBF))
     235           0 :                         return (0);
     236           0 :                 if (!output && (st & AMD8111_SMB_SC_ST_IBF) == 0)
     237           0 :                         return (0);
     238           0 :                 DELAY(1);
     239             :         }
     240             :         DPRINTF(("%s: %s wait timeout: st 0x%b\n", sc->sc_dev.dv_xname,
     241             :             (output ? "output" : "input"), st));
     242             : 
     243           0 :         return (1);
     244           0 : }
     245             : 
     246             : int
     247           0 : amdiic_i2c_acquire_bus(void *cookie, int flags)
     248             : {
     249           0 :         struct amdiic_softc *sc = cookie;
     250             : 
     251           0 :         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
     252           0 :                 return (0);
     253             : 
     254           0 :         return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
     255           0 : }
     256             : 
     257             : void
     258           0 : amdiic_i2c_release_bus(void *cookie, int flags)
     259             : {
     260           0 :         struct amdiic_softc *sc = cookie;
     261             : 
     262           0 :         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
     263           0 :                 return;
     264             : 
     265           0 :         rw_exit(&sc->sc_i2c_lock);
     266           0 : }
     267             : 
     268             : int
     269           0 : amdiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
     270             :     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
     271             : {
     272           0 :         struct amdiic_softc *sc = cookie;
     273             :         u_int8_t *b;
     274             :         u_int8_t proto, st;
     275             :         int retries;
     276             : 
     277             :         DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
     278             :             "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr,
     279             :             cmdlen, len, flags));
     280             : 
     281           0 :         if (cold || sc->sc_poll)
     282           0 :                 flags |= I2C_F_POLL;
     283             : 
     284           0 :         if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
     285           0 :                 return (1);
     286             : 
     287             :         /* Setup transfer */
     288           0 :         sc->sc_i2c_xfer.op = op;
     289           0 :         sc->sc_i2c_xfer.buf = buf;
     290           0 :         sc->sc_i2c_xfer.len = len;
     291           0 :         sc->sc_i2c_xfer.flags = flags;
     292           0 :         sc->sc_i2c_xfer.error = 0;
     293             : 
     294             :         /* Set slave address */
     295           0 :         if (amdiic_write(sc, AMD8111_SMB_ADDR,
     296           0 :             addr << AMD8111_SMB_ADDR_SHIFT) == -1)
     297           0 :                 return (1);
     298             : 
     299             :         b = (void *)cmdbuf;
     300           0 :         if (cmdlen > 0)
     301             :                 /* Set command byte */
     302           0 :                 if (amdiic_write(sc, AMD8111_SMB_CMD, b[0]) == -1)
     303           0 :                         return (1);
     304             : 
     305           0 :         if (I2C_OP_WRITE_P(op)) {
     306             :                 /* Write data */
     307             :                 b = buf;
     308           0 :                 if (len > 0)
     309           0 :                         if (amdiic_write(sc, AMD8111_SMB_DATA(0), b[0]) == -1)
     310           0 :                                 return (1);
     311           0 :                 if (len > 1)
     312           0 :                         if (amdiic_write(sc, AMD8111_SMB_DATA(1), b[1]) == -1)
     313           0 :                                 return (1);
     314             :         }
     315             : 
     316             :         /* Set SMBus command */
     317           0 :         if (len == 0)
     318           0 :                 proto = AMD8111_SMB_PROTO_BYTE;
     319           0 :         else if (len == 1)
     320           0 :                 proto = AMD8111_SMB_PROTO_BDATA;
     321           0 :         else if (len == 2)
     322             :                 proto = AMD8111_SMB_PROTO_WDATA;
     323             :         else
     324           0 :                 panic("%s: unexpected len %zd", __func__, len);
     325             : 
     326             :         /* Set direction */
     327           0 :         if (I2C_OP_READ_P(op))
     328           0 :                 proto |= AMD8111_SMB_PROTO_READ;
     329             : 
     330             :         /* Start transaction */
     331           0 :         amdiic_write(sc, AMD8111_SMB_PROTO, proto);
     332             : 
     333           0 :         if (flags & I2C_F_POLL) {
     334             :                 /* Poll for completion */
     335           0 :                 DELAY(AMDIIC_DELAY);
     336           0 :                 for (retries = 1000; retries > 0; retries--) {
     337           0 :                         st = amdiic_read(sc, AMD8111_SMB_STAT);
     338           0 :                         if (st != 0)
     339             :                                 break;
     340           0 :                         DELAY(AMDIIC_DELAY);
     341             :                 }
     342           0 :                 if (st == 0) {
     343           0 :                         printf("%s: exec: op %d, addr 0x%02x, cmdlen %zu, "
     344             :                             "len %zu, flags 0x%02x: timeout\n",
     345           0 :                             sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
     346           0 :                         return (1);
     347             :                 }
     348           0 :                 amdiic_intr(sc);
     349           0 :         } else {
     350             :                 /* Wait for interrupt */
     351           0 :                 if (tsleep(sc, PRIBIO, "amdiic", AMDIIC_TIMEOUT * hz))
     352           0 :                         return (1);
     353             :         }       
     354             : 
     355           0 :         if (sc->sc_i2c_xfer.error)
     356           0 :                 return (1);
     357             : 
     358           0 :         return (0);
     359           0 : }
     360             : 
     361             : int
     362           0 : amdiic_intr(void *arg)
     363             : {
     364           0 :         struct amdiic_softc *sc = arg;
     365             :         int st;
     366             :         u_int8_t *b;
     367             :         size_t len;
     368             : 
     369             :         /* Read status */
     370           0 :         if ((st = amdiic_read(sc, AMD8111_SMB_STAT)) == -1)
     371           0 :                 return (-1);
     372           0 :         if (st == 0)
     373             :                 /* Interrupt was not for us */
     374           0 :                 return (0);
     375             : 
     376             :         DPRINTF(("%s: intr: st 0x%02x\n", sc->sc_dev.dv_xname, st));
     377             : 
     378             :         /* Check for errors */
     379           0 :         if ((st & AMD8111_SMB_STAT_MASK) != 0) {
     380           0 :                 sc->sc_i2c_xfer.error = 1;
     381           0 :                 goto done;
     382             :         }
     383             : 
     384           0 :         if (st & AMD8111_SMB_STAT_DONE) {
     385           0 :                 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
     386             :                         goto done;
     387             : 
     388             :                 /* Read data */
     389           0 :                 b = sc->sc_i2c_xfer.buf;
     390           0 :                 len = sc->sc_i2c_xfer.len;
     391           0 :                 if (len > 0)
     392           0 :                         b[0] = amdiic_read(sc, AMD8111_SMB_DATA(0));
     393           0 :                 if (len > 1)
     394           0 :                         b[1] = amdiic_read(sc, AMD8111_SMB_DATA(1));
     395             :         }
     396             : 
     397             : done:
     398           0 :         if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
     399           0 :                 wakeup(sc);
     400           0 :         return (1);
     401           0 : }

Generated by: LCOV version 1.13