aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-74x164.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 16:05:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-02 16:05:10 -0700
commitdff8360a4a079692e65e55fbaa6c5dc605528403 (patch)
tree0ab8ef7595cdfb918b3fd9a8364c6ea6c9c2798f /drivers/gpio/gpio-74x164.c
parent916082b073ebb7f4e064cebce0768e34cacde508 (diff)
parent901acf5b2910434501c221a363bb3486b647b5c4 (diff)
Merge tag 'gpio-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO changes from Linus Walleij: "So this is the LW GPIO patch stack for v3.7: - refactoring from Thierry Redding at Arnd Bergmann's request to use the seq_file iterator interface in gpiolib. - A new driver for Avionic Design's N-bit GPIO expander. - Two instances of mutexes replaced by spinlocks from Axel Lin to code that is supposed to be fastpath compliant. - IRQ demuxer and gpio_to_irq() support for pcf857x by Kuninori Morimoto. - Dynamic GPIO numbers, device tree support, daisy chaining and some other fixes for the 74x164 driver by Maxime Ripard. - IRQ domain and device tree support for the tc3589x driver by Lee Jones. - Some conversion to use managed resources devm_* code. - Some instances of clk_prepare() or clk_prepare_enable() added to support the new, stricter common clock framework. - Some for_each_set_bit() simplifications. - Then a lot of fixes as we fixed up all of the above tripping over our own shoelaces and that kind of thing." * tag 'gpio-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (34 commits) gpio: pcf857x: select IRQ_DOMAIN gpio: Document device_node's det_debounce gpio-lpc32xx: Add GPI_28 gpio: adnp: dt: Reference generic interrupt binding gpio: Add Avionic Design N-bit GPIO expander support gpio: pxa: using for_each_set_bit to simplify the code gpio_msm: using for_each_set_bit to simplify the code gpio: Enable the tc3298x GPIO expander driver for Device Tree gpio: Provide the tc3589x GPIO expander driver with an IRQ domain ARM: shmobile: kzm9g: use gpio-keys instead of gpio-keys-polled gpio: pcf857x: fixup smatch WARNING gpio: 74x164: Add support for the daisy-chaining gpio: 74x164: dts: Add documentation for the dt binding dt: Fix incorrect reference in gpio-led documentation gpio: 74x164: Add device tree support gpio: 74x164: Use dynamic gpio number assignment if no pdata is present gpio: 74x164: Use devm_kzalloc gpio: 74x164: Use module_spi_driver boiler plate function gpio: sx150x: Use irq_data_get_irq_chip_data() at appropriate places gpio: em: Use irq_data_get_irq_chip_data() at appropriate places ...
Diffstat (limited to 'drivers/gpio/gpio-74x164.c')
-rw-r--r--drivers/gpio/gpio-74x164.c103
1 files changed, 75 insertions, 28 deletions
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index a31ad6f5d91..ed3e55161bd 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -14,14 +14,18 @@
#include <linux/spi/spi.h>
#include <linux/spi/74x164.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/module.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;
- u8 port_config;
+ u32 registers;
};
static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
@@ -31,17 +35,47 @@ static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
{
- return spi_write(chip->spi,
- &chip->port_config, sizeof(chip->port_config));
+ 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
+ * 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.
+ */
+ 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);
+
+ return ret;
}
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;
+ u8 pin = offset % 8;
int ret;
mutex_lock(&chip->lock);
- ret = (chip->port_config >> offset) & 0x1;
+ ret = (chip->buffer[bank] >> pin) & 0x1;
mutex_unlock(&chip->lock);
return ret;
@@ -51,12 +85,14 @@ static void gen_74x164_set_value(struct gpio_chip *gc,
unsigned offset, int val)
{
struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
+ u8 bank = offset / 8;
+ u8 pin = offset % 8;
mutex_lock(&chip->lock);
if (val)
- chip->port_config |= (1 << offset);
+ chip->buffer[bank] |= (1 << pin);
else
- chip->port_config &= ~(1 << offset);
+ chip->buffer[bank] &= ~(1 << pin);
__gen_74x164_write_config(chip);
mutex_unlock(&chip->lock);
@@ -75,9 +111,8 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
struct gen_74x164_chip_platform_data *pdata;
int ret;
- pdata = spi->dev.platform_data;
- if (!pdata || !pdata->base) {
- dev_dbg(&spi->dev, "incorrect or missing platform data\n");
+ if (!spi->dev.of_node) {
+ dev_err(&spi->dev, "No device tree data available.\n");
return -EINVAL;
}
@@ -90,10 +125,16 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&spi->dev, sizeof(*chip), 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;
+
mutex_init(&chip->lock);
dev_set_drvdata(&spi->dev, chip);
@@ -104,8 +145,20 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
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;
- chip->gpio_chip.base = pdata->base;
- chip->gpio_chip.ngpio = 8;
+
+ 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.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
+ chip->buffer = devm_kzalloc(&spi->dev, chip->gpio_chip.ngpio, 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.owner = THIS_MODULE;
@@ -125,7 +178,6 @@ static int __devinit gen_74x164_probe(struct spi_device *spi)
exit_destroy:
dev_set_drvdata(&spi->dev, NULL);
mutex_destroy(&chip->lock);
- kfree(chip);
return ret;
}
@@ -141,36 +193,31 @@ static int __devexit gen_74x164_remove(struct spi_device *spi)
dev_set_drvdata(&spi->dev, NULL);
ret = gpiochip_remove(&chip->gpio_chip);
- if (!ret) {
+ if (!ret)
mutex_destroy(&chip->lock);
- kfree(chip);
- } else
+ else
dev_err(&spi->dev, "Failed to remove the GPIO controller: %d\n",
ret);
return ret;
}
+static const struct of_device_id gen_74x164_dt_ids[] = {
+ { .compatible = "fairchild,74hc595" },
+ {},
+};
+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),
},
.probe = gen_74x164_probe,
.remove = __devexit_p(gen_74x164_remove),
};
-
-static int __init gen_74x164_init(void)
-{
- return spi_register_driver(&gen_74x164_driver);
-}
-subsys_initcall(gen_74x164_init);
-
-static void __exit gen_74x164_exit(void)
-{
- spi_unregister_driver(&gen_74x164_driver);
-}
-module_exit(gen_74x164_exit);
+module_spi_driver(gen_74x164_driver);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");