nfsmb.c revision 1.17
1/* $NetBSD: nfsmb.c,v 1.17 2009/05/06 09:25:16 cegger Exp $ */ 2/* 3 * Copyright (c) 2007 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 * 27 */ 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: nfsmb.c,v 1.17 2009/05/06 09:25:16 cegger Exp $"); 30 31#include <sys/param.h> 32#include <sys/device.h> 33#include <sys/errno.h> 34#include <sys/kernel.h> 35#include <sys/rwlock.h> 36#include <sys/proc.h> 37 38#include <sys/bus.h> 39 40#include <dev/i2c/i2cvar.h> 41 42#include <dev/pci/pcivar.h> 43#include <dev/pci/pcireg.h> 44#include <dev/pci/pcidevs.h> 45 46#include <dev/pci/nfsmbreg.h> 47 48 49struct nfsmbc_attach_args { 50 int nfsmb_num; 51 bus_space_tag_t nfsmb_iot; 52 int nfsmb_addr; 53}; 54 55struct nfsmb_softc; 56struct nfsmbc_softc { 57 device_t sc_dev; 58 59 pci_chipset_tag_t sc_pc; 60 pcitag_t sc_tag; 61 struct pci_attach_args *sc_pa; 62 63 bus_space_tag_t sc_iot; 64 struct device *sc_nfsmb[2]; 65}; 66 67struct nfsmb_softc { 68 device_t sc_dev; 69 int sc_num; 70 struct device *sc_nfsmbc; 71 72 bus_space_tag_t sc_iot; 73 bus_space_handle_t sc_ioh; 74 75 struct i2c_controller sc_i2c; /* i2c controller info */ 76 krwlock_t sc_rwlock; 77}; 78 79 80static int nfsmbc_match(device_t, cfdata_t, void *); 81static void nfsmbc_attach(device_t, device_t, void *); 82static int nfsmbc_print(void *, const char *); 83 84static int nfsmb_match(device_t, cfdata_t, void *); 85static void nfsmb_attach(device_t, device_t, void *); 86static int nfsmb_acquire_bus(void *, int); 87static void nfsmb_release_bus(void *, int); 88static int nfsmb_exec( 89 void *, i2c_op_t, i2c_addr_t, const void *, size_t, void *, size_t, int); 90static int nfsmb_check_done(struct nfsmb_softc *); 91static int 92 nfsmb_send_1(struct nfsmb_softc *, uint8_t, i2c_addr_t, i2c_op_t, int); 93static int nfsmb_write_1( 94 struct nfsmb_softc *, uint8_t, uint8_t, i2c_addr_t, i2c_op_t, int); 95static int nfsmb_write_2( 96 struct nfsmb_softc *, uint8_t, uint16_t, i2c_addr_t, i2c_op_t, int); 97static int nfsmb_receive_1(struct nfsmb_softc *, i2c_addr_t, i2c_op_t, int); 98static int 99 nfsmb_read_1(struct nfsmb_softc *, uint8_t, i2c_addr_t, i2c_op_t, int); 100static int 101 nfsmb_read_2(struct nfsmb_softc *, uint8_t, i2c_addr_t, i2c_op_t, int); 102static int 103 nfsmb_quick(struct nfsmb_softc *, i2c_addr_t, i2c_op_t, int); 104 105CFATTACH_DECL_NEW(nfsmbc, sizeof(struct nfsmbc_softc), 106 nfsmbc_match, nfsmbc_attach, NULL, NULL); 107 108static int 109nfsmbc_match(device_t parent, cfdata_t match, void *aux) 110{ 111 struct pci_attach_args *pa = aux; 112 113 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA) { 114 switch (PCI_PRODUCT(pa->pa_id)) { 115 case PCI_PRODUCT_NVIDIA_NFORCE2_SMBUS: 116 case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMBUS: 117 case PCI_PRODUCT_NVIDIA_NFORCE3_SMBUS: 118 case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMBUS: 119 case PCI_PRODUCT_NVIDIA_NFORCE4_SMBUS: 120 case PCI_PRODUCT_NVIDIA_NFORCE430_SMBUS: 121 case PCI_PRODUCT_NVIDIA_MCP04_SMBUS: 122 case PCI_PRODUCT_NVIDIA_MCP55_SMB: 123 case PCI_PRODUCT_NVIDIA_MCP61_SMB: 124 case PCI_PRODUCT_NVIDIA_MCP65_SMB: 125 case PCI_PRODUCT_NVIDIA_MCP67_SMB: 126 case PCI_PRODUCT_NVIDIA_MCP73_SMB: 127 return 1; 128 } 129 } 130 131 return 0; 132} 133 134static void 135nfsmbc_attach(device_t parent, device_t self, void *aux) 136{ 137 struct nfsmbc_softc *sc = device_private(self); 138 struct pci_attach_args *pa = aux; 139 struct nfsmbc_attach_args nfsmbca; 140 pcireg_t reg; 141 int baseregs[2]; 142 char devinfo[256]; 143 144 aprint_naive("\n"); 145 pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo)); 146 aprint_normal(": %s (rev. 0x%02x)\n", devinfo, 147 PCI_REVISION(pa->pa_class)); 148 149 sc->sc_dev = self; 150 sc->sc_pc = pa->pa_pc; 151 sc->sc_tag = pa->pa_tag; 152 sc->sc_pa = pa; 153 sc->sc_iot = pa->pa_iot; 154 155 nfsmbca.nfsmb_iot = sc->sc_iot; 156 157 switch (PCI_PRODUCT(pa->pa_id)) { 158 case PCI_PRODUCT_NVIDIA_NFORCE2_SMBUS: 159 case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMBUS: 160 case PCI_PRODUCT_NVIDIA_NFORCE3_SMBUS: 161 case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMBUS: 162 case PCI_PRODUCT_NVIDIA_NFORCE4_SMBUS: 163 baseregs[0] = NFORCE_OLD_SMB1; 164 baseregs[1] = NFORCE_OLD_SMB2; 165 break; 166 default: 167 baseregs[0] = NFORCE_SMB1; 168 baseregs[1] = NFORCE_SMB2; 169 break; 170 } 171 172 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[0]); 173 nfsmbca.nfsmb_num = 1; 174 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 175 sc->sc_nfsmb[0] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print); 176 177 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[1]); 178 nfsmbca.nfsmb_num = 2; 179 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 180 sc->sc_nfsmb[1] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print); 181 182 /* This driver is similar to an ISA bridge that doesn't 183 * need any special handling. So registering NULL handlers 184 * are sufficent. */ 185 if (!pmf_device_register(self, NULL, NULL)) 186 aprint_error_dev(self, "couldn't establish power handler\n"); 187} 188 189static int 190nfsmbc_print(void *aux, const char *pnp) 191{ 192 struct nfsmbc_attach_args *nfsmbcap = aux; 193 194 if (pnp) 195 aprint_normal("nfsmb SMBus %d at %s", 196 nfsmbcap->nfsmb_num, pnp); 197 else 198 aprint_normal(" SMBus %d", nfsmbcap->nfsmb_num); 199 return UNCONF; 200} 201 202 203CFATTACH_DECL_NEW(nfsmb, sizeof(struct nfsmb_softc), 204 nfsmb_match, nfsmb_attach, NULL, NULL); 205 206static int 207nfsmb_match(device_t parent, cfdata_t match, void *aux) 208{ 209 struct nfsmbc_attach_args *nfsmbcap = aux; 210 211 if (nfsmbcap->nfsmb_num == 1 || nfsmbcap->nfsmb_num == 2) 212 return 1; 213 return 0; 214} 215 216static void 217nfsmb_attach(device_t parent, device_t self, void *aux) 218{ 219 struct nfsmb_softc *sc = device_private(self); 220 struct nfsmbc_attach_args *nfsmbcap = aux; 221 struct i2cbus_attach_args iba; 222 223 aprint_naive("\n"); 224 aprint_normal("\n"); 225 226 sc->sc_dev = self; 227 sc->sc_nfsmbc = parent; 228 sc->sc_num = nfsmbcap->nfsmb_num; 229 sc->sc_iot = nfsmbcap->nfsmb_iot; 230 231 /* register with iic */ 232 sc->sc_i2c.ic_cookie = sc; 233 sc->sc_i2c.ic_acquire_bus = nfsmb_acquire_bus; 234 sc->sc_i2c.ic_release_bus = nfsmb_release_bus; 235 sc->sc_i2c.ic_send_start = NULL; 236 sc->sc_i2c.ic_send_stop = NULL; 237 sc->sc_i2c.ic_initiate_xfer = NULL; 238 sc->sc_i2c.ic_read_byte = NULL; 239 sc->sc_i2c.ic_write_byte = NULL; 240 sc->sc_i2c.ic_exec = nfsmb_exec; 241 242 rw_init(&sc->sc_rwlock); 243 244 if (bus_space_map(sc->sc_iot, nfsmbcap->nfsmb_addr, NFORCE_SMBSIZE, 0, 245 &sc->sc_ioh) != 0) { 246 aprint_error_dev(self, "failed to map SMBus space\n"); 247 return; 248 } 249 250 iba.iba_type = I2C_TYPE_SMBUS; 251 iba.iba_tag = &sc->sc_i2c; 252 (void) config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print); 253 254 /* This driver is similar to an ISA bridge that doesn't 255 * need any special handling. So registering NULL handlers 256 * are sufficent. */ 257 if (!pmf_device_register(self, NULL, NULL)) 258 aprint_error_dev(self, "couldn't establish power handler\n"); 259} 260 261static int 262nfsmb_acquire_bus(void *cookie, int flags) 263{ 264 struct nfsmb_softc *sc = cookie; 265 266 rw_enter(&sc->sc_rwlock, RW_WRITER); 267 return 0; 268} 269 270static void 271nfsmb_release_bus(void *cookie, int flags) 272{ 273 struct nfsmb_softc *sc = cookie; 274 275 rw_exit(&sc->sc_rwlock); 276} 277 278static int 279nfsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 280 size_t cmdlen, void *vbuf, size_t buflen, int flags) 281{ 282 struct nfsmb_softc *sc = (struct nfsmb_softc *)cookie; 283 uint8_t *p = vbuf; 284 int rv; 285 286 if ((cmdlen == 0) && (buflen == 0)) { 287 return nfsmb_quick(sc, addr, op, flags); 288 } 289 290 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 291 rv = nfsmb_receive_1(sc, addr, op, flags); 292 if (rv == -1) 293 return -1; 294 *p = (uint8_t)rv; 295 return 0; 296 } 297 298 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 299 rv = nfsmb_read_1(sc, *(const uint8_t*)cmd, addr, op, flags); 300 if (rv == -1) 301 return -1; 302 *p = (uint8_t)rv; 303 return 0; 304 } 305 306 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { 307 rv = nfsmb_read_2(sc, *(const uint8_t*)cmd, addr, op, flags); 308 if (rv == -1) 309 return -1; 310 *(uint16_t *)p = (uint16_t)rv; 311 return 0; 312 } 313 314 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 315 return nfsmb_send_1(sc, *(uint8_t*)vbuf, addr, op, flags); 316 317 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 318 return nfsmb_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf, 319 addr, op, flags); 320 321 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) 322 return nfsmb_write_2(sc, 323 *(const uint8_t*)cmd, *((uint16_t *)vbuf), addr, op, flags); 324 325 return -1; 326} 327 328static int 329nfsmb_check_done(struct nfsmb_softc *sc) 330{ 331 int us; 332 uint8_t stat; 333 334 us = 10 * 1000; /* XXXX: wait maximum 10 msec */ 335 do { 336 delay(10); 337 us -= 10; 338 if (us <= 0) 339 return -1; 340 } while (bus_space_read_1(sc->sc_iot, sc->sc_ioh, 341 NFORCE_SMB_PROTOCOL) != 0); 342 343 stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_STATUS); 344 if ((stat & NFORCE_SMB_STATUS_DONE) && 345 !(stat & NFORCE_SMB_STATUS_STATUS)) 346 return 0; 347 return -1; 348} 349 350/* ARGSUSED */ 351static int 352nfsmb_quick(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 353{ 354 uint8_t data; 355 356 /* write smbus slave address to register */ 357 data = addr << 1; 358 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 359 360 /* write smbus protocol to register */ 361 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_QUICK; 362 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 363 364 return nfsmb_check_done(sc); 365} 366 367/* ARGSUSED */ 368static int 369nfsmb_send_1(struct nfsmb_softc *sc, uint8_t val, i2c_addr_t addr, i2c_op_t op, 370 int flags) 371{ 372 uint8_t data; 373 374 /* store cmd */ 375 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, val); 376 377 /* write smbus slave address to register */ 378 data = addr << 1; 379 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 380 381 /* write smbus protocol to register */ 382 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 383 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 384 385 return nfsmb_check_done(sc); 386} 387 388/* ARGSUSED */ 389static int 390nfsmb_write_1(struct nfsmb_softc *sc, uint8_t cmd, uint8_t val, i2c_addr_t addr, 391 i2c_op_t op, int flags) 392{ 393 uint8_t data; 394 395 /* store cmd */ 396 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 397 398 /* store data */ 399 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, val); 400 401 /* write smbus slave address to register */ 402 data = addr << 1; 403 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 404 405 /* write smbus protocol to register */ 406 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 407 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 408 409 return nfsmb_check_done(sc); 410} 411 412static int 413nfsmb_write_2(struct nfsmb_softc *sc, uint8_t cmd, uint16_t val, 414 i2c_addr_t addr, i2c_op_t op, int flags) 415{ 416 uint8_t data, low, high; 417 418 /* store cmd */ 419 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 420 421 /* store data */ 422 low = val; 423 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, low); 424 high = val >> 8; 425 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, high); 426 427 /* write smbus slave address to register */ 428 data = addr << 1; 429 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 430 431 /* write smbus protocol to register */ 432 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 433 if (flags & I2C_F_PEC) 434 data |= NFORCE_SMB_PROTOCOL_PEC; 435 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 436 437 return nfsmb_check_done(sc); 438} 439 440/* ARGSUSED */ 441static int 442nfsmb_receive_1(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 443{ 444 uint8_t data; 445 446 /* write smbus slave address to register */ 447 data = addr << 1; 448 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 449 450 /* write smbus protocol to register */ 451 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 452 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 453 454 /* check for errors */ 455 if (nfsmb_check_done(sc) < 0) 456 return -1; 457 458 /* read data */ 459 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 460} 461 462/* ARGSUSED */ 463static int 464nfsmb_read_1(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 465 int flags) 466{ 467 uint8_t data; 468 469 /* store cmd */ 470 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 471 472 /* write smbus slave address to register */ 473 data = addr << 1; 474 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 475 476 /* write smbus protocol to register */ 477 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 478 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 479 480 /* check for errors */ 481 if (nfsmb_check_done(sc) < 0) 482 return -1; 483 484 /* read data */ 485 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 486} 487 488static int 489nfsmb_read_2(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 490 int flags) 491{ 492 uint8_t data, low, high; 493 494 /* store cmd */ 495 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 496 497 /* write smbus slave address to register */ 498 data = addr << 1; 499 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 500 501 /* write smbus protocol to register */ 502 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 503 if (flags & I2C_F_PEC) 504 data |= NFORCE_SMB_PROTOCOL_PEC; 505 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 506 507 /* check for errors */ 508 if (nfsmb_check_done(sc) < 0) 509 return -1; 510 511 /* read data */ 512 low = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 513 high = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 514 return low | high << 8; 515} 516