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