xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 6c05f3a74f30934ee60919cc97e16ec69b542b06)
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", DEVICE_UNIT_ANY);
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", DEVICE_UNIT_ANY);
156 
157 	/* probe and attach the smbus */
158 	bus_attach_children(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 	mtx_destroy(&sc->lock);
170 
171 	return (0);
172 }
173 
174 /*
175  * iicsmb_intr()
176  *
177  * iicbus interrupt handler
178  */
179 static int
180 iicsmb_intr(device_t dev, int event, char *buf)
181 {
182 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
183 
184 	mtx_lock(&sc->lock);
185 	switch (event) {
186 	case INTR_GENERAL:
187 	case INTR_START:
188 		sc->state = SMB_WAITING_ADDR;
189 		break;
190 
191 	case INTR_STOP:
192 		/* call smbus intr handler */
193 		smbus_intr(sc->smbus, sc->devaddr,
194 				sc->low, sc->high, SMB_ENOERR);
195 		break;
196 
197 	case INTR_RECEIVE:
198 		switch (sc->state) {
199 		case SMB_DONE:
200 			/* XXX too much data, discard */
201 			printf("%s: too much data from 0x%x\n", __func__,
202 				sc->devaddr & 0xff);
203 			goto end;
204 
205 		case SMB_WAITING_ADDR:
206 			sc->devaddr = (u_char)*buf;
207 			sc->state = SMB_WAITING_LOW;
208 			break;
209 
210 		case SMB_WAITING_LOW:
211 			sc->low = *buf;
212 			sc->state = SMB_WAITING_HIGH;
213 			break;
214 
215 		case SMB_WAITING_HIGH:
216 			sc->high = *buf;
217 			sc->state = SMB_DONE;
218 			break;
219 		}
220 end:
221 		break;
222 
223 	case INTR_TRANSMIT:
224 	case INTR_NOACK:
225 		break;
226 
227 	case INTR_ERROR:
228 		switch (*buf) {
229 		case IIC_EBUSERR:
230 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
231 			break;
232 
233 		default:
234 			printf("%s unknown error 0x%x!\n", __func__,
235 								(int)*buf);
236 			break;
237 		}
238 		break;
239 
240 	default:
241 		panic("%s: unknown event (%d)!", __func__, event);
242 	}
243 	mtx_unlock(&sc->lock);
244 
245 	return (0);
246 }
247 
248 static int
249 iicsmb_callback(device_t dev, int index, void *data)
250 {
251 	device_t parent = device_get_parent(dev);
252 	int error = 0;
253 	int how;
254 
255 	switch (index) {
256 	case SMB_REQUEST_BUS:
257 		/* request underlying iicbus */
258 		how = *(int *)data;
259 		error = iicbus_request_bus(parent, dev, how);
260 		break;
261 
262 	case SMB_RELEASE_BUS:
263 		/* release underlying iicbus */
264 		error = iicbus_release_bus(parent, dev);
265 		break;
266 
267 	default:
268 		error = EINVAL;
269 	}
270 
271 	return (error);
272 }
273 
274 static int
275 iic2smb_error(int error)
276 {
277 	switch (error) {
278 	case IIC_NOERR:
279 		return (SMB_ENOERR);
280 	case IIC_EBUSERR:
281 		return (SMB_EBUSERR);
282 	case IIC_ENOACK:
283 		return (SMB_ENOACK);
284 	case IIC_ETIMEOUT:
285 		return (SMB_ETIMEOUT);
286 	case IIC_EBUSBSY:
287 		return (SMB_EBUSY);
288 	case IIC_ESTATUS:
289 		return (SMB_EBUSERR);
290 	case IIC_EUNDERFLOW:
291 		return (SMB_EBUSERR);
292 	case IIC_EOVERFLOW:
293 		return (SMB_EBUSERR);
294 	case IIC_ENOTSUPP:
295 		return (SMB_ENOTSUPP);
296 	case IIC_ENOADDR:
297 		return (SMB_EBUSERR);
298 	case IIC_ERESOURCE:
299 		return (SMB_EBUSERR);
300 	default:
301 		return (SMB_EBUSERR);
302 	}
303 }
304 
305 #define	TRANSFER_MSGS(dev, msgs)	iicbus_transfer(dev, msgs, nitems(msgs))
306 
307 static int
308 iicsmb_quick(device_t dev, u_char slave, int how)
309 {
310 	struct iic_msg msgs[] = {
311 	     { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
312 	};
313 	int error;
314 
315 	switch (how) {
316 	case SMB_QWRITE:
317 	case SMB_QREAD:
318 		break;
319 	default:
320 		return (SMB_EINVAL);
321 	}
322 
323 	error = TRANSFER_MSGS(dev, msgs);
324 	return (iic2smb_error(error));
325 }
326 
327 static int
328 iicsmb_sendb(device_t dev, u_char slave, char byte)
329 {
330 	struct iic_msg msgs[] = {
331 	     { slave, IIC_M_WR, 1, &byte },
332 	};
333 	int error;
334 
335 	error = TRANSFER_MSGS(dev, msgs);
336 	return (iic2smb_error(error));
337 }
338 
339 static int
340 iicsmb_recvb(device_t dev, u_char slave, char *byte)
341 {
342 	struct iic_msg msgs[] = {
343 	     { slave, IIC_M_RD, 1, byte },
344 	};
345 	int error;
346 
347 	error = TRANSFER_MSGS(dev, msgs);
348 	return (iic2smb_error(error));
349 }
350 
351 static int
352 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
353 {
354 	uint8_t bytes[] = { cmd, byte };
355 	struct iic_msg msgs[] = {
356 	     { slave, IIC_M_WR, nitems(bytes), bytes },
357 	};
358 	int error;
359 
360 	error = TRANSFER_MSGS(dev, msgs);
361 	return (iic2smb_error(error));
362 }
363 
364 static int
365 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
366 {
367 	uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
368 	struct iic_msg msgs[] = {
369 	     { slave, IIC_M_WR, nitems(bytes), bytes },
370 	};
371 	int error;
372 
373 	error = TRANSFER_MSGS(dev, msgs);
374 	return (iic2smb_error(error));
375 }
376 
377 static int
378 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
379 {
380 	struct iic_msg msgs[] = {
381 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
382 	     { slave, IIC_M_RD, 1, byte },
383 	};
384 	int error;
385 
386 	error = TRANSFER_MSGS(dev, msgs);
387 	return (iic2smb_error(error));
388 }
389 
390 static int
391 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
392 {
393 	uint8_t buf[2];
394 	struct iic_msg msgs[] = {
395 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
396 	     { slave, IIC_M_RD, nitems(buf), buf },
397 	};
398 	int error;
399 
400 	error = TRANSFER_MSGS(dev, msgs);
401 	if (error == 0)
402 		*word = ((uint16_t)buf[1] << 8) | buf[0];
403 	return (iic2smb_error(error));
404 }
405 
406 static int
407 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
408 {
409 	uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
410 	uint8_t out[2];
411 	struct iic_msg msgs[] = {
412 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
413 	     { slave, IIC_M_RD, nitems(out), out },
414 	};
415 	int error;
416 
417 	error = TRANSFER_MSGS(dev, msgs);
418 	if (error == 0)
419 		*rdata = ((uint16_t)out[1] << 8) | out[0];
420 	return (iic2smb_error(error));
421 }
422 
423 static int
424 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
425 {
426 	uint8_t bytes[2] = { cmd, count };
427 	struct iic_msg msgs[] = {
428 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
429 	     { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
430 	};
431 	int error;
432 
433 	if (count > SMB_MAXBLOCKSIZE || count == 0)
434 		return (SMB_EINVAL);
435 	error = TRANSFER_MSGS(dev, msgs);
436 	return (iic2smb_error(error));
437 }
438 
439 static int
440 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
441 {
442 	struct iic_msg msgs[] = {
443 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
444 	     { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
445 	};
446 	struct iic_msg block_msg[] = {
447 	     { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
448 	};
449 	device_t parent = device_get_parent(dev);
450 	int error;
451 
452 	/* Have to do this because the command is split in two transfers. */
453 	error = iicbus_request_bus(parent, dev, IIC_WAIT | IIC_RECURSIVE);
454 	if (error == 0)
455 		error = TRANSFER_MSGS(dev, msgs);
456 	if (error == 0) {
457 		/*
458 		 * If the slave offers an empty or a too long reply,
459 		 * read one byte to generate the stop or abort.
460 		 */
461 		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
462 			block_msg[0].len = 1;
463 		else
464 			block_msg[0].len = *count;
465 		error = TRANSFER_MSGS(dev, block_msg);
466 		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
467 			error = SMB_EINVAL;
468 	}
469 	(void)iicbus_release_bus(parent, dev);
470 	return (iic2smb_error(error));
471 }
472 
473 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, 0, 0);
474 DRIVER_MODULE(smbus, iicsmb, smbus_driver, 0, 0);
475 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
476 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
477 MODULE_VERSION(iicsmb, 1);
478