@@ -1018,6 +1018,13 @@ config GPIO_MC33880
SPI driver for Freescale MC33880 high-side/low-side switch.
This provides GPIO interface supporting inputs and outputs.
+config GPIO_SN65HVS885
+ tristate "Texas Instruments sn65hvs885 input serializer 8-bit shift register"
+ depends on SPI_MASTER && OF
+ help
+ Driver for Texas Instruments sn65hvs885 input serializer.
+ This provides a GPIO interface supporting inputs.
+
endmenu
menu "SPI or I2C GPIO expanders"
@@ -1031,8 +1038,6 @@ config GPIO_MCP23S08
This provides a GPIO interface supporting inputs and outputs.
The I2C versions of the chips can be used as interrupt-controller.
-endmenu
-
menu "USB GPIO expanders"
depends on USB
@@ -120,3 +120,4 @@ obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
+obj-$(CONFIG_GPIO_SN65HVS885) += gpio-sn65hvs885.o
new file mode 100644
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 Sean Nyekjaer, Prevas
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#define SN65HVS885_NUMBER_GPIOS 8
+
+struct sn65hvs885_chip {
+ struct gpio_chip gpio_chip;
+ int latch_gpio;
+ struct mutex lock; /* protect from simultaneous accesses */
+};
+
+static struct sn65hvs885_chip *sn65hvs885_gpio_to_chip(struct gpio_chip *gc)
+{
+ return container_of(gc, struct sn65hvs885_chip, gpio_chip);
+}
+
+static int sn65hvs885_get_value(struct gpio_chip *gc, unsigned offset)
+{
+ struct sn65hvs885_chip *chip = sn65hvs885_gpio_to_chip(gc);
+ struct spi_device *spi = to_spi_device(chip->gpio_chip.dev);
+ int ret;
+ u8 pin = offset % SN65HVS885_NUMBER_GPIOS;
+ u8 d8;
+ struct spi_transfer t = {
+ .rx_buf = &d8,
+ .len = 1,
+ };
+
+ if (gpio_is_valid(chip->latch_gpio)) {
+ gpio_set_value_cansleep(chip->latch_gpio, 0);
+ gpio_set_value_cansleep(chip->latch_gpio, 1);
+ }
+
+ mutex_lock(&chip->lock);
+ spi_sync_transfer(spi, &t, 1);
+ mutex_unlock(&chip->lock);
+ ret = (d8 >> pin) & 0x1;
+
+ return ret;
+}
+
+static int sn65hvs885_probe(struct spi_device *spi)
+{
+ struct sn65hvs885_chip *chip;
+ int ret;
+
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, chip);
+
+ chip->gpio_chip.label = spi->modalias;
+ chip->gpio_chip.get = sn65hvs885_get_value;
+ chip->gpio_chip.base = -1;
+ chip->gpio_chip.ngpio = SN65HVS885_NUMBER_GPIOS;
+ chip->gpio_chip.can_sleep = true;
+ chip->gpio_chip.dev = &spi->dev;
+ chip->gpio_chip.owner = THIS_MODULE;
+
+ mutex_init(&chip->lock);
+
+ chip->latch_gpio = of_get_named_gpio(spi->dev.of_node, "latch-gpio", 0);
+ if (chip->latch_gpio < 0) {
+ dev_err(&spi->dev, "latch-gpio property not found\n");
+ return -ENODEV;
+ }
+
+ if (gpio_is_valid(chip->latch_gpio)) {
+ ret = devm_gpio_request_one(&spi->dev,
+ chip->latch_gpio,
+ GPIOF_OUT_INIT_HIGH, "gpio-latch");
+ if (ret) {
+ dev_err(&spi->dev, "unable to get latch gpio\n");
+ return -ENODEV;
+ }
+ }
+
+ ret = gpiochip_add(&chip->gpio_chip);
+
+ if (ret != 0) {
+ mutex_destroy(&chip->lock);
+ devm_gpio_free(&spi->dev, chip->latch_gpio);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int sn65hvs885_remove(struct spi_device *spi)
+{
+ struct sn65hvs885_chip *chip = spi_get_drvdata(spi);
+
+ devm_gpio_free(&spi->dev, chip->latch_gpio);
+
+ gpiochip_remove(&chip->gpio_chip);
+
+ mutex_destroy(&chip->lock);
+
+ return 0;
+}
+
+static const struct spi_device_id sn65hvs885_id[] = {
+ {"sn65hvs885", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, sn65hvs885_id);
+
+static struct spi_driver sn65hvs885_driver = {
+ .driver = {
+ .name = "sn65hvs885",
+ .owner = THIS_MODULE,
+ },
+ .probe = sn65hvs885_probe,
+ .remove = sn65hvs885_remove,
+ .id_table = sn65hvs885_id,
+};
+
+module_spi_driver(sn65hvs885_driver);
+
+MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("sn65hvs885 Industrial input serializer");