xref: /openbsd-src/sys/dev/pci/ichiic.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: ichiic.c,v 1.22 2009/03/29 21:53:52 sthen Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006 Alexander Yurchenko <grange@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 /*
20  * Intel ICH SMBus controller driver.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/kernel.h>
27 #include <sys/rwlock.h>
28 #include <sys/proc.h>
29 
30 #include <machine/bus.h>
31 
32 #include <dev/pci/pcidevs.h>
33 #include <dev/pci/pcireg.h>
34 #include <dev/pci/pcivar.h>
35 
36 #include <dev/pci/ichreg.h>
37 
38 #include <dev/i2c/i2cvar.h>
39 
40 #ifdef ICHIIC_DEBUG
41 #define DPRINTF(x) printf x
42 #else
43 #define DPRINTF(x)
44 #endif
45 
46 #define ICHIIC_DELAY	100
47 #define ICHIIC_TIMEOUT	1
48 
49 struct ichiic_softc {
50 	struct device		sc_dev;
51 
52 	bus_space_tag_t		sc_iot;
53 	bus_space_handle_t	sc_ioh;
54 	void *			sc_ih;
55 	int			sc_poll;
56 
57 	struct i2c_controller	sc_i2c_tag;
58 	struct rwlock		sc_i2c_lock;
59 	struct {
60 		i2c_op_t     op;
61 		void *       buf;
62 		size_t       len;
63 		int          flags;
64 		volatile int error;
65 	}			sc_i2c_xfer;
66 };
67 
68 int	ichiic_match(struct device *, void *, void *);
69 void	ichiic_attach(struct device *, struct device *, void *);
70 
71 int	ichiic_i2c_acquire_bus(void *, int);
72 void	ichiic_i2c_release_bus(void *, int);
73 int	ichiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
74 	    void *, size_t, int);
75 
76 int	ichiic_intr(void *);
77 
78 struct cfattach ichiic_ca = {
79 	sizeof(struct ichiic_softc),
80 	ichiic_match,
81 	ichiic_attach
82 };
83 
84 struct cfdriver ichiic_cd = {
85 	NULL, "ichiic", DV_DULL
86 };
87 
88 const struct pci_matchid ichiic_ids[] = {
89 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_SMB },
90 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_SMB },
91 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_SMB },
92 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_SMB },
93 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_SMB },
94 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_SMB },
95 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_SMB },
96 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_SMB },
97 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_SMB },
98 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_SMB },
99 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_SMB },
100 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_SMB },
101 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801I_SMB },
102 	{ PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801JI_SMB }
103 };
104 
105 int
106 ichiic_match(struct device *parent, void *match, void *aux)
107 {
108 	return (pci_matchbyid(aux, ichiic_ids,
109 	    sizeof(ichiic_ids) / sizeof(ichiic_ids[0])));
110 }
111 
112 void
113 ichiic_attach(struct device *parent, struct device *self, void *aux)
114 {
115 	struct ichiic_softc *sc = (struct ichiic_softc *)self;
116 	struct pci_attach_args *pa = aux;
117 	struct i2cbus_attach_args iba;
118 	pcireg_t conf;
119 	bus_size_t iosize;
120 	pci_intr_handle_t ih;
121 	const char *intrstr = NULL;
122 
123 	/* Read configuration */
124 	conf = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_SMB_HOSTC);
125 	DPRINTF((": conf 0x%08x", conf));
126 
127 	if ((conf & ICH_SMB_HOSTC_HSTEN) == 0) {
128 		printf(": SMBus disabled\n");
129 		return;
130 	}
131 
132 	/* Map I/O space */
133 	if (pci_mapreg_map(pa, ICH_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
134 	    &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
135 		printf(": can't map i/o space\n");
136 		return;
137 	}
138 
139 	sc->sc_poll = 1;
140 	if (conf & ICH_SMB_HOSTC_SMIEN) {
141 		/* No PCI IRQ */
142 		printf(": SMI");
143 	} else {
144 		/* Install interrupt handler */
145 		if (pci_intr_map(pa, &ih) == 0) {
146 			intrstr = pci_intr_string(pa->pa_pc, ih);
147 			sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
148 			    ichiic_intr, sc, sc->sc_dev.dv_xname);
149 			if (sc->sc_ih != NULL) {
150 				printf(": %s", intrstr);
151 				sc->sc_poll = 0;
152 			}
153 		}
154 		if (sc->sc_poll)
155 			printf(": polling");
156 	}
157 
158 	printf("\n");
159 
160 	/* Attach I2C bus */
161 	rw_init(&sc->sc_i2c_lock, "iiclk");
162 	sc->sc_i2c_tag.ic_cookie = sc;
163 	sc->sc_i2c_tag.ic_acquire_bus = ichiic_i2c_acquire_bus;
164 	sc->sc_i2c_tag.ic_release_bus = ichiic_i2c_release_bus;
165 	sc->sc_i2c_tag.ic_exec = ichiic_i2c_exec;
166 
167 	bzero(&iba, sizeof(iba));
168 	iba.iba_name = "iic";
169 	iba.iba_tag = &sc->sc_i2c_tag;
170 	config_found(self, &iba, iicbus_print);
171 
172 	return;
173 }
174 
175 int
176 ichiic_i2c_acquire_bus(void *cookie, int flags)
177 {
178 	struct ichiic_softc *sc = cookie;
179 
180 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
181 		return (0);
182 
183 	return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
184 }
185 
186 void
187 ichiic_i2c_release_bus(void *cookie, int flags)
188 {
189 	struct ichiic_softc *sc = cookie;
190 
191 	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
192 		return;
193 
194 	rw_exit(&sc->sc_i2c_lock);
195 }
196 
197 int
198 ichiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
199     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
200 {
201 	struct ichiic_softc *sc = cookie;
202 	u_int8_t *b;
203 	u_int8_t ctl, st;
204 	int retries;
205 
206 	DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
207 	    "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
208 	    len, flags));
209 
210 	/* Wait for bus to be idle */
211 	for (retries = 100; retries > 0; retries--) {
212 		st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
213 		if (!(st & ICH_SMB_HS_BUSY))
214 			break;
215 		DELAY(ICHIIC_DELAY);
216 	}
217 	DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
218 	    ICH_SMB_HS_BITS));
219 	if (st & ICH_SMB_HS_BUSY)
220 		return (1);
221 
222 	if (cold || sc->sc_poll)
223 		flags |= I2C_F_POLL;
224 
225 	if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
226 		return (1);
227 
228 	/* Setup transfer */
229 	sc->sc_i2c_xfer.op = op;
230 	sc->sc_i2c_xfer.buf = buf;
231 	sc->sc_i2c_xfer.len = len;
232 	sc->sc_i2c_xfer.flags = flags;
233 	sc->sc_i2c_xfer.error = 0;
234 
235 	/* Set slave address and transfer direction */
236 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA,
237 	    ICH_SMB_TXSLVA_ADDR(addr) |
238 	    (I2C_OP_READ_P(op) ? ICH_SMB_TXSLVA_READ : 0));
239 
240 	b = (void *)cmdbuf;
241 	if (cmdlen > 0)
242 		/* Set command byte */
243 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HCMD, b[0]);
244 
245 	if (I2C_OP_WRITE_P(op)) {
246 		/* Write data */
247 		b = buf;
248 		if (len > 0)
249 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
250 			    ICH_SMB_HD0, b[0]);
251 		if (len > 1)
252 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
253 			    ICH_SMB_HD1, b[1]);
254 	}
255 
256 	/* Set SMBus command */
257 	if (len == 0)
258 		ctl = ICH_SMB_HC_CMD_BYTE;
259 	else if (len == 1)
260 		ctl = ICH_SMB_HC_CMD_BDATA;
261 	else if (len == 2)
262 		ctl = ICH_SMB_HC_CMD_WDATA;
263 
264 	if ((flags & I2C_F_POLL) == 0)
265 		ctl |= ICH_SMB_HC_INTREN;
266 
267 	/* Start transaction */
268 	ctl |= ICH_SMB_HC_START;
269 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl);
270 
271 	if (flags & I2C_F_POLL) {
272 		/* Poll for completion */
273 		DELAY(ICHIIC_DELAY);
274 		for (retries = 1000; retries > 0; retries--) {
275 			st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
276 			    ICH_SMB_HS);
277 			if ((st & ICH_SMB_HS_BUSY) == 0)
278 				break;
279 			DELAY(ICHIIC_DELAY);
280 		}
281 		if (st & ICH_SMB_HS_BUSY)
282 			goto timeout;
283 		ichiic_intr(sc);
284 	} else {
285 		/* Wait for interrupt */
286 		if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz))
287 			goto timeout;
288 	}
289 
290 	if (sc->sc_i2c_xfer.error)
291 		return (1);
292 
293 	return (0);
294 
295 timeout:
296 	/*
297 	 * Transfer timeout. Kill the transaction and clear status bits.
298 	 */
299 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC,
300 	    ICH_SMB_HC_KILL);
301 	DELAY(ICHIIC_DELAY);
302 	st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
303 	if ((st & ICH_SMB_HS_FAILED) == 0)
304 		printf("%s: abort failed, status 0x%b\n",
305 		    sc->sc_dev.dv_xname, st, ICH_SMB_HS_BITS);
306 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
307 	return (1);
308 }
309 
310 int
311 ichiic_intr(void *arg)
312 {
313 	struct ichiic_softc *sc = arg;
314 	u_int8_t st;
315 	u_int8_t *b;
316 	size_t len;
317 
318 	/* Read status */
319 	st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
320 	if ((st & ICH_SMB_HS_BUSY) != 0 || (st & (ICH_SMB_HS_INTR |
321 	    ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED |
322 	    ICH_SMB_HS_SMBAL | ICH_SMB_HS_BDONE)) == 0)
323 		/* Interrupt was not for us */
324 		return (0);
325 
326 	DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
327 	    ICH_SMB_HS_BITS));
328 
329 	/* Clear status bits */
330 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
331 
332 	/* Check for errors */
333 	if (st & (ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED)) {
334 		sc->sc_i2c_xfer.error = 1;
335 		goto done;
336 	}
337 
338 	if (st & ICH_SMB_HS_INTR) {
339 		if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
340 			goto done;
341 
342 		/* Read data */
343 		b = sc->sc_i2c_xfer.buf;
344 		len = sc->sc_i2c_xfer.len;
345 		if (len > 0)
346 			b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
347 			    ICH_SMB_HD0);
348 		if (len > 1)
349 			b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
350 			    ICH_SMB_HD1);
351 	}
352 
353 done:
354 	if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
355 		wakeup(sc);
356 	return (1);
357 }
358