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 <gliese@elrest.de>
This commit is contained in:
committed by
Oleg Karfich
parent
974bd20418
commit
c8608b2fff
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
///------------------------------------------------------------------------------
|
||||
/// \file dio_drv.c
|
||||
///
|
||||
/// \brief Driver for "X3" (4 digital I/O) on TouchPanel devices
|
||||
///
|
||||
/// \author Ralf Gliese, elrest Automationssysteme GmbH
|
||||
///------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
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");
|
||||
Reference in New Issue
Block a user