nfsmb.c revision 1.25
1/* $NetBSD: nfsmb.c,v 1.25 2019/12/22 23:23:32 thorpej 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.25 2019/12/22 23:23:32 thorpej 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}; 77 78 79static int nfsmbc_match(device_t, cfdata_t, void *); 80static void nfsmbc_attach(device_t, device_t, void *); 81static int nfsmbc_print(void *, const char *); 82 83static int nfsmb_match(device_t, cfdata_t, void *); 84static void nfsmb_attach(device_t, device_t, void *); 85static int nfsmb_exec( 86 void *, i2c_op_t, i2c_addr_t, const void *, size_t, void *, size_t, int); 87static int nfsmb_check_done(struct nfsmb_softc *); 88static int 89 nfsmb_send_1(struct nfsmb_softc *, uint8_t, i2c_addr_t, i2c_op_t, int); 90static int nfsmb_write_1( 91 struct nfsmb_softc *, uint8_t, uint8_t, i2c_addr_t, i2c_op_t, int); 92static int nfsmb_write_2( 93 struct nfsmb_softc *, uint8_t, uint16_t, i2c_addr_t, i2c_op_t, int); 94static int nfsmb_receive_1(struct nfsmb_softc *, i2c_addr_t, i2c_op_t, int); 95static int 96 nfsmb_read_1(struct nfsmb_softc *, uint8_t, i2c_addr_t, i2c_op_t, int); 97static int 98 nfsmb_read_2(struct nfsmb_softc *, uint8_t, i2c_addr_t, i2c_op_t, int); 99static int 100 nfsmb_quick(struct nfsmb_softc *, i2c_addr_t, i2c_op_t, int); 101 102CFATTACH_DECL_NEW(nfsmbc, sizeof(struct nfsmbc_softc), 103 nfsmbc_match, nfsmbc_attach, NULL, NULL); 104 105static int 106nfsmbc_match(device_t parent, cfdata_t match, void *aux) 107{ 108 struct pci_attach_args *pa = aux; 109 110 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NVIDIA) { 111 switch (PCI_PRODUCT(pa->pa_id)) { 112 case PCI_PRODUCT_NVIDIA_NFORCE2_SMBUS: 113 case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMBUS: 114 case PCI_PRODUCT_NVIDIA_NFORCE3_SMBUS: 115 case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMBUS: 116 case PCI_PRODUCT_NVIDIA_NFORCE4_SMBUS: 117 case PCI_PRODUCT_NVIDIA_NFORCE430_SMBUS: 118 case PCI_PRODUCT_NVIDIA_MCP04_SMBUS: 119 case PCI_PRODUCT_NVIDIA_MCP55_SMB: 120 case PCI_PRODUCT_NVIDIA_MCP61_SMB: 121 case PCI_PRODUCT_NVIDIA_MCP65_SMB: 122 case PCI_PRODUCT_NVIDIA_MCP67_SMB: 123 case PCI_PRODUCT_NVIDIA_MCP73_SMB: 124 case PCI_PRODUCT_NVIDIA_MCP78S_SMB: 125 case PCI_PRODUCT_NVIDIA_MCP79_SMB: 126 return 1; 127 } 128 } 129 130 return 0; 131} 132 133static void 134nfsmbc_attach(device_t parent, device_t self, void *aux) 135{ 136 struct nfsmbc_softc *sc = device_private(self); 137 struct pci_attach_args *pa = aux; 138 struct nfsmbc_attach_args nfsmbca; 139 pcireg_t reg; 140 int baseregs[2]; 141 142 pci_aprint_devinfo(pa, NULL); 143 144 sc->sc_dev = self; 145 sc->sc_pc = pa->pa_pc; 146 sc->sc_tag = pa->pa_tag; 147 sc->sc_pa = pa; 148 sc->sc_iot = pa->pa_iot; 149 150 nfsmbca.nfsmb_iot = sc->sc_iot; 151 152 switch (PCI_PRODUCT(pa->pa_id)) { 153 case PCI_PRODUCT_NVIDIA_NFORCE2_SMBUS: 154 case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMBUS: 155 case PCI_PRODUCT_NVIDIA_NFORCE3_SMBUS: 156 case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMBUS: 157 case PCI_PRODUCT_NVIDIA_NFORCE4_SMBUS: 158 baseregs[0] = NFORCE_OLD_SMB1; 159 baseregs[1] = NFORCE_OLD_SMB2; 160 break; 161 default: 162 baseregs[0] = NFORCE_SMB1; 163 baseregs[1] = NFORCE_SMB2; 164 break; 165 } 166 167 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[0]); 168 nfsmbca.nfsmb_num = 1; 169 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 170 sc->sc_nfsmb[0] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print); 171 172 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[1]); 173 nfsmbca.nfsmb_num = 2; 174 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 175 sc->sc_nfsmb[1] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print); 176 177 /* This driver is similar to an ISA bridge that doesn't 178 * need any special handling. So registering NULL handlers 179 * are sufficent. */ 180 if (!pmf_device_register(self, NULL, NULL)) 181 aprint_error_dev(self, "couldn't establish power handler\n"); 182} 183 184static int 185nfsmbc_print(void *aux, const char *pnp) 186{ 187 struct nfsmbc_attach_args *nfsmbcap = aux; 188 189 if (pnp) 190 aprint_normal("nfsmb SMBus %d at %s", 191 nfsmbcap->nfsmb_num, pnp); 192 else 193 aprint_normal(" SMBus %d", nfsmbcap->nfsmb_num); 194 return UNCONF; 195} 196 197 198CFATTACH_DECL_NEW(nfsmb, sizeof(struct nfsmb_softc), 199 nfsmb_match, nfsmb_attach, NULL, NULL); 200 201static int 202nfsmb_match(device_t parent, cfdata_t match, void *aux) 203{ 204 struct nfsmbc_attach_args *nfsmbcap = aux; 205 206 if (nfsmbcap->nfsmb_num == 1 || nfsmbcap->nfsmb_num == 2) 207 return 1; 208 return 0; 209} 210 211static void 212nfsmb_attach(device_t parent, device_t self, void *aux) 213{ 214 struct nfsmb_softc *sc = device_private(self); 215 struct nfsmbc_attach_args *nfsmbcap = aux; 216 struct i2cbus_attach_args iba; 217 218 aprint_naive("\n"); 219 aprint_normal("\n"); 220 221 sc->sc_dev = self; 222 sc->sc_nfsmbc = parent; 223 sc->sc_num = nfsmbcap->nfsmb_num; 224 sc->sc_iot = nfsmbcap->nfsmb_iot; 225 226 /* register with iic */ 227 iic_tag_init(&sc->sc_i2c); 228 sc->sc_i2c.ic_cookie = sc; 229 sc->sc_i2c.ic_exec = nfsmb_exec; 230 231 if (bus_space_map(sc->sc_iot, nfsmbcap->nfsmb_addr, NFORCE_SMBSIZE, 0, 232 &sc->sc_ioh) != 0) { 233 aprint_error_dev(self, "failed to map SMBus space\n"); 234 return; 235 } 236 237 memset(&iba, 0, sizeof(iba)); 238 iba.iba_tag = &sc->sc_i2c; 239 (void) config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print); 240 241 /* This driver is similar to an ISA bridge that doesn't 242 * need any special handling. So registering NULL handlers 243 * are sufficent. */ 244 if (!pmf_device_register(self, NULL, NULL)) 245 aprint_error_dev(self, "couldn't establish power handler\n"); 246} 247 248static int 249nfsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 250 size_t cmdlen, void *vbuf, size_t buflen, int flags) 251{ 252 struct nfsmb_softc *sc = (struct nfsmb_softc *)cookie; 253 uint8_t *p = vbuf; 254 int rv; 255 256 if ((cmdlen == 0) && (buflen == 0)) { 257 return nfsmb_quick(sc, addr, op, flags); 258 } 259 260 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 261 rv = nfsmb_receive_1(sc, addr, op, flags); 262 if (rv == -1) 263 return -1; 264 *p = (uint8_t)rv; 265 return 0; 266 } 267 268 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 269 rv = nfsmb_read_1(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 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { 277 rv = nfsmb_read_2(sc, *(const uint8_t*)cmd, addr, op, flags); 278 if (rv == -1) 279 return -1; 280 *(uint16_t *)p = (uint16_t)rv; 281 return 0; 282 } 283 284 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 285 return nfsmb_send_1(sc, *(uint8_t*)vbuf, addr, op, flags); 286 287 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 288 return nfsmb_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf, 289 addr, op, flags); 290 291 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) 292 return nfsmb_write_2(sc, 293 *(const uint8_t*)cmd, *((uint16_t *)vbuf), addr, op, flags); 294 295 return -1; 296} 297 298static int 299nfsmb_check_done(struct nfsmb_softc *sc) 300{ 301 int us; 302 uint8_t stat; 303 304 us = 10 * 1000; /* XXXX: wait maximum 10 msec */ 305 do { 306 delay(10); 307 us -= 10; 308 if (us <= 0) 309 return -1; 310 } while (bus_space_read_1(sc->sc_iot, sc->sc_ioh, 311 NFORCE_SMB_PROTOCOL) != 0); 312 313 stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_STATUS); 314 if ((stat & NFORCE_SMB_STATUS_DONE) && 315 !(stat & NFORCE_SMB_STATUS_STATUS)) 316 return 0; 317 return -1; 318} 319 320/* ARGSUSED */ 321static int 322nfsmb_quick(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 323{ 324 uint8_t data; 325 326 /* write smbus slave address to register */ 327 data = addr << 1; 328 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 329 330 /* write smbus protocol to register */ 331 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_QUICK; 332 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 333 334 return nfsmb_check_done(sc); 335} 336 337/* ARGSUSED */ 338static int 339nfsmb_send_1(struct nfsmb_softc *sc, uint8_t val, i2c_addr_t addr, i2c_op_t op, 340 int flags) 341{ 342 uint8_t data; 343 344 /* store cmd */ 345 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, 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; 353 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 354 355 return nfsmb_check_done(sc); 356} 357 358/* ARGSUSED */ 359static int 360nfsmb_write_1(struct nfsmb_softc *sc, uint8_t cmd, uint8_t val, i2c_addr_t addr, 361 i2c_op_t op, int flags) 362{ 363 uint8_t data; 364 365 /* store cmd */ 366 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 367 368 /* store data */ 369 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, val); 370 371 /* write smbus slave address to register */ 372 data = addr << 1; 373 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 374 375 /* write smbus protocol to register */ 376 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 377 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 378 379 return nfsmb_check_done(sc); 380} 381 382static int 383nfsmb_write_2(struct nfsmb_softc *sc, uint8_t cmd, uint16_t val, 384 i2c_addr_t addr, i2c_op_t op, int flags) 385{ 386 uint8_t data, low, high; 387 388 /* store cmd */ 389 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 390 391 /* store data */ 392 low = val; 393 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, low); 394 high = val >> 8; 395 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA + 1, high); 396 397 /* write smbus slave address to register */ 398 data = addr << 1; 399 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 400 401 /* write smbus protocol to register */ 402 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 403 if (flags & I2C_F_PEC) 404 data |= NFORCE_SMB_PROTOCOL_PEC; 405 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 406 407 return nfsmb_check_done(sc); 408} 409 410/* ARGSUSED */ 411static int 412nfsmb_receive_1(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 413{ 414 uint8_t data; 415 416 /* write smbus slave address to register */ 417 data = addr << 1; 418 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 419 420 /* write smbus protocol to register */ 421 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 422 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 423 424 /* check for errors */ 425 if (nfsmb_check_done(sc) < 0) 426 return -1; 427 428 /* read data */ 429 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 430} 431 432/* ARGSUSED */ 433static int 434nfsmb_read_1(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 435 int flags) 436{ 437 uint8_t data; 438 439 /* store cmd */ 440 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 441 442 /* write smbus slave address to register */ 443 data = addr << 1; 444 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 445 446 /* write smbus protocol to register */ 447 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE_DATA; 448 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 449 450 /* check for errors */ 451 if (nfsmb_check_done(sc) < 0) 452 return -1; 453 454 /* read data */ 455 return bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 456} 457 458static int 459nfsmb_read_2(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 460 int flags) 461{ 462 uint8_t data, low, high; 463 464 /* store cmd */ 465 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 466 467 /* write smbus slave address to register */ 468 data = addr << 1; 469 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 470 471 /* write smbus protocol to register */ 472 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 473 if (flags & I2C_F_PEC) 474 data |= NFORCE_SMB_PROTOCOL_PEC; 475 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 476 477 /* check for errors */ 478 if (nfsmb_check_done(sc) < 0) 479 return -1; 480 481 /* read data */ 482 low = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 483 high = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA + 1); 484 return low | high << 8; 485} 486