xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
1 /*-
2  * Copyright (c) 1998 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$Id: iicsmb.c,v 1.1.2.2 1998/08/13 17:10:44 son Exp $
27  *
28  */
29 
30 /*
31  * I2C to SMB bridge
32  */
33 
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/systm.h>
37 #include <sys/module.h>
38 #include <sys/bus.h>
39 #include <sys/conf.h>
40 #include <sys/buf.h>
41 #include <sys/uio.h>
42 #include <sys/malloc.h>
43 
44 #include <machine/clock.h>
45 
46 #include <dev/iicbus/iiconf.h>
47 #include <dev/iicbus/iicbus.h>
48 
49 #include <dev/smbus/smbconf.h>
50 
51 #include "iicbus_if.h"
52 #include "smbus_if.h"
53 
54 struct iicsmb_softc {
55 
56 #define SMB_WAITING_ADDR	0x0
57 #define SMB_WAITING_LOW		0x1
58 #define SMB_WAITING_HIGH	0x2
59 #define SMB_DONE		0x3
60 	int state;
61 
62 	u_char devaddr;			/* slave device address */
63 
64 	char low;			/* low byte received first */
65 	char high;			/* high byte */
66 
67 	device_t smbus;
68 };
69 
70 static int iicsmb_probe(device_t);
71 static int iicsmb_attach(device_t);
72 static void iicsmb_print_child(device_t, device_t);
73 
74 static void iicsmb_intr(device_t dev, int event, char *buf);
75 static int iicsmb_quick(device_t dev, u_char slave, int how);
76 static int iicsmb_sendb(device_t dev, u_char slave, char byte);
77 static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
78 static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
79 static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
80 static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
81 static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
82 static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
83 static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
84 static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
85 
86 static devclass_t iicsmb_devclass;
87 
88 static device_method_t iicsmb_methods[] = {
89 	/* device interface */
90 	DEVMETHOD(device_probe,		iicsmb_probe),
91 	DEVMETHOD(device_attach,	iicsmb_attach),
92 
93 	/* bus interface */
94 	DEVMETHOD(bus_print_child,	iicsmb_print_child),
95 
96 	/* iicbus interface */
97 	DEVMETHOD(iicbus_intr,		iicsmb_intr),
98 
99 	/* smbus interface */
100 	DEVMETHOD(smbus_quick,		iicsmb_quick),
101 	DEVMETHOD(smbus_sendb,		iicsmb_sendb),
102 	DEVMETHOD(smbus_recvb,		iicsmb_recvb),
103 	DEVMETHOD(smbus_writeb,		iicsmb_writeb),
104 	DEVMETHOD(smbus_writew,		iicsmb_writew),
105 	DEVMETHOD(smbus_readb,		iicsmb_readb),
106 	DEVMETHOD(smbus_readw,		iicsmb_readw),
107 	DEVMETHOD(smbus_pcall,		iicsmb_pcall),
108 	DEVMETHOD(smbus_bwrite,		iicsmb_bwrite),
109 	DEVMETHOD(smbus_bread,		iicsmb_bread),
110 
111 	{ 0, 0 }
112 };
113 
114 static driver_t iicsmb_driver = {
115 	"iicsmb",
116 	iicsmb_methods,
117 	DRIVER_TYPE_MISC,
118 	sizeof(struct iicsmb_softc),
119 };
120 
121 static int
122 iicsmb_probe(device_t dev)
123 {
124 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
125 
126 	sc->smbus = smbus_alloc_bus(dev);
127 
128 	if (!sc->smbus)
129 		return (EINVAL);	/* XXX don't know what to return else */
130 
131 	return (0);
132 }
133 
134 static int
135 iicsmb_attach(device_t dev)
136 {
137 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
138 
139 	/* probe and attach the smbus */
140 	device_probe_and_attach(sc->smbus);
141 
142 	return (0);
143 }
144 
145 static void
146 iicsmb_print_child(device_t bus, device_t dev)
147 {
148 	printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
149 
150 	return;
151 }
152 
153 /*
154  * iicsmb_intr()
155  *
156  * iicbus interrupt handler
157  */
158 static void
159 iicsmb_intr(device_t dev, int event, char *buf)
160 {
161 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
162 
163 	switch (event) {
164 	case INTR_GENERAL:
165 	case INTR_START:
166 		sc->state = SMB_WAITING_ADDR;
167 		break;
168 
169 	case INTR_STOP:
170 		/* call smbus intr handler */
171 		smbus_intr(sc->smbus, sc->devaddr,
172 				sc->low, sc->high, SMB_ENOERR);
173 		break;
174 
175 	case INTR_RECEIVE:
176 		switch (sc->state) {
177 		case SMB_DONE:
178 			/* XXX too much data, discard */
179 			printf("%s: too much data from 0x%x\n", __FUNCTION__,
180 				sc->devaddr & 0xff);
181 			goto end;
182 
183 		case SMB_WAITING_ADDR:
184 			sc->devaddr = (u_char)*buf;
185 			sc->state = SMB_WAITING_LOW;
186 			break;
187 
188 		case SMB_WAITING_LOW:
189 			sc->low = *buf;
190 			sc->state = SMB_WAITING_HIGH;
191 			break;
192 
193 		case SMB_WAITING_HIGH:
194 			sc->high = *buf;
195 			sc->state = SMB_DONE;
196 			break;
197 		}
198 end:
199 		break;
200 
201 	case INTR_TRANSMIT:
202 	case INTR_NOACK:
203 		break;
204 
205 	case INTR_ERROR:
206 		switch (*buf) {
207 		case IIC_EBUSERR:
208 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
209 			break;
210 
211 		default:
212 			printf("%s unknown error 0x%x!\n", __FUNCTION__,
213 								(int)*buf);
214 			break;
215 		}
216 		break;
217 
218 	default:
219 		panic("%s: unknown event (%d)!", __FUNCTION__, event);
220 	}
221 
222 	return;
223 }
224 
225 static int
226 iicsmb_quick(device_t dev, u_char slave, int how)
227 {
228 	device_t parent = device_get_parent(dev);
229 	int error;
230 
231 	switch (how) {
232 	case SMB_QWRITE:
233 		error = iicbus_start(parent, slave & ~LSB);
234 		break;
235 
236 	case SMB_QREAD:
237 		error = iicbus_start(parent, slave | LSB);
238 		break;
239 
240 	default:
241 		error = EINVAL;
242 		break;
243 	}
244 
245 	if (!error)
246 		error = iicbus_stop(parent);
247 
248 	return (error);
249 }
250 
251 static int
252 iicsmb_sendb(device_t dev, u_char slave, char byte)
253 {
254 	device_t parent = device_get_parent(dev);
255 	int error, sent;
256 
257 	error = iicbus_start(parent, slave & ~LSB);
258 
259 	if (!error) {
260 		error = iicbus_write(parent, &byte, 1, &sent);
261 
262 		iicbus_stop(parent);
263 	}
264 
265 	return (error);
266 }
267 
268 static int
269 iicsmb_recvb(device_t dev, u_char slave, char *byte)
270 {
271 	device_t parent = device_get_parent(dev);
272 	int error, read;
273 
274 	error = iicbus_start(parent, slave | LSB);
275 
276 	if (!error)
277 		error = iicbus_read(parent, byte, 1, &read);
278 
279 	return (error);
280 }
281 
282 static int
283 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
284 {
285 	device_t parent = device_get_parent(dev);
286 	int error, sent;
287 
288 	error = iicbus_start(parent, slave & ~LSB);
289 
290 	if (!error) {
291 		if (!(error = iicbus_write(parent, &cmd, 1, &sent)))
292 			error = iicbus_write(parent, &byte, 1, &sent);
293 
294 		iicbus_stop(parent);
295 	}
296 
297 	return (error);
298 }
299 
300 static int
301 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
302 {
303 	device_t parent = device_get_parent(dev);
304 	int error, sent;
305 
306 	char low = (char)(word & 0xff);
307 	char high = (char)((word & 0xff00) >> 8);
308 
309 	error = iicbus_start(parent, slave & ~LSB);
310 
311 	if (!error) {
312 		if (!(error = iicbus_write(parent, &cmd, 1, &sent)))
313 		  if (!(error = iicbus_write(parent, &low, 1, &sent)))
314 		    error = iicbus_write(parent, &high, 1, &sent);
315 
316 		iicbus_stop(parent);
317 	}
318 
319 	return (error);
320 }
321 
322 static int
323 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
324 {
325 	device_t parent = device_get_parent(dev);
326 	int error, sent, read;
327 
328 	if ((error = iicbus_start(parent, slave & ~LSB)))
329 		goto error;
330 
331 	if ((error = iicbus_write(parent, &cmd, 1, &sent)))
332 		goto error;
333 
334 	if ((error = iicbus_repeated_start(parent, slave | LSB)))
335 		goto error;
336 
337 	if ((error = iicbus_read(parent, byte, 1, &read)))
338 		goto error;
339 
340 error:
341 	return (error);
342 }
343 
344 #define BUF2SHORT(low,high) \
345 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
346 
347 static int
348 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
349 {
350 	device_t parent = device_get_parent(dev);
351 	int error, sent, read;
352 	char buf[2];
353 
354 	if ((error = iicbus_start(parent, slave & ~LSB)))
355 		goto error;
356 
357 	if ((error = iicbus_write(parent, &cmd, 1, &sent)))
358 		goto error;
359 
360 	if ((error = iicbus_repeated_start(parent, slave | LSB)))
361 		goto error;
362 
363 	if ((error = iicbus_read(parent, buf, 2, &read)))
364 		goto error;
365 
366 	/* first, receive low, then high byte */
367 	*word = BUF2SHORT(buf[0], buf[1]);
368 
369 error:
370 	return (error);
371 }
372 
373 static int
374 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
375 {
376 	device_t parent = device_get_parent(dev);
377 	int error, sent, read;
378 	char buf[2];
379 
380 	if ((error = iicbus_start(parent, slave & ~LSB)))
381 		goto error;
382 
383 	if ((error = iicbus_write(parent, &cmd, 1, &sent)))
384 		goto error;
385 
386 	/* first, send low, then high byte */
387 	buf[0] = (char)(sdata & 0xff);
388 	buf[1] = (char)((sdata & 0xff00) >> 8);
389 
390 	if ((error = iicbus_write(parent, buf, 2, &sent)))
391 		goto error;
392 
393 	if ((error = iicbus_repeated_start(parent, slave | LSB)))
394 		goto error;
395 
396 	if ((error = iicbus_read(parent, buf, 2, &read)))
397 		goto error;
398 
399 	/* first, receive low, then high byte */
400 	*rdata = BUF2SHORT(buf[0], buf[1]);
401 
402 error:
403 	return (error);
404 }
405 
406 static int
407 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
408 {
409 	device_t parent = device_get_parent(dev);
410 	int error, sent;
411 
412 	if ((error = iicbus_start(parent, slave & ~LSB)))
413 		goto error;
414 
415 	if ((error = iicbus_write(parent, &cmd, 1, &sent)))
416 		goto error;
417 
418 	if ((error = iicbus_write(parent, buf, (int)count, &sent)))
419 		goto error;
420 
421 	if ((error = iicbus_stop(parent)))
422 		goto error;
423 
424 error:
425 	return (error);
426 }
427 
428 static int
429 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
430 {
431 	device_t parent = device_get_parent(dev);
432 	int error, sent, read;
433 
434 	if ((error = iicbus_start(parent, slave & ~LSB)))
435 		goto error;
436 
437 	if ((error = iicbus_write(parent, &cmd, 1, &sent)))
438 		goto error;
439 
440 	if ((error = iicbus_repeated_start(parent, slave | LSB)))
441 		goto error;
442 
443 	if ((error = iicbus_read(parent, buf, (int)count, &read)))
444 		goto error;
445 
446 error:
447 	return (error);
448 }
449 
450 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
451