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 struct sx lkpi_sx_i2c;
57 
58 static void
59 lkpi_sysinit_i2c(void *arg __unused)
60 {
61 
62 	sx_init(&lkpi_sx_i2c, "lkpi-i2c");
63 }
64 
65 static void
66 lkpi_sysuninit_i2c(void *arg __unused)
67 {
68 
69 	sx_destroy(&lkpi_sx_i2c);
70 }
71 
72 SYSINIT(lkpi_i2c, SI_SUB_DRIVERS, SI_ORDER_ANY,
73     lkpi_sysinit_i2c, NULL);
74 SYSUNINIT(lkpi_i2c, SI_SUB_DRIVERS, SI_ORDER_ANY,
75     lkpi_sysuninit_i2c, NULL);
76 
77 static int
78 lkpi_iic_probe(device_t dev)
79 {
80 
81 	device_set_desc(dev, "LinuxKPI I2C");
82 	return (BUS_PROBE_NOWILDCARD);
83 }
84 
85 static int
86 lkpi_iic_attach(device_t dev)
87 {
88 	struct lkpi_iic_softc *sc;
89 
90 	sc = device_get_softc(dev);
91 	sc->iicbus = device_add_child(dev, "iicbus", -1);
92 	if (sc->iicbus == NULL) {
93 		device_printf(dev, "Couldn't add iicbus child, aborting\n");
94 		return (ENXIO);
95 	}
96 	bus_generic_attach(dev);
97 	return (0);
98 }
99 
100 static int
101 lkpi_iic_detach(device_t dev)
102 {
103 	struct lkpi_iic_softc *sc;
104 
105 	sc = device_get_softc(dev);
106 	if (sc->iicbus)
107 		device_delete_child(dev, sc->iicbus);
108 	return (0);
109 }
110 
111 static int
112 lkpi_iic_add_adapter(device_t dev, struct i2c_adapter *adapter)
113 {
114 	struct lkpi_iic_softc *sc;
115 
116 	sc = device_get_softc(dev);
117 	sc->adapter = adapter;
118 
119 	return (0);
120 }
121 
122 static struct i2c_adapter *
123 lkpi_iic_get_adapter(device_t dev)
124 {
125 	struct lkpi_iic_softc *sc;
126 
127 	sc = device_get_softc(dev);
128 	return (sc->adapter);
129 }
130 
131 static device_method_t lkpi_iic_methods[] = {
132 	/* device interface */
133 	DEVMETHOD(device_probe,		lkpi_iic_probe),
134 	DEVMETHOD(device_attach,	lkpi_iic_attach),
135 	DEVMETHOD(device_detach,	lkpi_iic_detach),
136 	DEVMETHOD(device_suspend,	bus_generic_suspend),
137 	DEVMETHOD(device_resume,	bus_generic_resume),
138 
139 	/* iicbus interface */
140 	DEVMETHOD(iicbus_transfer,	lkpi_i2c_transfer),
141 	DEVMETHOD(iicbus_reset,		lkpi_i2c_reset),
142 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
143 
144 	/* lkpi_iic interface */
145 	DEVMETHOD(lkpi_iic_add_adapter,	lkpi_iic_add_adapter),
146 	DEVMETHOD(lkpi_iic_get_adapter,	lkpi_iic_get_adapter),
147 
148 	DEVMETHOD_END
149 };
150 
151 driver_t lkpi_iic_driver = {
152 	"lkpi_iic",
153 	lkpi_iic_methods,
154 	sizeof(struct lkpi_iic_softc),
155 };
156 
157 DRIVER_MODULE(lkpi_iic, drmn, lkpi_iic_driver, 0, 0);
158 DRIVER_MODULE(lkpi_iic, drm, lkpi_iic_driver, 0, 0);
159 DRIVER_MODULE(iicbus, lkpi_iic, iicbus_driver, 0, 0);
160 MODULE_DEPEND(linuxkpi, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
161 
162 static int
163 lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
164 {
165 
166 	/* That doesn't seems to be supported in linux */
167 	return (0);
168 }
169 
170 static int
171 lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
172 {
173 	struct lkpi_iic_softc *sc;
174 	struct i2c_msg *linux_msgs;
175 	int i, ret = 0;
176 
177 	sc = device_get_softc(dev);
178 	if (sc->adapter == NULL)
179 		return (ENXIO);
180 	linux_set_current(curthread);
181 
182 	linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs,
183 	    M_DEVBUF, M_WAITOK | M_ZERO);
184 
185 	for (i = 0; i < nmsgs; i++) {
186 		linux_msgs[i].addr = msgs[i].slave;
187 		linux_msgs[i].len = msgs[i].len;
188 		linux_msgs[i].buf = msgs[i].buf;
189 		if (msgs[i].flags & IIC_M_RD) {
190 			linux_msgs[i].flags |= I2C_M_RD;
191 			for (int j = 0; j < msgs[i].len; j++)
192 				msgs[i].buf[j] = 0;
193 		}
194 		if (msgs[i].flags & IIC_M_NOSTART)
195 			linux_msgs[i].flags |= I2C_M_NOSTART;
196 	}
197 	ret = i2c_transfer(sc->adapter, linux_msgs, nmsgs);
198 	free(linux_msgs, M_DEVBUF);
199 
200 	if (ret < 0)
201 		return (-ret);
202 	return (0);
203 }
204 
205 int
206 lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
207 {
208 	device_t lkpi_iic;
209 	int error;
210 
211 	if (adapter->name[0] == '\0')
212 		return (-EINVAL);
213 	if (bootverbose)
214 		device_printf(adapter->dev.parent->bsddev,
215 		    "Adding i2c adapter %s\n", adapter->name);
216 	sx_xlock(&lkpi_sx_i2c);
217 	lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
218 	if (lkpi_iic == NULL) {
219 		device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
220 		sx_xunlock(&lkpi_sx_i2c);
221 		return (ENXIO);
222 	}
223 
224 	bus_topo_lock();
225 	error = bus_generic_attach(adapter->dev.parent->bsddev);
226 	bus_topo_unlock();
227 	if (error) {
228 		device_printf(adapter->dev.parent->bsddev,
229 		  "failed to attach child: error %d\n", error);
230 		sx_xunlock(&lkpi_sx_i2c);
231 		return (ENXIO);
232 	}
233 	LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter);
234 	sx_xunlock(&lkpi_sx_i2c);
235 	return (0);
236 }
237 
238 int
239 lkpi_i2c_del_adapter(struct i2c_adapter *adapter)
240 {
241 	device_t child;
242 	int unit, rv;
243 
244 	if (adapter == NULL)
245 		return (-EINVAL);
246 	if (bootverbose)
247 		device_printf(adapter->dev.parent->bsddev,
248 		    "Removing i2c adapter %s\n", adapter->name);
249 	sx_xlock(&lkpi_sx_i2c);
250 	unit = 0;
251 	while ((child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", unit++)) != NULL) {
252 
253 		if (adapter == LKPI_IIC_GET_ADAPTER(child)) {
254 			bus_topo_lock();
255 			device_delete_child(adapter->dev.parent->bsddev, child);
256 			bus_topo_unlock();
257 			rv = 0;
258 			goto out;
259 		}
260 	}
261 
262 	unit = 0;
263 	while ((child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", unit++)) != NULL) {
264 
265 		if (adapter == LKPI_IIC_GET_ADAPTER(child)) {
266 			bus_topo_lock();
267 			device_delete_child(adapter->dev.parent->bsddev, child);
268 			bus_topo_unlock();
269 			rv = 0;
270 			goto out;
271 		}
272 	}
273 	rv = -EINVAL;
274 out:
275 	sx_xunlock(&lkpi_sx_i2c);
276 	return (rv);
277 }
278