summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-74x164.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-74x164.c')
-rw-r--r--drivers/gpio/gpio-74x164.c225
1 files changed, 97 insertions, 128 deletions
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index 721607904d0a..c226524efeba 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -1,101 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
*
* Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com>
- *
- * 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/init.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/74x164.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/property.h>
#include <linux/slab.h>
-#include <linux/module.h>
+#include <linux/spi/spi.h>
#define GEN_74X164_NUMBER_GPIOS 8
struct gen_74x164_chip {
- struct spi_device *spi;
- u8 *buffer;
struct gpio_chip gpio_chip;
struct mutex lock;
+ struct gpio_desc *gpiod_oe;
u32 registers;
-};
-
-static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
-{
- return container_of(gc, struct gen_74x164_chip, gpio_chip);
-}
-
-static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
-{
- struct spi_message message;
- struct spi_transfer *msg_buf;
- int i, ret = 0;
-
- msg_buf = kzalloc(chip->registers * sizeof(struct spi_transfer),
- GFP_KERNEL);
- if (!msg_buf)
- return -ENOMEM;
-
- spi_message_init(&message);
-
/*
* Since the registers are chained, every byte sent will make
* the previous byte shift to the next register in the
- * chain. Thus, the first byte send will end up in the last
+ * chain. Thus, the first byte sent will end up in the last
* register at the end of the transfer. So, to have a logical
- * numbering, send the bytes in reverse order so that the last
- * byte of the buffer will end up in the last register.
+ * numbering, store the bytes in reverse order.
*/
- for (i = chip->registers - 1; i >= 0; i--) {
- msg_buf[i].tx_buf = chip->buffer +i;
- msg_buf[i].len = sizeof(u8);
- spi_message_add_tail(msg_buf + i, &message);
- }
-
- ret = spi_sync(chip->spi, &message);
-
- kfree(msg_buf);
+ u8 buffer[] __counted_by(registers);
+};
- return ret;
+static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
+{
+ return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer,
+ chip->registers);
}
static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
{
- struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
- u8 bank = offset / 8;
+ struct gen_74x164_chip *chip = gpiochip_get_data(gc);
+ u8 bank = chip->registers - 1 - offset / 8;
u8 pin = offset % 8;
- int ret;
- mutex_lock(&chip->lock);
- ret = (chip->buffer[bank] >> pin) & 0x1;
- mutex_unlock(&chip->lock);
+ guard(mutex)(&chip->lock);
- return ret;
+ return !!(chip->buffer[bank] & BIT(pin));
}
-static void gen_74x164_set_value(struct gpio_chip *gc,
- unsigned offset, int val)
+static int gen_74x164_set_value(struct gpio_chip *gc,
+ unsigned int offset, int val)
{
- struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
- u8 bank = offset / 8;
+ struct gen_74x164_chip *chip = gpiochip_get_data(gc);
+ u8 bank = chip->registers - 1 - offset / 8;
u8 pin = offset % 8;
- mutex_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
+
if (val)
- chip->buffer[bank] |= (1 << pin);
+ chip->buffer[bank] |= BIT(pin);
else
- chip->buffer[bank] &= ~(1 << pin);
+ chip->buffer[bank] &= ~BIT(pin);
- __gen_74x164_write_config(chip);
- mutex_unlock(&chip->lock);
+ return __gen_74x164_write_config(chip);
+}
+
+static int gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct gen_74x164_chip *chip = gpiochip_get_data(gc);
+ unsigned long offset;
+ unsigned long bankmask;
+ size_t bank;
+ unsigned long bitmask;
+
+ guard(mutex)(&chip->lock);
+
+ for_each_set_clump8(offset, bankmask, mask, chip->registers * 8) {
+ bank = chip->registers - 1 - offset / 8;
+ bitmask = bitmap_get_value8(bits, offset) & bankmask;
+
+ chip->buffer[bank] &= ~bankmask;
+ chip->buffer[bank] |= bitmask;
+ }
+ return __gen_74x164_write_config(chip);
}
static int gen_74x164_direction_output(struct gpio_chip *gc,
@@ -105,17 +95,26 @@ static int gen_74x164_direction_output(struct gpio_chip *gc,
return 0;
}
+static void gen_74x164_deactivate(void *data)
+{
+ struct gen_74x164_chip *chip = data;
+
+ gpiod_set_value_cansleep(chip->gpiod_oe, 0);
+}
+
+static int gen_74x164_activate(struct device *dev, struct gen_74x164_chip *chip)
+{
+ gpiod_set_value_cansleep(chip->gpiod_oe, 1);
+ return devm_add_action_or_reset(dev, gen_74x164_deactivate, chip);
+}
+
static int gen_74x164_probe(struct spi_device *spi)
{
+ struct device *dev = &spi->dev;
struct gen_74x164_chip *chip;
- struct gen_74x164_chip_platform_data *pdata;
+ u32 nregs;
int ret;
- if (!spi->dev.of_node) {
- dev_err(&spi->dev, "No device tree data available.\n");
- return -EINVAL;
- }
-
/*
* bits_per_word cannot be configured in platform data
*/
@@ -125,85 +124,56 @@ static int gen_74x164_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
+ ret = device_property_read_u32(dev, "registers-number", &nregs);
+ if (ret)
+ return dev_err_probe(dev, ret, "Missing 'registers-number' property.\n");
+
+ chip = devm_kzalloc(dev, struct_size(chip, buffer, nregs), GFP_KERNEL);
if (!chip)
return -ENOMEM;
- pdata = spi->dev.platform_data;
- if (pdata && pdata->base)
- chip->gpio_chip.base = pdata->base;
- else
- chip->gpio_chip.base = -1;
+ chip->registers = nregs;
- mutex_init(&chip->lock);
-
- spi_set_drvdata(spi, chip);
-
- chip->spi = spi;
+ chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(chip->gpiod_oe))
+ return PTR_ERR(chip->gpiod_oe);
chip->gpio_chip.label = spi->modalias;
chip->gpio_chip.direction_output = gen_74x164_direction_output;
chip->gpio_chip.get = gen_74x164_get_value;
chip->gpio_chip.set = gen_74x164_set_value;
-
- if (of_property_read_u32(spi->dev.of_node, "registers-number", &chip->registers)) {
- dev_err(&spi->dev, "Missing registers-number property in the DT.\n");
- ret = -EINVAL;
- goto exit_destroy;
- }
-
+ chip->gpio_chip.set_multiple = gen_74x164_set_multiple;
+ chip->gpio_chip.base = -1;
chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
- chip->buffer = devm_kzalloc(&spi->dev, chip->registers, GFP_KERNEL);
- if (!chip->buffer) {
- ret = -ENOMEM;
- goto exit_destroy;
- }
-
- chip->gpio_chip.can_sleep = 1;
- chip->gpio_chip.dev = &spi->dev;
+ chip->gpio_chip.can_sleep = true;
+ chip->gpio_chip.parent = dev;
chip->gpio_chip.owner = THIS_MODULE;
- ret = __gen_74x164_write_config(chip);
- if (ret) {
- dev_err(&spi->dev, "Failed writing: %d\n", ret);
- goto exit_destroy;
- }
+ ret = devm_mutex_init(dev, &chip->lock);
+ if (ret)
+ return ret;
- ret = gpiochip_add(&chip->gpio_chip);
+ ret = __gen_74x164_write_config(chip);
if (ret)
- goto exit_destroy;
+ return dev_err_probe(dev, ret, "Config write failed\n");
- return ret;
+ ret = gen_74x164_activate(dev, chip);
+ if (ret)
+ return ret;
-exit_destroy:
- spi_set_drvdata(spi, NULL);
- mutex_destroy(&chip->lock);
- return ret;
+ return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip);
}
-static int gen_74x164_remove(struct spi_device *spi)
-{
- struct gen_74x164_chip *chip;
- int ret;
-
- chip = spi_get_drvdata(spi);
- if (chip == NULL)
- return -ENODEV;
-
- spi_set_drvdata(spi, NULL);
-
- ret = gpiochip_remove(&chip->gpio_chip);
- if (!ret)
- mutex_destroy(&chip->lock);
- else
- dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
- ret);
-
- return ret;
-}
+static const struct spi_device_id gen_74x164_spi_ids[] = {
+ { .name = "74hc595" },
+ { .name = "74lvc594" },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, gen_74x164_spi_ids);
static const struct of_device_id gen_74x164_dt_ids[] = {
{ .compatible = "fairchild,74hc595" },
+ { .compatible = "nxp,74lvc594" },
{},
};
MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
@@ -211,11 +181,10 @@ MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
static struct spi_driver gen_74x164_driver = {
.driver = {
.name = "74x164",
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(gen_74x164_dt_ids),
+ .of_match_table = gen_74x164_dt_ids,
},
.probe = gen_74x164_probe,
- .remove = gen_74x164_remove,
+ .id_table = gen_74x164_spi_ids,
};
module_spi_driver(gen_74x164_driver);