linux/drivers/gpio/gpio-74x164.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
   4 *
   5 *  Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
   6 *  Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com>
   7 */
   8
   9#include <linux/bitops.h>
  10#include <linux/gpio/consumer.h>
  11#include <linux/gpio/driver.h>
  12#include <linux/module.h>
  13#include <linux/mutex.h>
  14#include <linux/property.h>
  15#include <linux/slab.h>
  16#include <linux/spi/spi.h>
  17
  18#define GEN_74X164_NUMBER_GPIOS 8
  19
  20struct gen_74x164_chip {
  21        struct gpio_chip        gpio_chip;
  22        struct mutex            lock;
  23        struct gpio_desc        *gpiod_oe;
  24        u32                     registers;
  25        /*
  26         * Since the registers are chained, every byte sent will make
  27         * the previous byte shift to the next register in the
  28         * chain. Thus, the first byte sent will end up in the last
  29         * register at the end of the transfer. So, to have a logical
  30         * numbering, store the bytes in reverse order.
  31         */
  32        u8                      buffer[];
  33};
  34
  35static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
  36{
  37        return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer,
  38                         chip->registers);
  39}
  40
  41static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
  42{
  43        struct gen_74x164_chip *chip = gpiochip_get_data(gc);
  44        u8 bank = chip->registers - 1 - offset / 8;
  45        u8 pin = offset % 8;
  46        int ret;
  47
  48        mutex_lock(&chip->lock);
  49        ret = (chip->buffer[bank] >> pin) & 0x1;
  50        mutex_unlock(&chip->lock);
  51
  52        return ret;
  53}
  54
  55static void gen_74x164_set_value(struct gpio_chip *gc,
  56                unsigned offset, int val)
  57{
  58        struct gen_74x164_chip *chip = gpiochip_get_data(gc);
  59        u8 bank = chip->registers - 1 - offset / 8;
  60        u8 pin = offset % 8;
  61
  62        mutex_lock(&chip->lock);
  63        if (val)
  64                chip->buffer[bank] |= (1 << pin);
  65        else
  66                chip->buffer[bank] &= ~(1 << pin);
  67
  68        __gen_74x164_write_config(chip);
  69        mutex_unlock(&chip->lock);
  70}
  71
  72static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask,
  73                                    unsigned long *bits)
  74{
  75        struct gen_74x164_chip *chip = gpiochip_get_data(gc);
  76        unsigned long offset;
  77        unsigned long bankmask;
  78        size_t bank;
  79        unsigned long bitmask;
  80
  81        mutex_lock(&chip->lock);
  82        for_each_set_clump8(offset, bankmask, mask, chip->registers * 8) {
  83                bank = chip->registers - 1 - offset / 8;
  84                bitmask = bitmap_get_value8(bits, offset) & bankmask;
  85
  86                chip->buffer[bank] &= ~bankmask;
  87                chip->buffer[bank] |= bitmask;
  88        }
  89        __gen_74x164_write_config(chip);
  90        mutex_unlock(&chip->lock);
  91}
  92
  93static int gen_74x164_direction_output(struct gpio_chip *gc,
  94                unsigned offset, int val)
  95{
  96        gen_74x164_set_value(gc, offset, val);
  97        return 0;
  98}
  99
 100static int gen_74x164_probe(struct spi_device *spi)
 101{
 102        struct gen_74x164_chip *chip;
 103        u32 nregs;
 104        int ret;
 105
 106        /*
 107         * bits_per_word cannot be configured in platform data
 108         */
 109        spi->bits_per_word = 8;
 110
 111        ret = spi_setup(spi);
 112        if (ret < 0)
 113                return ret;
 114
 115        ret = device_property_read_u32(&spi->dev, "registers-number", &nregs);
 116        if (ret) {
 117                dev_err(&spi->dev, "Missing 'registers-number' property.\n");
 118                return -EINVAL;
 119        }
 120
 121        chip = devm_kzalloc(&spi->dev, sizeof(*chip) + nregs, GFP_KERNEL);
 122        if (!chip)
 123                return -ENOMEM;
 124
 125        chip->gpiod_oe = devm_gpiod_get_optional(&spi->dev, "enable",
 126                                                 GPIOD_OUT_LOW);
 127        if (IS_ERR(chip->gpiod_oe))
 128                return PTR_ERR(chip->gpiod_oe);
 129
 130        gpiod_set_value_cansleep(chip->gpiod_oe, 1);
 131
 132        spi_set_drvdata(spi, chip);
 133
 134        chip->gpio_chip.label = spi->modalias;
 135        chip->gpio_chip.direction_output = gen_74x164_direction_output;
 136        chip->gpio_chip.get = gen_74x164_get_value;
 137        chip->gpio_chip.set = gen_74x164_set_value;
 138        chip->gpio_chip.set_multiple = gen_74x164_set_multiple;
 139        chip->gpio_chip.base = -1;
 140
 141        chip->registers = nregs;
 142        chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
 143
 144        chip->gpio_chip.can_sleep = true;
 145        chip->gpio_chip.parent = &spi->dev;
 146        chip->gpio_chip.owner = THIS_MODULE;
 147
 148        mutex_init(&chip->lock);
 149
 150        ret = __gen_74x164_write_config(chip);
 151        if (ret) {
 152                dev_err(&spi->dev, "Failed writing: %d\n", ret);
 153                goto exit_destroy;
 154        }
 155
 156        ret = gpiochip_add_data(&chip->gpio_chip, chip);
 157        if (!ret)
 158                return 0;
 159
 160exit_destroy:
 161        mutex_destroy(&chip->lock);
 162
 163        return ret;
 164}
 165
 166static int gen_74x164_remove(struct spi_device *spi)
 167{
 168        struct gen_74x164_chip *chip = spi_get_drvdata(spi);
 169
 170        gpiod_set_value_cansleep(chip->gpiod_oe, 0);
 171        gpiochip_remove(&chip->gpio_chip);
 172        mutex_destroy(&chip->lock);
 173
 174        return 0;
 175}
 176
 177static const struct spi_device_id gen_74x164_spi_ids[] = {
 178        { .name = "74hc595" },
 179        { .name = "74lvc594" },
 180        {},
 181};
 182MODULE_DEVICE_TABLE(spi, gen_74x164_spi_ids);
 183
 184static const struct of_device_id gen_74x164_dt_ids[] = {
 185        { .compatible = "fairchild,74hc595" },
 186        { .compatible = "nxp,74lvc594" },
 187        {},
 188};
 189MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
 190
 191static struct spi_driver gen_74x164_driver = {
 192        .driver = {
 193                .name           = "74x164",
 194                .of_match_table = gen_74x164_dt_ids,
 195        },
 196        .probe          = gen_74x164_probe,
 197        .remove         = gen_74x164_remove,
 198        .id_table       = gen_74x164_spi_ids,
 199};
 200module_spi_driver(gen_74x164_driver);
 201
 202MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
 203MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 204MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register");
 205MODULE_LICENSE("GPL v2");
 206