From c8608b2ffffd95aa7d912e60cd75d143f5af24c0 Mon Sep 17 00:00:00 2001 From: Ralf Gliese Date: Mon, 24 Oct 2022 12:33:17 +0200 Subject: [PATCH] drivers: vtpctp/ec: add 4-bit digital i/o-driver add vtpctp/ec specific 4-bit digital input/output-driver Signed-off-by: Ralf Gliese --- arch/arm/configs/imx6_vtpctp_defconfig | 1 + drivers/misc/Kconfig | 9 ++ drivers/misc/Makefile | 1 + drivers/misc/dio_drv.c | 201 +++++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 drivers/misc/dio_drv.c diff --git a/arch/arm/configs/imx6_vtpctp_defconfig b/arch/arm/configs/imx6_vtpctp_defconfig index ee990c6b819f..094eaad275f5 100644 --- a/arch/arm/configs/imx6_vtpctp_defconfig +++ b/arch/arm/configs/imx6_vtpctp_defconfig @@ -209,6 +209,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=65536 # CONFIG_ENCSW is not set +CONFIG_DIO_SPI_DRV=m CONFIG_EEPROM_AT24=y CONFIG_EEPROM_AT25=y CONFIG_SCSI=y diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 716732cea2c6..5c95bc004752 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -594,6 +594,15 @@ config DIN_SPI_DRV Expose digial inputs using sysfs interface, e.g. /sys/bus/spi/devices/spi0.0/din +config DIO_SPI_DRV + tristate "SPI and SYSFS based digital input/output driver" + depends on SYSFS && GPIOLIB && SPI + help + This option enables the SPI and SYSFS based digital + input/output driver for VTPCT/EC devices. + To compile this driver as a module, choose M here: the + module will be called dio_drv. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 1554044bedd5..62ae525a5ed1 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o obj-$(CONFIG_TI_SN74LV165A) += ti_sn74lv165a.o obj-$(CONFIG_DOUT_DRV) += dout_drv.o obj-$(CONFIG_DIN_SPI_DRV) += din_spi_drv.o +obj-$(CONFIG_DIO_SPI_DRV) += dio_drv.o diff --git a/drivers/misc/dio_drv.c b/drivers/misc/dio_drv.c new file mode 100644 index 000000000000..6ff434761833 --- /dev/null +++ b/drivers/misc/dio_drv.c @@ -0,0 +1,201 @@ +//------------------------------------------------------------------------------ +/// Copyright (c) 2019-2022 WAGO GmbH & Co. KG +/// +/// This program is free software: you can redistribute it and/or modify +/// it under the terms of the GNU General Public License as published by +/// the Free Software Foundation, version 2. +/// +/// This program is distributed in the hope that it will be useful, but +/// WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +/// General Public License for more details. +/// +/// You should have received a copy of the GNU General Public License +/// along with this program. If not, see . +//------------------------------------------------------------------------------ + +///------------------------------------------------------------------------------ +/// \file dio_drv.c +/// +/// \brief Driver for "X3" (4 digital I/O) on TouchPanel devices +/// +/// \author Ralf Gliese, elrest Automationssysteme GmbH +///------------------------------------------------------------------------------ + + +#include +#include +#include +#include +#include +#include +#include +#include + +struct dio_data { + unsigned char dma_buffer[2]; + int gpio_load; + int gpio_reset; + struct mutex lock; + unsigned char tx_data; +}; + +static unsigned char reverse_low_nibble(unsigned char input) +{ + static const unsigned char lookup[] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf + }; + return lookup[input & 0xf] | (input & 0xf0); +}; + +/* SPI Write/Read cycle */ +static int dio_spi_exchange(struct spi_device *spi, int data) +{ + struct dio_data *dio = spi_get_drvdata(spi); + struct spi_message msg; + struct spi_transfer xfer; + int ret; + + if (data >= 0) + dio->tx_data = reverse_low_nibble(data); + + dio->dma_buffer[0] = 0; + dio->dma_buffer[1] = dio->tx_data; + + /* Latch input data */ + gpio_set_value(dio->gpio_load, 0); + udelay(10); + gpio_set_value(dio->gpio_load, 1); + + /* Initialize the SPI message and transfer data structures */ + spi_message_init(&msg); + memset(&xfer, 0, sizeof xfer); + xfer.tx_buf = xfer.rx_buf = dio->dma_buffer; + xfer.len = sizeof dio->dma_buffer; + spi_message_add_tail(&xfer, &msg); + + /* Send the message and wait for completion */ + ret = spi_sync(spi, &msg); + if (ret != 0) { + dev_err(&spi->dev, "SPI transmission failed\n"); + return 0; + } + dev_dbg(&spi->dev, "sent %02x %02x, received %02x %02x\n", + dio->tx_data, dio->tx_data, + dio->dma_buffer[0], dio->dma_buffer[1]); + return reverse_low_nibble(dio->dma_buffer[0]) | dio->dma_buffer[1] << 8; +} + +static ssize_t dio_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct dio_data *dio = spi_get_drvdata(spi); + int ret; + + ret = mutex_lock_interruptible(&dio->lock); + if (ret) + return ret; + ret = dio_spi_exchange(spi, -1); + mutex_unlock(&dio->lock); + return sprintf(buf, "%d\n", ret); +} + +static ssize_t dio_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct spi_device *spi = to_spi_device(dev); + struct dio_data *dio = spi_get_drvdata(spi); + unsigned long data; + int ret; + + ret = kstrtoul(buf, 0, &data); + if (ret < 0) + return ret; + if (data > 0xf) + return -EINVAL; + + ret = mutex_lock_interruptible(&dio->lock); + if (ret) + return ret; + dio_spi_exchange(spi, data); + mutex_unlock(&dio->lock); + return count; +} + +static DEVICE_ATTR_RW(dio); + +static int alloc_gpio(struct device *dev, const char *name) +{ + int ret, gpio = of_get_named_gpio(dev->of_node, name, 0); + + if (gpio < 0) { + dev_err(dev, "GPIO '%s' not found in device tree\n", name); + return gpio; + } + ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, name); + if (ret < 0) { + dev_err(dev, "Cannot request GPIO '%s' %d\n", name, gpio); + return ret; + } + dev_dbg(dev, "GPIO '%s' has Number %d\n", name, gpio); + return gpio; +} + +static int dio_drv_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct dio_data *dio; + + dio = devm_kzalloc(dev, sizeof *dio, GFP_KERNEL); + if (!dio) + return -ENOMEM; + + dio->gpio_load = alloc_gpio(dev, "gpio-load"); + if (dio->gpio_load < 0) + return dio->gpio_load; + + dio->gpio_reset = alloc_gpio(dev, "gpio-reset"); + if (dio->gpio_reset < 0) + return dio->gpio_reset; + + mutex_init(&dio->lock); + spi_set_drvdata(spi, dio); + + /* Initialize and enable output lines */ + dio_spi_exchange(spi, 0); + gpio_set_value(dio->gpio_reset, 0); + + return device_create_file(dev, &dev_attr_dio); +} + +static void dio_drv_remove(struct spi_device *spi) +{ + struct dio_data *dio = spi_get_drvdata(spi); + + gpio_set_value(dio->gpio_reset, 1); + device_remove_file(&spi->dev, &dev_attr_dio); +} + +static const struct spi_device_id dio_spi_ids[] = { + { "dio_spi", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, dio_spi_ids); + +static struct spi_driver dio_driver = { + .driver = { + .name = "dio_spi", + .owner = THIS_MODULE, + }, + .id_table = dio_spi_ids, + .probe = dio_drv_probe, + .remove = dio_drv_remove, +}; + +module_spi_driver(dio_driver); + +MODULE_AUTHOR("elrest"); +MODULE_DESCRIPTION("dio_spi driver"); +MODULE_LICENSE("GPL");