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

          Line data    Source code
       1             : /*      $OpenBSD: piixpm.c,v 1.39 2013/10/01 20:06:02 sf 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             : 
      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/pci/piixreg.h>
      36             : 
      37             : #include <dev/i2c/i2cvar.h>
      38             : 
      39             : #ifdef PIIXPM_DEBUG
      40             : #define DPRINTF(x) printf x
      41             : #else
      42             : #define DPRINTF(x)
      43             : #endif
      44             : 
      45             : #define PIIXPM_DELAY    200
      46             : #define PIIXPM_TIMEOUT  1
      47             : 
      48             : struct piixpm_softc {
      49             :         struct device           sc_dev;
      50             : 
      51             :         bus_space_tag_t         sc_iot;
      52             :         bus_space_handle_t      sc_ioh;
      53             :         void *                  sc_ih;
      54             :         int                     sc_poll;
      55             : 
      56             :         struct i2c_controller   sc_i2c_tag;
      57             :         struct rwlock           sc_i2c_lock;
      58             :         struct {
      59             :                 i2c_op_t     op;
      60             :                 void *       buf;
      61             :                 size_t       len;
      62             :                 int          flags;
      63             :                 volatile int error;
      64             :         }                       sc_i2c_xfer;
      65             : };
      66             : 
      67             : int     piixpm_match(struct device *, void *, void *);
      68             : void    piixpm_attach(struct device *, struct device *, void *);
      69             : 
      70             : int     piixpm_i2c_acquire_bus(void *, int);
      71             : void    piixpm_i2c_release_bus(void *, int);
      72             : int     piixpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
      73             :             void *, size_t, int);
      74             : 
      75             : int     piixpm_intr(void *);
      76             : 
      77             : struct cfattach piixpm_ca = {
      78             :         sizeof(struct piixpm_softc),
      79             :         piixpm_match,
      80             :         piixpm_attach
      81             : };
      82             : 
      83             : struct cfdriver piixpm_cd = {
      84             :         NULL, "piixpm", DV_DULL
      85             : };
      86             : 
      87             : const struct pci_matchid piixpm_ids[] = {
      88             :         { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_HUDSON2_SMB },
      89             : 
      90             :         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB200_SMB },
      91             :         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB300_SMB },
      92             :         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB400_SMB },
      93             :         { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SBX00_SMB },
      94             : 
      95             :         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_PM },
      96             :         { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_PM },
      97             : 
      98             :         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB5 },
      99             :         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB6 },
     100             :         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_HT_1000 },
     101             :         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_HT_1100 },
     102             :         { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_OSB4 },
     103             : 
     104             :         { PCI_VENDOR_SMSC, PCI_PRODUCT_SMSC_VICTORY66_PM }
     105             : };
     106             : 
     107             : int
     108           0 : piixpm_match(struct device *parent, void *match, void *aux)
     109             : {
     110           0 :         return (pci_matchbyid(aux, piixpm_ids,
     111             :             sizeof(piixpm_ids) / sizeof(piixpm_ids[0])));
     112             : }
     113             : 
     114             : void
     115           0 : piixpm_attach(struct device *parent, struct device *self, void *aux)
     116             : {
     117           0 :         struct piixpm_softc *sc = (struct piixpm_softc *)self;
     118           0 :         struct pci_attach_args *pa = aux;
     119           0 :         bus_space_handle_t ioh;
     120             :         u_int16_t smb0en;
     121             :         bus_addr_t base;
     122             :         pcireg_t conf;
     123           0 :         pci_intr_handle_t ih;
     124             :         const char *intrstr = NULL;
     125           0 :         struct i2cbus_attach_args iba;
     126             : 
     127           0 :         sc->sc_iot = pa->pa_iot;
     128             : 
     129           0 :         if ((PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
     130           0 :             PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_HUDSON2_SMB) ||
     131           0 :             (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI &&
     132           0 :             PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SBX00_SMB &&
     133           0 :             PCI_REVISION(pa->pa_class) >= 0x40)) {
     134             :                 /* 
     135             :                  * On the AMD SB800+, the SMBus I/O registers are well
     136             :                  * hidden.  We need to look at the "SMBus0En" Power
     137             :                  * Management register to find out where they live.
     138             :                  * We use indirect IO access through the index/data
     139             :                  * pair at 0xcd6/0xcd7 to access "SMBus0En".  Since
     140             :                  * the index/data pair may be needed by other drivers,
     141             :                  * we only map them for the duration that we actually
     142             :                  * need them.
     143             :                  */
     144           0 :                 if (bus_space_map(sc->sc_iot, SB800_PMREG_BASE,
     145           0 :                     SB800_PMREG_SIZE, 0, &ioh) != 0) {
     146           0 :                         printf(": can't map i/o space\n");
     147           0 :                         return;
     148             :                 }
     149             : 
     150             :                 /* Read "SmBus0En" */
     151           0 :                 bus_space_write_1(sc->sc_iot, ioh, 0, SB800_PMREG_SMB0EN);
     152           0 :                 smb0en = bus_space_read_1(sc->sc_iot, ioh, 1);
     153           0 :                 bus_space_write_1(sc->sc_iot, ioh, 0, SB800_PMREG_SMB0EN + 1);
     154           0 :                 smb0en |= (bus_space_read_1(sc->sc_iot, ioh, 1) << 8);
     155             : 
     156           0 :                 bus_space_unmap(sc->sc_iot, ioh, SB800_PMREG_SIZE);
     157             : 
     158           0 :                 if ((smb0en & SB800_SMB0EN_EN) == 0) {
     159           0 :                         printf(": SMBus disabled\n");
     160           0 :                         return;
     161             :                 }
     162             : 
     163             :                 /* Map I/O space */
     164           0 :                 base = smb0en & SB800_SMB0EN_BASE_MASK;
     165           0 :                 if (base == 0 || bus_space_map(sc->sc_iot, base,
     166           0 :                     SB800_SMB_SIZE, 0, &sc->sc_ioh)) {
     167           0 :                         printf(": can't map i/o space");
     168           0 :                         return;
     169             :                 }
     170             : 
     171             :                 /* Read configuration */
     172           0 :                 conf = bus_space_read_1(sc->sc_iot, sc->sc_ioh, SB800_SMB_HOSTC);
     173           0 :                 if (conf & SB800_SMB_HOSTC_SMI)
     174           0 :                         conf = PIIX_SMB_HOSTC_SMI;
     175             :                 else
     176             :                         conf = PIIX_SMB_HOSTC_IRQ;
     177             :         } else {
     178             :                 /* Read configuration */
     179           0 :                 conf = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_HOSTC);
     180             :                 DPRINTF((": conf 0x%08x", conf));
     181             : 
     182           0 :                 if ((conf & PIIX_SMB_HOSTC_HSTEN) == 0) {
     183           0 :                         printf(": SMBus disabled\n");
     184           0 :                         return;
     185             :                 }
     186             : 
     187             :                 /* Map I/O space */
     188           0 :                 base = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_BASE) &
     189             :                     PIIX_SMB_BASE_MASK;
     190           0 :                 if (base == 0 || bus_space_map(sc->sc_iot, base,
     191           0 :                     PIIX_SMB_SIZE, 0, &sc->sc_ioh)) {
     192           0 :                         printf(": can't map i/o space\n");
     193           0 :                         return;
     194             :                 }
     195             :         }
     196             : 
     197           0 :         sc->sc_poll = 1;
     198           0 :         if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_SMI) {
     199             :                 /* No PCI IRQ */
     200           0 :                 printf(": SMI");
     201           0 :         } else {
     202           0 :                 if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_IRQ) {
     203             :                         /* Install interrupt handler */
     204           0 :                         if (pci_intr_map(pa, &ih) == 0) {
     205           0 :                                 intrstr = pci_intr_string(pa->pa_pc, ih);
     206           0 :                                 sc->sc_ih = pci_intr_establish(pa->pa_pc,
     207           0 :                                     ih, IPL_BIO, piixpm_intr, sc,
     208           0 :                                     sc->sc_dev.dv_xname);
     209           0 :                                 if (sc->sc_ih != NULL) {
     210           0 :                                         printf(": %s", intrstr);
     211           0 :                                         sc->sc_poll = 0;
     212           0 :                                 }
     213             :                         }
     214             :                 }
     215           0 :                 if (sc->sc_poll)
     216           0 :                         printf(": polling");
     217             :         }
     218             : 
     219           0 :         printf("\n");
     220             : 
     221             :         /* Attach I2C bus */
     222           0 :         rw_init(&sc->sc_i2c_lock, "iiclk");
     223           0 :         sc->sc_i2c_tag.ic_cookie = sc;
     224           0 :         sc->sc_i2c_tag.ic_acquire_bus = piixpm_i2c_acquire_bus;
     225           0 :         sc->sc_i2c_tag.ic_release_bus = piixpm_i2c_release_bus;
     226           0 :         sc->sc_i2c_tag.ic_exec = piixpm_i2c_exec;
     227             : 
     228           0 :         bzero(&iba, sizeof(iba));
     229           0 :         iba.iba_name = "iic";
     230           0 :         iba.iba_tag = &sc->sc_i2c_tag;
     231           0 :         config_found(self, &iba, iicbus_print);
     232             : 
     233           0 :         return;
     234           0 : }
     235             : 
     236             : int
     237           0 : piixpm_i2c_acquire_bus(void *cookie, int flags)
     238             : {
     239           0 :         struct piixpm_softc *sc = cookie;
     240             : 
     241           0 :         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
     242           0 :                 return (0);
     243             : 
     244           0 :         return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
     245           0 : }
     246             : 
     247             : void
     248           0 : piixpm_i2c_release_bus(void *cookie, int flags)
     249             : {
     250           0 :         struct piixpm_softc *sc = cookie;
     251             : 
     252           0 :         if (cold || sc->sc_poll || (flags & I2C_F_POLL))
     253           0 :                 return;
     254             : 
     255           0 :         rw_exit(&sc->sc_i2c_lock);
     256           0 : }
     257             : 
     258             : int
     259           0 : piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
     260             :     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
     261             : {
     262           0 :         struct piixpm_softc *sc = cookie;
     263             :         u_int8_t *b;
     264             :         u_int8_t ctl, st;
     265             :         int retries;
     266             : 
     267             :         DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
     268             :             "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
     269             :             len, flags));
     270             : 
     271             :         /* Wait for bus to be idle */
     272           0 :         for (retries = 100; retries > 0; retries--) {
     273           0 :                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
     274           0 :                 if (!(st & PIIX_SMB_HS_BUSY))
     275             :                         break;
     276           0 :                 DELAY(PIIXPM_DELAY);
     277             :         }
     278             :         DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
     279             :             PIIX_SMB_HS_BITS));
     280           0 :         if (st & PIIX_SMB_HS_BUSY)
     281           0 :                 return (1);
     282             : 
     283           0 :         if (cold || sc->sc_poll)
     284           0 :                 flags |= I2C_F_POLL;
     285             : 
     286           0 :         if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
     287           0 :                 return (1);
     288             : 
     289             :         /* Setup transfer */
     290           0 :         sc->sc_i2c_xfer.op = op;
     291           0 :         sc->sc_i2c_xfer.buf = buf;
     292           0 :         sc->sc_i2c_xfer.len = len;
     293           0 :         sc->sc_i2c_xfer.flags = flags;
     294           0 :         sc->sc_i2c_xfer.error = 0;
     295             : 
     296             :         /* Set slave address and transfer direction */
     297           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_TXSLVA,
     298             :             PIIX_SMB_TXSLVA_ADDR(addr) |
     299             :             (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0));
     300             : 
     301             :         b = (void *)cmdbuf;
     302           0 :         if (cmdlen > 0)
     303             :                 /* Set command byte */
     304           0 :                 bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HCMD, b[0]);
     305             : 
     306           0 :         if (I2C_OP_WRITE_P(op)) {
     307             :                 /* Write data */
     308             :                 b = buf;
     309           0 :                 if (len > 0)
     310           0 :                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
     311             :                             PIIX_SMB_HD0, b[0]);
     312           0 :                 if (len > 1)
     313           0 :                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
     314             :                             PIIX_SMB_HD1, b[1]);
     315             :         }
     316             : 
     317             :         /* Set SMBus command */
     318           0 :         if (len == 0)
     319           0 :                 ctl = PIIX_SMB_HC_CMD_BYTE;
     320           0 :         else if (len == 1)
     321           0 :                 ctl = PIIX_SMB_HC_CMD_BDATA;
     322           0 :         else if (len == 2)
     323             :                 ctl = PIIX_SMB_HC_CMD_WDATA;
     324             :         else
     325           0 :                 panic("%s: unexpected len %zd", __func__, len);
     326             : 
     327           0 :         if ((flags & I2C_F_POLL) == 0)
     328           0 :                 ctl |= PIIX_SMB_HC_INTREN;
     329             : 
     330             :         /* Start transaction */
     331           0 :         ctl |= PIIX_SMB_HC_START;
     332           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC, ctl);
     333             : 
     334           0 :         if (flags & I2C_F_POLL) {
     335             :                 /* Poll for completion */
     336           0 :                 DELAY(PIIXPM_DELAY);
     337           0 :                 for (retries = 1000; retries > 0; retries--) {
     338           0 :                         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
     339             :                             PIIX_SMB_HS);
     340           0 :                         if ((st & PIIX_SMB_HS_BUSY) == 0)
     341             :                                 break;
     342           0 :                         DELAY(PIIXPM_DELAY);
     343             :                 }
     344           0 :                 if (st & PIIX_SMB_HS_BUSY)
     345             :                         goto timeout;
     346           0 :                 piixpm_intr(sc);
     347           0 :         } else {
     348             :                 /* Wait for interrupt */
     349           0 :                 if (tsleep(sc, PRIBIO, "piixpm", PIIXPM_TIMEOUT * hz))
     350             :                         goto timeout;
     351             :         }
     352             : 
     353           0 :         if (sc->sc_i2c_xfer.error)
     354           0 :                 return (1);
     355             : 
     356           0 :         return (0);
     357             : 
     358             : timeout:
     359             :         /*
     360             :          * Transfer timeout. Kill the transaction and clear status bits.
     361             :          */
     362           0 :         printf("%s: exec: op %d, addr 0x%02x, cmdlen %zu, len %zu, "
     363             :             "flags 0x%02x: timeout, status 0x%b\n",
     364           0 :             sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
     365           0 :             st, PIIX_SMB_HS_BITS);
     366           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC,
     367             :             PIIX_SMB_HC_KILL);
     368           0 :         DELAY(PIIXPM_DELAY);
     369           0 :         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
     370           0 :         if ((st & PIIX_SMB_HS_FAILED) == 0)
     371           0 :                 printf("%s: abort failed, status 0x%b\n",
     372             :                     sc->sc_dev.dv_xname, st, PIIX_SMB_HS_BITS);
     373           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
     374           0 :         return (1);
     375           0 : }
     376             : 
     377             : int
     378           0 : piixpm_intr(void *arg)
     379             : {
     380           0 :         struct piixpm_softc *sc = arg;
     381             :         u_int8_t st;
     382             :         u_int8_t *b;
     383             :         size_t len;
     384             : 
     385             :         /* Read status */
     386           0 :         st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
     387           0 :         if ((st & PIIX_SMB_HS_BUSY) != 0 || (st & (PIIX_SMB_HS_INTR |
     388             :             PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
     389           0 :             PIIX_SMB_HS_FAILED)) == 0)
     390             :                 /* Interrupt was not for us */
     391           0 :                 return (0);
     392             : 
     393             :         DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
     394             :             PIIX_SMB_HS_BITS));
     395             : 
     396             :         /* Clear status bits */
     397           0 :         bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
     398             : 
     399             :         /* Check for errors */
     400           0 :         if (st & (PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
     401             :             PIIX_SMB_HS_FAILED)) {
     402           0 :                 sc->sc_i2c_xfer.error = 1;
     403           0 :                 goto done;
     404             :         }
     405             : 
     406           0 :         if (st & PIIX_SMB_HS_INTR) {
     407           0 :                 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
     408             :                         goto done;
     409             : 
     410             :                 /* Read data */
     411           0 :                 b = sc->sc_i2c_xfer.buf;
     412           0 :                 len = sc->sc_i2c_xfer.len;
     413           0 :                 if (len > 0)
     414           0 :                         b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
     415             :                             PIIX_SMB_HD0);
     416           0 :                 if (len > 1)
     417           0 :                         b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
     418             :                             PIIX_SMB_HD1);
     419             :         }
     420             : 
     421             : done:
     422           0 :         if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
     423           0 :                 wakeup(sc);
     424           0 :         return (1);
     425           0 : }

Generated by: LCOV version 1.13