nfsmb.c revision 1.23
1/* $NetBSD: nfsmb.c,v 1.23 2012/02/14 15:08:07 pgoyette 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.23 2012/02/14 15:08:07 pgoyette 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/mutex.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 device_t sc_nfsmb[2]; 65}; 66 67struct nfsmb_softc { 68 device_t sc_dev; 69 int sc_num; 70 device_t 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 kmutex_t sc_mutex; 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 case PCI_PRODUCT_NVIDIA_MCP78S_SMB: 128 case PCI_PRODUCT_NVIDIA_MCP79_SMB: 129 return 1; 130 } 131 } 132 133 return 0; 134} 135 136static void 137nfsmbc_attach(device_t parent, device_t self, void *aux) 138{ 139 struct nfsmbc_softc *sc = device_private(self); 140 struct pci_attach_args *pa = aux; 141 struct nfsmbc_attach_args nfsmbca; 142 pcireg_t reg; 143 int baseregs[2]; 144 145 pci_aprint_devinfo(pa, NULL); 146 147 sc->sc_dev = self; 148 sc->sc_pc = pa->pa_pc; 149 sc->sc_tag = pa->pa_tag; 150 sc->sc_pa = pa; 151 sc->sc_iot = pa->pa_iot; 152 153 nfsmbca.nfsmb_iot = sc->sc_iot; 154 155 switch (PCI_PRODUCT(pa->pa_id)) { 156 case PCI_PRODUCT_NVIDIA_NFORCE2_SMBUS: 157 case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMBUS: 158 case PCI_PRODUCT_NVIDIA_NFORCE3_SMBUS: 159 case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMBUS: 160 case PCI_PRODUCT_NVIDIA_NFORCE4_SMBUS: 161 baseregs[0] = NFORCE_OLD_SMB1; 162 baseregs[1] = NFORCE_OLD_SMB2; 163 break; 164 default: 165 baseregs[0] = NFORCE_SMB1; 166 baseregs[1] = NFORCE_SMB2; 167 break; 168 } 169 170 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[0]); 171 nfsmbca.nfsmb_num = 1; 172 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 173 sc->sc_nfsmb[0] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print); 174 175 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[1]); 176 nfsmbca.nfsmb_num = 2; 177 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 178 sc->sc_nfsmb[1] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print); 179 180 /* This driver is similar to an ISA bridge that doesn't 181 * need any special handling. So registering NULL handlers 182 * are sufficent. */ 183 if (!pmf_device_register(self, NULL, NULL)) 184 aprint_error_dev(self, "couldn't establish power handler\n"); 185} 186 187static int 188nfsmbc_print(void *aux, const char *pnp) 189{ 190 struct nfsmbc_attach_args *nfsmbcap = aux; 191 192 if (pnp) 193 aprint_normal("nfsmb SMBus %d at %s", 194 nfsmbcap->nfsmb_num, pnp); 195 else 196 aprint_normal(" SMBus %d", nfsmbcap->nfsmb_num); 197 return UNCONF; 198} 199 200 201CFATTACH_DECL_NEW(nfsmb, sizeof(struct nfsmb_softc), 202 nfsmb_match, nfsmb_attach, NULL, NULL); 203 204static int 205nfsmb_match(device_t parent, cfdata_t match, void *aux) 206{ 207 struct nfsmbc_attach_args *nfsmbcap = aux; 208 209 if (nfsmbcap->nfsmb_num == 1 || nfsmbcap->nfsmb_num == 2) 210 return 1; 211 return 0; 212} 213 214static void 215nfsmb_attach(device_t parent, device_t self, void *aux) 216{ 217 struct nfsmb_softc *sc = device_private(self); 218 struct nfsmbc_attach_args *nfsmbcap = aux; 219 struct i2cbus_attach_args iba; 220 221 aprint_naive("\n"); 222 aprint_normal("\n"); 223 224 sc->sc_dev = self; 225 sc->sc_nfsmbc = parent; 226 sc->sc_num = nfsmbcap->nfsmb_num; 227 sc->sc_iot = nfsmbcap->nfsmb_iot; 228 229 /* register with iic */ 230 sc->sc_i2c.ic_cookie = sc; 231 sc->sc_i2c.ic_acquire_bus = nfsmb_acquire_bus; 232 sc->sc_i2c.ic_release_bus = nfsmb_release_bus; 233 sc->sc_i2c.ic_send_start = NULL; 234 sc->sc_i2c.ic_send_stop = NULL; 235 sc->sc_i2c.ic_initiate_xfer = NULL; 236 sc->sc_i2c.ic_read_byte = NULL; 237 sc->sc_i2c.ic_write_byte = NULL; 238 sc->sc_i2c.ic_exec = nfsmb_exec; 239 240 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); 241 242 if (bus_space_map(sc->sc_iot, nfsmbcap->nfsmb_addr, NFORCE_SMBSIZE, 0, 243 &sc->sc_ioh) != 0) { 244 aprint_error_dev(self, "failed to map SMBus space\n"); 245 return; 246 } 247 248 iba.iba_type = I2C_TYPE_SMBUS; 249 iba.iba_tag = &sc->sc_i2c; 250 (void) config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print); 251 252 /* This driver is similar to an ISA bridge that doesn't 253 * need any special handling. So registering NULL handlers 254 * are sufficent. */ 255 if (!pmf_device_register(self, NULL, NULL)) 256 aprint_error_dev(self, "couldn't establish power handler\n"); 257} 258 259static int 260nfsmb_acquire_bus(void *cookie, int flags) 261{ 262 struct nfsmb_softc *sc = cookie; 263 264 mutex_enter(&sc->sc_mutex); 265 return 0; 266} 267 268static void 269nfsmb_release_bus(void *cookie, int flags) 270{ 271 struct nfsmb_softc *sc = cookie; 272 273 mutex_exit(&sc->sc_mutex); 274} 275 276static int 277nfsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 278 size_t cmdlen, void *vbuf, size_t buflen, int flags) 279{ 280 struct nfsmb_softc *sc = (struct nfsmb_softc *)cookie; 281 uint8_t *p = vbuf; 282 int rv; 283 284 if ((cmdlen == 0) && (buflen == 0)) { 285 return nfsmb_quick(sc, addr, op, flags); 286 } 287 288 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 289 rv = nfsmb_receive_1(sc, addr, op, flags); 290 if (rv == -1) 291 return -1; 292 *p = (uint8_t)rv; 293 return 0; 294 } 295 296 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 297 rv = nfsmb_read_1(sc, *(const uint8_t*)cmd, addr, op, flags); 298 if (rv == -1) 299 return -1; 300 *p = (uint8_t)rv; 301 return 0; 302 } 303 304 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { 305 rv = nfsmb_read_2(sc, *(const uint8_t*)cmd, addr, op, flags); 306 if (rv == -1) 307 return -1; 308 *(uint16_t *)p = (uint16_t)rv; 309 return 0; 310 } 311 312 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 313 return nfsmb_send_1(sc, *(uint8_t*)vbuf, addr, op, flags); 314 315 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 316 return nfsmb_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf, 317 addr, op, flags); 318 319 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) 320 return nfsmb_write_2(sc, 321 *(const uint8_t*)cmd, *((uint16_t *)vbuf), addr, op, flags); 322 323 return -1; 324} 325 326static int 327nfsmb_check_done(struct nfsmb_softc *sc) 328{ 329 int us; 330 uint8_t stat; 331 332 us = 10 * 1000; /* XXXX: wait maximum 10 msec */ 333 do { 334 delay(10); 335 us -= 10; 336 if (us <= 0) 337 return -1; 338 } while (bus_space_read_1(sc->sc_iot, sc->sc_ioh, 339 NFORCE_SMB_PROTOCOL) != 0); 340 341 stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_STATUS); 342 if ((stat & NFORCE_SMB_STATUS_DONE) && 343 !(stat & NFORCE_SMB_STATUS_STATUS)) 344 return 0; 345 return -1; 346} 347 348/* ARGSUSED */ 349static int 350nfsmb_quick(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 351{ 352 uint8_t data; 353 354 /* write smbus slave address to register */ 355 data = addr << 1; 356 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 357 358 /* write smbus protocol to register */ 359 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_QUICK; 360 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 361 362 return nfsmb_check_done(sc); 363} 364 365/* ARGSUSED */ 366static int 367nfsmb_send_1(struct nfsmb_softc *sc, uint8_t val, i2c_addr_t addr, i2c_op_t op, 368 int flags) 369{ 370 uint8_t data; 371 372 /* store cmd */ 373 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, val); 374 375 /* write smbus slave address to register */ 376 data = addr << 1; 377 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 378 379 /* write smbus protocol to register */ 380 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 381 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 382 383 return nfsmb_check_done(sc); 384} 385 386/* ARGSUSED */ 387static int 388nfsmb_write_1(struct nfsmb_softc *sc, uint8_t cmd, uint8_t val, i2c_addr_t addr, 389 i2c_op_t op, int flags) 390{ 391 uint8_t data; 392 393 /* store cmd */ 394 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 395 396 /* store data */ 397 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, val); 398 399 /* write smbus slave address to register */ 400 data = addr << 1; 401 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 402 403 /* write smbus protocol to register */ 404 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 405 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 406 407 return nfsmb_check_done(sc); 408} 409 410static int 411nfsmb_write_2(struct nfsmb_softc *sc, uint8_t cmd, uint16_t val, 412 i2c_addr_t addr, i2c_op_t op, int flags) 413{ 414 uint8_t data, low, high; 415 416 /* store cmd */ 417 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 418 419 /* store data */ 420 low = val; 421 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, low); 422 high = val >> 8; 423 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA + 1, high); 424 425 /* write smbus slave address to register */ 426 data = addr << 1; 427 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 428 429 /* write smbus protocol to register */ 430 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 431 if (flags & I2C_F_PEC) 432 data |= NFORCE_SMB_PROTOCOL_PEC; 433 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 434 435 return nfsmb_check_done(sc); 436} 437 438/* ARGSUSED */ 439static int 440nfsmb_receive_1(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 441{ 442 uint8_t data; 443 444 /* write smbus slave address to register */ 445 data = addr << 1; 446 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 447 448 /* write smbus protocol to register */ 449 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 450 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 451 452 /* check for errors */ 453 if (nfsmb_check_done(sc) < 0) 454 return -1; 455 456 /* read data */ 457 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 458} 459 460/* ARGSUSED */ 461static int 462nfsmb_read_1(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 463 int flags) 464{ 465 uint8_t data; 466 467 /* store cmd */ 468 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 469 470 /* write smbus slave address to register */ 471 data = addr << 1; 472 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 473 474 /* write smbus protocol to register */ 475 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 476 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 477 478 /* check for errors */ 479 if (nfsmb_check_done(sc) < 0) 480 return -1; 481 482 /* read data */ 483 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 484} 485 486static int 487nfsmb_read_2(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 488 int flags) 489{ 490 uint8_t data, low, high; 491 492 /* store cmd */ 493 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 494 495 /* write smbus slave address to register */ 496 data = addr << 1; 497 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 498 499 /* write smbus protocol to register */ 500 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 501 if (flags & I2C_F_PEC) 502 data |= NFORCE_SMB_PROTOCOL_PEC; 503 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 504 505 /* check for errors */ 506 if (nfsmb_check_done(sc) < 0) 507 return -1; 508 509 /* read data */ 510 low = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 511 high = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA + 1); 512 return low | high << 8; 513} 514