root/dev/pci/alipm.c

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

DEFINITIONS

This source file includes following definitions.
  1. alipm_match
  2. alipm_attach
  3. alipm_smb_acquire_bus
  4. alipm_smb_release_bus
  5. alipm_smb_exec

    1 /*      $OpenBSD: alipm.c,v 1.13 2007/05/03 12:19:01 dlg Exp $  */
    2 
    3 /*
    4  * Copyright (c) 2005 Mark Kettenis
    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 #include <sys/param.h>
   20 #include <sys/device.h>
   21 #include <sys/kernel.h>
   22 #include <sys/rwlock.h>
   23 #include <sys/proc.h>
   24 #include <sys/systm.h>
   25 
   26 #include <dev/i2c/i2cvar.h>
   27 
   28 #include <dev/pci/pcidevs.h>
   29 #include <dev/pci/pcireg.h>
   30 #include <dev/pci/pcivar.h>
   31 
   32 #ifdef __sparc64__
   33 #include <arch/sparc64/dev/ofwi2cvar.h>
   34 #endif
   35 
   36 /*
   37  * Acer Labs M7101 Power register definitions.
   38  */
   39 
   40 /* PCI configuration registers. */
   41 #define ALIPM_CONF      0xd0            /* general configuration */
   42 #define ALIPM_CONF_SMBEN        0x0400          /* enable SMBus */
   43 #define ALIPM_BASE      0xe0            /* ACPI and SMBus base address */
   44 #define ALIPM_SMB_HOSTC 0xf0            /* host configuration */
   45 #define ALIPM_SMB_HOSTC_HSTEN   0x00000001      /* enable host controller */
   46 #define ALIPM_SMB_HOSTC_CLOCK   0x00e00000      /* clock speed */
   47 #define ALIPM_SMB_HOSTC_149K    0x00000000      /* 149 KHz clock */
   48 #define ALIPM_SMB_HOSTC_74K     0x00200000      /*  74 KHz clock */
   49 #define ALIPM_SMB_HOSTC_37K     0x00400000      /*  37 KHz clock */
   50 #define ALIPM_SMB_HOSTC_223K    0x00800000      /* 223 KHz clock */
   51 #define ALIPM_SMB_HOSTC_111K    0x00a00000      /* 111 KHz clock */
   52 #define ALIPM_SMB_HOSTC_55K     0x00c00000      /*  55 KHz clock */
   53 
   54 #define ALIPM_SMB_SIZE          32      /* SMBus I/O space size */
   55 
   56 /* SMBus I/O registers */
   57 #define ALIPM_SMB_HS    0x00            /* host status */
   58 #define ALIPM_SMB_HS_IDLE       0x04
   59 #define ALIPM_SMB_HS_BUSY       0x08    /* running a command */
   60 #define ALIPM_SMB_HS_DONE       0x10    /* command completed */
   61 #define ALIPM_SMB_HS_DEVERR     0x20    /* command error */
   62 #define ALIPM_SMB_HS_BUSERR     0x40    /* transaction collision */
   63 #define ALIPM_SMB_HS_FAILED     0x80    /* failed bus transaction */
   64 #define ALIPM_SMB_HS_BITS \
   65   "\020\003IDLE\004BUSY\005DONE\006DEVERR\007BUSERR\010FAILED"
   66 #define ALIPM_SMB_HC    0x01            /* host control */
   67 #define ALIPM_SMB_HC_KILL       0x04            /* kill command */
   68 #define ALIPM_SMB_HC_RESET      0x08            /* reset bus */
   69 #define ALIPM_SMB_HC_CMD_QUICK  0x00            /* QUICK command */
   70 #define ALIPM_SMB_HC_CMD_BYTE   0x10            /* BYTE command */
   71 #define ALIPM_SMB_HC_CMD_BDATA  0x20            /* BYTE DATA command */
   72 #define ALIPM_SMB_HC_CMD_WDATA  0x30            /* WORD DATA command */
   73 #define ALIPM_SMB_HC_CMD_BLOCK 0x40             /* BLOCK command */
   74 #define ALIPM_SMB_START         0x02    /* start command */
   75 #define ALIPM_SMB_TXSLVA        0x03    /* transmit slave address */
   76 #define ALIPM_SMB_TXSLVA_READ   (1 << 0)        /* read direction */
   77 #define ALIPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
   78 #define ALIPM_SMB_HD0           0x04    /* host data 0 */
   79 #define ALIPM_SMB_HD1           0x05    /* host data 1 */
   80 #define ALIPM_SMB_HBDB          0x06    /* host block data byte */
   81 #define ALIPM_SMB_HCMD          0x07    /* host command */
   82 
   83 /*
   84  * Newer chips have a more standard, but different PCI configuration
   85  * register layout.
   86  */
   87 
   88 #define ALIPM_SMB_BASE  0x14            /* SMBus base address */
   89 #define ALIPM_SMB_HOSTX 0xe0            /* host configuration */
   90 
   91 #ifdef ALIPM_DEBUG
   92 #define DPRINTF(x) printf x
   93 #else
   94 #define DPRINTF(x)
   95 #endif
   96 
   97 #define ALIPM_DELAY     100
   98 #define ALIPM_TIMEOUT   1
   99 
  100 struct alipm_softc {
  101         struct device sc_dev;
  102 
  103         bus_space_tag_t sc_iot;
  104         bus_space_handle_t sc_ioh;
  105 
  106         struct i2c_controller sc_smb_tag;
  107         struct rwlock sc_smb_lock;
  108 };
  109 
  110 int     alipm_match(struct device *, void *, void *);
  111 void    alipm_attach(struct device *, struct device *, void *);
  112 
  113 int     alipm_smb_acquire_bus(void *, int);
  114 void    alipm_smb_release_bus(void *, int);
  115 int     alipm_smb_exec(void *, i2c_op_t, i2c_addr_t, const void *,
  116             size_t, void *, size_t, int);
  117 
  118 struct cfattach alipm_ca = {
  119         sizeof(struct alipm_softc),
  120         alipm_match,
  121         alipm_attach
  122 };
  123 
  124 struct cfdriver alipm_cd = {
  125         NULL, "alipm", DV_DULL
  126 };
  127 
  128 int
  129 alipm_match(struct device *parent, void *match, void *aux)
  130 {
  131         struct pci_attach_args *pa = aux;
  132 
  133         if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
  134             (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M7101))
  135                 return (1);
  136         return (0);
  137 }
  138 
  139 void
  140 alipm_attach(struct device *parent, struct device *self, void *aux)
  141 {
  142         struct alipm_softc *sc = (struct alipm_softc *) self;
  143         struct pci_attach_args *pa = aux;
  144         struct i2cbus_attach_args iba;
  145         pcireg_t iobase, reg;
  146         bus_size_t iosize = ALIPM_SMB_SIZE;
  147 
  148         /* Old chips don't have the PCI 2.2 Capabilities List. */
  149         reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
  150         if ((reg & PCI_STATUS_CAPLIST_SUPPORT) == 0) {
  151                 /* Map I/O space */
  152                 iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_BASE);
  153                 sc->sc_iot = pa->pa_iot;
  154                 if (iobase == 0 ||
  155                     bus_space_map(sc->sc_iot, iobase >> 16,
  156                     iosize, 0, &sc->sc_ioh)) {
  157                         printf(": can't map I/O space\n");
  158                         return;
  159                 }
  160 
  161                 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_CONF);
  162                 if ((reg & ALIPM_CONF_SMBEN) == 0) {
  163                         printf(": SMBus disabled\n");
  164                         goto fail;
  165                 }
  166 
  167                 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTC);
  168                 if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
  169                         printf(": SMBus host disabled\n");
  170                         goto fail;
  171                 }
  172         } else {
  173                 /* Map I/O space */
  174                 if (pci_mapreg_map(pa, ALIPM_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
  175                     &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, ALIPM_SMB_SIZE)) {
  176                         printf(": can't map I/O space\n");
  177                         return;
  178                 }
  179 
  180                 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, ALIPM_SMB_HOSTX);
  181                 if ((reg & ALIPM_SMB_HOSTC_HSTEN) == 0) {
  182                         printf(": SMBus host disabled\n");
  183                         goto fail;
  184                 }
  185         }
  186 
  187         switch (reg & ALIPM_SMB_HOSTC_CLOCK) {
  188         case ALIPM_SMB_HOSTC_149K:
  189                 printf(": 149KHz clock");
  190                 break;
  191         case ALIPM_SMB_HOSTC_74K:
  192                 printf(": 74KHz clock");
  193                 break;
  194         case ALIPM_SMB_HOSTC_37K:
  195                 printf(": 37KHz clock");
  196                 break;
  197         case ALIPM_SMB_HOSTC_223K:
  198                 printf(": 223KHz clock");
  199                 break;
  200         case ALIPM_SMB_HOSTC_111K:
  201                 printf(": 111KHz clock");
  202                 break;
  203         case ALIPM_SMB_HOSTC_55K:
  204                 printf(": 55KHz clock");
  205                 break;
  206         default:
  207                 printf(" unknown clock speed");
  208                 break;
  209         }
  210 
  211         printf("\n");
  212 
  213         /* Attach I2C bus */
  214         rw_init(&sc->sc_smb_lock, "alipm");
  215         sc->sc_smb_tag.ic_cookie = sc;
  216         sc->sc_smb_tag.ic_acquire_bus = alipm_smb_acquire_bus;
  217         sc->sc_smb_tag.ic_release_bus = alipm_smb_release_bus;
  218         sc->sc_smb_tag.ic_exec = alipm_smb_exec;
  219 
  220         bzero(&iba, sizeof iba);
  221         iba.iba_name = "iic";
  222         iba.iba_tag = &sc->sc_smb_tag;
  223 #ifdef __sparc64__
  224         iba.iba_bus_scan = ofwiic_pci_scan;
  225         iba.iba_bus_scan_arg = pa;
  226 #endif
  227         config_found(&sc->sc_dev, &iba, iicbus_print);
  228 
  229         return;
  230 
  231 fail:
  232         bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
  233 }
  234 
  235 int
  236 alipm_smb_acquire_bus(void *cookie, int flags)
  237 {
  238         struct alipm_softc *sc = cookie;
  239 
  240         if (flags & I2C_F_POLL)
  241                 return (0);
  242 
  243         return (rw_enter(&sc->sc_smb_lock, RW_WRITE | RW_INTR));
  244 }
  245 
  246 void
  247 alipm_smb_release_bus(void *cookie, int flags)
  248 {
  249         struct alipm_softc *sc = cookie;
  250 
  251         if (flags & I2C_F_POLL)
  252                 return;
  253 
  254         rw_exit(&sc->sc_smb_lock);
  255 }
  256 
  257 int
  258 alipm_smb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
  259     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
  260 {
  261         struct alipm_softc *sc = cookie;
  262         u_int8_t *b;
  263         u_int8_t ctl, st;
  264         int retries, error = 0;
  265 
  266         DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
  267             "flags 0x%x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
  268             len, flags));
  269 
  270         if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
  271                 return (EOPNOTSUPP);
  272 
  273         /* Clear status bits */
  274         bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS,
  275             ALIPM_SMB_HS_DONE | ALIPM_SMB_HS_FAILED |
  276             ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR);
  277         bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
  278             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
  279 
  280         /* Wait until bus is idle */
  281         for (retries = 1000; retries > 0; retries--) {
  282                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
  283                 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
  284                     BUS_SPACE_BARRIER_READ);
  285                 if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
  286                     ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
  287                         break;
  288                 DELAY(ALIPM_DELAY);
  289         }
  290         if (retries == 0) {
  291                 printf("%s: timeout st 0x%b\n", sc->sc_dev.dv_xname,
  292                     st, ALIPM_SMB_HS_BITS);
  293                 return (ETIMEDOUT);
  294         }
  295         if (st & (ALIPM_SMB_HS_FAILED |
  296             ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
  297                 printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
  298                     st, ALIPM_SMB_HS_BITS);
  299                 return (EIO);
  300         }
  301 
  302         /* Set slave address and transfer direction. */
  303         bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_TXSLVA,
  304             ALIPM_SMB_TXSLVA_ADDR(addr) |
  305             (I2C_OP_READ_P(op) ? ALIPM_SMB_TXSLVA_READ : 0));
  306 
  307         b = (void *)cmdbuf;
  308         if (cmdlen > 0)
  309                 /* Set command byte */
  310                 bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  311                      ALIPM_SMB_HCMD, b[0]);
  312 
  313         if (I2C_OP_WRITE_P(op)) {
  314                 /* Write data. */
  315                 b = buf;
  316                 if (len > 0)
  317                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  318                             ALIPM_SMB_HD0, b[0]);
  319                 if (len > 1)
  320                         bus_space_write_1(sc->sc_iot, sc->sc_ioh,
  321                             ALIPM_SMB_HD1, b[1]);
  322         }
  323 
  324         /* Set SMBus command */
  325         if (len == 0)
  326                 ctl = ALIPM_SMB_HC_CMD_BYTE;
  327         else if (len == 1)
  328                 ctl = ALIPM_SMB_HC_CMD_BDATA;
  329         else if (len == 2)
  330                 ctl = ALIPM_SMB_HC_CMD_WDATA;
  331         bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC, ctl);
  332 
  333         /* Start transaction */
  334         bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
  335             BUS_SPACE_BARRIER_WRITE);
  336         bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_START, 0xff);
  337         bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
  338             BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
  339 
  340         /* Poll for completion */
  341         DELAY(ALIPM_DELAY);
  342         for (retries = 1000; retries > 0; retries--) {
  343                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
  344                 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
  345                     BUS_SPACE_BARRIER_READ);
  346                 if (st & (ALIPM_SMB_HS_IDLE | ALIPM_SMB_HS_FAILED |
  347                     ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR))
  348                         break;
  349                 DELAY(ALIPM_DELAY);
  350         }
  351         if (retries == 0) {
  352                 printf("%s: timeout st 0x%b, resetting\n",
  353                     sc->sc_dev.dv_xname, st, ALIPM_SMB_HS_BITS);
  354                 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
  355                     ALIPM_SMB_HC_RESET);
  356                 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
  357                      BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
  358                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
  359                 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
  360                     BUS_SPACE_BARRIER_READ);
  361                 error = ETIMEDOUT;
  362                 goto done;
  363         }
  364 
  365         if ((st & ALIPM_SMB_HS_DONE) == 0) {
  366                 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HC,
  367                      ALIPM_SMB_HC_KILL);
  368                 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, ALIPM_SMB_SIZE,
  369                      BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
  370                 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS);
  371                 bus_space_barrier(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, 1,
  372                     BUS_SPACE_BARRIER_READ);
  373                 if ((st & ALIPM_SMB_HS_FAILED) == 0)
  374                         printf("%s: error st 0x%b\n", sc->sc_dev.dv_xname,
  375                             st, ALIPM_SMB_HS_BITS);
  376         }
  377 
  378         /* Check for errors */
  379         if (st & (ALIPM_SMB_HS_FAILED |
  380             ALIPM_SMB_HS_BUSERR | ALIPM_SMB_HS_DEVERR)) {
  381                 error = EIO;
  382                 goto done;
  383         }
  384 
  385         if (I2C_OP_READ_P(op)) {
  386                 /* Read data */
  387                 b = buf;
  388                 if (len > 0) {
  389                         b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  390                             ALIPM_SMB_HD0);
  391                         bus_space_barrier(sc->sc_iot, sc->sc_ioh,
  392                             ALIPM_SMB_HD0, 1, BUS_SPACE_BARRIER_READ);
  393                 }
  394                 if (len > 1) {
  395                         b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
  396                             ALIPM_SMB_HD1);
  397                         bus_space_barrier(sc->sc_iot, sc->sc_ioh,
  398                             ALIPM_SMB_HD1, 1, BUS_SPACE_BARRIER_READ);
  399                 }
  400         }
  401 
  402 done:
  403         /* Clear status bits */
  404         bus_space_write_1(sc->sc_iot, sc->sc_ioh, ALIPM_SMB_HS, st);
  405 
  406         return (error);
  407 }

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