xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1998, 2001 Nicolas Souchu
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
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/bus.h>
50 #include <sys/kernel.h>
51 #include <sys/lock.h>
52 #include <sys/module.h>
53 #include <sys/mutex.h>
54 #include <sys/systm.h>
55 #include <sys/uio.h>
56 
57 #include <dev/iicbus/iiconf.h>
58 #include <dev/iicbus/iicbus.h>
59 
60 #include <dev/smbus/smb.h>
61 #include <dev/smbus/smbconf.h>
62 
63 #include "iicbus_if.h"
64 #include "smbus_if.h"
65 
66 struct iicsmb_softc {
67 
68 #define SMB_WAITING_ADDR	0x0
69 #define SMB_WAITING_LOW		0x1
70 #define SMB_WAITING_HIGH	0x2
71 #define SMB_DONE		0x3
72 	int state;
73 
74 	u_char devaddr;			/* slave device address */
75 
76 	char low;			/* low byte received first */
77 	char high;			/* high byte */
78 
79 	struct mtx lock;
80 	device_t smbus;
81 };
82 
83 static int iicsmb_probe(device_t);
84 static int iicsmb_attach(device_t);
85 static int iicsmb_detach(device_t);
86 static void iicsmb_identify(driver_t *driver, device_t parent);
87 
88 static int iicsmb_intr(device_t dev, int event, char *buf);
89 static int iicsmb_callback(device_t dev, int index, void *data);
90 static int iicsmb_quick(device_t dev, u_char slave, int how);
91 static int iicsmb_sendb(device_t dev, u_char slave, char byte);
92 static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
93 static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
94 static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
95 static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
96 static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
97 static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
98 static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
99 static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
100 
101 static device_method_t iicsmb_methods[] = {
102 	/* device interface */
103 	DEVMETHOD(device_identify,	iicsmb_identify),
104 	DEVMETHOD(device_probe,		iicsmb_probe),
105 	DEVMETHOD(device_attach,	iicsmb_attach),
106 	DEVMETHOD(device_detach,	iicsmb_detach),
107 
108 	/* iicbus interface */
109 	DEVMETHOD(iicbus_intr,		iicsmb_intr),
110 
111 	/* smbus interface */
112 	DEVMETHOD(smbus_callback,	iicsmb_callback),
113 	DEVMETHOD(smbus_quick,		iicsmb_quick),
114 	DEVMETHOD(smbus_sendb,		iicsmb_sendb),
115 	DEVMETHOD(smbus_recvb,		iicsmb_recvb),
116 	DEVMETHOD(smbus_writeb,		iicsmb_writeb),
117 	DEVMETHOD(smbus_writew,		iicsmb_writew),
118 	DEVMETHOD(smbus_readb,		iicsmb_readb),
119 	DEVMETHOD(smbus_readw,		iicsmb_readw),
120 	DEVMETHOD(smbus_pcall,		iicsmb_pcall),
121 	DEVMETHOD(smbus_bwrite,		iicsmb_bwrite),
122 	DEVMETHOD(smbus_bread,		iicsmb_bread),
123 
124 	DEVMETHOD_END
125 };
126 
127 static driver_t iicsmb_driver = {
128 	"iicsmb",
129 	iicsmb_methods,
130 	sizeof(struct iicsmb_softc),
131 };
132 
133 static void
134 iicsmb_identify(driver_t *driver, device_t parent)
135 {
136 
137 	if (device_find_child(parent, "iicsmb", -1) == NULL)
138 		BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
139 }
140 
141 static int
142 iicsmb_probe(device_t dev)
143 {
144 	device_set_desc(dev, "SMBus over I2C bridge");
145 	return (BUS_PROBE_NOWILDCARD);
146 }
147 
148 static int
149 iicsmb_attach(device_t dev)
150 {
151 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
152 
153 	mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
154 
155 	sc->smbus = device_add_child(dev, "smbus", -1);
156 
157 	/* probe and attach the smbus */
158 	bus_generic_attach(dev);
159 
160 	return (0);
161 }
162 
163 static int
164 iicsmb_detach(device_t dev)
165 {
166 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
167 
168 	bus_generic_detach(dev);
169 	device_delete_children(dev);
170 	mtx_destroy(&sc->lock);
171 
172 	return (0);
173 }
174 
175 /*
176  * iicsmb_intr()
177  *
178  * iicbus interrupt handler
179  */
180 static int
181 iicsmb_intr(device_t dev, int event, char *buf)
182 {
183 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
184 
185 	mtx_lock(&sc->lock);
186 	switch (event) {
187 	case INTR_GENERAL:
188 	case INTR_START:
189 		sc->state = SMB_WAITING_ADDR;
190 		break;
191 
192 	case INTR_STOP:
193 		/* call smbus intr handler */
194 		smbus_intr(sc->smbus, sc->devaddr,
195 				sc->low, sc->high, SMB_ENOERR);
196 		break;
197 
198 	case INTR_RECEIVE:
199 		switch (sc->state) {
200 		case SMB_DONE:
201 			/* XXX too much data, discard */
202 			printf("%s: too much data from 0x%x\n", __func__,
203 				sc->devaddr & 0xff);
204 			goto end;
205 
206 		case SMB_WAITING_ADDR:
207 			sc->devaddr = (u_char)*buf;
208 			sc->state = SMB_WAITING_LOW;
209 			break;
210 
211 		case SMB_WAITING_LOW:
212 			sc->low = *buf;
213 			sc->state = SMB_WAITING_HIGH;
214 			break;
215 
216 		case SMB_WAITING_HIGH:
217 			sc->high = *buf;
218 			sc->state = SMB_DONE;
219 			break;
220 		}
221 end:
222 		break;
223 
224 	case INTR_TRANSMIT:
225 	case INTR_NOACK:
226 		break;
227 
228 	case INTR_ERROR:
229 		switch (*buf) {
230 		case IIC_EBUSERR:
231 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
232 			break;
233 
234 		default:
235 			printf("%s unknown error 0x%x!\n", __func__,
236 								(int)*buf);
237 			break;
238 		}
239 		break;
240 
241 	default:
242 		panic("%s: unknown event (%d)!", __func__, event);
243 	}
244 	mtx_unlock(&sc->lock);
245 
246 	return (0);
247 }
248 
249 static int
250 iicsmb_callback(device_t dev, int index, void *data)
251 {
252 	device_t parent = device_get_parent(dev);
253 	int error = 0;
254 	int how;
255 
256 	switch (index) {
257 	case SMB_REQUEST_BUS:
258 		/* request underlying iicbus */
259 		how = *(int *)data;
260 		error = iicbus_request_bus(parent, dev, how);
261 		break;
262 
263 	case SMB_RELEASE_BUS:
264 		/* release underlying iicbus */
265 		error = iicbus_release_bus(parent, dev);
266 		break;
267 
268 	default:
269 		error = EINVAL;
270 	}
271 
272 	return (error);
273 }
274 
275 static int
276 iic2smb_error(int error)
277 {
278 	switch (error) {
279 	case IIC_NOERR:
280 		return (SMB_ENOERR);
281 	case IIC_EBUSERR:
282 		return (SMB_EBUSERR);
283 	case IIC_ENOACK:
284 		return (SMB_ENOACK);
285 	case IIC_ETIMEOUT:
286 		return (SMB_ETIMEOUT);
287 	case IIC_EBUSBSY:
288 		return (SMB_EBUSY);
289 	case IIC_ESTATUS:
290 		return (SMB_EBUSERR);
291 	case IIC_EUNDERFLOW:
292 		return (SMB_EBUSERR);
293 	case IIC_EOVERFLOW:
294 		return (SMB_EBUSERR);
295 	case IIC_ENOTSUPP:
296 		return (SMB_ENOTSUPP);
297 	case IIC_ENOADDR:
298 		return (SMB_EBUSERR);
299 	case IIC_ERESOURCE:
300 		return (SMB_EBUSERR);
301 	default:
302 		return (SMB_EBUSERR);
303 	}
304 }
305 
306 #define	TRANSFER_MSGS(dev, msgs)	iicbus_transfer(dev, msgs, nitems(msgs))
307 
308 static int
309 iicsmb_quick(device_t dev, u_char slave, int how)
310 {
311 	struct iic_msg msgs[] = {
312 	     { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
313 	};
314 	int error;
315 
316 	switch (how) {
317 	case SMB_QWRITE:
318 	case SMB_QREAD:
319 		break;
320 	default:
321 		return (SMB_EINVAL);
322 	}
323 
324 	error = TRANSFER_MSGS(dev, msgs);
325 	return (iic2smb_error(error));
326 }
327 
328 static int
329 iicsmb_sendb(device_t dev, u_char slave, char byte)
330 {
331 	struct iic_msg msgs[] = {
332 	     { slave, IIC_M_WR, 1, &byte },
333 	};
334 	int error;
335 
336 	error = TRANSFER_MSGS(dev, msgs);
337 	return (iic2smb_error(error));
338 }
339 
340 static int
341 iicsmb_recvb(device_t dev, u_char slave, char *byte)
342 {
343 	struct iic_msg msgs[] = {
344 	     { slave, IIC_M_RD, 1, byte },
345 	};
346 	int error;
347 
348 	error = TRANSFER_MSGS(dev, msgs);
349 	return (iic2smb_error(error));
350 }
351 
352 static int
353 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
354 {
355 	uint8_t bytes[] = { cmd, byte };
356 	struct iic_msg msgs[] = {
357 	     { slave, IIC_M_WR, nitems(bytes), bytes },
358 	};
359 	int error;
360 
361 	error = TRANSFER_MSGS(dev, msgs);
362 	return (iic2smb_error(error));
363 }
364 
365 static int
366 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
367 {
368 	uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
369 	struct iic_msg msgs[] = {
370 	     { slave, IIC_M_WR, nitems(bytes), bytes },
371 	};
372 	int error;
373 
374 	error = TRANSFER_MSGS(dev, msgs);
375 	return (iic2smb_error(error));
376 }
377 
378 static int
379 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
380 {
381 	struct iic_msg msgs[] = {
382 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
383 	     { slave, IIC_M_RD, 1, byte },
384 	};
385 	int error;
386 
387 	error = TRANSFER_MSGS(dev, msgs);
388 	return (iic2smb_error(error));
389 }
390 
391 static int
392 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
393 {
394 	uint8_t buf[2];
395 	struct iic_msg msgs[] = {
396 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
397 	     { slave, IIC_M_RD, nitems(buf), buf },
398 	};
399 	int error;
400 
401 	error = TRANSFER_MSGS(dev, msgs);
402 	if (error == 0)
403 		*word = ((uint16_t)buf[1] << 8) | buf[0];
404 	return (iic2smb_error(error));
405 }
406 
407 static int
408 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
409 {
410 	uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
411 	uint8_t out[2];
412 	struct iic_msg msgs[] = {
413 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
414 	     { slave, IIC_M_RD, nitems(out), out },
415 	};
416 	int error;
417 
418 	error = TRANSFER_MSGS(dev, msgs);
419 	if (error == 0)
420 		*rdata = ((uint16_t)out[1] << 8) | out[0];
421 	return (iic2smb_error(error));
422 }
423 
424 static int
425 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
426 {
427 	uint8_t bytes[2] = { cmd, count };
428 	struct iic_msg msgs[] = {
429 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
430 	     { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
431 	};
432 	int error;
433 
434 	if (count > SMB_MAXBLOCKSIZE || count == 0)
435 		return (SMB_EINVAL);
436 	error = TRANSFER_MSGS(dev, msgs);
437 	return (iic2smb_error(error));
438 }
439 
440 static int
441 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
442 {
443 	struct iic_msg msgs[] = {
444 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
445 	     { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
446 	};
447 	struct iic_msg block_msg[] = {
448 	     { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
449 	};
450 	device_t parent = device_get_parent(dev);
451 	int error;
452 
453 	/* Have to do this because the command is split in two transfers. */
454 	error = iicbus_request_bus(parent, dev, IIC_WAIT | IIC_RECURSIVE);
455 	if (error == 0)
456 		error = TRANSFER_MSGS(dev, msgs);
457 	if (error == 0) {
458 		/*
459 		 * If the slave offers an empty or a too long reply,
460 		 * read one byte to generate the stop or abort.
461 		 */
462 		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
463 			block_msg[0].len = 1;
464 		else
465 			block_msg[0].len = *count;
466 		error = TRANSFER_MSGS(dev, block_msg);
467 		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
468 			error = SMB_EINVAL;
469 	}
470 	(void)iicbus_release_bus(parent, dev);
471 	return (iic2smb_error(error));
472 }
473 
474 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, 0, 0);
475 DRIVER_MODULE(smbus, iicsmb, smbus_driver, 0, 0);
476 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
477 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
478 MODULE_VERSION(iicsmb, 1);
479