summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-syscon.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 16:44:15 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 16:44:15 -0700
commit154d6f18a48e00dec2dc84a96914fe1a24669577 (patch)
tree7b92ac64a00eb173af396752f3bfc72c9eba615c /drivers/gpio/gpio-syscon.c
parent76ca7d1cca761bb9712dfcad9a27d70b520874ae (diff)
parentb22978fc33dec72e5f8e17f90eb63ea9137aafd5 (diff)
Merge tag 'gpio-v3.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull bulk of gpio updates from Linus Walleij: "A pretty big chunk of changes this time, but it has all been on rotation in linux-next and had some testing. Of course there will be some amount of fixes on top... - Merged in a branch of irqchip changes from Thomas Gleixner: we need to have new callbacks from the irqchip to determine if the GPIO line will be eligible for IRQs, and this callback must be able to say "no". After some thinking I got the branch from tglx and have switched all current users over to use this. - Based on tglx patches, we have added some generic irqchip helpers in the gpiolib core. These will help centralize code when GPIO drivers have simple chained/cascaded IRQs. Drivers will still define their irqchip vtables, but the gpiolib core will take care of irqdomain set-up, mapping from local offsets to Linux irqs, and reserve resources by marking the GPIO lines for IRQs. - Initially the PL061 and Nomadik GPIO/pin control drivers have been switched over to use the new gpiochip-to-irqchip infrastructure with more drivers expected for the next kernel cycle. The factoring of just two drivers still makes it worth it so it is already a win. - A new driver for the Synopsys DesignWare APB GPIO block. - Modify the DaVinci GPIO driver to be reusable also for the new TI Keystone architecture. - A new driver for the LSI ZEVIO SoCs. - Delete the obsolte tnetv107x driver. - Some incremental work on GPIO descriptors: have gpiod_direction_output() use a logical level, respecting assertion polarity through ACTIVE_LOW flags, adding gpiod_direction_output_raw() for the case where you want to set that very value. Add gpiochip_get_desc() to fetch a GPIO descriptor from a specific offset on a certain chip inside driver code. - Switch ACPI GPIO code over to using gpiochip_get_desc() and get rid of gpio_to_desc(). - The ACPI GPIO event handling code has been reworked after encountering an actual real life implementation. - Support for ACPI GPIO operation regions. - Generic GPIO chips can now be assigned labels/names from platform data. - We now clamp values returned from GPIO drivers to the boolean [0,1] range. - Some improved documentation on how to use the polarity flag was added. - a large slew of incremental driver updates and non-critical fixes. Some targeted for stable" * tag 'gpio-v3.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (80 commits) gpio: rcar: Add helper variable dev = &pdev->dev gpio-lynxpoint: force gpio_get() to return "1" and "0" only gpio: unmap gpio irqs properly pch_gpio: set value before enabling output direction gpio: moxart: Actually set output state in moxart_gpio_direction_output() gpio: moxart: Avoid forward declaration gpio: mxs: Allow for recursive enable_irq_wake() call gpio: samsung: Add missing "break" statement gpio: twl4030: Remove redundant assignment gpio: dwapb: correct gpio-cells in binding document gpio: iop: fix devm_ioremap_resource() return value checking pinctrl: coh901: convert driver to use gpiolib irqchip pinctrl: nomadik: convert driver to use gpiolib irqchip gpio: pl061: convert driver to use gpiolib irqchip gpio: add IRQ chip helpers in gpiolib pinctrl: nomadik: factor in platform data container pinctrl: nomadik: rename secondary to latent gpio: Driver for SYSCON-based GPIOs gpio: generic: Use platform_device_id->driver_data field for driver flags pinctrl: coh901: move irq line locking to resource callbacks ...
Diffstat (limited to 'drivers/gpio/gpio-syscon.c')
-rw-r--r--drivers/gpio/gpio-syscon.c191
1 files changed, 191 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
new file mode 100644
index 000000000000..b50fe1297748
--- /dev/null
+++ b/drivers/gpio/gpio-syscon.c
@@ -0,0 +1,191 @@
+/*
+ * SYSCON GPIO driver
+ *
+ * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define GPIO_SYSCON_FEAT_IN BIT(0)
+#define GPIO_SYSCON_FEAT_OUT BIT(1)
+#define GPIO_SYSCON_FEAT_DIR BIT(2)
+
+/* SYSCON driver is designed to use 32-bit wide registers */
+#define SYSCON_REG_SIZE (4)
+#define SYSCON_REG_BITS (SYSCON_REG_SIZE * 8)
+
+/**
+ * struct syscon_gpio_data - Configuration for the device.
+ * compatible: SYSCON driver compatible string.
+ * flags: Set of GPIO_SYSCON_FEAT_ flags:
+ * GPIO_SYSCON_FEAT_IN: GPIOs supports input,
+ * GPIO_SYSCON_FEAT_OUT: GPIOs supports output,
+ * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction.
+ * bit_count: Number of bits used as GPIOs.
+ * dat_bit_offset: Offset (in bits) to the first GPIO bit.
+ * dir_bit_offset: Optional offset (in bits) to the first bit to switch
+ * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
+ */
+
+struct syscon_gpio_data {
+ const char *compatible;
+ unsigned int flags;
+ unsigned int bit_count;
+ unsigned int dat_bit_offset;
+ unsigned int dir_bit_offset;
+};
+
+struct syscon_gpio_priv {
+ struct gpio_chip chip;
+ struct regmap *syscon;
+ const struct syscon_gpio_data *data;
+};
+
+static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct syscon_gpio_priv, chip);
+}
+
+static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int val, offs = priv->data->dat_bit_offset + offset;
+ int ret;
+
+ ret = regmap_read(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(offs % SYSCON_REG_BITS));
+}
+
+static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+ unsigned int offs = priv->data->dat_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS),
+ val ? BIT(offs % SYSCON_REG_BITS) : 0);
+}
+
+static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+
+ if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
+ unsigned int offs = priv->data->dir_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS), 0);
+ }
+
+ return 0;
+}
+
+static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
+{
+ struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
+
+ if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
+ unsigned int offs = priv->data->dir_bit_offset + offset;
+
+ regmap_update_bits(priv->syscon,
+ (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
+ BIT(offs % SYSCON_REG_BITS),
+ BIT(offs % SYSCON_REG_BITS));
+ }
+
+ syscon_gpio_set(chip, offset, val);
+
+ return 0;
+}
+
+static const struct syscon_gpio_data clps711x_mctrl_gpio = {
+ /* ARM CLPS711X SYSFLG1 Bits 8-10 */
+ .compatible = "cirrus,clps711x-syscon1",
+ .flags = GPIO_SYSCON_FEAT_IN,
+ .bit_count = 3,
+ .dat_bit_offset = 0x40 * 8 + 8,
+};
+
+static const struct of_device_id syscon_gpio_ids[] = {
+ {
+ .compatible = "cirrus,clps711x-mctrl-gpio",
+ .data = &clps711x_mctrl_gpio,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
+
+static int syscon_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id = of_match_device(syscon_gpio_ids, dev);
+ struct syscon_gpio_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->data = of_id->data;
+
+ priv->syscon =
+ syscon_regmap_lookup_by_compatible(priv->data->compatible);
+ if (IS_ERR(priv->syscon))
+ return PTR_ERR(priv->syscon);
+
+ priv->chip.dev = dev;
+ priv->chip.owner = THIS_MODULE;
+ priv->chip.label = dev_name(dev);
+ priv->chip.base = -1;
+ priv->chip.ngpio = priv->data->bit_count;
+ priv->chip.get = syscon_gpio_get;
+ if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
+ priv->chip.direction_input = syscon_gpio_dir_in;
+ if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
+ priv->chip.set = syscon_gpio_set;
+ priv->chip.direction_output = syscon_gpio_dir_out;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return gpiochip_add(&priv->chip);
+}
+
+static int syscon_gpio_remove(struct platform_device *pdev)
+{
+ struct syscon_gpio_priv *priv = platform_get_drvdata(pdev);
+
+ return gpiochip_remove(&priv->chip);
+}
+
+static struct platform_driver syscon_gpio_driver = {
+ .driver = {
+ .name = "gpio-syscon",
+ .owner = THIS_MODULE,
+ .of_match_table = syscon_gpio_ids,
+ },
+ .probe = syscon_gpio_probe,
+ .remove = syscon_gpio_remove,
+};
+module_platform_driver(syscon_gpio_driver);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("SYSCON GPIO driver");
+MODULE_LICENSE("GPL");