Deleted Added
sdiff udiff text old ( 46743 ) new ( 49195 )
full compact
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.3 1999/05/08 21:59:05 dfr Exp $
27 *
28 */
29
30/*
31 * I2C to SMB bridge
32 *
33 * Example:
34 *
35 * smb bttv
36 * \ /
37 * smbus
38 * / \
39 * iicsmb bti2c
40 * |
41 * iicbus
42 * / | \
43 * iicbb pcf ...
44 * |
45 * lpbb
46 */
47
48#include <sys/param.h>
49#include <sys/kernel.h>
50#include <sys/systm.h>
51#include <sys/module.h>
52#include <sys/bus.h>
53#include <sys/conf.h>
54#include <sys/buf.h>
55#include <sys/uio.h>
56#include <sys/malloc.h>
57
58#include <machine/clock.h>
59
60#include <dev/iicbus/iiconf.h>
61#include <dev/iicbus/iicbus.h>
62
63#include <dev/smbus/smbconf.h>
64
65#include "iicbus_if.h"
66#include "smbus_if.h"
67
68struct iicsmb_softc {
69
70#define SMB_WAITING_ADDR 0x0
71#define SMB_WAITING_LOW 0x1
72#define SMB_WAITING_HIGH 0x2
73#define SMB_DONE 0x3
74 int state;
75
76 u_char devaddr; /* slave device address */
77
78 char low; /* low byte received first */
79 char high; /* high byte */
80
81 device_t smbus;
82};
83
84static int iicsmb_probe(device_t);
85static int iicsmb_attach(device_t);
86
87static void iicsmb_intr(device_t dev, int event, char *buf);
88static int iicsmb_callback(device_t dev, int index, caddr_t data);
89static int iicsmb_quick(device_t dev, u_char slave, int how);
90static int iicsmb_sendb(device_t dev, u_char slave, char byte);
91static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
92static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
93static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
94static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
95static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
96static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
97static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
98static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
99
100static devclass_t iicsmb_devclass;
101
102static device_method_t iicsmb_methods[] = {
103 /* device interface */
104 DEVMETHOD(device_probe, iicsmb_probe),
105 DEVMETHOD(device_attach, iicsmb_attach),
106
107 /* bus interface */
108 DEVMETHOD(bus_print_child, bus_generic_print_child),
109
110 /* iicbus interface */
111 DEVMETHOD(iicbus_intr, iicsmb_intr),
112
113 /* smbus interface */
114 DEVMETHOD(smbus_callback, iicsmb_callback),
115 DEVMETHOD(smbus_quick, iicsmb_quick),
116 DEVMETHOD(smbus_sendb, iicsmb_sendb),
117 DEVMETHOD(smbus_recvb, iicsmb_recvb),
118 DEVMETHOD(smbus_writeb, iicsmb_writeb),
119 DEVMETHOD(smbus_writew, iicsmb_writew),
120 DEVMETHOD(smbus_readb, iicsmb_readb),
121 DEVMETHOD(smbus_readw, iicsmb_readw),
122 DEVMETHOD(smbus_pcall, iicsmb_pcall),
123 DEVMETHOD(smbus_bwrite, iicsmb_bwrite),
124 DEVMETHOD(smbus_bread, iicsmb_bread),
125
126 { 0, 0 }
127};
128
129static driver_t iicsmb_driver = {
130 "iicsmb",
131 iicsmb_methods,
132 sizeof(struct iicsmb_softc),
133};
134
135static int
136iicsmb_probe(device_t dev)
137{
138 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
139
140 sc->smbus = smbus_alloc_bus(dev);
141
142 if (!sc->smbus)
143 return (EINVAL); /* XXX don't know what to return else */
144
145 return (0);
146}
147
148static int
149iicsmb_attach(device_t dev)
150{
151 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
152
153 /* probe and attach the smbus */
154 device_probe_and_attach(sc->smbus);
155
156 return (0);
157}
158
159/*
160 * iicsmb_intr()
161 *
162 * iicbus interrupt handler
163 */
164static void
165iicsmb_intr(device_t dev, int event, char *buf)
166{
167 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
168
169 switch (event) {
170 case INTR_GENERAL:
171 case INTR_START:
172 sc->state = SMB_WAITING_ADDR;
173 break;
174
175 case INTR_STOP:
176 /* call smbus intr handler */
177 smbus_intr(sc->smbus, sc->devaddr,
178 sc->low, sc->high, SMB_ENOERR);
179 break;
180
181 case INTR_RECEIVE:
182 switch (sc->state) {
183 case SMB_DONE:
184 /* XXX too much data, discard */
185 printf("%s: too much data from 0x%x\n", __FUNCTION__,
186 sc->devaddr & 0xff);
187 goto end;
188
189 case SMB_WAITING_ADDR:
190 sc->devaddr = (u_char)*buf;
191 sc->state = SMB_WAITING_LOW;
192 break;
193
194 case SMB_WAITING_LOW:
195 sc->low = *buf;
196 sc->state = SMB_WAITING_HIGH;
197 break;
198
199 case SMB_WAITING_HIGH:
200 sc->high = *buf;
201 sc->state = SMB_DONE;
202 break;
203 }
204end:
205 break;
206
207 case INTR_TRANSMIT:
208 case INTR_NOACK:
209 break;
210
211 case INTR_ERROR:
212 switch (*buf) {
213 case IIC_EBUSERR:
214 smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
215 break;
216
217 default:
218 printf("%s unknown error 0x%x!\n", __FUNCTION__,
219 (int)*buf);
220 break;
221 }
222 break;
223
224 default:
225 panic("%s: unknown event (%d)!", __FUNCTION__, event);
226 }
227
228 return;
229}
230
231static int
232iicsmb_callback(device_t dev, int index, caddr_t data)
233{
234 device_t parent = device_get_parent(dev);
235 int error = 0;
236 int how;
237
238 switch (index) {
239 case SMB_REQUEST_BUS:
240 /* request underlying iicbus */
241 how = *(int *)data;
242 error = iicbus_request_bus(parent, dev, how);
243 break;
244
245 case SMB_RELEASE_BUS:
246 /* release underlying iicbus */
247 error = iicbus_release_bus(parent, dev);
248 break;
249
250 default:
251 error = EINVAL;
252 }
253
254 return (error);
255}
256
257static int
258iicsmb_quick(device_t dev, u_char slave, int how)
259{
260 device_t parent = device_get_parent(dev);
261 int error;
262
263 switch (how) {
264 case SMB_QWRITE:
265 error = iicbus_start(parent, slave & ~LSB, 0);
266 break;
267
268 case SMB_QREAD:
269 error = iicbus_start(parent, slave | LSB, 0);
270 break;
271
272 default:
273 error = EINVAL;
274 break;
275 }
276
277 if (!error)
278 error = iicbus_stop(parent);
279
280 return (error);
281}
282
283static int
284iicsmb_sendb(device_t dev, u_char slave, char byte)
285{
286 device_t parent = device_get_parent(dev);
287 int error, sent;
288
289 error = iicbus_start(parent, slave & ~LSB, 0);
290
291 if (!error) {
292 error = iicbus_write(parent, &byte, 1, &sent, 0);
293
294 iicbus_stop(parent);
295 }
296
297 return (error);
298}
299
300static int
301iicsmb_recvb(device_t dev, u_char slave, char *byte)
302{
303 device_t parent = device_get_parent(dev);
304 int error, read;
305
306 error = iicbus_start(parent, slave | LSB, 0);
307
308 if (!error) {
309 error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0);
310
311 iicbus_stop(parent);
312 }
313
314 return (error);
315}
316
317static int
318iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
319{
320 device_t parent = device_get_parent(dev);
321 int error, sent;
322
323 error = iicbus_start(parent, slave & ~LSB, 0);
324
325 if (!error) {
326 if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
327 error = iicbus_write(parent, &byte, 1, &sent, 0);
328
329 iicbus_stop(parent);
330 }
331
332 return (error);
333}
334
335static int
336iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
337{
338 device_t parent = device_get_parent(dev);
339 int error, sent;
340
341 char low = (char)(word & 0xff);
342 char high = (char)((word & 0xff00) >> 8);
343
344 error = iicbus_start(parent, slave & ~LSB, 0);
345
346 if (!error) {
347 if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
348 if (!(error = iicbus_write(parent, &low, 1, &sent, 0)))
349 error = iicbus_write(parent, &high, 1, &sent, 0);
350
351 iicbus_stop(parent);
352 }
353
354 return (error);
355}
356
357static int
358iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
359{
360 device_t parent = device_get_parent(dev);
361 int error, sent, read;
362
363 if ((error = iicbus_start(parent, slave & ~LSB, 0)))
364 return (error);
365
366 if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
367 goto error;
368
369 if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
370 goto error;
371
372 if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0)))
373 goto error;
374
375error:
376 iicbus_stop(parent);
377 return (error);
378}
379
380#define BUF2SHORT(low,high) \
381 ((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
382
383static int
384iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
385{
386 device_t parent = device_get_parent(dev);
387 int error, sent, read;
388 char buf[2];
389
390 if ((error = iicbus_start(parent, slave & ~LSB, 0)))
391 return (error);
392
393 if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
394 goto error;
395
396 if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
397 goto error;
398
399 if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
400 goto error;
401
402 /* first, receive low, then high byte */
403 *word = BUF2SHORT(buf[0], buf[1]);
404
405error:
406 iicbus_stop(parent);
407 return (error);
408}
409
410static int
411iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
412{
413 device_t parent = device_get_parent(dev);
414 int error, sent, read;
415 char buf[2];
416
417 if ((error = iicbus_start(parent, slave & ~LSB, 0)))
418 return (error);
419
420 if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
421 goto error;
422
423 /* first, send low, then high byte */
424 buf[0] = (char)(sdata & 0xff);
425 buf[1] = (char)((sdata & 0xff00) >> 8);
426
427 if ((error = iicbus_write(parent, buf, 2, &sent, 0)))
428 goto error;
429
430 if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
431 goto error;
432
433 if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
434 goto error;
435
436 /* first, receive low, then high byte */
437 *rdata = BUF2SHORT(buf[0], buf[1]);
438
439error:
440 iicbus_stop(parent);
441 return (error);
442}
443
444static int
445iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
446{
447 device_t parent = device_get_parent(dev);
448 int error, sent;
449
450 if ((error = iicbus_start(parent, slave & ~LSB, 0)))
451 goto error;
452
453 if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
454 goto error;
455
456 if ((error = iicbus_write(parent, buf, (int)count, &sent, 0)))
457 goto error;
458
459 if ((error = iicbus_stop(parent)))
460 goto error;
461
462error:
463 return (error);
464}
465
466static int
467iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
468{
469 device_t parent = device_get_parent(dev);
470 int error, sent, read;
471
472 if ((error = iicbus_start(parent, slave & ~LSB, 0)))
473 return (error);
474
475 if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
476 goto error;
477
478 if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
479 goto error;
480
481 if ((error = iicbus_read(parent, buf, (int)count, &read,
482 IIC_LAST_READ, 0)))
483 goto error;
484
485error:
486 iicbus_stop(parent);
487 return (error);
488}
489
490DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);