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 >> 1;
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 }
Cache object: 23cf4c63306f50403fb4d5a53321a26c
|