xref: /freebsd-src/sys/dev/iicbus/icee.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
166c7612aSWarner Losh /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4f86e6000SWarner Losh  * Copyright (c) 2006 M. Warner Losh <imp@FreeBSD.org>
566c7612aSWarner Losh  *
666c7612aSWarner Losh  * Redistribution and use in source and binary forms, with or without
766c7612aSWarner Losh  * modification, are permitted provided that the following conditions
866c7612aSWarner Losh  * are met:
966c7612aSWarner Losh  * 1. Redistributions of source code must retain the above copyright
1066c7612aSWarner Losh  *    notice, this list of conditions and the following disclaimer.
1166c7612aSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
1266c7612aSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
1366c7612aSWarner Losh  *    documentation and/or other materials provided with the distribution.
1466c7612aSWarner Losh  *
1566c7612aSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1666c7612aSWarner Losh  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1766c7612aSWarner Losh  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1866c7612aSWarner Losh  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1966c7612aSWarner Losh  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2066c7612aSWarner Losh  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2166c7612aSWarner Losh  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2266c7612aSWarner Losh  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2366c7612aSWarner Losh  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2466c7612aSWarner Losh  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2566c7612aSWarner Losh  */
2666c7612aSWarner Losh 
2766c7612aSWarner Losh #include <sys/cdefs.h>
2866c7612aSWarner Losh /*
2966c7612aSWarner Losh  * Generic IIC eeprom support, modeled after the AT24C family of products.
3066c7612aSWarner Losh  */
31b180eb21SIan Lepore 
32b180eb21SIan Lepore #include "opt_platform.h"
33b180eb21SIan Lepore 
3466c7612aSWarner Losh #include <sys/param.h>
3566c7612aSWarner Losh #include <sys/systm.h>
3666c7612aSWarner Losh #include <sys/bus.h>
3766c7612aSWarner Losh #include <sys/conf.h>
3866c7612aSWarner Losh #include <sys/kernel.h>
3966c7612aSWarner Losh #include <sys/module.h>
4066c7612aSWarner Losh #include <sys/resource.h>
41225f9723SJohn Baldwin #include <sys/sx.h>
425af4ab65SIan Lepore #include <sys/sysctl.h>
4366c7612aSWarner Losh #include <sys/uio.h>
4466c7612aSWarner Losh #include <machine/bus.h>
45b180eb21SIan Lepore 
46b180eb21SIan Lepore #ifdef FDT
47b180eb21SIan Lepore #include <dev/ofw/ofw_bus.h>
48b180eb21SIan Lepore #include <dev/ofw/ofw_bus_subr.h>
49b180eb21SIan Lepore #endif
50b180eb21SIan Lepore 
5166c7612aSWarner Losh #include <dev/iicbus/iiconf.h>
5266c7612aSWarner Losh #include <dev/iicbus/iicbus.h>
5366c7612aSWarner Losh 
5466c7612aSWarner Losh #include "iicbus_if.h"
5566c7612aSWarner Losh 
56b180eb21SIan Lepore /*
57b180eb21SIan Lepore  * AT24 parts have a "write page size" that differs per-device, and a "read page
58b180eb21SIan Lepore  * size" that is always equal to the full device size.  We define maximum values
59b180eb21SIan Lepore  * here to limit how long we occupy the bus with a single transfer, and because
60b180eb21SIan Lepore  * there are temporary buffers of these sizes allocated on the stack.
61b180eb21SIan Lepore  */
6266c7612aSWarner Losh #define	MAX_RD_SZ	256	/* Largest read size we support */
6366c7612aSWarner Losh #define	MAX_WR_SZ	256	/* Largest write size we support */
6466c7612aSWarner Losh 
6566c7612aSWarner Losh struct icee_softc {
66b180eb21SIan Lepore 	device_t	dev;		/* Myself */
6766c7612aSWarner Losh 	struct cdev	*cdev;		/* user interface */
68b180eb21SIan Lepore 	int		addr;		/* Slave address on the bus */
6966c7612aSWarner Losh 	int		size;		/* How big am I? */
70b180eb21SIan Lepore 	int		type;		/* What address type 8 or 16 bit? */
7166c7612aSWarner Losh 	int		wr_sz;		/* What's the write page size */
7266c7612aSWarner Losh };
7366c7612aSWarner Losh 
74b180eb21SIan Lepore #ifdef FDT
75b180eb21SIan Lepore struct eeprom_desc {
76b180eb21SIan Lepore 	int	    type;
77b180eb21SIan Lepore 	int	    size;
78b180eb21SIan Lepore 	int	    wr_sz;
79b180eb21SIan Lepore 	const char *name;
80b180eb21SIan Lepore };
81b180eb21SIan Lepore 
82b180eb21SIan Lepore static struct eeprom_desc type_desc[] = {
83b180eb21SIan Lepore 	{ 8,        128,   8, "AT24C01"},
84b180eb21SIan Lepore 	{ 8,        256,   8, "AT24C02"},
85b180eb21SIan Lepore 	{ 8,        512,  16, "AT24C04"},
86b180eb21SIan Lepore 	{ 8,       1024,  16, "AT24C08"},
87b180eb21SIan Lepore 	{ 8,   2 * 1024,  16, "AT24C16"},
88b180eb21SIan Lepore 	{16,   4 * 1024,  32, "AT24C32"},
89b180eb21SIan Lepore 	{16,   8 * 1024,  32, "AT24C64"},
90b180eb21SIan Lepore 	{16,  16 * 1024,  64, "AT24C128"},
91b180eb21SIan Lepore 	{16,  32 * 1024,  64, "AT24C256"},
92b180eb21SIan Lepore 	{16,  64 * 1024, 128, "AT24C512"},
93b180eb21SIan Lepore 	{16, 128 * 1024, 256, "AT24CM01"},
94b180eb21SIan Lepore };
95b180eb21SIan Lepore 
96b180eb21SIan Lepore static struct ofw_compat_data compat_data[] = {
97b180eb21SIan Lepore 	{"atmel,24c01",	  (uintptr_t)(&type_desc[0])},
98b180eb21SIan Lepore 	{"atmel,24c02",	  (uintptr_t)(&type_desc[1])},
99b180eb21SIan Lepore 	{"atmel,24c04",	  (uintptr_t)(&type_desc[2])},
100b180eb21SIan Lepore 	{"atmel,24c08",	  (uintptr_t)(&type_desc[3])},
101b180eb21SIan Lepore 	{"atmel,24c16",	  (uintptr_t)(&type_desc[4])},
102b180eb21SIan Lepore 	{"atmel,24c32",	  (uintptr_t)(&type_desc[5])},
103b180eb21SIan Lepore 	{"atmel,24c64",	  (uintptr_t)(&type_desc[6])},
104b180eb21SIan Lepore 	{"atmel,24c128",  (uintptr_t)(&type_desc[7])},
105b180eb21SIan Lepore 	{"atmel,24c256",  (uintptr_t)(&type_desc[8])},
106b180eb21SIan Lepore 	{"atmel,24c512",  (uintptr_t)(&type_desc[9])},
107b180eb21SIan Lepore 	{"atmel,24c1024", (uintptr_t)(&type_desc[10])},
108b180eb21SIan Lepore 	{NULL,		  (uintptr_t)NULL},
109b180eb21SIan Lepore };
110b180eb21SIan Lepore #endif
111b180eb21SIan Lepore 
11266c7612aSWarner Losh #define CDEV2SOFTC(dev)		((dev)->si_drv1)
11366c7612aSWarner Losh 
11466c7612aSWarner Losh /* cdev routines */
11566c7612aSWarner Losh static d_read_t icee_read;
11666c7612aSWarner Losh static d_write_t icee_write;
11766c7612aSWarner Losh 
11866c7612aSWarner Losh static struct cdevsw icee_cdevsw =
11966c7612aSWarner Losh {
12066c7612aSWarner Losh 	.d_version = D_VERSION,
12166c7612aSWarner Losh 	.d_read = icee_read,
12266c7612aSWarner Losh 	.d_write = icee_write
12366c7612aSWarner Losh };
12466c7612aSWarner Losh 
125b180eb21SIan Lepore static int
icee_probe(device_t dev)126b180eb21SIan Lepore icee_probe(device_t dev)
127b180eb21SIan Lepore {
12801e34923SAndriy Gapon #ifdef FDT
129b180eb21SIan Lepore 	struct eeprom_desc *d;
130b180eb21SIan Lepore 
131b180eb21SIan Lepore 	if (!ofw_bus_status_okay(dev))
132b180eb21SIan Lepore 		return (ENXIO);
133b180eb21SIan Lepore 
134b180eb21SIan Lepore 	d = (struct eeprom_desc *)
135b180eb21SIan Lepore 	    ofw_bus_search_compatible(dev, compat_data)->ocd_data;
13601e34923SAndriy Gapon 	if (d != NULL) {
137b180eb21SIan Lepore 		device_set_desc(dev, d->name);
138b180eb21SIan Lepore 		return (BUS_PROBE_DEFAULT);
139b180eb21SIan Lepore 	}
14001e34923SAndriy Gapon #endif
14166c7612aSWarner Losh 	device_set_desc(dev, "I2C EEPROM");
142789c4b9dSNathan Whitehorn 	return (BUS_PROBE_NOWILDCARD);
14366c7612aSWarner Losh }
14466c7612aSWarner Losh 
14501e34923SAndriy Gapon static int
icee_init(struct icee_softc * sc)146b180eb21SIan Lepore icee_init(struct icee_softc *sc)
147b180eb21SIan Lepore {
148b180eb21SIan Lepore 	const char *dname;
149b180eb21SIan Lepore 	int dunit;
15001e34923SAndriy Gapon #ifdef FDT
15101e34923SAndriy Gapon 	struct eeprom_desc *d;
152b180eb21SIan Lepore 
15301e34923SAndriy Gapon 	d = (struct eeprom_desc *)
15401e34923SAndriy Gapon 	    ofw_bus_search_compatible(sc->dev, compat_data)->ocd_data;
15501e34923SAndriy Gapon 	if (d != NULL) {
15601e34923SAndriy Gapon 		sc->size  = d->size;
15701e34923SAndriy Gapon 		sc->type  = d->type;
15801e34923SAndriy Gapon 		sc->wr_sz = d->wr_sz;
15901e34923SAndriy Gapon 		return (0);
16001e34923SAndriy Gapon 	}
16101e34923SAndriy Gapon #endif
162b180eb21SIan Lepore 	dname = device_get_name(sc->dev);
163b180eb21SIan Lepore 	dunit = device_get_unit(sc->dev);
16401e34923SAndriy Gapon 	if (resource_int_value(dname, dunit, "type", &sc->type) != 0)
16501e34923SAndriy Gapon 		return (ENOENT);
16601e34923SAndriy Gapon 	if (resource_int_value(dname, dunit, "size", &sc->size) != 0)
16701e34923SAndriy Gapon 		return (ENOENT);
16801e34923SAndriy Gapon 	if (resource_int_value(dname, dunit, "wr_sz", &sc->wr_sz) != 0)
16901e34923SAndriy Gapon 		return (ENOENT);
17001e34923SAndriy Gapon 	return (0);
171b180eb21SIan Lepore }
172b180eb21SIan Lepore 
17366c7612aSWarner Losh static int
icee_attach(device_t dev)17466c7612aSWarner Losh icee_attach(device_t dev)
17566c7612aSWarner Losh {
17666c7612aSWarner Losh 	struct icee_softc *sc = device_get_softc(dev);
1775af4ab65SIan Lepore 	struct sysctl_ctx_list *ctx;
1785af4ab65SIan Lepore 	struct sysctl_oid_list *tree;
17966c7612aSWarner Losh 
180b180eb21SIan Lepore 	sc->dev = dev;
1814bdfea0fSStanislav Sedov 	sc->addr = iicbus_get_addr(dev);
18201e34923SAndriy Gapon 	if (icee_init(sc) != 0)
183b180eb21SIan Lepore 		return (EINVAL);
18466c7612aSWarner Losh 	if (bootverbose)
185b180eb21SIan Lepore 		device_printf(dev, "size: %d bytes, addressing: %d-bits\n",
18666c7612aSWarner Losh 		    sc->size, sc->type);
18766c7612aSWarner Losh 	sc->cdev = make_dev(&icee_cdevsw, device_get_unit(dev), UID_ROOT,
18866c7612aSWarner Losh 	    GID_WHEEL, 0600, "icee%d", device_get_unit(dev));
18966c7612aSWarner Losh 	if (sc->cdev == NULL) {
190b180eb21SIan Lepore 		return (ENOMEM);
19166c7612aSWarner Losh 	}
19266c7612aSWarner Losh 	sc->cdev->si_drv1 = sc;
1935af4ab65SIan Lepore 
1945af4ab65SIan Lepore 	ctx = device_get_sysctl_ctx(dev);
1955af4ab65SIan Lepore 	tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
1965af4ab65SIan Lepore 	SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "address_size", CTLFLAG_RD,
1975af4ab65SIan Lepore 	    &sc->type, 0, "Memory array address size in bits");
1985af4ab65SIan Lepore 	SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "device_size", CTLFLAG_RD,
1995af4ab65SIan Lepore 	    &sc->size, 0, "Memory array capacity in bytes");
2005af4ab65SIan Lepore 	SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "write_size", CTLFLAG_RD,
2015af4ab65SIan Lepore 	    &sc->wr_sz, 0, "Memory array page write size in bytes");
2025af4ab65SIan Lepore 
203b180eb21SIan Lepore 	return (0);
20466c7612aSWarner Losh }
20566c7612aSWarner Losh 
20666c7612aSWarner Losh static int
icee_detach(device_t dev)207d18915faSIan Lepore icee_detach(device_t dev)
208d18915faSIan Lepore {
209d18915faSIan Lepore 	struct icee_softc *sc = device_get_softc(dev);
210d18915faSIan Lepore 
211d18915faSIan Lepore 	destroy_dev(sc->cdev);
212d18915faSIan Lepore 	return (0);
213d18915faSIan Lepore }
214d18915faSIan Lepore 
215d18915faSIan Lepore static int
icee_read(struct cdev * dev,struct uio * uio,int ioflag)21666c7612aSWarner Losh icee_read(struct cdev *dev, struct uio *uio, int ioflag)
21766c7612aSWarner Losh {
21866c7612aSWarner Losh 	struct icee_softc *sc;
21966c7612aSWarner Losh 	uint8_t addr[2];
22066c7612aSWarner Losh 	uint8_t data[MAX_RD_SZ];
22166c7612aSWarner Losh 	int error, i, len, slave;
22266c7612aSWarner Losh 	struct iic_msg msgs[2] = {
22366c7612aSWarner Losh 	     { 0, IIC_M_WR, 1, addr },
22466c7612aSWarner Losh 	     { 0, IIC_M_RD, 0, data },
22566c7612aSWarner Losh 	};
22666c7612aSWarner Losh 
22766c7612aSWarner Losh 	sc = CDEV2SOFTC(dev);
22866c7612aSWarner Losh 	if (uio->uio_offset == sc->size)
22966c7612aSWarner Losh 		return (0);
23066c7612aSWarner Losh 	if (uio->uio_offset > sc->size)
23166c7612aSWarner Losh 		return (EIO);
23266c7612aSWarner Losh 	if (sc->type != 8 && sc->type != 16)
23366c7612aSWarner Losh 		return (EINVAL);
23466c7612aSWarner Losh 	slave = error = 0;
23566c7612aSWarner Losh 	while (uio->uio_resid > 0) {
23666c7612aSWarner Losh 		if (uio->uio_offset >= sc->size)
23766c7612aSWarner Losh 			break;
238b180eb21SIan Lepore 		len = MIN(MAX_RD_SZ - (uio->uio_offset & (MAX_RD_SZ - 1)),
23966c7612aSWarner Losh 		    uio->uio_resid);
24066c7612aSWarner Losh 		switch (sc->type) {
24166c7612aSWarner Losh 		case 8:
24266c7612aSWarner Losh 			slave = (uio->uio_offset >> 7) | sc->addr;
24366c7612aSWarner Losh 			msgs[0].len = 1;
24466c7612aSWarner Losh 			msgs[1].len = len;
24566c7612aSWarner Losh 			addr[0] = uio->uio_offset & 0xff;
24666c7612aSWarner Losh 			break;
24766c7612aSWarner Losh 		case 16:
24866c7612aSWarner Losh 			slave = sc->addr | (uio->uio_offset >> 15);
24966c7612aSWarner Losh 			msgs[0].len = 2;
25066c7612aSWarner Losh 			msgs[1].len = len;
25166c7612aSWarner Losh 			addr[0] = (uio->uio_offset >> 8) & 0xff;
25266c7612aSWarner Losh 			addr[1] = uio->uio_offset & 0xff;
25366c7612aSWarner Losh 			break;
25466c7612aSWarner Losh 		}
25566c7612aSWarner Losh 		for (i = 0; i < 2; i++)
25666c7612aSWarner Losh 			msgs[i].slave = slave;
257b180eb21SIan Lepore 		error = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_INTRWAIT);
258b16c6d23SIan Lepore 		if (error) {
259b16c6d23SIan Lepore 			error = iic2errno(error);
26066c7612aSWarner Losh 			break;
261b16c6d23SIan Lepore 		}
26266c7612aSWarner Losh 		error = uiomove(data, len, uio);
26366c7612aSWarner Losh 		if (error)
26466c7612aSWarner Losh 			break;
26566c7612aSWarner Losh 	}
26666c7612aSWarner Losh 	return (error);
26766c7612aSWarner Losh }
26866c7612aSWarner Losh 
26966c7612aSWarner Losh /*
27066c7612aSWarner Losh  * Write to the part.  We use three transfers here since we're actually
27166c7612aSWarner Losh  * doing a write followed by a read to make sure that the write finished.
27266c7612aSWarner Losh  * It is easier to encode the dummy read here than to break things up
27366c7612aSWarner Losh  * into smaller chunks...
27466c7612aSWarner Losh  */
27566c7612aSWarner Losh static int
icee_write(struct cdev * dev,struct uio * uio,int ioflag)27666c7612aSWarner Losh icee_write(struct cdev *dev, struct uio *uio, int ioflag)
27766c7612aSWarner Losh {
27866c7612aSWarner Losh 	struct icee_softc *sc;
27966c7612aSWarner Losh 	int error, len, slave, waitlimit;
28066c7612aSWarner Losh 	uint8_t data[MAX_WR_SZ + 2];
28166c7612aSWarner Losh 	struct iic_msg wr[1] = {
28266c7612aSWarner Losh 	     { 0, IIC_M_WR, 0, data },
28366c7612aSWarner Losh 	};
28466c7612aSWarner Losh 	struct iic_msg rd[1] = {
28566c7612aSWarner Losh 	     { 0, IIC_M_RD, 1, data },
28666c7612aSWarner Losh 	};
28766c7612aSWarner Losh 
28866c7612aSWarner Losh 	sc = CDEV2SOFTC(dev);
28966c7612aSWarner Losh 	if (uio->uio_offset >= sc->size)
29066c7612aSWarner Losh 		return (EIO);
29166c7612aSWarner Losh 	if (sc->type != 8 && sc->type != 16)
29266c7612aSWarner Losh 		return (EINVAL);
29373440c33SIan Lepore 
29466c7612aSWarner Losh 	slave = error = 0;
29566c7612aSWarner Losh 	while (uio->uio_resid > 0) {
29666c7612aSWarner Losh 		if (uio->uio_offset >= sc->size)
29766c7612aSWarner Losh 			break;
29866c7612aSWarner Losh 		len = MIN(sc->wr_sz - (uio->uio_offset & (sc->wr_sz - 1)),
29966c7612aSWarner Losh 		    uio->uio_resid);
30066c7612aSWarner Losh 		switch (sc->type) {
30166c7612aSWarner Losh 		case 8:
30266c7612aSWarner Losh 			slave = (uio->uio_offset >> 7) | sc->addr;
30366c7612aSWarner Losh 			wr[0].len = 1 + len;
30466c7612aSWarner Losh 			data[0] = uio->uio_offset & 0xff;
30566c7612aSWarner Losh 			break;
30666c7612aSWarner Losh 		case 16:
30766c7612aSWarner Losh 			slave = sc->addr | (uio->uio_offset >> 15);
30866c7612aSWarner Losh 			wr[0].len = 2 + len;
30966c7612aSWarner Losh 			data[0] = (uio->uio_offset >> 8) & 0xff;
31066c7612aSWarner Losh 			data[1] = uio->uio_offset & 0xff;
31166c7612aSWarner Losh 			break;
31266c7612aSWarner Losh 		}
31366c7612aSWarner Losh 		wr[0].slave = slave;
31466c7612aSWarner Losh 		error = uiomove(data + sc->type / 8, len, uio);
31566c7612aSWarner Losh 		if (error)
31666c7612aSWarner Losh 			break;
317b180eb21SIan Lepore 		error = iicbus_transfer_excl(sc->dev, wr, 1, IIC_INTRWAIT);
318b16c6d23SIan Lepore 		if (error) {
319b16c6d23SIan Lepore 			error = iic2errno(error);
32066c7612aSWarner Losh 			break;
321b16c6d23SIan Lepore 		}
3226f279052SIan Lepore 		/* Read after write to wait for write-done. */
32366c7612aSWarner Losh 		waitlimit = 10000;
32466c7612aSWarner Losh 		rd[0].slave = slave;
3256f279052SIan Lepore 		do {
326b180eb21SIan Lepore 			error = iicbus_transfer_excl(sc->dev, rd, 1,
327b180eb21SIan Lepore 			    IIC_INTRWAIT);
32866c7612aSWarner Losh 		} while (waitlimit-- > 0 && error != 0);
329b16c6d23SIan Lepore 		if (error) {
330b16c6d23SIan Lepore 			error = iic2errno(error);
33166c7612aSWarner Losh 			break;
33266c7612aSWarner Losh 		}
333b16c6d23SIan Lepore 	}
33466c7612aSWarner Losh 	return error;
33566c7612aSWarner Losh }
33666c7612aSWarner Losh 
33766c7612aSWarner Losh static device_method_t icee_methods[] = {
33866c7612aSWarner Losh 	DEVMETHOD(device_probe,		icee_probe),
33966c7612aSWarner Losh 	DEVMETHOD(device_attach,	icee_attach),
340d18915faSIan Lepore 	DEVMETHOD(device_detach,	icee_detach),
34166c7612aSWarner Losh 
34261bfd867SSofian Brabez 	DEVMETHOD_END
34366c7612aSWarner Losh };
34466c7612aSWarner Losh 
34566c7612aSWarner Losh static driver_t icee_driver = {
34666c7612aSWarner Losh 	"icee",
34766c7612aSWarner Losh 	icee_methods,
34866c7612aSWarner Losh 	sizeof(struct icee_softc),
34966c7612aSWarner Losh };
35066c7612aSWarner Losh 
3513a866152SJohn Baldwin DRIVER_MODULE(icee, iicbus, icee_driver, 0, 0);
35266c7612aSWarner Losh MODULE_VERSION(icee, 1);
35366c7612aSWarner Losh MODULE_DEPEND(icee, iicbus, 1, 1, 1);
3547a038f29SIan Lepore IICBUS_FDT_PNP_INFO(compat_data);
355