xref: /freebsd/sys/compat/linuxkpi/common/src/linux_i2cbb.c (revision 1323ec571215a77ddd21294f0871979d5ad6b992)
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 "iicbb_if.h"
45 #include "lkpi_iic_if.h"
46 
47 static void lkpi_iicbb_setsda(device_t dev, int val);
48 static void lkpi_iicbb_setscl(device_t dev, int val);
49 static int lkpi_iicbb_getscl(device_t dev);
50 static int lkpi_iicbb_getsda(device_t dev);
51 static int lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr);
52 
53 struct lkpi_iicbb_softc {
54 	device_t		iicbb;
55 	struct i2c_adapter	*adapter;
56 };
57 
58 static int
59 lkpi_iicbb_probe(device_t dev)
60 {
61 
62 	device_set_desc(dev, "LinuxKPI I2CBB");
63 	return (BUS_PROBE_NOWILDCARD);
64 }
65 
66 static int
67 lkpi_iicbb_attach(device_t dev)
68 {
69 	struct lkpi_iicbb_softc *sc;
70 
71 	sc = device_get_softc(dev);
72 	sc->iicbb = device_add_child(dev, "iicbb", -1);
73 	if (sc->iicbb == NULL) {
74 		device_printf(dev, "Couldn't add iicbb child, aborting\n");
75 		return (ENXIO);
76 	}
77 	bus_generic_attach(dev);
78 	return (0);
79 }
80 
81 static int
82 lkpi_iicbb_detach(device_t dev)
83 {
84 	struct lkpi_iicbb_softc *sc;
85 
86 	sc = device_get_softc(dev);
87 	if (sc->iicbb)
88 		device_delete_child(dev, sc->iicbb);
89 	return (0);
90 }
91 
92 static int
93 lkpi_iicbb_add_adapter(device_t dev, struct i2c_adapter *adapter)
94 {
95 	struct lkpi_iicbb_softc *sc;
96 
97 	sc = device_get_softc(dev);
98 	sc->adapter = adapter;
99 
100 	return (0);
101 }
102 
103 static device_method_t lkpi_iicbb_methods[] = {
104 	/* device interface */
105 	DEVMETHOD(device_probe,		lkpi_iicbb_probe),
106 	DEVMETHOD(device_attach,	lkpi_iicbb_attach),
107 	DEVMETHOD(device_detach,	lkpi_iicbb_detach),
108 	DEVMETHOD(device_suspend,	bus_generic_suspend),
109 	DEVMETHOD(device_resume,	bus_generic_resume),
110 
111 	/* iicbb interface */
112 	DEVMETHOD(iicbb_setsda,		lkpi_iicbb_setsda),
113 	DEVMETHOD(iicbb_setscl,		lkpi_iicbb_setscl),
114 	DEVMETHOD(iicbb_getsda,		lkpi_iicbb_getsda),
115 	DEVMETHOD(iicbb_getscl,		lkpi_iicbb_getscl),
116 	DEVMETHOD(iicbb_reset,		lkpi_iicbb_reset),
117 
118 	/* lkpi_iicbb interface */
119 	DEVMETHOD(lkpi_iic_add_adapter,	lkpi_iicbb_add_adapter),
120 
121 	DEVMETHOD_END
122 };
123 
124 static devclass_t lkpi_iicbb_devclass;
125 
126 driver_t lkpi_iicbb_driver = {
127 	"lkpi_iicbb",
128 	lkpi_iicbb_methods,
129 	sizeof(struct lkpi_iicbb_softc),
130 };
131 
132 DRIVER_MODULE(lkpi_iicbb, lkpi_iic, lkpi_iicbb_driver, lkpi_iicbb_devclass, 0, 0);
133 DRIVER_MODULE(iicbb, lkpi_iicbb, iicbb_driver, iicbb_devclass, 0, 0);
134 MODULE_DEPEND(lkpi_iicbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
135 
136 static void
137 lkpi_iicbb_setsda(device_t dev, int val)
138 {
139 	struct lkpi_iicbb_softc *sc;
140 	struct i2c_algo_bit_data *algo_data;
141 
142 	sc = device_get_softc(dev);
143 	algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
144 	algo_data->setsda(algo_data->data, val);
145 	cpu_spinwait();
146 	DELAY(algo_data->udelay);
147 }
148 
149 static void
150 lkpi_iicbb_setscl(device_t dev, int val)
151 {
152 	struct lkpi_iicbb_softc *sc;
153 	struct i2c_algo_bit_data *algo_data;
154 
155 	sc = device_get_softc(dev);
156 
157 	algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
158 	algo_data->setscl(algo_data->data, val);
159 	cpu_spinwait();
160 	DELAY(algo_data->udelay);
161 }
162 
163 static int
164 lkpi_iicbb_getscl(device_t dev)
165 {
166 	struct lkpi_iicbb_softc *sc;
167 	struct i2c_algo_bit_data *algo_data;
168 	unsigned long orig_ticks;
169 	int ret = 0;
170 
171 	sc = device_get_softc(dev);
172 
173 	algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
174 
175 	orig_ticks = ticks;
176 	while (!ret) {
177 		ret = algo_data->getscl(algo_data->data);
178 
179 		if (ret)
180 			break;
181 
182 		if (ticks > orig_ticks + algo_data->timeout)
183 			return (ETIMEDOUT);
184 
185 		cpu_spinwait();
186 		DELAY(algo_data->udelay);
187 	}
188 	DELAY(algo_data->udelay);
189 	return (ret);
190 }
191 
192 static int
193 lkpi_iicbb_getsda(device_t dev)
194 {
195 	struct lkpi_iicbb_softc *sc;
196 	struct i2c_algo_bit_data *algo_data;
197 	int ret = 0;
198 
199 	sc = device_get_softc(dev);
200 	algo_data = (struct i2c_algo_bit_data *)sc->adapter->algo_data;
201 
202 	cpu_spinwait();
203 	DELAY(algo_data->udelay);
204 	ret = algo_data->getsda(algo_data->data);
205 	cpu_spinwait();
206 	DELAY(algo_data->udelay);
207 	return (ret);
208 }
209 
210 static int
211 lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
212 {
213 
214 	return (0);
215 }
216 
217 int
218 lkpi_i2cbb_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int nmsgs)
219 {
220 
221 	/* TODO: convert from i2c_msg to iic_msg and call IICBUS_TRANFER */
222 	return (0);
223 }
224 
225 int
226 lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)
227 {
228 	device_t lkpi_iicbb;
229 	int error;
230 
231 	if (bootverbose)
232 		device_printf(adapter->dev.parent->bsddev,
233 		    "Adding i2c adapter %s\n", adapter->name);
234 	lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb", -1);
235 	if (lkpi_iicbb == NULL) {
236 		device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iicbb\n");
237 		return (ENXIO);
238 	}
239 
240 	error = bus_generic_attach(adapter->dev.parent->bsddev);
241 	if (error) {
242 		device_printf(adapter->dev.parent->bsddev,
243 		  "failed to attach child: error %d\n", error);
244 		return (ENXIO);
245 	}
246 	LKPI_IIC_ADD_ADAPTER(lkpi_iicbb, adapter);
247 	return (0);
248 }
249 
250