xref: /freebsd/sys/compat/linuxkpi/common/src/linux_i2c.c (revision 82d4dc0621c92e3c05a86013eec35afbdec057a5)
1 /*-
2  * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/malloc.h>
34 
35 #include <dev/iicbus/iicbus.h>
36 #include <dev/iicbus/iiconf.h>
37 
38 #include <linux/device.h>
39 #include <linux/i2c.h>
40 #include <linux/i2c-algo-bit.h>
41 #include <linux/list.h>
42 #include <linux/pci.h>
43 
44 #include "iicbus_if.h"
45 #include "iicbb_if.h"
46 #include "lkpi_iic_if.h"
47 
48 static int lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
49 static int lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr);
50 
51 struct lkpi_iic_softc {
52 	device_t		iicbus;
53 	struct i2c_adapter	*adapter;
54 };
55 
56 static int
57 lkpi_iic_probe(device_t dev)
58 {
59 
60 	device_set_desc(dev, "LinuxKPI I2C");
61 	return (BUS_PROBE_NOWILDCARD);
62 }
63 
64 static int
65 lkpi_iic_attach(device_t dev)
66 {
67 	struct lkpi_iic_softc *sc;
68 
69 	sc = device_get_softc(dev);
70 	sc->iicbus = device_add_child(dev, "iicbus", -1);
71 	if (sc->iicbus == NULL) {
72 		device_printf(dev, "Couldn't add iicbus child, aborting\n");
73 		return (ENXIO);
74 	}
75 	bus_generic_attach(dev);
76 	return (0);
77 }
78 
79 static int
80 lkpi_iic_detach(device_t dev)
81 {
82 	struct lkpi_iic_softc *sc;
83 
84 	sc = device_get_softc(dev);
85 	if (sc->iicbus)
86 		device_delete_child(dev, sc->iicbus);
87 	return (0);
88 }
89 
90 static int
91 lkpi_iic_add_adapter(device_t dev, struct i2c_adapter *adapter)
92 {
93 	struct lkpi_iic_softc *sc;
94 
95 	sc = device_get_softc(dev);
96 	sc->adapter = adapter;
97 
98 	return (0);
99 }
100 
101 static device_method_t lkpi_iic_methods[] = {
102 	/* device interface */
103 	DEVMETHOD(device_probe,		lkpi_iic_probe),
104 	DEVMETHOD(device_attach,	lkpi_iic_attach),
105 	DEVMETHOD(device_detach,	lkpi_iic_detach),
106 	DEVMETHOD(device_suspend,	bus_generic_suspend),
107 	DEVMETHOD(device_resume,	bus_generic_resume),
108 
109 	/* iicbus interface */
110 	DEVMETHOD(iicbus_transfer,	lkpi_i2c_transfer),
111 	DEVMETHOD(iicbus_reset,		lkpi_i2c_reset),
112 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
113 
114 	/* lkpi_iic interface */
115 	DEVMETHOD(lkpi_iic_add_adapter,	lkpi_iic_add_adapter),
116 
117 	DEVMETHOD_END
118 };
119 
120 driver_t lkpi_iic_driver = {
121 	"lkpi_iic",
122 	lkpi_iic_methods,
123 	sizeof(struct lkpi_iic_softc),
124 };
125 
126 DRIVER_MODULE(lkpi_iic, drmn, lkpi_iic_driver, 0, 0);
127 DRIVER_MODULE(iicbus, lkpi_iic, iicbus_driver, 0, 0);
128 MODULE_DEPEND(linuxkpi, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
129 
130 static int
131 lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
132 {
133 
134 	/* That doesn't seems to be supported in linux */
135 	return (0);
136 }
137 
138 static int
139 lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
140 {
141 	struct lkpi_iic_softc *sc;
142 	struct i2c_msg *linux_msgs;
143 	int i, ret = 0;
144 
145 	sc = device_get_softc(dev);
146 	if (sc->adapter == NULL)
147 		return (ENXIO);
148 	linux_set_current(curthread);
149 
150 	linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs,
151 	    M_DEVBUF, M_WAITOK | M_ZERO);
152 
153 	for (i = 0; i < nmsgs; i++) {
154 		linux_msgs[i].addr = msgs[i].slave;
155 		linux_msgs[i].len = msgs[i].len;
156 		linux_msgs[i].buf = msgs[i].buf;
157 		if (msgs[i].flags & IIC_M_RD) {
158 			linux_msgs[i].flags |= I2C_M_RD;
159 			for (int j = 0; j < msgs[i].len; j++)
160 				msgs[i].buf[j] = 0;
161 		}
162 		if (msgs[i].flags & IIC_M_NOSTART)
163 			linux_msgs[i].flags |= I2C_M_NOSTART;
164 	}
165 	ret = i2c_transfer(sc->adapter, linux_msgs, nmsgs);
166 	free(linux_msgs, M_DEVBUF);
167 
168 	if (ret < 0)
169 		return (-ret);
170 	return (0);
171 }
172 
173 int
174 lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
175 {
176 	device_t lkpi_iic;
177 	int error;
178 
179 	if (bootverbose)
180 		device_printf(adapter->dev.parent->bsddev,
181 		    "Adding i2c adapter %s\n", adapter->name);
182 	lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
183 	if (lkpi_iic == NULL) {
184 		device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
185 		return (ENXIO);
186 	}
187 
188 	error = bus_generic_attach(adapter->dev.parent->bsddev);
189 	if (error) {
190 		device_printf(adapter->dev.parent->bsddev,
191 		  "failed to attach child: error %d\n", error);
192 		return (ENXIO);
193 	}
194 	LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter);
195 	return (0);
196 }
197 
198 int
199 lkpi_i2c_del_adapter(struct i2c_adapter *adapter)
200 {
201 	device_t child;
202 
203 	if (bootverbose)
204 		device_printf(adapter->dev.parent->bsddev,
205 		    "Removing i2c adapter %s\n", adapter->name);
206 
207 	child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
208 	if (child != NULL)
209 		device_delete_child(adapter->dev.parent->bsddev, child);
210 
211 	child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
212 	if (child != NULL)
213 		device_delete_child(adapter->dev.parent->bsddev, child);
214 
215 	return (0);
216 }
217