linux/drivers/gpio/gpio-pisosr.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
   3 *      Andrew F. Davis <afd@ti.com>
   4 *
   5 * This program is free software; you can redistribute it and/or
   6 * modify it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  10 * kind, whether expressed or implied; without even the implied warranty
  11 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License version 2 for more details.
  13 */
  14
  15#include <linux/bitmap.h>
  16#include <linux/bitops.h>
  17#include <linux/delay.h>
  18#include <linux/gpio/consumer.h>
  19#include <linux/gpio/driver.h>
  20#include <linux/module.h>
  21#include <linux/mutex.h>
  22#include <linux/spi/spi.h>
  23
  24#define DEFAULT_NGPIO 8
  25
  26/**
  27 * struct pisosr_gpio - GPIO driver data
  28 * @chip: GPIO controller chip
  29 * @spi: SPI device pointer
  30 * @buffer: Buffer for device reads
  31 * @buffer_size: Size of buffer
  32 * @load_gpio: GPIO pin used to load input into device
  33 * @lock: Protects read sequences
  34 */
  35struct pisosr_gpio {
  36        struct gpio_chip chip;
  37        struct spi_device *spi;
  38        u8 *buffer;
  39        size_t buffer_size;
  40        struct gpio_desc *load_gpio;
  41        struct mutex lock;
  42};
  43
  44static int pisosr_gpio_refresh(struct pisosr_gpio *gpio)
  45{
  46        int ret;
  47
  48        mutex_lock(&gpio->lock);
  49
  50        if (gpio->load_gpio) {
  51                gpiod_set_value_cansleep(gpio->load_gpio, 1);
  52                udelay(1); /* registers load time (~10ns) */
  53                gpiod_set_value_cansleep(gpio->load_gpio, 0);
  54                udelay(1); /* registers recovery time (~5ns) */
  55        }
  56
  57        ret = spi_read(gpio->spi, gpio->buffer, gpio->buffer_size);
  58
  59        mutex_unlock(&gpio->lock);
  60
  61        return ret;
  62}
  63
  64static int pisosr_gpio_get_direction(struct gpio_chip *chip,
  65                                     unsigned offset)
  66{
  67        /* This device always input */
  68        return GPIO_LINE_DIRECTION_IN;
  69}
  70
  71static int pisosr_gpio_direction_input(struct gpio_chip *chip,
  72                                       unsigned offset)
  73{
  74        /* This device always input */
  75        return 0;
  76}
  77
  78static int pisosr_gpio_direction_output(struct gpio_chip *chip,
  79                                        unsigned offset, int value)
  80{
  81        /* This device is input only */
  82        return -EINVAL;
  83}
  84
  85static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset)
  86{
  87        struct pisosr_gpio *gpio = gpiochip_get_data(chip);
  88
  89        /* Refresh may not always be needed */
  90        pisosr_gpio_refresh(gpio);
  91
  92        return (gpio->buffer[offset / 8] >> (offset % 8)) & 0x1;
  93}
  94
  95static int pisosr_gpio_get_multiple(struct gpio_chip *chip,
  96                                    unsigned long *mask, unsigned long *bits)
  97{
  98        struct pisosr_gpio *gpio = gpiochip_get_data(chip);
  99        unsigned long offset;
 100        unsigned long gpio_mask;
 101        unsigned long buffer_state;
 102
 103        pisosr_gpio_refresh(gpio);
 104
 105        bitmap_zero(bits, chip->ngpio);
 106        for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
 107                buffer_state = gpio->buffer[offset / 8] & gpio_mask;
 108                bitmap_set_value8(bits, buffer_state, offset);
 109        }
 110
 111        return 0;
 112}
 113
 114static const struct gpio_chip template_chip = {
 115        .label                  = "pisosr-gpio",
 116        .owner                  = THIS_MODULE,
 117        .get_direction          = pisosr_gpio_get_direction,
 118        .direction_input        = pisosr_gpio_direction_input,
 119        .direction_output       = pisosr_gpio_direction_output,
 120        .get                    = pisosr_gpio_get,
 121        .get_multiple           = pisosr_gpio_get_multiple,
 122        .base                   = -1,
 123        .ngpio                  = DEFAULT_NGPIO,
 124        .can_sleep              = true,
 125};
 126
 127static int pisosr_gpio_probe(struct spi_device *spi)
 128{
 129        struct device *dev = &spi->dev;
 130        struct pisosr_gpio *gpio;
 131        int ret;
 132
 133        gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
 134        if (!gpio)
 135                return -ENOMEM;
 136
 137        spi_set_drvdata(spi, gpio);
 138
 139        gpio->chip = template_chip;
 140        gpio->chip.parent = dev;
 141        of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio);
 142
 143        gpio->spi = spi;
 144
 145        gpio->buffer_size = DIV_ROUND_UP(gpio->chip.ngpio, 8);
 146        gpio->buffer = devm_kzalloc(dev, gpio->buffer_size, GFP_KERNEL);
 147        if (!gpio->buffer)
 148                return -ENOMEM;
 149
 150        gpio->load_gpio = devm_gpiod_get_optional(dev, "load", GPIOD_OUT_LOW);
 151        if (IS_ERR(gpio->load_gpio))
 152                return dev_err_probe(dev, PTR_ERR(gpio->load_gpio),
 153                                     "Unable to allocate load GPIO\n");
 154
 155        mutex_init(&gpio->lock);
 156
 157        ret = gpiochip_add_data(&gpio->chip, gpio);
 158        if (ret < 0) {
 159                dev_err(dev, "Unable to register gpiochip\n");
 160                return ret;
 161        }
 162
 163        return 0;
 164}
 165
 166static int pisosr_gpio_remove(struct spi_device *spi)
 167{
 168        struct pisosr_gpio *gpio = spi_get_drvdata(spi);
 169
 170        gpiochip_remove(&gpio->chip);
 171
 172        mutex_destroy(&gpio->lock);
 173
 174        return 0;
 175}
 176
 177static const struct spi_device_id pisosr_gpio_id_table[] = {
 178        { "pisosr-gpio", },
 179        { /* sentinel */ }
 180};
 181MODULE_DEVICE_TABLE(spi, pisosr_gpio_id_table);
 182
 183static const struct of_device_id pisosr_gpio_of_match_table[] = {
 184        { .compatible = "pisosr-gpio", },
 185        { /* sentinel */ }
 186};
 187MODULE_DEVICE_TABLE(of, pisosr_gpio_of_match_table);
 188
 189static struct spi_driver pisosr_gpio_driver = {
 190        .driver = {
 191                .name = "pisosr-gpio",
 192                .of_match_table = pisosr_gpio_of_match_table,
 193        },
 194        .probe = pisosr_gpio_probe,
 195        .remove = pisosr_gpio_remove,
 196        .id_table = pisosr_gpio_id_table,
 197};
 198module_spi_driver(pisosr_gpio_driver);
 199
 200MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
 201MODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver");
 202MODULE_LICENSE("GPL v2");
 203