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