nfsmb.c revision 1.26
1/* $NetBSD: nfsmb.c,v 1.26 2021/04/24 23:36:57 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.26 2021/04/24 23:36:57 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 CFARG_EOL); 172 173 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[1]); 174 nfsmbca.nfsmb_num = 2; 175 nfsmbca.nfsmb_addr = NFORCE_SMBBASE(reg); 176 sc->sc_nfsmb[1] = config_found(sc->sc_dev, &nfsmbca, nfsmbc_print, 177 CFARG_EOL); 178 179 /* This driver is similar to an ISA bridge that doesn't 180 * need any special handling. So registering NULL handlers 181 * are sufficent. */ 182 if (!pmf_device_register(self, NULL, NULL)) 183 aprint_error_dev(self, "couldn't establish power handler\n"); 184} 185 186static int 187nfsmbc_print(void *aux, const char *pnp) 188{ 189 struct nfsmbc_attach_args *nfsmbcap = aux; 190 191 if (pnp) 192 aprint_normal("nfsmb SMBus %d at %s", 193 nfsmbcap->nfsmb_num, pnp); 194 else 195 aprint_normal(" SMBus %d", nfsmbcap->nfsmb_num); 196 return UNCONF; 197} 198 199 200CFATTACH_DECL_NEW(nfsmb, sizeof(struct nfsmb_softc), 201 nfsmb_match, nfsmb_attach, NULL, NULL); 202 203static int 204nfsmb_match(device_t parent, cfdata_t match, void *aux) 205{ 206 struct nfsmbc_attach_args *nfsmbcap = aux; 207 208 if (nfsmbcap->nfsmb_num == 1 || nfsmbcap->nfsmb_num == 2) 209 return 1; 210 return 0; 211} 212 213static void 214nfsmb_attach(device_t parent, device_t self, void *aux) 215{ 216 struct nfsmb_softc *sc = device_private(self); 217 struct nfsmbc_attach_args *nfsmbcap = aux; 218 struct i2cbus_attach_args iba; 219 220 aprint_naive("\n"); 221 aprint_normal("\n"); 222 223 sc->sc_dev = self; 224 sc->sc_nfsmbc = parent; 225 sc->sc_num = nfsmbcap->nfsmb_num; 226 sc->sc_iot = nfsmbcap->nfsmb_iot; 227 228 /* register with iic */ 229 iic_tag_init(&sc->sc_i2c); 230 sc->sc_i2c.ic_cookie = sc; 231 sc->sc_i2c.ic_exec = nfsmb_exec; 232 233 if (bus_space_map(sc->sc_iot, nfsmbcap->nfsmb_addr, NFORCE_SMBSIZE, 0, 234 &sc->sc_ioh) != 0) { 235 aprint_error_dev(self, "failed to map SMBus space\n"); 236 return; 237 } 238 239 memset(&iba, 0, sizeof(iba)); 240 iba.iba_tag = &sc->sc_i2c; 241 config_found(sc->sc_dev, &iba, iicbus_print, CFARG_EOL); 242 243 /* This driver is similar to an ISA bridge that doesn't 244 * need any special handling. So registering NULL handlers 245 * are sufficent. */ 246 if (!pmf_device_register(self, NULL, NULL)) 247 aprint_error_dev(self, "couldn't establish power handler\n"); 248} 249 250static int 251nfsmb_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 252 size_t cmdlen, void *vbuf, size_t buflen, int flags) 253{ 254 struct nfsmb_softc *sc = (struct nfsmb_softc *)cookie; 255 uint8_t *p = vbuf; 256 int rv; 257 258 if ((cmdlen == 0) && (buflen == 0)) { 259 return nfsmb_quick(sc, addr, op, flags); 260 } 261 262 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) { 263 rv = nfsmb_receive_1(sc, addr, op, flags); 264 if (rv == -1) 265 return -1; 266 *p = (uint8_t)rv; 267 return 0; 268 } 269 270 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 271 rv = nfsmb_read_1(sc, *(const uint8_t*)cmd, addr, op, flags); 272 if (rv == -1) 273 return -1; 274 *p = (uint8_t)rv; 275 return 0; 276 } 277 278 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { 279 rv = nfsmb_read_2(sc, *(const uint8_t*)cmd, addr, op, flags); 280 if (rv == -1) 281 return -1; 282 *(uint16_t *)p = (uint16_t)rv; 283 return 0; 284 } 285 286 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 287 return nfsmb_send_1(sc, *(uint8_t*)vbuf, addr, op, flags); 288 289 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) 290 return nfsmb_write_1(sc, *(const uint8_t*)cmd, *(uint8_t*)vbuf, 291 addr, op, flags); 292 293 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) 294 return nfsmb_write_2(sc, 295 *(const uint8_t*)cmd, *((uint16_t *)vbuf), addr, op, flags); 296 297 return -1; 298} 299 300static int 301nfsmb_check_done(struct nfsmb_softc *sc) 302{ 303 int us; 304 uint8_t stat; 305 306 us = 10 * 1000; /* XXXX: wait maximum 10 msec */ 307 do { 308 delay(10); 309 us -= 10; 310 if (us <= 0) 311 return -1; 312 } while (bus_space_read_1(sc->sc_iot, sc->sc_ioh, 313 NFORCE_SMB_PROTOCOL) != 0); 314 315 stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_STATUS); 316 if ((stat & NFORCE_SMB_STATUS_DONE) && 317 !(stat & NFORCE_SMB_STATUS_STATUS)) 318 return 0; 319 return -1; 320} 321 322/* ARGSUSED */ 323static int 324nfsmb_quick(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 325{ 326 uint8_t data; 327 328 /* write smbus slave address to register */ 329 data = addr << 1; 330 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 331 332 /* write smbus protocol to register */ 333 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_QUICK; 334 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 335 336 return nfsmb_check_done(sc); 337} 338 339/* ARGSUSED */ 340static int 341nfsmb_send_1(struct nfsmb_softc *sc, uint8_t val, i2c_addr_t addr, i2c_op_t op, 342 int flags) 343{ 344 uint8_t data; 345 346 /* store cmd */ 347 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, val); 348 349 /* write smbus slave address to register */ 350 data = addr << 1; 351 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 352 353 /* write smbus protocol to register */ 354 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_BYTE; 355 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 356 357 return nfsmb_check_done(sc); 358} 359 360/* ARGSUSED */ 361static int 362nfsmb_write_1(struct nfsmb_softc *sc, uint8_t cmd, uint8_t val, i2c_addr_t addr, 363 i2c_op_t op, int flags) 364{ 365 uint8_t data; 366 367 /* store cmd */ 368 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 369 370 /* store data */ 371 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, val); 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_BYTE_DATA; 379 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 380 381 return nfsmb_check_done(sc); 382} 383 384static int 385nfsmb_write_2(struct nfsmb_softc *sc, uint8_t cmd, uint16_t val, 386 i2c_addr_t addr, i2c_op_t op, int flags) 387{ 388 uint8_t data, low, high; 389 390 /* store cmd */ 391 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 392 393 /* store data */ 394 low = val; 395 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA, low); 396 high = val >> 8; 397 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA + 1, high); 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_WORD_DATA; 405 if (flags & I2C_F_PEC) 406 data |= NFORCE_SMB_PROTOCOL_PEC; 407 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 408 409 return nfsmb_check_done(sc); 410} 411 412/* ARGSUSED */ 413static int 414nfsmb_receive_1(struct nfsmb_softc *sc, i2c_addr_t addr, i2c_op_t op, int flags) 415{ 416 uint8_t data; 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; 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 434/* ARGSUSED */ 435static int 436nfsmb_read_1(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 437 int flags) 438{ 439 uint8_t data; 440 441 /* store cmd */ 442 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 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_DATA; 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 460static int 461nfsmb_read_2(struct nfsmb_softc *sc, uint8_t cmd, i2c_addr_t addr, i2c_op_t op, 462 int flags) 463{ 464 uint8_t data, low, high; 465 466 /* store cmd */ 467 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_COMMAND, cmd); 468 469 /* write smbus slave address to register */ 470 data = addr << 1; 471 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_ADDRESS, data); 472 473 /* write smbus protocol to register */ 474 data = I2C_OP_READ_P(op) | NFORCE_SMB_PROTOCOL_WORD_DATA; 475 if (flags & I2C_F_PEC) 476 data |= NFORCE_SMB_PROTOCOL_PEC; 477 bus_space_write_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_PROTOCOL, data); 478 479 /* check for errors */ 480 if (nfsmb_check_done(sc) < 0) 481 return -1; 482 483 /* read data */ 484 low = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA); 485 high = bus_space_read_1(sc->sc_iot, sc->sc_ioh, NFORCE_SMB_DATA + 1); 486 return low | high << 8; 487} 488