Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106135707
D24363.id70396.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
15 KB
Referenced Files
None
Subscribers
None
D24363.id70396.diff
View Options
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1806,6 +1806,7 @@
dev/iicbus/rtc8583.c optional rtc8583
dev/iicbus/s35390a.c optional s35390a
dev/iicbus/sy8106a.c optional sy8106a ext_resources fdt
+dev/iicbus/tca6416.c optional tca6416 fdt
dev/iir/iir.c optional iir
dev/iir/iir_ctrl.c optional iir
dev/iir/iir_pci.c optional iir pci
Index: sys/dev/iicbus/tca6416.c
===================================================================
--- /dev/null
+++ sys/dev/iicbus/tca6416.c
@@ -0,0 +1,595 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Alstom Group.
+ * Copyright (c) 2020 Semihalf.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Driver for TI TCA6416 I2C GPIO expander module.
+ *
+ * This driver only supports basic functionality
+ * (interrupt handling and polarity inversion were omitted).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include "gpio_if.h"
+
+/* Base addresses of registers. LSB omitted. */
+#define IN_PORT_REG 0x00
+#define OUT_PORT_REG 0x02
+#define POLARITY_INV_REG 0x04
+#define CONF_REG 0x06
+
+#define NUM_PINS 16
+#define PINS_PER_REG 8
+#define PIN_CAPS \
+ (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT \
+ | GPIO_PIN_PUSHPULL)
+
+#ifdef DEBUG
+#define dbg_dev_printf(dev, fmt, args...) \
+ device_printf(dev, fmt, ##args)
+#else
+#define dbg_dev_printf(dev, fmt, args...)
+#endif
+
+#define TCA6416_BIT_FROM_PIN(pin) (pin % PINS_PER_REG)
+#define TCA6416_REG_ADDR(pin, baseaddr) (baseaddr | (pin / PINS_PER_REG))
+
+struct tca6416_softc {
+ device_t dev;
+ device_t busdev;
+ struct mtx mtx;
+ uint32_t addr;
+
+ struct intr_config_hook intr_hook;
+};
+
+static int tca6416_read(device_t, uint8_t, uint8_t*);
+static int tca6416_write(device_t, uint8_t, uint8_t);
+static int tca6416_probe(device_t);
+static int tca6416_attach(device_t);
+static int tca6416_detach(device_t);
+static device_t tca6416_get_bus(device_t);
+static int tca6416_pin_max(device_t, int*);
+static int tca6416_pin_getcaps(device_t, uint32_t, uint32_t*);
+static int tca6416_pin_getflags(device_t, uint32_t, uint32_t*);
+static int tca6416_pin_setflags(device_t, uint32_t, uint32_t);
+static int tca6416_pin_getname(device_t, uint32_t, char*);
+static int tca6416_pin_get(device_t, uint32_t, unsigned int*);
+static int tca6416_pin_set(device_t, uint32_t, unsigned int);
+static int tca6416_pin_toggle(device_t, uint32_t);
+#ifdef DEBUG
+static void tca6416_regdump_setup(device_t dev);
+static int tca6416_regdump_sysctl(SYSCTL_HANDLER_ARGS);
+#endif
+
+static device_method_t tca6416_methods[] = {
+ DEVMETHOD(device_probe, tca6416_probe),
+ DEVMETHOD(device_attach, tca6416_attach),
+ DEVMETHOD(device_detach, tca6416_detach),
+
+ /* GPIO methods */
+ DEVMETHOD(gpio_get_bus, tca6416_get_bus),
+ DEVMETHOD(gpio_pin_max, tca6416_pin_max),
+ DEVMETHOD(gpio_pin_getcaps, tca6416_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, tca6416_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, tca6416_pin_setflags),
+ DEVMETHOD(gpio_pin_getname, tca6416_pin_getname),
+ DEVMETHOD(gpio_pin_get, tca6416_pin_get),
+ DEVMETHOD(gpio_pin_set, tca6416_pin_set),
+ DEVMETHOD(gpio_pin_toggle, tca6416_pin_toggle),
+
+ DEVMETHOD_END
+};
+
+static driver_t tca6416_driver = {
+ "tca6416",
+ tca6416_methods,
+ sizeof(struct tca6416_softc)
+};
+
+static devclass_t tca6416_devclass;
+
+extern devclass_t gpioc_devclass;
+extern driver_t gpioc_driver;
+
+DRIVER_MODULE(tca6416, iicbus, tca6416_driver, tca6416_devclass, 0, 0);
+DRIVER_MODULE(gpioc, tca6416, gpioc_driver, gpioc_devclass, 0, 0);
+extern devclass_t ofwgpiobus_devclass;
+extern driver_t ofw_gpiobus_driver;
+DRIVER_MODULE(ofw_gpiobus, tca6416, ofw_gpiobus_driver,
+ ofwgpiobus_devclass, 0, 0);
+extern devclass_t gpiobus_devclass;
+extern driver_t gpiobus_driver;
+DRIVER_MODULE(gpiobus, tca6416, gpiobus_driver, gpiobus_devclass, 0, 0);
+MODULE_VERSION(tca6416, 1);
+
+static struct ofw_compat_data compat_data[] = {
+ {"ti,tca6416", 1},
+ {0,0}
+};
+
+static int
+tca6416_read(device_t dev, uint8_t reg, uint8_t *data)
+{
+ struct iic_msg msgs[2];
+ struct tca6416_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ if (data == NULL)
+ return (EINVAL);
+
+ msgs[0].slave = sc->addr;
+ msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
+ msgs[0].len = 1;
+ msgs[0].buf = ®
+
+ msgs[1].slave = sc->addr;
+ msgs[1].flags = IIC_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = data;
+
+ error = iicbus_transfer_excl(dev, msgs, 2, IIC_WAIT);
+ return (iic2errno(error));
+}
+
+static int
+tca6416_write(device_t dev, uint8_t reg, uint8_t val)
+{
+ struct iic_msg msg;
+ struct tca6416_softc *sc;
+ int error;
+ uint8_t buffer[2] = {reg, val};
+
+ sc = device_get_softc(dev);
+
+ msg.slave = sc->addr;
+ msg.flags = IIC_M_WR;
+ msg.len = 2;
+ msg.buf = buffer;
+
+ error = iicbus_transfer_excl(dev, &msg, 1, IIC_WAIT);
+ return (iic2errno(error));
+}
+
+static void
+tca6416_conf(void *arg)
+{
+ device_t dev;
+ struct tca6416_softc *sc;
+ int error = 0;
+ uint8_t regval;
+
+ dev = (device_t)arg;
+ sc = device_get_softc(dev);
+
+ config_intrhook_disestablish(&sc->intr_hook);
+
+ mtx_lock(&sc->mtx);
+
+ error = tca6416_write(dev, POLARITY_INV_REG, 0x00);
+ if (error != 0) {
+ device_printf(dev, "Could not write to device.\n");
+ mtx_unlock(&sc->mtx);
+ return;
+ }
+ error = tca6416_write(dev, POLARITY_INV_REG | 1, 0x00);
+ if (error != 0) {
+ device_printf(dev, "Could not write to device\n");
+ mtx_unlock(&sc->mtx);
+ return;
+ }
+
+ /*
+ * Read from registers to confirm that the device
+ * can establish communication in both ways.
+ */
+ error = tca6416_read(dev, POLARITY_INV_REG, ®val);
+ if (error || regval != 0x00) {
+ device_printf(dev, "Error while reading from device\n");
+ mtx_unlock(&sc->mtx);
+ return;
+ }
+ error = tca6416_read(dev, POLARITY_INV_REG | 1, ®val);
+ if (error || regval != 0x00) {
+ device_printf(dev, "Error while reading from device\n");
+ mtx_unlock(&sc->mtx);
+ return;
+ }
+
+ mtx_unlock(&sc->mtx);
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (!sc->busdev){
+ device_printf(dev, "Could not create busdev child.\n");
+ }
+
+#ifdef DEBUG
+ tca6416_regdump_setup(dev);
+#endif
+
+}
+
+static int
+tca6416_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "TCA6416 I/O expander");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+tca6416_attach(device_t dev)
+{
+ struct tca6416_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->addr = iicbus_get_addr(dev);
+
+ mtx_init(&sc->mtx, "tca6416 gpio", "gpio", MTX_DEF);
+
+ /*
+ * Since many i2c controllers do not operate properly
+ * before the interrupts start, the rest of configuration
+ * is executed after interrupts are enabled.
+ */
+ sc->intr_hook.ich_func = tca6416_conf;
+ sc->intr_hook.ich_arg = dev;
+
+ error = config_intrhook_establish(&sc->intr_hook);
+ if (error != 0) {
+ mtx_destroy(&sc->mtx);
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+static int
+tca6416_detach(device_t dev)
+{
+ struct tca6416_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->busdev != NULL)
+ gpiobus_detach_bus(sc->busdev);
+
+ mtx_destroy(&sc->mtx);
+
+ return (0);
+}
+
+static device_t
+tca6416_get_bus(device_t dev)
+{
+ struct tca6416_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->busdev);
+}
+
+static int
+tca6416_pin_max(device_t dev __unused, int *maxpin)
+{
+
+ if (maxpin == NULL)
+ return (EINVAL);
+
+ *maxpin = NUM_PINS - 1;
+ return (0);
+}
+
+static int
+tca6416_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+
+ if (pin >= NUM_PINS || caps == NULL)
+ return (EINVAL);
+
+ *caps = PIN_CAPS;
+ return (0);
+}
+
+static int
+tca6416_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags)
+{
+ struct tca6416_softc *sc;
+ int error;
+ uint8_t reg_addr, reg_bit, val;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= NUM_PINS || pflags == NULL)
+ return (EINVAL);
+
+ reg_addr = TCA6416_REG_ADDR(pin, CONF_REG);
+ reg_bit = TCA6416_BIT_FROM_PIN(pin);
+
+ error = tca6416_read(dev, reg_addr, &val);
+ if (error != 0)
+ return (error);
+
+ *pflags = (val & (1 << reg_bit))
+ ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT;
+
+ return (0);
+}
+
+static int
+tca6416_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct tca6416_softc *sc;
+ uint8_t reg_addr, reg_bit, val;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= NUM_PINS)
+ return (EINVAL);
+
+ reg_addr = TCA6416_REG_ADDR(pin, CONF_REG);
+ reg_bit = TCA6416_BIT_FROM_PIN(pin);
+
+ mtx_lock(&sc->mtx);
+
+ error = tca6416_read(dev, reg_addr, &val);
+ if (error != 0) {
+ mtx_unlock(&sc->mtx);
+ return (error);
+ }
+
+ if (flags == GPIO_PIN_INPUT) {
+ val |= (1 << reg_bit);
+ } else if (flags == GPIO_PIN_OUTPUT) {
+ val &= ~(1 << reg_bit);
+ } else {
+ mtx_unlock(&sc->mtx);
+ return (EOPNOTSUPP);
+ }
+
+ error = tca6416_write(dev, reg_addr, val);
+
+ mtx_unlock(&sc->mtx);
+
+ return (error);
+}
+
+static int
+tca6416_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+
+ if (pin >= NUM_PINS || name == NULL)
+ return (EINVAL);
+
+ snprintf(name, GPIOMAXNAME, "gpio_P%d%d", pin / PINS_PER_REG,
+ pin % PINS_PER_REG);
+
+ return (0);
+}
+
+static int
+tca6416_pin_get(device_t dev, uint32_t pin, unsigned int *pval)
+{
+ struct tca6416_softc *sc;
+ uint8_t reg_bit, reg_addr, reg_pvalue;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= NUM_PINS || pval == NULL)
+ return (EINVAL);
+
+ reg_bit = TCA6416_BIT_FROM_PIN(pin);
+ reg_addr = TCA6416_REG_ADDR(pin, IN_PORT_REG);
+
+ dbg_dev_printf(dev, "Reading pin %u pvalue.", pin);
+
+ error = tca6416_read(dev, reg_addr, ®_pvalue);
+ if (error != 0)
+ return (error);
+
+ *pval = reg_pvalue & (1 << reg_bit) ? 1 : 0;
+
+ return (0);
+}
+
+static int
+tca6416_pin_set(device_t dev, uint32_t pin, unsigned int val)
+{
+ struct tca6416_softc *sc;
+ uint8_t reg_addr, reg_bit, reg_value;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= NUM_PINS)
+ return (EINVAL);
+
+ reg_addr = TCA6416_REG_ADDR(pin , OUT_PORT_REG);
+ reg_bit = TCA6416_BIT_FROM_PIN(pin);
+
+ dbg_dev_printf(dev, "Setting pin: %u to %u\n", pin, val);
+
+ mtx_lock(&sc->mtx);
+
+ error = tca6416_read(dev, reg_addr, ®_value);
+ if (error != 0) {
+ dbg_dev_printf(dev, "Failed to read from register.\n");
+ mtx_unlock(&sc->mtx);
+ return (error);
+ }
+
+ if (val != 0)
+ reg_value |= (1 << reg_bit);
+ else
+ reg_value &= ~(1 << reg_bit);
+
+
+ error = tca6416_write(dev, reg_addr, reg_value);
+ if (error != 0) {
+ dbg_dev_printf(dev, "Could not write to register.\n");
+ mtx_unlock(&sc->mtx);
+ return (error);
+ }
+
+ mtx_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static int
+tca6416_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct tca6416_softc *sc;
+ int error;
+ uint8_t reg_addr, reg_bit, reg_value;
+
+ sc = device_get_softc(dev);
+
+ if (pin >= NUM_PINS)
+ return (EINVAL);
+
+ reg_addr = TCA6416_REG_ADDR(pin, OUT_PORT_REG);
+ reg_bit = TCA6416_BIT_FROM_PIN(pin);
+
+ dbg_dev_printf(dev, "Toggling pin: %d\n", pin);
+
+ mtx_lock(&sc->mtx);
+
+ error = tca6416_read(dev, reg_addr, ®_value);
+ if (error != 0) {
+ mtx_unlock(&sc->mtx);
+ dbg_dev_printf(dev, "Cannot read from register.\n");
+ return (error);
+ }
+
+ reg_value ^= (1 << reg_bit);
+
+ error = tca6416_write(dev, reg_addr, reg_value);
+ if (error != 0)
+ dbg_dev_printf(dev, "Cannot write to register.\n");
+
+ mtx_unlock(&sc->mtx);
+
+ return (error);
+}
+
+#ifdef DEBUG
+static void
+tca6416_regdump_setup(device_t dev)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *node;
+
+ ctx = device_get_sysctl_ctx(dev);
+ node = device_get_sysctl_tree(dev);
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_1",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, IN_PORT_REG,
+ tca6416_regdump_sysctl, "A", "Input port 1");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_2",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
+ IN_PORT_REG | 1, tca6416_regdump_sysctl, "A", "Input port 2");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_1",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, OUT_PORT_REG,
+ tca6416_regdump_sysctl, "A", "Output port 1");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_2",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, OUT_PORT_REG
+ | 1, tca6416_regdump_sysctl, "A", "Output port 2");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_1",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
+ POLARITY_INV_REG, tca6416_regdump_sysctl, "A", "Polarity inv 1");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_2",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
+ POLARITY_INV_REG | 1, tca6416_regdump_sysctl, "A",
+ "Polarity inv 2");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_1",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
+ CONF_REG, tca6416_regdump_sysctl, "A", "Configuration 1");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_2",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
+ CONF_REG | 1, tca6416_regdump_sysctl, "A", "Configuration 2");
+}
+
+static int
+tca6416_regdump_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev;
+ char buf[5];
+ struct tca6416_softc *sc;
+ int len, error;
+ uint8_t reg, regval;
+
+ dev = (device_t)arg1;
+ reg = (uint8_t)arg2;
+ sc = device_get_softc(dev);
+
+
+ error = tca6416_read(dev, reg, ®val);
+ if (error != 0) {
+ return (error);
+ }
+
+ len = snprintf(buf, 5, "0x%02x", regval);
+
+ error = sysctl_handle_string(oidp, buf, len, req);
+
+ return (error);
+}
+#endif
Index: sys/modules/i2c/Makefile
===================================================================
--- sys/modules/i2c/Makefile
+++ sys/modules/i2c/Makefile
@@ -24,4 +24,8 @@
smb \
smbus \
+.if !empty(OPT_FDT)
+SUBDIR += tca6416
+.endif
+
.include <bsd.subdir.mk>
Index: sys/modules/i2c/tca6416/Makefile
===================================================================
--- /dev/null
+++ sys/modules/i2c/tca6416/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = tca6416
+SRCS = tca6416.c opt_platform.h gpio_if.h
+
+.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Dec 26, 11:58 PM (11 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15609180
Default Alt Text
D24363.id70396.diff (15 KB)
Attached To
Mode
D24363: Add TCA6416 GPIO expander support.
Attached
Detach File
Event Timeline
Log In to Comment