xref: /freebsd/sys/compat/linuxkpi/common/src/linux_i2c.c (revision 13ec1e3155c7e9bf037b12af186351b7fa9b9450)
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 devclass_t lkpi_iic_devclass;
121 
122 driver_t lkpi_iic_driver = {
123 	"lkpi_iic",
124 	lkpi_iic_methods,
125 	sizeof(struct lkpi_iic_softc),
126 };
127 
128 DRIVER_MODULE(lkpi_iic, drmn, lkpi_iic_driver, lkpi_iic_devclass, 0, 0);
129 DRIVER_MODULE(iicbus, lkpi_iic, iicbus_driver, iicbus_devclass, 0, 0);
130 MODULE_DEPEND(linuxkpi, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
131 
132 static int
133 lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
134 {
135 
136 	/* That doesn't seems to be supported in linux */
137 	return (0);
138 }
139 
140 static int
141 lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
142 {
143 	struct lkpi_iic_softc *sc;
144 	struct i2c_msg *linux_msgs;
145 	int i, ret = 0;
146 
147 	sc = device_get_softc(dev);
148 	if (sc->adapter == NULL)
149 		return (ENXIO);
150 	linux_set_current(curthread);
151 
152 	linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs,
153 	    M_DEVBUF, M_WAITOK | M_ZERO);
154 
155 	for (i = 0; i < nmsgs; i++) {
156 		linux_msgs[i].addr = msgs[i].slave;
157 		linux_msgs[i].len = msgs[i].len;
158 		linux_msgs[i].buf = msgs[i].buf;
159 		if (msgs[i].flags & IIC_M_RD) {
160 			linux_msgs[i].flags |= I2C_M_RD;
161 			for (int j = 0; j < msgs[i].len; j++)
162 				msgs[i].buf[j] = 0;
163 		}
164 		if (msgs[i].flags & IIC_M_NOSTART)
165 			linux_msgs[i].flags |= I2C_M_NOSTART;
166 	}
167 	ret = i2c_transfer(sc->adapter, linux_msgs, nmsgs);
168 	free(linux_msgs, M_DEVBUF);
169 
170 	if (ret < 0)
171 		return (-ret);
172 	return (0);
173 }
174 
175 int
176 lkpi_i2c_add_adapter(struct i2c_adapter *adapter)
177 {
178 	device_t lkpi_iic;
179 	int error;
180 
181 	if (bootverbose)
182 		device_printf(adapter->dev.parent->bsddev,
183 		    "Adding i2c adapter %s\n", adapter->name);
184 	lkpi_iic = device_add_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
185 	if (lkpi_iic == NULL) {
186 		device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iic\n");
187 		return (ENXIO);
188 	}
189 
190 	error = bus_generic_attach(adapter->dev.parent->bsddev);
191 	if (error) {
192 		device_printf(adapter->dev.parent->bsddev,
193 		  "failed to attach child: error %d\n", error);
194 		return (ENXIO);
195 	}
196 	LKPI_IIC_ADD_ADAPTER(lkpi_iic, adapter);
197 	return (0);
198 }
199 
200 int
201 lkpi_i2c_del_adapter(struct i2c_adapter *adapter)
202 {
203 	device_t child;
204 
205 	if (bootverbose)
206 		device_printf(adapter->dev.parent->bsddev,
207 		    "Removing i2c adapter %s\n", adapter->name);
208 
209 	child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iic", -1);
210 	if (child != NULL)
211 		device_delete_child(adapter->dev.parent->bsddev, child);
212 
213 	child = device_find_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
214 	if (child != NULL)
215 		device_delete_child(adapter->dev.parent->bsddev, child);
216 
217 	return (0);
218 }
219