Annotation of sys/dev/pci/nviic.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: nviic.c,v 1.11 2007/05/03 09:36:26 dlg Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/systm.h>
! 21: #include <sys/device.h>
! 22: #include <sys/kernel.h>
! 23: #include <sys/rwlock.h>
! 24: #include <sys/proc.h>
! 25:
! 26: #include <machine/bus.h>
! 27:
! 28: #include <dev/pci/pcidevs.h>
! 29: #include <dev/pci/pcireg.h>
! 30: #include <dev/pci/pcivar.h>
! 31:
! 32: #include <dev/i2c/i2cvar.h>
! 33:
! 34: /* PCI Configuration space registers */
! 35: #define NVI_PCI_SMBASE1 0x20
! 36: #define NVI_PCI_SMBASE2 0x24
! 37:
! 38: #define NVI_OLD_PCI_SMBASE1 0x50
! 39: #define NVI_OLD_PCI_SMBASE2 0x54
! 40:
! 41: #define NVI_SMBASE(x) ((x) & 0xfffc)
! 42: #define NVI_SMBASE_SIZE 8
! 43:
! 44: /* SMBus 2.0 registers */
! 45: #define NVI_SMB_PRTCL 0x00 /* protocol, PEC */
! 46: #define NVI_SMB_STS 0x01 /* status */
! 47: #define NVI_SMB_ADDR 0x02 /* address */
! 48: #define NVI_SMB_CMD 0x03 /* command */
! 49: #define NVI_SMB_DATA(o) (0x04 + (o)) /* 32 data registers */
! 50: #define NVI_SMB_BCNT 0x24 /* number of data bytes */
! 51: #define NVI_SMB_ALRM_A 0x25 /* alarm address */
! 52: #define NVI_SMB_ALRM_D 0x26 /* 2 bytes alarm data */
! 53:
! 54: #define NVI_SMB_STS_DONE 0x80
! 55: #define NVI_SMB_STS_ALRM 0x40
! 56: #define NVI_SMB_STS_RES 0x20
! 57: #define NVI_SMB_STS_STATUS 0x1f
! 58:
! 59: #define NVI_SMB_PRTCL_WRITE 0x00
! 60: #define NVI_SMB_PRTCL_READ 0x01
! 61: #define NVI_SMB_PRTCL_QUICK 0x02
! 62: #define NVI_SMB_PRTCL_BYTE 0x04
! 63: #define NVI_SMB_PRTCL_BYTE_DATA 0x06
! 64: #define NVI_SMB_PRTCL_WORD_DATA 0x08
! 65: #define NVI_SMB_PRTCL_BLOCK_DATA 0x0a
! 66: #define NVI_SMB_PRTCL_PROC_CALL 0x0c
! 67: #define NVI_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
! 68: #define NVI_SMB_PRTCL_PEC 0x80
! 69:
! 70: #ifdef NVIIC_DEBUG
! 71: #define DPRINTF(x...) do { if (nviic_debug) printf(x); } while (0)
! 72: int nviic_debug = 1;
! 73: #else
! 74: #define DPRINTF(x...) /* x */
! 75: #endif
! 76:
! 77: /* there are two iic busses on this pci device */
! 78: #define NVIIC_NBUS 2
! 79:
! 80: int nviic_match(struct device *, void *, void *);
! 81: void nviic_attach(struct device *, struct device *, void *);
! 82:
! 83: struct nviic_softc;
! 84:
! 85: struct nviic_controller {
! 86: struct nviic_softc *nc_sc;
! 87: bus_space_handle_t nc_ioh;
! 88: struct rwlock nc_lock;
! 89: struct i2c_controller nc_i2c;
! 90: };
! 91:
! 92: struct nviic_softc {
! 93: struct device sc_dev;
! 94: bus_space_tag_t sc_iot;
! 95: struct nviic_controller sc_nc[NVIIC_NBUS];
! 96: };
! 97:
! 98: struct cfattach nviic_ca = {
! 99: sizeof(struct nviic_softc), nviic_match, nviic_attach
! 100: };
! 101:
! 102: struct cfdriver nviic_cd = {
! 103: NULL, "nviic", DV_DULL
! 104: };
! 105:
! 106: int nviic_i2c_acquire_bus(void *, int);
! 107: void nviic_i2c_release_bus(void *, int);
! 108: int nviic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
! 109: size_t, void *, size_t, int);
! 110:
! 111: u_int8_t nviic_read(struct nviic_controller *, bus_size_t);
! 112: void nviic_write(struct nviic_controller *, bus_size_t, u_int8_t);
! 113:
! 114: #define DEVNAME(s) ((sc)->sc_dev.dv_xname)
! 115:
! 116: const struct pci_matchid nviic_ids[] = {
! 117: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_SMB },
! 118: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_400_SMB },
! 119: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_SMB },
! 120: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_250_SMB },
! 121: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_SMB },
! 122: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP51_SMB },
! 123: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP55_SMB },
! 124: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_SMB },
! 125: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_SMB },
! 126: { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP67_SMB }
! 127: };
! 128:
! 129: int
! 130: nviic_match(struct device *parent, void *match, void *aux)
! 131: {
! 132: return (pci_matchbyid(aux, nviic_ids,
! 133: sizeof(nviic_ids) / sizeof(nviic_ids[0])));
! 134: }
! 135:
! 136: void
! 137: nviic_attach(struct device *parent, struct device *self, void *aux)
! 138: {
! 139: struct nviic_softc *sc = (struct nviic_softc *)self;
! 140: struct pci_attach_args *pa = aux;
! 141: struct nviic_controller *nc;
! 142: struct i2cbus_attach_args iba;
! 143: int baseregs[NVIIC_NBUS];
! 144: pcireg_t reg;
! 145: int i;
! 146:
! 147: sc->sc_iot = pa->pa_iot;
! 148:
! 149: printf("\n");
! 150:
! 151: /* Older chipsets used non-standard BARs */
! 152: switch (PCI_PRODUCT(pa->pa_id)) {
! 153: case PCI_PRODUCT_NVIDIA_NFORCE2_SMB:
! 154: case PCI_PRODUCT_NVIDIA_NFORCE2_400_SMB:
! 155: case PCI_PRODUCT_NVIDIA_NFORCE3_SMB:
! 156: case PCI_PRODUCT_NVIDIA_NFORCE3_250_SMB:
! 157: case PCI_PRODUCT_NVIDIA_NFORCE4_SMB:
! 158: baseregs[0] = NVI_OLD_PCI_SMBASE1;
! 159: baseregs[1] = NVI_OLD_PCI_SMBASE2;
! 160: break;
! 161: default:
! 162: baseregs[0] = NVI_PCI_SMBASE1;
! 163: baseregs[1] = NVI_PCI_SMBASE2;
! 164: }
! 165:
! 166: for (i = 0; i < NVIIC_NBUS; i++) {
! 167: nc = &sc->sc_nc[i];
! 168:
! 169: reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[i]);
! 170: if (NVI_SMBASE(reg) == 0 ||
! 171: bus_space_map(sc->sc_iot, NVI_SMBASE(reg), NVI_SMBASE_SIZE,
! 172: 0, &nc->nc_ioh)) {
! 173: printf("%s: unable to map space for bus %d\n",
! 174: DEVNAME(sc), i);
! 175: continue;
! 176: }
! 177:
! 178: nc->nc_sc = sc;
! 179: rw_init(&nc->nc_lock, "nviic");
! 180: nc->nc_i2c.ic_cookie = nc;
! 181: nc->nc_i2c.ic_acquire_bus = nviic_i2c_acquire_bus;
! 182: nc->nc_i2c.ic_release_bus = nviic_i2c_release_bus;
! 183: nc->nc_i2c.ic_exec = nviic_i2c_exec;
! 184:
! 185: bzero(&iba, sizeof(iba));
! 186: iba.iba_name = "iic";
! 187: iba.iba_tag = &nc->nc_i2c;
! 188: config_found(self, &iba, iicbus_print);
! 189: }
! 190: }
! 191:
! 192: int
! 193: nviic_i2c_acquire_bus(void *arg, int flags)
! 194: {
! 195: struct nviic_controller *nc = arg;
! 196:
! 197: if (cold || (flags & I2C_F_POLL))
! 198: return (0);
! 199:
! 200: return (rw_enter(&nc->nc_lock, RW_WRITE | RW_INTR));
! 201: }
! 202:
! 203: void
! 204: nviic_i2c_release_bus(void *arg, int flags)
! 205: {
! 206: struct nviic_controller *nc = arg;
! 207:
! 208: if (cold || (flags & I2C_F_POLL))
! 209: return;
! 210:
! 211: rw_exit(&nc->nc_lock);
! 212: }
! 213:
! 214: int
! 215: nviic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr,
! 216: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
! 217: {
! 218: struct nviic_controller *nc = arg;
! 219: #ifdef NVIIC_DEBUG
! 220: struct nviic_softc *sc = nc->nc_sc;
! 221: #endif
! 222: u_int8_t protocol;
! 223: u_int8_t *b;
! 224: u_int8_t sts;
! 225: int i;
! 226:
! 227: DPRINTF("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n",
! 228: DEVNAME(sc), op, addr, cmdlen, len, flags);
! 229:
! 230: if (cold)
! 231: flags |= I2C_F_POLL;
! 232:
! 233: if (I2C_OP_STOP_P(op) == 0 || cmdlen > 1 || len > 2)
! 234: return (1);
! 235:
! 236: /* set slave address */
! 237: nviic_write(nc, NVI_SMB_ADDR, addr << 1);
! 238:
! 239: /* set command byte */
! 240: if (cmdlen > 0) {
! 241: b = (u_int8_t *)cmdbuf;
! 242: nviic_write(nc, NVI_SMB_CMD, b[0]);
! 243: }
! 244:
! 245: b = (u_int8_t *)buf;
! 246:
! 247: /* write data */
! 248: if (I2C_OP_WRITE_P(op)) {
! 249: for (i = 0; i < len; i++)
! 250: nviic_write(nc, NVI_SMB_DATA(i), b[i]);
! 251: }
! 252:
! 253: switch (len) {
! 254: case 0:
! 255: protocol = NVI_SMB_PRTCL_BYTE;
! 256: break;
! 257: case 1:
! 258: protocol = NVI_SMB_PRTCL_BYTE_DATA;
! 259: break;
! 260: case 2:
! 261: protocol = NVI_SMB_PRTCL_WORD_DATA;
! 262: break;
! 263: }
! 264:
! 265: /* set direction */
! 266: if (I2C_OP_READ_P(op))
! 267: protocol |= NVI_SMB_PRTCL_READ;
! 268:
! 269: /* start transaction */
! 270: nviic_write(nc, NVI_SMB_PRTCL, protocol);
! 271:
! 272: for (i = 1000; i > 0; i--) {
! 273: delay(100);
! 274: if (nviic_read(nc, NVI_SMB_PRTCL) == 0)
! 275: break;
! 276: }
! 277: if (i == 0) {
! 278: DPRINTF("%s: timeout\n", DEVNAME(sc));
! 279: return (1);
! 280: }
! 281:
! 282: sts = nviic_read(nc, NVI_SMB_STS);
! 283: if (sts & NVI_SMB_STS_STATUS)
! 284: return (1);
! 285:
! 286: /* read data */
! 287: if (I2C_OP_READ_P(op)) {
! 288: for (i = 0; i < len; i++)
! 289: b[i] = nviic_read(nc, NVI_SMB_DATA(i));
! 290: }
! 291:
! 292: return (0);
! 293: }
! 294:
! 295: u_int8_t
! 296: nviic_read(struct nviic_controller *nc, bus_size_t r)
! 297: {
! 298: struct nviic_softc *sc = nc->nc_sc;
! 299:
! 300: bus_space_barrier(sc->sc_iot, nc->nc_ioh, r, 1,
! 301: BUS_SPACE_BARRIER_READ);
! 302: return (bus_space_read_1(sc->sc_iot, nc->nc_ioh, r));
! 303: }
! 304:
! 305: void
! 306: nviic_write(struct nviic_controller *nc, bus_size_t r, u_int8_t v)
! 307: {
! 308: struct nviic_softc *sc = nc->nc_sc;
! 309:
! 310: bus_space_write_1(sc->sc_iot, nc->nc_ioh, r, v);
! 311: bus_space_barrier(sc->sc_iot, nc->nc_ioh, r, 1,
! 312: BUS_SPACE_BARRIER_WRITE);
! 313: }
CVSweb