nfsmb.c revision 1.3
1/* $NetBSD: nfsmb.c,v 1.3 2007/07/28 12:31:50 kiyohara 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.3 2007/07/28 12:31:50 kiyohara 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/lock.h> 36#include <sys/proc.h> 37 38#include <machine/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 struct device 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 struct device 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 struct lock sc_lock; 77}; 78 79 80static int nfsmbc_match(struct device *, struct cfdata *, void *); 81static void nfsmbc_attach(struct device *, struct device *, void *); 82static int nfsmbc_print(void *, const char *); 83 84static int nfsmb_match(struct device *, struct cfdata *, void *); 85static void nfsmb_attach(struct device *, struct device *, 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); 102 103 104CFATTACH_DECL(nfsmbc, sizeof(struct nfsmbc_softc), 105 nfsmbc_match, nfsmbc_attach, NULL, NULL); 106 107static int 108nfsmbc_match(struct device *parent, struct cfdata *match, void *aux) 109{ 110 struct pci_attach_args *pa = aux; 111 112 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA) { 113 switch (PCI_PRODUCT(pa->pa_id)) { 114 case PCI_PRODUCT_NVIDIA_NFORCE2_SMBUS: 115 case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMBUS: 116 case PCI_PRODUCT_NVIDIA_NFORCE3_SMBUS: 117 case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMBUS: 118 case PCI_PRODUCT_NVIDIA_NFORCE4_SMBUS: 119 case PCI_PRODUCT_NVIDIA_MCP04_SMBUS: 120 return 1; 121 } 122 } 123 124 return 0; 125} 126 127static void 128nfsmbc_attach(struct device *parent, struct device *self, void *aux) 129{ 130 struct nfsmbc_softc *sc = (struct nfsmbc_softc *) self; 131 struct pci_attach_args *pa = aux; 132 struct nfsmbc_attach_args nfsmbca; 133 pcireg_t reg; 134 char devinfo[256]; 135 136 aprint_naive("\n"); 137 pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo)); 138 aprint_normal(": %s (rev. 0x%02x)\n", devinfo, 139 PCI_REVISION(pa->pa_class)); 140 141 sc->sc_pc = pa->pa_pc; 142 sc->sc_tag = pa->pa_tag; 143 sc->sc_pa = pa; 144 sc->sc_iot = pa->pa_iot; 145 146 nfsmbca.nfsmb_iot = sc->sc_iot; 147 148 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, NFORCE_SMB1); 149 nfsmbca.nfsmb_num = 1; 150 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 151 sc->sc_nfsmb[0] = config_found(&sc->sc_dev, &nfsmbca, nfsmbc_print); 152 153 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, NFORCE_SMB2); 154 nfsmbca.nfsmb_num = 2; 155 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 156 sc->sc_nfsmb[1] = config_found(&sc->sc_dev, &nfsmbca, nfsmbc_print); 157} 158 159static int 160nfsmbc_print(void *aux, const char *pnp) 161{ 162 struct nfsmbc_attach_args *nfsmbcap = aux; 163 164 if (pnp) 165 aprint_normal("nfsmb SMBus %d at %s", 166 nfsmbcap->nfsmb_num, pnp); 167 else 168 aprint_normal(" SMBus %d", nfsmbcap->nfsmb_num); 169 return UNCONF; 170} 171 172 173CFATTACH_DECL(nfsmb, sizeof(struct nfsmb_softc), 174 nfsmb_match, nfsmb_attach, NULL, NULL); 175 176static int 177nfsmb_match(struct device *parent, struct cfdata *match, void *aux) 178{ 179 struct nfsmbc_attach_args *nfsmbcap = aux; 180 181 if (nfsmbcap->nfsmb_num == 1 || nfsmbcap->nfsmb_num == 2) 182 return 1; 183 return 0; 184} 185 186static void 187nfsmb_attach(struct device *parent, struct device *self, void *aux) 188{ 189 struct nfsmb_softc *sc = (struct nfsmb_softc *) self; 190 struct nfsmbc_attach_args *nfsmbcap = aux; 191 struct i2cbus_attach_args iba; 192 193 aprint_naive("\n"); 194 aprint_normal("\n"); 195 196 sc->sc_nfsmbc = parent; 197 sc->sc_num = nfsmbcap->nfsmb_num; 198 sc->sc_iot = nfsmbcap->nfsmb_iot; 199 200 /* register with iic */ 201 sc->sc_i2c.ic_cookie = sc; 202 sc->sc_i2c.ic_acquire_bus = nfsmb_acquire_bus; 203 sc->sc_i2c.ic_release_bus = nfsmb_release_bus; 204 sc->sc_i2c.ic_send_start = NULL; 205 sc->sc_i2c.ic_send_stop = NULL; 206 sc->sc_i2c.ic_initiate_xfer = NULL; 207 sc->sc_i2c.ic_read_byte = NULL; 208 sc->sc_i2c.ic_write_byte = NULL; 209 sc->sc_i2c.ic_exec = nfsmb_exec; 210 211 lockinit(&sc->sc_lock, PZERO, "nfsmb", 0, 0); 212 213 if (bus_space_map(sc->sc_iot, nfsmbcap->nfsmb_addr, NFORCE_SMBSIZE, 0, 214 &sc->sc_ioh) != 0) { 215 aprint_error("%s: failed to map SMBus space\n", 216 sc->sc_dev.dv_xname); 217 return; 218 } 219 220 iba.iba_tag = &sc->sc_i2c; 221 (void) config_found_ia(&sc->sc_dev, "i2cbus", &iba, iicbus_print); 222} 223 224static int 225nfsmb_acquire_bus(void *cookie, int flags) 226{ 227 struct nfsmb_softc *sc = cookie; 228 int err; 229 230 err = lockmgr(&sc->sc_lock, LK_EXCLUSIVE, NULL); 231 232 return err; 233} 234 235static void 236nfsmb_release_bus(void *cookie, int flags) 237{ 238 struct nfsmb_softc *sc = cookie; 239 240 lockmgr(&sc->sc_lock, LK_RELEASE, NULL); 241 242 return; 243} 244 245static int 246nfsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 247 size_t cmdlen, void *vbuf, size_t buflen, int flags) 248{ 249 struct nfsmb_softc *sc = (struct nfsmb_softc *)cookie; 250 uint8_t *p = vbuf; 251 int rv; 252 253 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 254 rv = nfsmb_receive_1(sc, addr, op, flags); 255 if (rv == -1) 256 return -1; 257 *p = (uint8_t)rv; 258 return 0; 259 } 260 261 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 262 rv = nfsmb_read_1(sc, *(const uint8_t*)cmd, addr, op, flags); 263 if (rv == -1) 264 return -1; 265 *p = (uint8_t)rv; 266 return 0; 267 } 268 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { 269 rv = nfsmb_read_2(sc, *(const uint8_t*)cmd, addr, op, flags); 270 if (rv == -1) 271 return -1; 272 *p = (uint8_t)rv; 273 return 0; 274 } 275 276 277 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 278 return nfsmb_send_1(sc, *(uint8_t*)vbuf, addr, op, flags); 279 280 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 281 return nfsmb_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf, 282 addr, op, flags); 283 284 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) 285 return nfsmb_write_2(sc, 286 *(const uint8_t*)cmd, *((uint16_t *)vbuf), addr, op, flags); 287 288 return (-1); 289} 290 291static int 292nfsmb_check_done(struct nfsmb_softc *sc) 293{ 294 int us; 295 uint8_t stat; 296 297 us = 10 * 1000; /* XXXX: wait maximum 10 msec */ 298 do { 299 delay(10); 300 us -= 10; 301 if (us <= 0) 302 return -1; 303 } while (bus_space_read_1(sc->sc_iot, sc->sc_ioh, 304 NFORCE_SMB_PROTOCOL) != 0); 305 306 stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_STATUS); 307 if ((stat & NFORCE_SMB_STATUS_DONE) && 308 !(stat & NFORCE_SMB_STATUS_STATUS)) 309 return 0; 310 return -1; 311} 312 313/* ARGSUSED */ 314static int 315nfsmb_send_1(struct nfsmb_softc *sc, uint8_t val, i2c_addr_t addr, i2c_op_t op, 316 int flags) 317{ 318 uint8_t data; 319 320 /* store cmd */ 321 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, val); 322 323 /* write smbus slave address to register */ 324 data = addr << 1; 325 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 326 327 /* write smbus protocol to register */ 328 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 329 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 330 331 return nfsmb_check_done(sc); 332} 333 334/* ARGSUSED */ 335static int 336nfsmb_write_1(struct nfsmb_softc *sc, uint8_t cmd, uint8_t val, i2c_addr_t addr, 337 i2c_op_t op, int flags) 338{ 339 uint8_t data; 340 341 /* store cmd */ 342 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 343 344 /* store data */ 345 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, val); 346 347 /* write smbus slave address to register */ 348 data = addr << 1; 349 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 350 351 /* write smbus protocol to register */ 352 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 353 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 354 355 return nfsmb_check_done(sc); 356} 357 358static int 359nfsmb_write_2(struct nfsmb_softc *sc, uint8_t cmd, uint16_t val, 360 i2c_addr_t addr, i2c_op_t op, int flags) 361{ 362 uint8_t data, low, high; 363 364 /* store cmd */ 365 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 366 367 /* store data */ 368 low = val; 369 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, low); 370 high = val >> 8; 371 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, high); 372 373 /* write smbus slave address to register */ 374 data = addr << 1; 375 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 376 377 /* write smbus protocol to register */ 378 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 379 if (flags & I2C_F_PEC) 380 data |= NFORCE_SMB_PROTOCOL_PEC; 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_receive_1(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 389{ 390 uint8_t data; 391 392 /* write smbus slave address to register */ 393 data = addr << 1; 394 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 395 396 /* write smbus protocol to register */ 397 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 398 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 399 400 /* check for errors */ 401 if (nfsmb_check_done(sc) < 0) 402 return -1; 403 404 /* read data */ 405 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 406} 407 408/* ARGSUSED */ 409static int 410nfsmb_read_1(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 411 int flags) 412{ 413 uint8_t data; 414 415 /* store cmd */ 416 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 417 418 /* write smbus slave address to register */ 419 data = addr << 1; 420 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 421 422 /* write smbus protocol to register */ 423 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 424 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 425 426 /* check for errors */ 427 if (nfsmb_check_done(sc) < 0) 428 return (-1); 429 430 /* read data */ 431 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 432} 433 434static int 435nfsmb_read_2(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 436 int flags) 437{ 438 uint8_t data, low, high; 439 440 /* store cmd */ 441 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 442 443 /* write smbus slave address to register */ 444 data = addr << 1; 445 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 446 447 /* write smbus protocol to register */ 448 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 449 if (flags & I2C_F_PEC) 450 data |= NFORCE_SMB_PROTOCOL_PEC; 451 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 452 453 /* check for errors */ 454 if (nfsmb_check_done(sc) < 0) 455 return (-1); 456 457 /* read data */ 458 low = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 459 high = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 460 return low | high << 8; 461} 462