FreeBSD kernel IICBUS device code
tca6416.c
Go to the documentation of this file.
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2020 Alstom Group.
5 * Copyright (c) 2020 Semihalf.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29/*
30 * Driver for TI TCA6416 I2C GPIO expander module.
31 *
32 * This driver only supports basic functionality
33 * (interrupt handling and polarity inversion were omitted).
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include <sys/param.h>
40#include <sys/bus.h>
41#include <sys/gpio.h>
42#include <sys/kernel.h>
43#include <sys/module.h>
44#include <sys/proc.h>
45#include <sys/systm.h>
46#include <sys/sysctl.h>
47
48#include <machine/bus.h>
49
50#include <dev/ofw/openfirm.h>
51#include <dev/ofw/ofw_bus.h>
52#include <dev/ofw/ofw_bus_subr.h>
53
54#include <dev/iicbus/iicbus.h>
55#include <dev/iicbus/iiconf.h>
56
57#include <dev/gpio/gpiobusvar.h>
58
59#include "gpio_if.h"
60
61/* Base addresses of registers. LSB omitted. */
62#define IN_PORT_REG 0x00
63#define OUT_PORT_REG 0x02
64#define POLARITY_INV_REG 0x04
65#define CONF_REG 0x06
66
67#define NUM_PINS 16
68#define PINS_PER_REG 8
69#define PIN_CAPS \
70 (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT \
71 | GPIO_PIN_PUSHPULL | GPIO_PIN_INVIN)
72
73#ifdef DEBUG
74#define dbg_dev_printf(dev, fmt, args...) \
75 device_printf(dev, fmt, ##args)
76#else
77#define dbg_dev_printf(dev, fmt, args...)
78#endif
79
80#define TCA6416_BIT_FROM_PIN(pin) (pin % PINS_PER_REG)
81#define TCA6416_REG_ADDR(pin, baseaddr) (baseaddr | (pin / PINS_PER_REG))
82
84 device_t dev;
85 device_t busdev;
86 struct mtx mtx;
87 uint32_t addr;
88};
89
90static int tca6416_read(device_t, uint8_t, uint8_t*);
91static int tca6416_write(device_t, uint8_t, uint8_t);
92static int tca6416_probe(device_t);
93static int tca6416_attach(device_t);
94static int tca6416_detach(device_t);
95static device_t tca6416_get_bus(device_t);
96static int tca6416_pin_max(device_t, int*);
97static int tca6416_pin_getcaps(device_t, uint32_t, uint32_t*);
98static int tca6416_pin_getflags(device_t, uint32_t, uint32_t*);
99static int tca6416_pin_setflags(device_t, uint32_t, uint32_t);
100static int tca6416_pin_getname(device_t, uint32_t, char*);
101static int tca6416_pin_get(device_t, uint32_t, unsigned int*);
102static int tca6416_pin_set(device_t, uint32_t, unsigned int);
103static int tca6416_pin_toggle(device_t, uint32_t);
104#ifdef DEBUG
105static void tca6416_regdump_setup(device_t dev);
106static int tca6416_regdump_sysctl(SYSCTL_HANDLER_ARGS);
107#endif
108
109static device_method_t tca6416_methods[] = {
110 DEVMETHOD(device_probe, tca6416_probe),
111 DEVMETHOD(device_attach, tca6416_attach),
112 DEVMETHOD(device_detach, tca6416_detach),
113
114 /* GPIO methods */
115 DEVMETHOD(gpio_get_bus, tca6416_get_bus),
116 DEVMETHOD(gpio_pin_max, tca6416_pin_max),
117 DEVMETHOD(gpio_pin_getcaps, tca6416_pin_getcaps),
118 DEVMETHOD(gpio_pin_getflags, tca6416_pin_getflags),
119 DEVMETHOD(gpio_pin_setflags, tca6416_pin_setflags),
120 DEVMETHOD(gpio_pin_getname, tca6416_pin_getname),
121 DEVMETHOD(gpio_pin_get, tca6416_pin_get),
122 DEVMETHOD(gpio_pin_set, tca6416_pin_set),
123 DEVMETHOD(gpio_pin_toggle, tca6416_pin_toggle),
124
125 DEVMETHOD_END
126};
127
128static driver_t tca6416_driver = {
129 "gpio",
131 sizeof(struct tca6416_softc)
132};
133
134static devclass_t tca6416_devclass;
135
137MODULE_VERSION(tca6416, 1);
138
139static struct ofw_compat_data compat_data[] = {
140 {"ti,tca6416", 1},
141 {0,0}
142};
143
144static int
145tca6416_read(device_t dev, uint8_t reg, uint8_t *data)
146{
147 struct iic_msg msgs[2];
148 struct tca6416_softc *sc;
149 int error;
150
151 sc = device_get_softc(dev);
152 if (data == NULL)
153 return (EINVAL);
154
155 msgs[0].slave = sc->addr;
157 msgs[0].len = 1;
158 msgs[0].buf = &reg;
159
160 msgs[1].slave = sc->addr;
161 msgs[1].flags = IIC_M_RD;
162 msgs[1].len = 1;
163 msgs[1].buf = data;
164
166 return (iic2errno(error));
167}
168
169static int
170tca6416_write(device_t dev, uint8_t reg, uint8_t val)
171{
172 struct iic_msg msg;
173 struct tca6416_softc *sc;
174 int error;
175 uint8_t buffer[2] = {reg, val};
176
177 sc = device_get_softc(dev);
178
179 msg.slave = sc->addr;
180 msg.flags = IIC_M_WR;
181 msg.len = 2;
182 msg.buf = buffer;
183
184 error = iicbus_transfer_excl(dev, &msg, 1, IIC_WAIT);
185 return (iic2errno(error));
186}
187
188static int
190{
191
192 if (!ofw_bus_status_okay(dev))
193 return (ENXIO);
194
195 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
196 return (ENXIO);
197
198 device_set_desc(dev, "TCA6416 I/O expander");
199 return (BUS_PROBE_DEFAULT);
200}
201
202static int
204{
205 struct tca6416_softc *sc;
206
207 sc = device_get_softc(dev);
208 sc->dev = dev;
209 sc->addr = iicbus_get_addr(dev);
210
211 mtx_init(&sc->mtx, "tca6416 gpio", "gpio", MTX_DEF);
212
213 sc->busdev = gpiobus_attach_bus(dev);
214 if (sc->busdev == NULL) {
215 device_printf(dev, "Could not create busdev child\n");
216 return (ENXIO);
217 }
218
219#ifdef DEBUG
220 tca6416_regdump_setup(dev);
221#endif
222
223 return (0);
224}
225
226static int
228{
229 struct tca6416_softc *sc;
230
231 sc = device_get_softc(dev);
232
233 if (sc->busdev != NULL)
234 gpiobus_detach_bus(sc->busdev);
235
236 mtx_destroy(&sc->mtx);
237
238 return (0);
239}
240
241static device_t
243{
244 struct tca6416_softc *sc;
245
246 sc = device_get_softc(dev);
247
248 return (sc->busdev);
249}
250
251static int
252tca6416_pin_max(device_t dev __unused, int *maxpin)
253{
254
255 if (maxpin == NULL)
256 return (EINVAL);
257
258 *maxpin = NUM_PINS - 1;
259 return (0);
260}
261
262static int
263tca6416_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
264{
265
266 if (pin >= NUM_PINS || caps == NULL)
267 return (EINVAL);
268
269 *caps = PIN_CAPS;
270 return (0);
271}
272
273static int
274tca6416_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags)
275{
276 struct tca6416_softc *sc;
277 int error;
278 uint8_t reg_addr, reg_bit, val;
279
280 sc = device_get_softc(dev);
281
282 if (pin >= NUM_PINS || pflags == NULL)
283 return (EINVAL);
284
285 reg_addr = TCA6416_REG_ADDR(pin, CONF_REG);
286 reg_bit = TCA6416_BIT_FROM_PIN(pin);
287
288 error = tca6416_read(dev, reg_addr, &val);
289 if (error != 0)
290 return (error);
291
292 *pflags = (val & (1 << reg_bit))
293 ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT;
294
295 reg_addr = TCA6416_REG_ADDR(pin, POLARITY_INV_REG);
296
297 error = tca6416_read(dev, reg_addr, &val);
298 if (error != 0)
299 return (error);
300
301 if (val & (1 << reg_bit))
302 *pflags |= GPIO_PIN_INVIN;
303
304 return (0);
305}
306
307static int
308tca6416_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
309{
310 uint8_t reg_addr, inv_reg_addr, reg_bit, val, inv_val;
311 struct tca6416_softc *sc;
312 int error;
313
314 sc = device_get_softc(dev);
315
316 if (pin >= NUM_PINS)
317 return (EINVAL);
318
319 reg_addr = TCA6416_REG_ADDR(pin, CONF_REG);
320 inv_reg_addr = TCA6416_REG_ADDR(pin, POLARITY_INV_REG);
321 reg_bit = TCA6416_BIT_FROM_PIN(pin);
322
323 mtx_lock(&sc->mtx);
324
325 error = tca6416_read(dev, reg_addr, &val);
326 if (error != 0)
327 goto fail;
328 error = tca6416_read(dev, inv_reg_addr, &inv_val);
329 if (error != 0)
330 goto fail;
331
332 if (flags & GPIO_PIN_INPUT)
333 val |= (1 << reg_bit);
334 else if (flags & GPIO_PIN_OUTPUT)
335 val &= ~(1 << reg_bit);
336
337 if (flags & GPIO_PIN_INVIN)
338 inv_val |= (1 << reg_bit);
339 else
340 inv_val &= ~(1 << reg_bit);
341
342 error = tca6416_write(dev, reg_addr, val);
343 if (error != 0)
344 goto fail;
345 error = tca6416_write(dev, inv_reg_addr, val);
346
347fail:
348 mtx_unlock(&sc->mtx);
349 return (error);
350}
351
352static int
353tca6416_pin_getname(device_t dev, uint32_t pin, char *name)
354{
355
356 if (pin >= NUM_PINS || name == NULL)
357 return (EINVAL);
358
359 snprintf(name, GPIOMAXNAME, "gpio_P%d%d", pin / PINS_PER_REG,
360 pin % PINS_PER_REG);
361
362 return (0);
363}
364
365static int
366tca6416_pin_get(device_t dev, uint32_t pin, unsigned int *pval)
367{
368 struct tca6416_softc *sc;
369 uint8_t reg_bit, reg_addr, reg_pvalue;
370 int error;
371
372 sc = device_get_softc(dev);
373
374 if (pin >= NUM_PINS || pval == NULL)
375 return (EINVAL);
376
377 reg_bit = TCA6416_BIT_FROM_PIN(pin);
378 reg_addr = TCA6416_REG_ADDR(pin, IN_PORT_REG);
379
380 dbg_dev_printf(dev, "Reading pin %u pvalue.", pin);
381
382 error = tca6416_read(dev, reg_addr, &reg_pvalue);
383 if (error != 0)
384 return (error);
385
386 *pval = reg_pvalue & (1 << reg_bit) ? 1 : 0;
387
388 return (0);
389}
390
391static int
392tca6416_pin_set(device_t dev, uint32_t pin, unsigned int val)
393{
394 struct tca6416_softc *sc;
395 uint8_t reg_addr, reg_bit, reg_value;
396 int error;
397
398 sc = device_get_softc(dev);
399
400 if (pin >= NUM_PINS)
401 return (EINVAL);
402
403 reg_addr = TCA6416_REG_ADDR(pin , OUT_PORT_REG);
404 reg_bit = TCA6416_BIT_FROM_PIN(pin);
405
406 dbg_dev_printf(dev, "Setting pin: %u to %u\n", pin, val);
407
408 mtx_lock(&sc->mtx);
409
410 error = tca6416_read(dev, reg_addr, &reg_value);
411 if (error != 0) {
412 dbg_dev_printf(dev, "Failed to read from register.\n");
413 mtx_unlock(&sc->mtx);
414 return (error);
415 }
416
417 if (val != 0)
418 reg_value |= (1 << reg_bit);
419 else
420 reg_value &= ~(1 << reg_bit);
421
422
423 error = tca6416_write(dev, reg_addr, reg_value);
424 if (error != 0) {
425 dbg_dev_printf(dev, "Could not write to register.\n");
426 mtx_unlock(&sc->mtx);
427 return (error);
428 }
429
430 mtx_unlock(&sc->mtx);
431
432 return (0);
433}
434
435static int
436tca6416_pin_toggle(device_t dev, uint32_t pin)
437{
438 struct tca6416_softc *sc;
439 int error;
440 uint8_t reg_addr, reg_bit, reg_value;
441
442 sc = device_get_softc(dev);
443
444 if (pin >= NUM_PINS)
445 return (EINVAL);
446
447 reg_addr = TCA6416_REG_ADDR(pin, OUT_PORT_REG);
448 reg_bit = TCA6416_BIT_FROM_PIN(pin);
449
450 dbg_dev_printf(dev, "Toggling pin: %d\n", pin);
451
452 mtx_lock(&sc->mtx);
453
454 error = tca6416_read(dev, reg_addr, &reg_value);
455 if (error != 0) {
456 mtx_unlock(&sc->mtx);
457 dbg_dev_printf(dev, "Cannot read from register.\n");
458 return (error);
459 }
460
461 reg_value ^= (1 << reg_bit);
462
463 error = tca6416_write(dev, reg_addr, reg_value);
464 if (error != 0)
465 dbg_dev_printf(dev, "Cannot write to register.\n");
466
467 mtx_unlock(&sc->mtx);
468
469 return (error);
470}
471
472#ifdef DEBUG
473static void
474tca6416_regdump_setup(device_t dev)
475{
476 struct sysctl_ctx_list *ctx;
477 struct sysctl_oid *node;
478
479 ctx = device_get_sysctl_ctx(dev);
480 node = device_get_sysctl_tree(dev);
481
482 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_1",
483 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, IN_PORT_REG,
484 tca6416_regdump_sysctl, "A", "Input port 1");
485 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_2",
486 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
487 IN_PORT_REG | 1, tca6416_regdump_sysctl, "A", "Input port 2");
488 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_1",
489 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, OUT_PORT_REG,
490 tca6416_regdump_sysctl, "A", "Output port 1");
491 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_2",
492 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, OUT_PORT_REG
493 | 1, tca6416_regdump_sysctl, "A", "Output port 2");
494 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_1",
495 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
496 POLARITY_INV_REG, tca6416_regdump_sysctl, "A", "Polarity inv 1");
497 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_2",
498 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
499 POLARITY_INV_REG | 1, tca6416_regdump_sysctl, "A",
500 "Polarity inv 2");
501 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_1",
502 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
503 CONF_REG, tca6416_regdump_sysctl, "A", "Configuration 1");
504 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_2",
505 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
506 CONF_REG | 1, tca6416_regdump_sysctl, "A", "Configuration 2");
507}
508
509static int
510tca6416_regdump_sysctl(SYSCTL_HANDLER_ARGS)
511{
512 device_t dev;
513 char buf[5];
514 struct tca6416_softc *sc;
515 int len, error;
516 uint8_t reg, regval;
517
518 dev = (device_t)arg1;
519 reg = (uint8_t)arg2;
520 sc = device_get_softc(dev);
521
522
523 error = tca6416_read(dev, reg, &regval);
524 if (error != 0) {
525 return (error);
526 }
527
528 len = snprintf(buf, 5, "0x%02x", regval);
529
530 error = sysctl_handle_string(oidp, buf, len, req);
531
532 return (error);
533}
534#endif
#define IIC_M_WR
Definition: ad7418.c:45
#define IIC_M_RD
Definition: iic.h:42
#define IIC_M_NOSTOP
Definition: iic.h:43
caddr_t data
Definition: iicbb_if.m:61
int val
Definition: iicbb_if.m:83
char * buf
Definition: iicbus_if.m:55
struct iic_msg * msgs
Definition: iicbus_if.m:134
INTERFACE iicbus
Definition: iicbus_if.m:32
int len
Definition: iicbus_if.m:102
int iic2errno(int iic_status)
Definition: iiconf.c:60
int iicbus_transfer_excl(device_t dev, struct iic_msg *msgs, uint32_t nmsgs, int how)
Definition: iiconf.c:449
#define IIC_WAIT
Definition: iiconf.h:46
device_t dev
Definition: ofw_iicbus_if.m:38
Definition: iic.h:38
uint16_t slave
Definition: iic.h:39
uint16_t len
Definition: iic.h:45
uint8_t * buf
Definition: iic.h:46
uint16_t flags
Definition: iic.h:40
uint32_t addr
Definition: tca6416.c:87
device_t dev
Definition: tca6416.c:84
device_t busdev
Definition: tca6416.c:85
struct mtx mtx
Definition: tca6416.c:86
#define CONF_REG
Definition: tca6416.c:65
static int tca6416_pin_getname(device_t, uint32_t, char *)
Definition: tca6416.c:353
#define dbg_dev_printf(dev, fmt, args...)
Definition: tca6416.c:77
#define PINS_PER_REG
Definition: tca6416.c:68
#define IN_PORT_REG
Definition: tca6416.c:62
#define POLARITY_INV_REG
Definition: tca6416.c:64
static int tca6416_attach(device_t)
Definition: tca6416.c:203
DRIVER_MODULE(tca6416, iicbus, tca6416_driver, tca6416_devclass, 0, 0)
static int tca6416_pin_get(device_t, uint32_t, unsigned int *)
Definition: tca6416.c:366
#define TCA6416_BIT_FROM_PIN(pin)
Definition: tca6416.c:80
#define OUT_PORT_REG
Definition: tca6416.c:63
#define PIN_CAPS
Definition: tca6416.c:69
static int tca6416_pin_getcaps(device_t, uint32_t, uint32_t *)
Definition: tca6416.c:263
static devclass_t tca6416_devclass
Definition: tca6416.c:134
#define NUM_PINS
Definition: tca6416.c:67
static int tca6416_pin_max(device_t, int *)
static int tca6416_pin_toggle(device_t, uint32_t)
Definition: tca6416.c:436
static int tca6416_pin_set(device_t, uint32_t, unsigned int)
Definition: tca6416.c:392
static device_method_t tca6416_methods[]
Definition: tca6416.c:109
static int tca6416_pin_setflags(device_t, uint32_t, uint32_t)
Definition: tca6416.c:308
static int tca6416_write(device_t, uint8_t, uint8_t)
Definition: tca6416.c:170
static int tca6416_detach(device_t)
Definition: tca6416.c:227
__FBSDID("$FreeBSD$")
static int tca6416_read(device_t, uint8_t, uint8_t *)
Definition: tca6416.c:145
#define TCA6416_REG_ADDR(pin, baseaddr)
Definition: tca6416.c:81
static struct ofw_compat_data compat_data[]
Definition: tca6416.c:139
static int tca6416_pin_getflags(device_t, uint32_t, uint32_t *)
Definition: tca6416.c:274
static driver_t tca6416_driver
Definition: tca6416.c:128
MODULE_VERSION(tca6416, 1)
static device_t tca6416_get_bus(device_t)
Definition: tca6416.c:242
static int tca6416_probe(device_t)
Definition: tca6416.c:189