1 /* $OpenBSD: viapm.c,v 1.10 2009/03/29 21:53:53 sthen 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_VT8237S_ISA }, 134 { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8251_ISA }, 135 { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_CX700_ISA } 136 }; 137 138 int 139 viapm_match(struct device *parent, void *match, void *aux) 140 { 141 return (pci_matchbyid(aux, viapm_ids, 142 sizeof(viapm_ids) / sizeof(viapm_ids[0]))); 143 } 144 145 void 146 viapm_attach(struct device *parent, struct device *self, void *aux) 147 { 148 struct viapm_softc *sc = (struct viapm_softc *)self; 149 struct pci_attach_args *pa = aux; 150 struct i2cbus_attach_args iba; 151 pcireg_t conf, iobase; 152 #if 0 153 pci_intr_handle_t ih; 154 const char *intrstr = NULL; 155 #endif 156 157 /* Map I/O space */ 158 sc->sc_iot = pa->pa_iot; 159 iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_SMB_BASE); 160 if (iobase == 0 || 161 bus_space_map(sc->sc_iot, iobase & 0xfffe, 162 VIAPM_SMB_SIZE, 0, &sc->sc_ioh)) { 163 printf(": can't map i/o space\n"); 164 return; 165 } 166 167 /* Read configuration */ 168 conf = (iobase >> 16); 169 DPRINTF((": conf 0x%x", conf)); 170 171 if ((conf & VIAPM_SMB_HOSTC_HSTEN) == 0) { 172 printf(": SMBus host disabled\n"); 173 goto fail; 174 } 175 176 if (conf & VIAPM_SMB_HOSTC_INTEN) { 177 if (conf & VIAPM_SMB_HOSTC_SCIEN) 178 printf(": SCI"); 179 else 180 printf(": SMI"); 181 sc->sc_poll = 1; 182 } else { 183 #if 0 184 /* Install interrupt handler */ 185 if (pci_intr_map(pa, &ih)) { 186 printf(": can't map interrupt\n"); 187 goto fail; 188 } 189 intrstr = pci_intr_string(pa->pa_pc, ih); 190 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO, 191 viapm_intr, sc, sc->sc_dev.dv_xname); 192 if (sc->sc_ih == NULL) { 193 printf(": can't establish interrupt"); 194 if (intrstr != NULL) 195 printf(" at %s", intrstr); 196 printf("\n"); 197 goto fail; 198 } 199 printf(": %s", intrstr); 200 #endif 201 sc->sc_poll = 1; 202 } 203 204 printf("\n"); 205 206 /* Attach I2C bus */ 207 rw_init(&sc->sc_i2c_lock, "iiclk"); 208 sc->sc_i2c_tag.ic_cookie = sc; 209 sc->sc_i2c_tag.ic_acquire_bus = viapm_i2c_acquire_bus; 210 sc->sc_i2c_tag.ic_release_bus = viapm_i2c_release_bus; 211 sc->sc_i2c_tag.ic_exec = viapm_i2c_exec; 212 213 bzero(&iba, sizeof iba); 214 iba.iba_name = "iic"; 215 iba.iba_tag = &sc->sc_i2c_tag; 216 config_found(self, &iba, iicbus_print); 217 218 return; 219 220 fail: 221 bus_space_unmap(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_SIZE); 222 } 223 224 int 225 viapm_i2c_acquire_bus(void *cookie, int flags) 226 { 227 struct viapm_softc *sc = cookie; 228 229 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 230 return (0); 231 232 return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR)); 233 } 234 235 void 236 viapm_i2c_release_bus(void *cookie, int flags) 237 { 238 struct viapm_softc *sc = cookie; 239 240 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 241 return; 242 243 rw_exit(&sc->sc_i2c_lock); 244 } 245 246 int 247 viapm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 248 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 249 { 250 struct viapm_softc *sc = cookie; 251 u_int8_t *b; 252 u_int8_t ctl, st; 253 int retries; 254 255 DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, " 256 "flags 0x%x, status 0x%b\n", sc->sc_dev.dv_xname, op, addr, 257 cmdlen, len, flags, bus_space_read_1(sc->sc_iot, sc->sc_ioh, 258 VIAPM_SMB_HS), VIAPM_SMB_HS_BITS)); 259 260 /* Check if there's a transfer already running */ 261 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS); 262 DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st, 263 VIAPM_SMB_HS_BITS)); 264 if (st & VIAPM_SMB_HS_BUSY) 265 return (1); 266 267 if (cold || sc->sc_poll) 268 flags |= I2C_F_POLL; 269 270 if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2) 271 return (1); 272 273 /* Setup transfer */ 274 sc->sc_i2c_xfer.op = op; 275 sc->sc_i2c_xfer.buf = buf; 276 sc->sc_i2c_xfer.len = len; 277 sc->sc_i2c_xfer.flags = flags; 278 sc->sc_i2c_xfer.error = 0; 279 280 /* Set slave address and transfer direction */ 281 bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_TXSLVA, 282 VIAPM_SMB_TXSLVA_ADDR(addr) | 283 (I2C_OP_READ_P(op) ? VIAPM_SMB_TXSLVA_READ : 0)); 284 285 b = (void *)cmdbuf; 286 if (cmdlen > 0) 287 /* Set command byte */ 288 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 289 VIAPM_SMB_HCMD, b[0]); 290 291 if (I2C_OP_WRITE_P(op)) { 292 /* Write data */ 293 b = buf; 294 if (len > 0) 295 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 296 VIAPM_SMB_HD0, b[0]); 297 if (len > 1) 298 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 299 VIAPM_SMB_HD1, b[1]); 300 } 301 302 /* Set SMBus command */ 303 if (len == 0) 304 ctl = VIAPM_SMB_HC_CMD_BYTE; 305 else if (len == 1) 306 ctl = VIAPM_SMB_HC_CMD_BDATA; 307 else if (len == 2) 308 ctl = VIAPM_SMB_HC_CMD_WDATA; 309 310 if ((flags & I2C_F_POLL) == 0) 311 ctl |= VIAPM_SMB_HC_INTREN; 312 313 /* Start transaction */ 314 ctl |= VIAPM_SMB_HC_START; 315 bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC, ctl); 316 317 if (flags & I2C_F_POLL) { 318 /* Poll for completion */ 319 DELAY(VIAPM_DELAY); 320 for (retries = 1000; retries > 0; retries--) { 321 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 322 VIAPM_SMB_HS); 323 if ((st & VIAPM_SMB_HS_BUSY) == 0) 324 break; 325 DELAY(VIAPM_DELAY); 326 } 327 if (st & VIAPM_SMB_HS_BUSY) 328 goto timeout; 329 viapm_intr(sc); 330 } else { 331 /* Wait for interrupt */ 332 if (tsleep(sc, PRIBIO, "iicexec", VIAPM_TIMEOUT * hz)) 333 goto timeout; 334 } 335 336 if (sc->sc_i2c_xfer.error) 337 return (1); 338 339 return (0); 340 341 timeout: 342 /* 343 * Transfer timeout. Kill the transaction and clear status bits. 344 */ 345 printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st, 346 VIAPM_SMB_HS_BITS); 347 bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC, 348 VIAPM_SMB_HC_KILL); 349 DELAY(VIAPM_DELAY); 350 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS); 351 if ((st & VIAPM_SMB_HS_FAILED) == 0) 352 printf("%s: transaction abort failed, status 0x%b\n", 353 sc->sc_dev.dv_xname, st, VIAPM_SMB_HS_BITS); 354 bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st); 355 return (1); 356 } 357 358 int 359 viapm_intr(void *arg) 360 { 361 struct viapm_softc *sc = arg; 362 u_int8_t st; 363 u_int8_t *b; 364 size_t len; 365 366 /* Read status */ 367 st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS); 368 if ((st & VIAPM_SMB_HS_BUSY) != 0 || (st & (VIAPM_SMB_HS_INTR | 369 VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR | 370 VIAPM_SMB_HS_FAILED)) == 0) 371 /* Interrupt was not for us */ 372 return (0); 373 374 DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st, 375 VIAPM_SMB_HS_BITS)); 376 377 /* Clear status bits */ 378 bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st); 379 380 /* Check for errors */ 381 if (st & (VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR | 382 VIAPM_SMB_HS_FAILED)) { 383 sc->sc_i2c_xfer.error = 1; 384 goto done; 385 } 386 387 if (st & VIAPM_SMB_HS_INTR) { 388 if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op)) 389 goto done; 390 391 /* Read data */ 392 b = sc->sc_i2c_xfer.buf; 393 len = sc->sc_i2c_xfer.len; 394 if (len > 0) 395 b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 396 VIAPM_SMB_HD0); 397 if (len > 1) 398 b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, 399 VIAPM_SMB_HD1); 400 } 401 402 done: 403 if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0) 404 wakeup(sc); 405 return (1); 406 } 407