diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2006-01-04 00:40:09 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2006-01-04 00:40:09 +0000 |
commit | 3cc1783adda3df5771ca8b07629d6913069dc639 (patch) | |
tree | 8bf600991fa0310ca3e1010cb2ca0c38ead24fab /sys/dev | |
parent | bbbacc2b2fb274685a411143b63116f3db40ab09 (diff) |
driver for the nvidia nforce2/3/4 smbus controller.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
-rw-r--r-- | sys/dev/pci/nviic.c | 289 |
2 files changed, 295 insertions, 1 deletions
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index e4e5d96cec1..1d7c52fc635 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.196 2005/12/31 06:20:22 grange Exp $ +# $OpenBSD: files.pci,v 1.197 2006/01/04 00:40:08 dlg Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -588,3 +588,8 @@ file dev/pci/viapm.c viapm device amdiic: i2cbus attach amdiic at pci file dev/pci/amdiic.c amdiic + +# NVIDIA nForce2/3/4 SMBus controller +device nviic: i2cbus +attach nviic at pci +file dev/pci/nviic.c nviic diff --git a/sys/dev/pci/nviic.c b/sys/dev/pci/nviic.c new file mode 100644 index 00000000000..bd81b00d0d9 --- /dev/null +++ b/sys/dev/pci/nviic.c @@ -0,0 +1,289 @@ +/* $OpenBSD: nviic.c,v 1.1 2006/01/04 00:40:08 dlg Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/proc.h> + +#include <machine/bus.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/i2c/i2cvar.h> + +/* PCI Configuration space registers */ +#define NVI_PCI_SMBASE1 0x50 +#define NVI_PCI_SMBASE2 0x54 + +#define NVI_SMBASE(x) ((x) & 0xfffc) +#define NVI_SMBASE_SIZE 8 + +/* SMBus 2.0 registers */ +#define NVI_SMB_PRTCL 0x00 /* protocol, PEC */ +#define NVI_SMB_STS 0x01 /* status */ +#define NVI_SMB_ADDR 0x02 /* address */ +#define NVI_SMB_CMD 0x03 /* command */ +#define NVI_SMB_DATA(o) (0x04 + (o)) /* 32 data registers */ +#define NVI_SMB_BCNT 0x24 /* number of data bytes */ +#define NVI_SMB_ALRM_A 0x25 /* alarm address */ +#define NVI_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ + +#define NVI_SMB_STS_DONE 0x80 +#define NVI_SMB_STS_ALRM 0x40 +#define NVI_SMB_STS_RES 0x20 +#define NVI_SMB_STS_STATUS 0x1f + +#define NVI_SMB_PRTCL_WRITE 0x00 +#define NVI_SMB_PRTCL_READ 0x01 +#define NVI_SMB_PRTCL_QUICK 0x02 +#define NVI_SMB_PRTCL_BYTE 0x04 +#define NVI_SMB_PRTCL_BYTE_DATA 0x06 +#define NVI_SMB_PRTCL_WORD_DATA 0x08 +#define NVI_SMB_PRTCL_BLOCK_DATA 0x0a +#define NVI_SMB_PRTCL_PROC_CALL 0x0c +#define NVI_SMB_PRTCL_BLOCK_PROC_CALL 0x0d +#define NVI_SMB_PRTCL_PEC 0x80 + +#ifdef NVIIC_DEBUG +#define DPRINTF(x...) do { if (nviic_debug) printf(x); } while (0) +int nviic_debug = 1; +#else +#define DPRINTF(x...) /* x */ +#endif + +/* there are two iic busses on this pci device */ +#define NVIIC_NBUS 2 + +int nviic_match(struct device *, void *, void *); +void nviic_attach(struct device *, struct device *, void *); + +struct nviic_softc; + +struct nviic_controller { + struct nviic_softc *nc_sc; + bus_space_handle_t nc_ioh; + struct lock nc_lock; + struct i2c_controller nc_i2c; +}; + +struct nviic_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + struct nviic_controller sc_nc[NVIIC_NBUS]; +}; + +struct cfattach nviic_ca = { + sizeof(struct nviic_softc), nviic_match, nviic_attach +}; + +struct cfdriver nviic_cd = { + NULL, "nviic", DV_DULL +}; + +int nviic_i2c_acquire_bus(void *, int); +void nviic_i2c_release_bus(void *, int); +int nviic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); + +u_int8_t nviic_read(struct nviic_controller *, bus_size_t); +void nviic_write(struct nviic_controller *, bus_size_t, u_int8_t); + +#define DEVNAME(s) ((sc)->sc_dev.dv_xname) + +const struct pci_matchid nviic_ids[] = { + { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_SMB }, + { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_400_SMB }, + { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_SMB }, + { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_250_SMB }, + { PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE4_SMB } +}; + +int +nviic_match(struct device *parent, void *match, void *aux) +{ + return (pci_matchbyid(aux, nviic_ids, + sizeof(nviic_ids) / sizeof(nviic_ids[0]))); +} + +void +nviic_attach(struct device *parent, struct device *self, void *aux) +{ + struct nviic_softc *sc = (struct nviic_softc *)self; + struct pci_attach_args *pa = aux; + struct nviic_controller *nc; + struct i2cbus_attach_args iba; + int baseregs[NVIIC_NBUS]; + pcireg_t reg; + int i; + + sc->sc_iot = pa->pa_iot; + + printf("\n"); + + baseregs[0] = NVI_PCI_SMBASE1; + baseregs[1] = NVI_PCI_SMBASE2; + for (i = 0; i < NVIIC_NBUS; i++) { + nc = &sc->sc_nc[i]; + + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, baseregs[i]); + if (bus_space_map(sc->sc_iot, NVI_SMBASE(reg), NVI_SMBASE_SIZE, + 0, &nc->nc_ioh)) { + printf("%s: unable to map space for bus %d\n", + DEVNAME(sc), i); + continue; + } + + nc->nc_sc = sc; + lockinit(&nc->nc_lock, PRIBIO | PCATCH, "iiclk", 0, 0); + nc->nc_i2c.ic_cookie = nc; + nc->nc_i2c.ic_acquire_bus = nviic_i2c_acquire_bus; + nc->nc_i2c.ic_release_bus = nviic_i2c_release_bus; + nc->nc_i2c.ic_exec = nviic_i2c_exec; + + bzero(&iba, sizeof(iba)); + iba.iba_name = "iic"; + iba.iba_tag = &nc->nc_i2c; + config_found(self, &iba, iicbus_print); + } +} + +int +nviic_i2c_acquire_bus(void *arg, int flags) +{ + struct nviic_controller *nc = arg; + + if (cold || (flags & I2C_F_POLL)) + return (0); + + return (lockmgr(&nc->nc_lock, LK_EXCLUSIVE, NULL)); +} + +void +nviic_i2c_release_bus(void *arg, int flags) +{ + struct nviic_controller *nc = arg; + + if (cold || (flags & I2C_F_POLL)) + return; + + lockmgr(&nc->nc_lock, LK_EXCLUSIVE, NULL); +} + +int +nviic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr, + const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) +{ + struct nviic_controller *nc = arg; + struct nviic_softc *sc = nc->nc_sc; + u_int8_t protocol; + u_int8_t *b; + u_int8_t sts; + int i; + + DPRINTF("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n", + DEVNAME(sc), op, addr, cmdlen, len, flags); + + if (cold) + flags |= I2C_F_POLL; + + if (I2C_OP_STOP_P(op) == 0 || cmdlen > 1 || len > 2) + return (1); + + /* set slave address */ + nviic_write(nc, NVI_SMB_ADDR, addr << 1); + + /* set command byte */ + if (cmdlen > 0) { + b = (u_int8_t *)cmdbuf; + nviic_write(nc, NVI_SMB_CMD, b[0]); + } + + b = (u_int8_t *)buf; + + /* write data */ + if (I2C_OP_WRITE_P(op)) { + for (i = 0; i < len; i++) + nviic_write(nc, NVI_SMB_DATA(i), b[i]); + } + + switch (len) { + case 0: + protocol = NVI_SMB_PRTCL_BYTE; + break; + case 1: + protocol = NVI_SMB_PRTCL_BYTE_DATA; + break; + case 2: + protocol = NVI_SMB_PRTCL_WORD_DATA; + break; + } + + /* set direction */ + if (I2C_OP_READ_P(op)) + protocol |= NVI_SMB_PRTCL_READ; + + /* start transaction */ + nviic_write(nc, NVI_SMB_PRTCL, protocol); + + for (i = 10000; i > 0; i--) { + delay(500); + if (nviic_read(nc, NVI_SMB_PRTCL) == 0) + break; + } + if (i == 0) { + printf("%s: timeout\n", DEVNAME(sc)); + return (1); + } + + sts = nviic_read(nc, NVI_SMB_STS); + if (sts & NVI_SMB_STS_STATUS) + return (1); + + /* read data */ + if (I2C_OP_READ_P(op)) { + for (i = 0; i < len; i++) + b[i] = nviic_read(nc, NVI_SMB_DATA(i)); + } + + return (0); +} + +u_int8_t +nviic_read(struct nviic_controller *nc, bus_size_t r) +{ + struct nviic_softc *sc = nc->nc_sc; + + bus_space_barrier(sc->sc_iot, nc->nc_ioh, r, 1, + BUS_SPACE_BARRIER_READ); + return (bus_space_read_1(sc->sc_iot, nc->nc_ioh, r)); +} + +void +nviic_write(struct nviic_controller *nc, bus_size_t r, u_int8_t v) +{ + struct nviic_softc *sc = nc->nc_sc; + + bus_space_write_1(sc->sc_iot, nc->nc_ioh, r, v); + bus_space_barrier(sc->sc_iot, nc->nc_ioh, r, 1, + BUS_SPACE_BARRIER_WRITE); +} |