Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/compat/linuxkpi/common/src/linux_i2cbb.c
39586 views
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/param.h>
28
#include <sys/systm.h>
29
#include <sys/bus.h>
30
#include <sys/malloc.h>
31
32
#include <dev/iicbus/iicbus.h>
33
#include <dev/iicbus/iiconf.h>
34
35
#include <linux/device.h>
36
#include <linux/i2c.h>
37
#include <linux/i2c-algo-bit.h>
38
#include <linux/list.h>
39
#include <linux/pci.h>
40
41
#include "iicbus_if.h"
42
#include "iicbb_if.h"
43
#include "lkpi_iic_if.h"
44
45
static void lkpi_iicbb_setsda(device_t dev, int val);
46
static void lkpi_iicbb_setscl(device_t dev, int val);
47
static int lkpi_iicbb_getscl(device_t dev);
48
static int lkpi_iicbb_getsda(device_t dev);
49
static int lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr);
50
static int lkpi_iicbb_pre_xfer(device_t dev);
51
static void lkpi_iicbb_post_xfer(device_t dev);
52
53
struct lkpi_iicbb_softc {
54
device_t iicbb;
55
struct i2c_adapter *adapter;
56
};
57
58
static struct sx lkpi_sx_i2cbb;
59
60
static void
61
lkpi_sysinit_i2cbb(void *arg __unused)
62
{
63
64
sx_init(&lkpi_sx_i2cbb, "lkpi-i2cbb");
65
}
66
67
static void
68
lkpi_sysuninit_i2cbb(void *arg __unused)
69
{
70
71
sx_destroy(&lkpi_sx_i2cbb);
72
}
73
74
SYSINIT(lkpi_i2cbb, SI_SUB_DRIVERS, SI_ORDER_ANY,
75
lkpi_sysinit_i2cbb, NULL);
76
SYSUNINIT(lkpi_i2cbb, SI_SUB_DRIVERS, SI_ORDER_ANY,
77
lkpi_sysuninit_i2cbb, NULL);
78
79
static int
80
lkpi_iicbb_probe(device_t dev)
81
{
82
83
device_set_desc(dev, "LinuxKPI I2CBB");
84
return (BUS_PROBE_NOWILDCARD);
85
}
86
87
static int
88
lkpi_iicbb_attach(device_t dev)
89
{
90
struct lkpi_iicbb_softc *sc;
91
92
sc = device_get_softc(dev);
93
sc->iicbb = device_add_child(dev, "iicbb", DEVICE_UNIT_ANY);
94
if (sc->iicbb == NULL) {
95
device_printf(dev, "Couldn't add iicbb child, aborting\n");
96
return (ENXIO);
97
}
98
bus_attach_children(dev);
99
return (0);
100
}
101
102
static int
103
lkpi_iicbb_detach(device_t dev)
104
{
105
struct lkpi_iicbb_softc *sc;
106
107
sc = device_get_softc(dev);
108
if (sc->iicbb)
109
device_delete_child(dev, sc->iicbb);
110
return (0);
111
}
112
113
static int
114
lkpi_iicbb_add_adapter(device_t dev, struct i2c_adapter *adapter)
115
{
116
struct lkpi_iicbb_softc *sc;
117
struct i2c_algo_bit_data *algo_data;
118
119
sc = device_get_softc(dev);
120
sc->adapter = adapter;
121
122
/*
123
* Set iicbb timing parameters deriving speed from the protocol delay.
124
*/
125
algo_data = adapter->algo_data;
126
if (algo_data->udelay != 0)
127
IICBUS_RESET(sc->iicbb, 1000000 / algo_data->udelay, 0, NULL);
128
return (0);
129
}
130
131
static struct i2c_adapter *
132
lkpi_iicbb_get_adapter(device_t dev)
133
{
134
struct lkpi_iicbb_softc *sc;
135
136
sc = device_get_softc(dev);
137
return (sc->adapter);
138
}
139
140
static device_method_t lkpi_iicbb_methods[] = {
141
/* device interface */
142
DEVMETHOD(device_probe, lkpi_iicbb_probe),
143
DEVMETHOD(device_attach, lkpi_iicbb_attach),
144
DEVMETHOD(device_detach, lkpi_iicbb_detach),
145
DEVMETHOD(device_suspend, bus_generic_suspend),
146
DEVMETHOD(device_resume, bus_generic_resume),
147
148
/* iicbb interface */
149
DEVMETHOD(iicbb_setsda, lkpi_iicbb_setsda),
150
DEVMETHOD(iicbb_setscl, lkpi_iicbb_setscl),
151
DEVMETHOD(iicbb_getsda, lkpi_iicbb_getsda),
152
DEVMETHOD(iicbb_getscl, lkpi_iicbb_getscl),
153
DEVMETHOD(iicbb_reset, lkpi_iicbb_reset),
154
DEVMETHOD(iicbb_pre_xfer, lkpi_iicbb_pre_xfer),
155
DEVMETHOD(iicbb_post_xfer, lkpi_iicbb_post_xfer),
156
157
/* lkpi_iicbb interface */
158
DEVMETHOD(lkpi_iic_add_adapter, lkpi_iicbb_add_adapter),
159
DEVMETHOD(lkpi_iic_get_adapter, lkpi_iicbb_get_adapter),
160
161
DEVMETHOD_END
162
};
163
164
driver_t lkpi_iicbb_driver = {
165
"lkpi_iicbb",
166
lkpi_iicbb_methods,
167
sizeof(struct lkpi_iicbb_softc),
168
};
169
170
DRIVER_MODULE(lkpi_iicbb, drmn, lkpi_iicbb_driver, 0, 0);
171
DRIVER_MODULE(lkpi_iicbb, drm, lkpi_iicbb_driver, 0, 0);
172
DRIVER_MODULE(iicbb, lkpi_iicbb, iicbb_driver, 0, 0);
173
MODULE_DEPEND(linuxkpi, iicbb, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
174
175
static void
176
lkpi_iicbb_setsda(device_t dev, int val)
177
{
178
struct lkpi_iicbb_softc *sc;
179
struct i2c_algo_bit_data *algo_data;
180
181
sc = device_get_softc(dev);
182
algo_data = sc->adapter->algo_data;
183
algo_data->setsda(algo_data->data, val);
184
}
185
186
static void
187
lkpi_iicbb_setscl(device_t dev, int val)
188
{
189
struct lkpi_iicbb_softc *sc;
190
struct i2c_algo_bit_data *algo_data;
191
192
sc = device_get_softc(dev);
193
algo_data = sc->adapter->algo_data;
194
algo_data->setscl(algo_data->data, val);
195
}
196
197
static int
198
lkpi_iicbb_getscl(device_t dev)
199
{
200
struct lkpi_iicbb_softc *sc;
201
struct i2c_algo_bit_data *algo_data;
202
int ret;
203
204
sc = device_get_softc(dev);
205
algo_data = sc->adapter->algo_data;
206
ret = algo_data->getscl(algo_data->data);
207
return (ret);
208
}
209
210
static int
211
lkpi_iicbb_getsda(device_t dev)
212
{
213
struct lkpi_iicbb_softc *sc;
214
struct i2c_algo_bit_data *algo_data;
215
int ret;
216
217
sc = device_get_softc(dev);
218
algo_data = sc->adapter->algo_data;
219
ret = algo_data->getsda(algo_data->data);
220
return (ret);
221
}
222
223
static int
224
lkpi_iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
225
{
226
227
/* That doesn't seems to be supported in linux */
228
return (0);
229
}
230
231
static int
232
lkpi_iicbb_pre_xfer(device_t dev)
233
{
234
struct lkpi_iicbb_softc *sc;
235
struct i2c_algo_bit_data *algo_data;
236
int rc = 0;
237
238
sc = device_get_softc(dev);
239
algo_data = sc->adapter->algo_data;
240
if (algo_data->pre_xfer != 0)
241
rc = algo_data->pre_xfer(sc->adapter);
242
return (rc);
243
}
244
245
static void
246
lkpi_iicbb_post_xfer(device_t dev)
247
{
248
struct lkpi_iicbb_softc *sc;
249
struct i2c_algo_bit_data *algo_data;
250
251
sc = device_get_softc(dev);
252
algo_data = sc->adapter->algo_data;
253
if (algo_data->post_xfer != NULL)
254
algo_data->post_xfer(sc->adapter);
255
}
256
257
int
258
lkpi_i2cbb_transfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
259
int nmsgs)
260
{
261
struct iic_msg *bsd_msgs;
262
int ret = ENXIO;
263
264
linux_set_current(curthread);
265
266
bsd_msgs = malloc(sizeof(struct iic_msg) * nmsgs,
267
M_DEVBUF, M_WAITOK | M_ZERO);
268
269
for (int i = 0; i < nmsgs; i++) {
270
bsd_msgs[i].slave = msgs[i].addr << 1;
271
bsd_msgs[i].len = msgs[i].len;
272
bsd_msgs[i].buf = msgs[i].buf;
273
if (msgs[i].flags & I2C_M_RD)
274
bsd_msgs[i].flags |= IIC_M_RD;
275
if (msgs[i].flags & I2C_M_NOSTART)
276
bsd_msgs[i].flags |= IIC_M_NOSTART;
277
}
278
279
for (int unit = 0; ; unit++) {
280
device_t child;
281
struct lkpi_iicbb_softc *sc;
282
283
child = device_find_child(adapter->dev.parent->bsddev,
284
"lkpi_iicbb", unit);
285
if (child == NULL)
286
break;
287
if (adapter == LKPI_IIC_GET_ADAPTER(child)) {
288
sc = device_get_softc(child);
289
ret = IICBUS_TRANSFER(sc->iicbb, bsd_msgs, nmsgs);
290
ret = iic2errno(ret);
291
break;
292
}
293
}
294
295
free(bsd_msgs, M_DEVBUF);
296
297
if (ret != 0)
298
return (-ret);
299
return (nmsgs);
300
}
301
302
int
303
lkpi_i2c_bit_add_bus(struct i2c_adapter *adapter)
304
{
305
device_t lkpi_iicbb;
306
307
if (bootverbose)
308
device_printf(adapter->dev.parent->bsddev,
309
"Adding i2c adapter %s\n", adapter->name);
310
sx_xlock(&lkpi_sx_i2cbb);
311
lkpi_iicbb = device_add_child(adapter->dev.parent->bsddev, "lkpi_iicbb",
312
DEVICE_UNIT_ANY);
313
if (lkpi_iicbb == NULL) {
314
device_printf(adapter->dev.parent->bsddev, "Couldn't add lkpi_iicbb\n");
315
sx_xunlock(&lkpi_sx_i2cbb);
316
return (ENXIO);
317
}
318
319
bus_topo_lock();
320
bus_attach_children(adapter->dev.parent->bsddev);
321
bus_topo_unlock();
322
LKPI_IIC_ADD_ADAPTER(lkpi_iicbb, adapter);
323
sx_xunlock(&lkpi_sx_i2cbb);
324
return (0);
325
}
326
327