1/*-
2 * Copyright (c) 1998, 2001 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
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/sys/dev/iicbus/iicsmb.c 310522 2016-12-24 14:48:49Z avg $");
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/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
66struct 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
83static int iicsmb_probe(device_t);
84static int iicsmb_attach(device_t);
85static int iicsmb_detach(device_t);
86static void iicsmb_identify(driver_t *driver, device_t parent);
87
88static int iicsmb_intr(device_t dev, int event, char *buf);
89static int iicsmb_callback(device_t dev, int index, void *data);
90static int iicsmb_quick(device_t dev, u_char slave, int how);
91static int iicsmb_sendb(device_t dev, u_char slave, char byte);
92static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
93static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
94static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
95static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
96static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
97static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
98static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
99static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
100
101static devclass_t iicsmb_devclass;
102
103static device_method_t iicsmb_methods[] = {
104	/* device interface */
105	DEVMETHOD(device_identify,	iicsmb_identify),
106	DEVMETHOD(device_probe,		iicsmb_probe),
107	DEVMETHOD(device_attach,	iicsmb_attach),
108	DEVMETHOD(device_detach,	iicsmb_detach),
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	DEVMETHOD_END
127};
128
129static driver_t iicsmb_driver = {
130	"iicsmb",
131	iicsmb_methods,
132	sizeof(struct iicsmb_softc),
133};
134
135static void
136iicsmb_identify(driver_t *driver, device_t parent)
137{
138
139	if (device_find_child(parent, "iicsmb", -1) == NULL)
140		BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
141}
142
143static int
144iicsmb_probe(device_t dev)
145{
146	device_set_desc(dev, "SMBus over I2C bridge");
147	return (BUS_PROBE_NOWILDCARD);
148}
149
150static int
151iicsmb_attach(device_t dev)
152{
153	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
154
155	mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
156
157	sc->smbus = device_add_child(dev, "smbus", -1);
158
159	/* probe and attach the smbus */
160	bus_generic_attach(dev);
161
162	return (0);
163}
164
165static int
166iicsmb_detach(device_t dev)
167{
168	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
169
170	bus_generic_detach(dev);
171	device_delete_children(dev);
172	mtx_destroy(&sc->lock);
173
174	return (0);
175}
176
177/*
178 * iicsmb_intr()
179 *
180 * iicbus interrupt handler
181 */
182static int
183iicsmb_intr(device_t dev, int event, char *buf)
184{
185	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
186
187	mtx_lock(&sc->lock);
188	switch (event) {
189	case INTR_GENERAL:
190	case INTR_START:
191		sc->state = SMB_WAITING_ADDR;
192		break;
193
194	case INTR_STOP:
195		/* call smbus intr handler */
196		smbus_intr(sc->smbus, sc->devaddr,
197				sc->low, sc->high, SMB_ENOERR);
198		break;
199
200	case INTR_RECEIVE:
201		switch (sc->state) {
202		case SMB_DONE:
203			/* XXX too much data, discard */
204			printf("%s: too much data from 0x%x\n", __func__,
205				sc->devaddr & 0xff);
206			goto end;
207
208		case SMB_WAITING_ADDR:
209			sc->devaddr = (u_char)*buf;
210			sc->state = SMB_WAITING_LOW;
211			break;
212
213		case SMB_WAITING_LOW:
214			sc->low = *buf;
215			sc->state = SMB_WAITING_HIGH;
216			break;
217
218		case SMB_WAITING_HIGH:
219			sc->high = *buf;
220			sc->state = SMB_DONE;
221			break;
222		}
223end:
224		break;
225
226	case INTR_TRANSMIT:
227	case INTR_NOACK:
228		break;
229
230	case INTR_ERROR:
231		switch (*buf) {
232		case IIC_EBUSERR:
233			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
234			break;
235
236		default:
237			printf("%s unknown error 0x%x!\n", __func__,
238								(int)*buf);
239			break;
240		}
241		break;
242
243	default:
244		panic("%s: unknown event (%d)!", __func__, event);
245	}
246	mtx_unlock(&sc->lock);
247
248	return (0);
249}
250
251static int
252iicsmb_callback(device_t dev, int index, void *data)
253{
254	device_t parent = device_get_parent(dev);
255	int error = 0;
256	int how;
257
258	switch (index) {
259	case SMB_REQUEST_BUS:
260		/* request underlying iicbus */
261		how = *(int *)data;
262		error = iicbus_request_bus(parent, dev, how);
263		break;
264
265	case SMB_RELEASE_BUS:
266		/* release underlying iicbus */
267		error = iicbus_release_bus(parent, dev);
268		break;
269
270	default:
271		error = EINVAL;
272	}
273
274	return (error);
275}
276
277static int
278iic2smb_error(int error)
279{
280	switch (error) {
281	case IIC_NOERR:
282		return (SMB_ENOERR);
283	case IIC_EBUSERR:
284		return (SMB_EBUSERR);
285	case IIC_ENOACK:
286		return (SMB_ENOACK);
287	case IIC_ETIMEOUT:
288		return (SMB_ETIMEOUT);
289	case IIC_EBUSBSY:
290		return (SMB_EBUSY);
291	case IIC_ESTATUS:
292		return (SMB_EBUSERR);
293	case IIC_EUNDERFLOW:
294		return (SMB_EBUSERR);
295	case IIC_EOVERFLOW:
296		return (SMB_EBUSERR);
297	case IIC_ENOTSUPP:
298		return (SMB_ENOTSUPP);
299	case IIC_ENOADDR:
300		return (SMB_EBUSERR);
301	case IIC_ERESOURCE:
302		return (SMB_EBUSERR);
303	default:
304		return (SMB_EBUSERR);
305	}
306}
307
308#define	TRANSFER_MSGS(dev, msgs)	iicbus_transfer(dev, msgs, nitems(msgs))
309
310static int
311iicsmb_quick(device_t dev, u_char slave, int how)
312{
313	struct iic_msg msgs[] = {
314	     { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
315	};
316	int error;
317
318	switch (how) {
319	case SMB_QWRITE:
320	case SMB_QREAD:
321		break;
322	default:
323		return (SMB_EINVAL);
324	}
325
326	error = TRANSFER_MSGS(dev, msgs);
327	return (iic2smb_error(error));
328}
329
330static int
331iicsmb_sendb(device_t dev, u_char slave, char byte)
332{
333	struct iic_msg msgs[] = {
334	     { slave, IIC_M_WR, 1, &byte },
335	};
336	int error;
337
338	error = TRANSFER_MSGS(dev, msgs);
339	return (iic2smb_error(error));
340}
341
342static int
343iicsmb_recvb(device_t dev, u_char slave, char *byte)
344{
345	struct iic_msg msgs[] = {
346	     { slave, IIC_M_RD, 1, byte },
347	};
348	int error;
349
350	error = TRANSFER_MSGS(dev, msgs);
351	return (iic2smb_error(error));
352}
353
354static int
355iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
356{
357	uint8_t bytes[] = { cmd, byte };
358	struct iic_msg msgs[] = {
359	     { slave, IIC_M_WR, nitems(bytes), bytes },
360	};
361	int error;
362
363	error = TRANSFER_MSGS(dev, msgs);
364	return (iic2smb_error(error));
365}
366
367static int
368iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
369{
370	uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
371	struct iic_msg msgs[] = {
372	     { slave, IIC_M_WR, nitems(bytes), bytes },
373	};
374	int error;
375
376	error = TRANSFER_MSGS(dev, msgs);
377	return (iic2smb_error(error));
378}
379
380static int
381iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
382{
383	struct iic_msg msgs[] = {
384	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
385	     { slave, IIC_M_RD, 1, byte },
386	};
387	int error;
388
389	error = TRANSFER_MSGS(dev, msgs);
390	return (iic2smb_error(error));
391}
392
393static int
394iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
395{
396	uint8_t buf[2];
397	struct iic_msg msgs[] = {
398	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
399	     { slave, IIC_M_RD, nitems(buf), buf },
400	};
401	int error;
402
403	error = TRANSFER_MSGS(dev, msgs);
404	if (error == 0)
405		*word = ((uint16_t)buf[1] << 8) | buf[0];
406	return (iic2smb_error(error));
407}
408
409static int
410iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
411{
412	uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
413	uint8_t out[2];
414	struct iic_msg msgs[] = {
415	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
416	     { slave, IIC_M_RD, nitems(out), out },
417	};
418	int error;
419
420	error = TRANSFER_MSGS(dev, msgs);
421	if (error == 0)
422		*rdata = ((uint16_t)out[1] << 8) | out[0];
423	return (iic2smb_error(error));
424}
425
426static int
427iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
428{
429	uint8_t bytes[2] = { cmd, count };
430	struct iic_msg msgs[] = {
431	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
432	     { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
433	};
434	int error;
435
436	if (count > SMB_MAXBLOCKSIZE || count == 0)
437		return (SMB_EINVAL);
438	error = TRANSFER_MSGS(dev, msgs);
439	return (iic2smb_error(error));
440}
441
442static int
443iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
444{
445	struct iic_msg msgs[] = {
446	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
447	     { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
448	};
449	struct iic_msg block_msg[] = {
450	     { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
451	};
452	device_t parent = device_get_parent(dev);
453	int error;
454
455	/* Have to do this because the command is split in two transfers. */
456	error = iicbus_request_bus(parent, dev, IIC_WAIT);
457	if (error == 0)
458		error = TRANSFER_MSGS(dev, msgs);
459	if (error == 0) {
460		/*
461		 * If the slave offers an empty or a too long reply,
462		 * read one byte to generate the stop or abort.
463		 */
464		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
465			block_msg[0].len = 1;
466		else
467			block_msg[0].len = *count;
468		error = TRANSFER_MSGS(dev, block_msg);
469		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
470			error = SMB_EINVAL;
471	}
472	(void)iicbus_release_bus(parent, dev);
473	return (iic2smb_error(error));
474}
475
476DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
477DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
478MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
479MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
480MODULE_VERSION(iicsmb, 1);
481