summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-12-29 07:00:21 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2018-12-29 07:00:21 +0300
commit24dc83635ffe3c93d8122099a83ee228c9b7e4f7 (patch)
tree4fafa40785d591a735c78ec3bbee7a05b944c1be /drivers
parent7e59fad9c9d1aeacdc96dfffd35f9e12ddc34dbf (diff)
parenta7c23f8d154f7919c5fcfceea6e0897be2d5ab71 (diff)
downloadlinux-24dc83635ffe3c93d8122099a83ee228c9b7e4f7.tar.xz
Merge tag 'gpio-v4.21-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij: "This is the bulk of GPIO changes for the v4.21 kernel series. Core changes: - Some core changes are already in outside of this pull request as they came through the regulator tree, most notably devm_gpiod_unhinge() that removes devres refcount management from a GPIO descriptor. This is needed in subsystems such as regulators where the regulator core need to take over the reference counting and lifecycle management for a GPIO descriptor. - We dropped devm_gpiochip_remove() and devm_gpio_chip_match() as nothing needs it. We can bring it back if need be. - Add a global TODO so people see where we are going. This helps setting the direction now that we are two GPIO maintainers. - Handle the MMC CD/WP properties in the device tree core. (The bulk of patches activating this code is already merged through the MMC/SD tree.) - Augment gpiochip_request_own_desc() to pass a flag so we as gpiochips can request lines as active low or open drain etc even from ourselves. New drivers: - New driver for Cadence GPIO blocks. - New driver for Atmel SAMA5D2 PIOBU GPIO lines. Driver improvements: - A major refactoring of the PCA953x driver - this driver has been around for ages, and is now modernized to reduce code duplication that has stacked up and is using regmap to read write and cache registers. - Intel drivers are now maintained in a separate tree and start with a round of cleanups and unifications" * tag 'gpio-v4.21-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (99 commits) gpio: sama5d2-piobu: Depend on OF_GPIO gpio: Add Cadence GPIO driver dt-bindings: gpio: Add bindings for Cadence GPIO gpiolib-acpi: remove unused variable 'err', cleans up build warning gpio: mxs: read pin level directly instead of using .get gpio: aspeed: remove duplicated statement gpio: add driver for SAMA5D2 PIOBU pins dt-bindings: arm: atmel: describe SECUMOD usage as a GPIO controller gpio/mmc/of: Respect polarity in the device tree dt-bindings: gpio: rcar: Add r8a774c0 (RZ/G2E) support memory: omap-gpmc: Get the header of the enum ARM: omap1: Fix new user of gpiochip_request_own_desc() gpio: pca953x: Add regmap dependency for PCA953x driver gpio: raspberrypi-exp: decrease refcount on firmware dt node gpiolib: Fix return value of gpio_to_desc() stub if !GPIOLIB gpio: pca953x: Restore registers after suspend/resume cycle gpio: pca953x: Zap single use of pca953x_read_single() gpio: pca953x: Zap ad-hoc reg_output cache gpio: pca953x: Zap ad-hoc reg_direction cache gpio: pca953x: Perform basic regmap conversion ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig22
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/TODO109
-rw-r--r--drivers/gpio/gpio-104-dio-48e.c2
-rw-r--r--drivers/gpio/gpio-104-idi-48.c2
-rw-r--r--drivers/gpio/gpio-aspeed.c1
-rw-r--r--drivers/gpio/gpio-cadence.c291
-rw-r--r--drivers/gpio/gpio-dwapb.c6
-rw-r--r--drivers/gpio/gpio-gpio-mm.c2
-rw-r--r--drivers/gpio/gpio-grgpio.c1
-rw-r--r--drivers/gpio/gpio-ich.c73
-rw-r--r--drivers/gpio/gpio-intel-mid.c16
-rw-r--r--drivers/gpio/gpio-ks8695.c15
-rw-r--r--drivers/gpio/gpio-lpc18xx.c286
-rw-r--r--drivers/gpio/gpio-lynxpoint.c47
-rw-r--r--drivers/gpio/gpio-merrifield.c6
-rw-r--r--drivers/gpio/gpio-mt7621.c10
-rw-r--r--drivers/gpio/gpio-mvebu.c2
-rw-r--r--drivers/gpio/gpio-mxc.c41
-rw-r--r--drivers/gpio/gpio-mxs.c2
-rw-r--r--drivers/gpio/gpio-omap.c12
-rw-r--r--drivers/gpio/gpio-pca953x.c484
-rw-r--r--drivers/gpio/gpio-pch.c165
-rw-r--r--drivers/gpio/gpio-pci-idio-16.c2
-rw-r--r--drivers/gpio/gpio-pcie-idio-24.c2
-rw-r--r--drivers/gpio/gpio-pl061.c21
-rw-r--r--drivers/gpio/gpio-raspberrypi-exp.c1
-rw-r--r--drivers/gpio/gpio-rcar.c34
-rw-r--r--drivers/gpio/gpio-sama5d2-piobu.c253
-rw-r--r--drivers/gpio/gpio-sch.c27
-rw-r--r--drivers/gpio/gpio-sch311x.c2
-rw-r--r--drivers/gpio/gpio-sodaville.c73
-rw-r--r--drivers/gpio/gpio-tegra.c6
-rw-r--r--drivers/gpio/gpio-tegra186.c6
-rw-r--r--drivers/gpio/gpio-uniphier.c17
-rw-r--r--drivers/gpio/gpio-vf610.c44
-rw-r--r--drivers/gpio/gpio-ws16c48.c2
-rw-r--r--drivers/gpio/gpio-zynq.c29
-rw-r--r--drivers/gpio/gpiolib-acpi.c14
-rw-r--r--drivers/gpio/gpiolib-of.c26
-rw-r--r--drivers/gpio/gpiolib.c76
-rw-r--r--drivers/hid/hid-cp2112.c2
-rw-r--r--drivers/memory/omap-gpmc.c4
43 files changed, 1588 insertions, 650 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 833a1b51c948..b5a2845347ec 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -160,6 +160,14 @@ config GPIO_BRCMSTB
help
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
+config GPIO_CADENCE
+ tristate "Cadence GPIO support"
+ depends on OF_GPIO
+ select GPIO_GENERIC
+ select GPIOLIB_IRQCHIP
+ help
+ Say yes here to enable support for Cadence GPIO controller.
+
config GPIO_CLPS711X
tristate "CLPS711X GPIO support"
depends on ARCH_CLPS711X || COMPILE_TEST
@@ -288,6 +296,7 @@ config GPIO_LPC18XX
tristate "NXP LPC18XX/43XX GPIO support"
default y if ARCH_LPC18XX
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
+ select IRQ_DOMAIN_HIERARCHY
help
Select this option to enable GPIO driver for
NXP LPC18XX/43XX devices.
@@ -429,6 +438,18 @@ config GPIO_REG
A 32-bit single register GPIO fixed in/out implementation. This
can be used to represent any register as a set of GPIO signals.
+config GPIO_SAMA5D2_PIOBU
+ tristate "SAMA5D2 PIOBU GPIO support"
+ depends on MFD_SYSCON
+ depends on OF_GPIO
+ select GPIO_SYSCON
+ help
+ Say yes here to use the PIOBU pins as GPIOs.
+
+ PIOBU pins on the SAMA5D2 can be used as GPIOs.
+ The difference from regular GPIOs is that they
+ maintain their value during backup/self-refresh.
+
config GPIO_SIOX
tristate "SIOX GPIO support"
depends on SIOX
@@ -849,6 +870,7 @@ config GPIO_MC9S08DZ60
config GPIO_PCA953X
tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
+ select REGMAP_I2C
help
Say yes here to provide access to several register-oriented
SMBus I/O expanders, made mostly by NXP or TI. Compatible
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 671c4477c951..37628f8dbf70 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
+obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
@@ -108,6 +109,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
+obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO
new file mode 100644
index 000000000000..19d27c904916
--- /dev/null
+++ b/drivers/gpio/TODO
@@ -0,0 +1,109 @@
+This is a place for planning the ongoing long-term work in the GPIO
+subsystem.
+
+
+GPIO descriptors
+
+Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey
+to move away from the global GPIO numberspace and toward a decriptor-based
+approach. This means that GPIO consumers, drivers and machine descriptions
+ideally have no use or idea of the global GPIO numberspace that has/was
+used in the inception of the GPIO subsystem.
+
+Work items:
+
+- Convert all GPIO device drivers to only #include <linux/gpio/driver.h>
+
+- Convert all consumer drivers to only #include <linux/gpio/consumer.h>
+
+- Convert all machine descriptors in "boardfiles" to only
+ #include <linux/gpio/machine.h>, the other option being to convert it
+ to a machine description such as device tree, ACPI or fwnode that
+ implicitly does not use global GPIO numbers.
+
+- When this work is complete (will require some of the items in the
+ following ongoing work as well) we can delete the old global
+ numberspace accessors from <linux/gpio.h> and eventually delete
+ <linux/gpio.h> altogether.
+
+
+Get rid of <linux/of_gpio.h>
+
+This header and helpers appeared at one point when there was no proper
+driver infrastructure for doing simpler MMIO GPIO devices and there was
+no core support for parsing device tree GPIOs from the core library with
+the [devm_]gpiod_get() calls we have today that will implicitly go into
+the device tree back-end.
+
+Work items:
+
+- Get rid of struct of_mm_gpio_chip altogether: use the generic MMIO
+ GPIO for all current users (see below). Delete struct of_mm_gpio_chip,
+ to_of_mm_gpio_chip(), of_mm_gpiochip_add_data(), of_mm_gpiochip_add()
+ of_mm_gpiochip_remove() from the kernel.
+
+- Change all consumer drivers that #include <linux/of_gpio.h> to
+ #include <linux/gpio/consumer.h> and stop doing custom parsing of the
+ GPIO lines from the device tree. This can be tricky and often ivolves
+ changing boardfiles, etc.
+
+- Pull semantics for legacy device tree (OF) GPIO lookups into
+ gpiolib-of.c: in some cases subsystems are doing custom flags and
+ lookups for polarity inversion, open drain and what not. As we now
+ handle this with generic OF bindings, pull all legacy handling into
+ gpiolib so the library API becomes narrow and deep and handle all
+ legacy bindings internally. (See e.g. commits 6953c57ab172,
+ 6a537d48461d etc)
+
+- Delete <linux/of_gpio.h> when all the above is complete and everything
+ uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead.
+
+
+Collect drivers
+
+Collect GPIO drivers from arch/* and other places that should be placed
+in drivers/gpio/gpio-*. Augment platforms to create platform devices or
+similar and probe a proper driver in the gpiolib subsystem.
+
+In some cases it makes sense to create a GPIO chip from the local driver
+for a few GPIOs. Those should stay where they are.
+
+
+Generic MMIO GPIO
+
+The GPIO drivers can utilize the generic MMIO helper library in many
+cases, and the helper library should be as helpful as possible for MMIO
+drivers. (drivers/gpio/gpio-mmio.c)
+
+Work items:
+
+- Look over and identify any remaining easily converted drivers and
+ dry-code conversions to MMIO GPIO for maintainers to test
+
+- Expand the MMIO GPIO or write a new library for port-mapped I/O
+ helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use
+ this with dry-coding and sending to maintainers to test
+
+
+GPIOLIB irqchip
+
+The GPIOLIB irqchip is a helper irqchip for "simple cases" that should
+try to cover any generic kind of irqchip cascaded from a GPIO.
+
+- Look over and identify any remaining easily converted drivers and
+ dry-code conversions to gpiolib irqchip for maintainers to test
+
+- Support generic hierarchical GPIO interrupts: these are for the
+ non-cascading case where there is one IRQ per GPIO line, there is
+ currently no common infrastructure for this.
+
+
+Increase integration with pin control
+
+There are already ways to use pin control as back-end for GPIO and
+it may make sense to bring these subsystems closer. One reason for
+creating pin control as its own subsystem was that we could avoid any
+use of the global GPIO numbers. Once the above is complete, it may
+make sense to simply join the subsystems into one and make pin
+multiplexing, pin configuration, GPIO, etc selectable options in one
+and the same pin control and GPIO subsystem.
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index 9c4e07fcb74b..92c8f944bf64 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -222,7 +222,7 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
port_state = inb(dio48egpio->base + ports[i]);
/* store acquired bits at respective bits array offset */
- bits[word_index] |= port_state << word_offset;
+ bits[word_index] |= (port_state << word_offset) & word_mask;
}
return 0;
diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c
index 2c9738adb3a6..88dc6f2449f6 100644
--- a/drivers/gpio/gpio-104-idi-48.c
+++ b/drivers/gpio/gpio-104-idi-48.c
@@ -128,7 +128,7 @@ static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
port_state = inb(idi48gpio->base + ports[i]);
/* store acquired bits at respective bits array offset */
- bits[word_index] |= port_state << word_offset;
+ bits[word_index] |= (port_state << word_offset) & word_mask;
}
return 0;
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 2342e154029b..854bce4fb9e7 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -1185,7 +1185,6 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
gpio->chip.parent = &pdev->dev;
gpio->chip.ngpio = gpio->config->nr_gpios;
- gpio->chip.parent = &pdev->dev;
gpio->chip.direction_input = aspeed_gpio_dir_in;
gpio->chip.direction_output = aspeed_gpio_dir_out;
gpio->chip.get_direction = aspeed_gpio_get_direction;
diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c
new file mode 100644
index 000000000000..aec8d5df9f30
--- /dev/null
+++ b/drivers/gpio/gpio-cadence.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2017-2018 Cadence
+ *
+ * Authors:
+ * Jan Kotas <jank@cadence.com>
+ * Boris Brezillon <boris.brezillon@free-electrons.com>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define CDNS_GPIO_BYPASS_MODE 0x00
+#define CDNS_GPIO_DIRECTION_MODE 0x04
+#define CDNS_GPIO_OUTPUT_EN 0x08
+#define CDNS_GPIO_OUTPUT_VALUE 0x0c
+#define CDNS_GPIO_INPUT_VALUE 0x10
+#define CDNS_GPIO_IRQ_MASK 0x14
+#define CDNS_GPIO_IRQ_EN 0x18
+#define CDNS_GPIO_IRQ_DIS 0x1c
+#define CDNS_GPIO_IRQ_STATUS 0x20
+#define CDNS_GPIO_IRQ_TYPE 0x24
+#define CDNS_GPIO_IRQ_VALUE 0x28
+#define CDNS_GPIO_IRQ_ANY_EDGE 0x2c
+
+struct cdns_gpio_chip {
+ struct gpio_chip gc;
+ struct clk *pclk;
+ void __iomem *regs;
+ u32 bypass_orig;
+};
+
+static int cdns_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->bgpio_lock, flags);
+
+ iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) & ~BIT(offset),
+ cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+
+ spin_unlock_irqrestore(&chip->bgpio_lock, flags);
+ return 0;
+}
+
+static void cdns_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->bgpio_lock, flags);
+
+ iowrite32(ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE) |
+ (BIT(offset) & cgpio->bypass_orig),
+ cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+
+ spin_unlock_irqrestore(&chip->bgpio_lock, flags);
+}
+
+static void cdns_gpio_irq_mask(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
+
+ iowrite32(BIT(d->hwirq), cgpio->regs + CDNS_GPIO_IRQ_DIS);
+}
+
+static void cdns_gpio_irq_unmask(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
+
+ iowrite32(BIT(d->hwirq), cgpio->regs + CDNS_GPIO_IRQ_EN);
+}
+
+static int cdns_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+ struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
+ unsigned long flags;
+ u32 int_value;
+ u32 int_type;
+ u32 mask = BIT(d->hwirq);
+ int ret = 0;
+
+ spin_lock_irqsave(&chip->bgpio_lock, flags);
+
+ int_value = ioread32(cgpio->regs + CDNS_GPIO_IRQ_VALUE) & ~mask;
+ int_type = ioread32(cgpio->regs + CDNS_GPIO_IRQ_TYPE) & ~mask;
+
+ /*
+ * The GPIO controller doesn't have an ACK register.
+ * All interrupt statuses are cleared on a status register read.
+ * Don't support edge interrupts for now.
+ */
+
+ if (type == IRQ_TYPE_LEVEL_HIGH) {
+ int_type |= mask;
+ int_value |= mask;
+ } else if (type == IRQ_TYPE_LEVEL_LOW) {
+ int_type |= mask;
+ } else {
+ ret = -EINVAL;
+ goto err_irq_type;
+ }
+
+ iowrite32(int_value, cgpio->regs + CDNS_GPIO_IRQ_VALUE);
+ iowrite32(int_type, cgpio->regs + CDNS_GPIO_IRQ_TYPE);
+
+err_irq_type:
+ spin_unlock_irqrestore(&chip->bgpio_lock, flags);
+ return ret;
+}
+
+static void cdns_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
+ struct cdns_gpio_chip *cgpio = gpiochip_get_data(chip);
+ struct irq_chip *irqchip = irq_desc_get_chip(desc);
+ unsigned long status;
+ int hwirq;
+
+ chained_irq_enter(irqchip, desc);
+
+ status = ioread32(cgpio->regs + CDNS_GPIO_IRQ_STATUS) &
+ ~ioread32(cgpio->regs + CDNS_GPIO_IRQ_MASK);
+
+ for_each_set_bit(hwirq, &status, chip->ngpio)
+ generic_handle_irq(irq_find_mapping(chip->irq.domain, hwirq));
+
+ chained_irq_exit(irqchip, desc);
+}
+
+static struct irq_chip cdns_gpio_irqchip = {
+ .name = "cdns-gpio",
+ .irq_mask = cdns_gpio_irq_mask,
+ .irq_unmask = cdns_gpio_irq_unmask,
+ .irq_set_type = cdns_gpio_irq_set_type
+};
+
+static int cdns_gpio_probe(struct platform_device *pdev)
+{
+ struct cdns_gpio_chip *cgpio;
+ struct resource *res;
+ int ret, irq;
+ u32 dir_prev;
+ u32 num_gpios = 32;
+
+ cgpio = devm_kzalloc(&pdev->dev, sizeof(*cgpio), GFP_KERNEL);
+ if (!cgpio)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cgpio->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cgpio->regs))
+ return PTR_ERR(cgpio->regs);
+
+ of_property_read_u32(pdev->dev.of_node, "ngpios", &num_gpios);
+
+ if (num_gpios > 32) {
+ dev_err(&pdev->dev, "ngpios must be less or equal 32\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Set all pins as inputs by default, otherwise:
+ * gpiochip_lock_as_irq:
+ * tried to flag a GPIO set as output for IRQ
+ * Generic GPIO driver stores the direction value internally,
+ * so it needs to be changed before bgpio_init() is called.
+ */
+ dir_prev = ioread32(cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
+ iowrite32(GENMASK(num_gpios - 1, 0),
+ cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
+
+ ret = bgpio_init(&cgpio->gc, &pdev->dev, 4,
+ cgpio->regs + CDNS_GPIO_INPUT_VALUE,
+ cgpio->regs + CDNS_GPIO_OUTPUT_VALUE,
+ NULL,
+ NULL,
+ cgpio->regs + CDNS_GPIO_DIRECTION_MODE,
+ BGPIOF_READ_OUTPUT_REG_SET);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register generic gpio, %d\n",
+ ret);
+ goto err_revert_dir;
+ }
+
+ cgpio->gc.label = dev_name(&pdev->dev);
+ cgpio->gc.ngpio = num_gpios;
+ cgpio->gc.parent = &pdev->dev;
+ cgpio->gc.base = -1;
+ cgpio->gc.owner = THIS_MODULE;
+ cgpio->gc.request = cdns_gpio_request;
+ cgpio->gc.free = cdns_gpio_free;
+
+ cgpio->pclk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(cgpio->pclk)) {
+ ret = PTR_ERR(cgpio->pclk);
+ dev_err(&pdev->dev,
+ "Failed to retrieve peripheral clock, %d\n", ret);
+ goto err_revert_dir;
+ }
+
+ ret = clk_prepare_enable(cgpio->pclk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to enable the peripheral clock, %d\n", ret);
+ goto err_revert_dir;
+ }
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
+ goto err_disable_clk;
+ }
+
+ /*
+ * irq_chip support
+ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0) {
+ ret = gpiochip_irqchip_add(&cgpio->gc, &cdns_gpio_irqchip,
+ 0, handle_level_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not add irqchip, %d\n",
+ ret);
+ goto err_disable_clk;
+ }
+ gpiochip_set_chained_irqchip(&cgpio->gc, &cdns_gpio_irqchip,
+ irq, cdns_gpio_irq_handler);
+ }
+
+ cgpio->bypass_orig = ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+
+ /*
+ * Enable gpio outputs, ignored for input direction
+ */
+ iowrite32(GENMASK(num_gpios - 1, 0),
+ cgpio->regs + CDNS_GPIO_OUTPUT_EN);
+ iowrite32(0, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+
+ platform_set_drvdata(pdev, cgpio);
+ return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(cgpio->pclk);
+
+err_revert_dir:
+ iowrite32(dir_prev, cgpio->regs + CDNS_GPIO_DIRECTION_MODE);
+
+ return ret;
+}
+
+static int cdns_gpio_remove(struct platform_device *pdev)
+{
+ struct cdns_gpio_chip *cgpio = platform_get_drvdata(pdev);
+
+ iowrite32(cgpio->bypass_orig, cgpio->regs + CDNS_GPIO_BYPASS_MODE);
+ clk_disable_unprepare(cgpio->pclk);
+
+ return 0;
+}
+
+static const struct of_device_id cdns_of_ids[] = {
+ { .compatible = "cdns,gpio-r1p02" },
+ { /* sentinel */ },
+};
+
+static struct platform_driver cdns_gpio_driver = {
+ .driver = {
+ .name = "cdns-gpio",
+ .of_match_table = cdns_of_ids,
+ },
+ .probe = cdns_gpio_probe,
+ .remove = cdns_gpio_remove,
+};
+module_platform_driver(cdns_gpio_driver);
+
+MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
+MODULE_DESCRIPTION("Cadence GPIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cdns-gpio");
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index 044888fd96a1..84ae04402f70 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -748,8 +748,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int dwapb_gpio_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+ struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags;
int i;
@@ -793,8 +792,7 @@ static int dwapb_gpio_suspend(struct device *dev)
static int dwapb_gpio_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
+ struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags;
int i;
diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c
index b56ff2efbf36..8c150fd68d9d 100644
--- a/drivers/gpio/gpio-gpio-mm.c
+++ b/drivers/gpio/gpio-gpio-mm.c
@@ -211,7 +211,7 @@ static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
port_state = inb(gpiommgpio->base + ports[i]);
/* store acquired bits at respective bits array offset */
- bits[word_index] |= port_state << word_offset;
+ bits[word_index] |= (port_state << word_offset) & word_mask;
}
return 0;
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c
index 60a1556c570a..45b8d6a02b87 100644
--- a/drivers/gpio/gpio-grgpio.c
+++ b/drivers/gpio/gpio-grgpio.c
@@ -30,7 +30,6 @@
#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/err.h>
-#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index dba392221042..90bf7742f9b0 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -1,32 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
*
* Copyright (C) 2010 Extreme Engineering Solutions.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
#include <linux/ioport.h>
+#include <linux/mfd/lpc_ich.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
-#include <linux/mfd/lpc_ich.h>
-#include <linux/bitops.h>
#define DRV_NAME "gpio_ich"
@@ -100,7 +86,7 @@ struct ichx_desc {
static struct {
spinlock_t lock;
- struct platform_device *dev;
+ struct device *dev;
struct gpio_chip chip;
struct resource *gpio_base; /* GPIO IO base */
struct resource *pm_base; /* Power Mangagment IO base */
@@ -112,8 +98,7 @@ static struct {
static int modparam_gpiobase = -1; /* dynamic */
module_param_named(gpiobase, modparam_gpiobase, int, 0444);
-MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, "
- "which is the default.");
+MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default.");
static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
{
@@ -121,7 +106,6 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
u32 data, tmp;
int reg_nr = nr / 32;
int bit = nr & 0x1f;
- int ret = 0;
spin_lock_irqsave(&ichx_priv.lock, flags);
@@ -142,12 +126,10 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
ichx_priv.gpio_base);
- if (verify && data != tmp)
- ret = -EPERM;
spin_unlock_irqrestore(&ichx_priv.lock, flags);
- return ret;
+ return (verify && data != tmp) ? -EPERM : 0;
}
static int ichx_read_bit(int reg, unsigned nr)
@@ -186,10 +168,7 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
* Try setting pin as an input and verify it worked since many pins
* are output-only.
*/
- if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1))
- return -EINVAL;
-
- return 0;
+ return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1);
}
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
@@ -206,10 +185,7 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
* Try setting pin as an output and verify it worked since many pins
* are input-only.
*/
- if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1))
- return -EINVAL;
-
- return 0;
+ return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1);
}
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
@@ -284,7 +260,7 @@ static void ichx_gpiolib_setup(struct gpio_chip *chip)
{
chip->owner = THIS_MODULE;
chip->label = DRV_NAME;
- chip->parent = &ichx_priv.dev->dev;
+ chip->parent = ichx_priv.dev;
/* Allow chip-specific overrides of request()/get() */
chip->request = ichx_priv.desc->request ?
@@ -407,15 +383,14 @@ static int ichx_gpio_request_regions(struct device *dev,
static int ichx_gpio_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct lpc_ich_info *ich_info = dev_get_platdata(dev);
struct resource *res_base, *res_pm;
int err;
- struct lpc_ich_info *ich_info = dev_get_platdata(&pdev->dev);
if (!ich_info)
return -ENODEV;
- ichx_priv.dev = pdev;
-
switch (ich_info->gpio_version) {
case ICH_I3100_GPIO:
ichx_priv.desc = &i3100_desc;
@@ -445,19 +420,21 @@ static int ichx_gpio_probe(struct platform_device *pdev)
return -ENODEV;
}
+ ichx_priv.dev = dev;
spin_lock_init(&ichx_priv.lock);
+
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
- ichx_priv.use_gpio = ich_info->use_gpio;
- err = ichx_gpio_request_regions(&pdev->dev, res_base, pdev->name,
- ichx_priv.use_gpio);
+ err = ichx_gpio_request_regions(dev, res_base, pdev->name,
+ ich_info->use_gpio);
if (err)
return err;
ichx_priv.gpio_base = res_base;
+ ichx_priv.use_gpio = ich_info->use_gpio;
/*
* If necessary, determine the I/O address of ACPI/power management
- * registers which are needed to read the the GPE0 register for GPI pins
+ * registers which are needed to read the GPE0 register for GPI pins
* 0 - 15 on some chipsets.
*/
if (!ichx_priv.desc->uses_gpe0)
@@ -465,13 +442,13 @@ static int ichx_gpio_probe(struct platform_device *pdev)
res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
if (!res_pm) {
- pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
+ dev_warn(dev, "ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
goto init;
}
- if (!devm_request_region(&pdev->dev, res_pm->start,
- resource_size(res_pm), pdev->name)) {
- pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");
+ if (!devm_request_region(dev, res_pm->start, resource_size(res_pm),
+ pdev->name)) {
+ dev_warn(dev, "ACPI BAR is busy, GPI 0 - 15 unavailable\n");
goto init;
}
@@ -481,12 +458,12 @@ init:
ichx_gpiolib_setup(&ichx_priv.chip);
err = gpiochip_add_data(&ichx_priv.chip, NULL);
if (err) {
- pr_err("Failed to register GPIOs\n");
+ dev_err(dev, "Failed to register GPIOs\n");
return err;
}
- pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,
- ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);
+ dev_info(dev, "GPIO from %d to %d\n", ichx_priv.chip.base,
+ ichx_priv.chip.base + ichx_priv.chip.ngpio - 1);
return 0;
}
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
index 028d64c2cb1e..4e803baf980e 100644
--- a/drivers/gpio/gpio-intel-mid.c
+++ b/drivers/gpio/gpio-intel-mid.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel MID GPIO driver
*
* Copyright (c) 2008-2014,2016 Intel Corporation.
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
/* Supports:
@@ -20,12 +12,11 @@
*/
#include <linux/delay.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include <linux/gpio/driver.h>
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -273,9 +264,8 @@ static const struct pci_device_id intel_gpio_ids[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
},
- { 0 }
+ { }
};
-MODULE_DEVICE_TABLE(pci, intel_gpio_ids);
static void intel_mid_irq_handler(struct irq_desc *desc)
{
diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c
index 55d562e1278e..d6d6140ffc40 100644
--- a/drivers/gpio/gpio-ks8695.c
+++ b/drivers/gpio/gpio-ks8695.c
@@ -282,22 +282,13 @@ static int ks8695_gpio_show(struct seq_file *s, void *unused)
return 0;
}
-static int ks8695_gpio_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ks8695_gpio_show, NULL);
-}
-
-static const struct file_operations ks8695_gpio_operations = {
- .open = ks8695_gpio_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(ks8695_gpio);
static int __init ks8695_gpio_debugfs_init(void)
{
/* /sys/kernel/debug/ks8695_gpio */
- (void) debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, &ks8695_gpio_operations);
+ debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL,
+ &ks8695_gpio_fops);
return 0;
}
postcore_initcall(ks8695_gpio_debugfs_init);
diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c
index f12e02e1016d..d441dbaed7a3 100644
--- a/drivers/gpio/gpio-lpc18xx.c
+++ b/drivers/gpio/gpio-lpc18xx.c
@@ -1,20 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* GPIO driver for NXP LPC18xx/43xx.
*
+ * Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com>
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.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/clk.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
@@ -24,13 +25,246 @@
#define LPC18XX_MAX_PORTS 8
#define LPC18XX_PINS_PER_PORT 32
+/* LPC18xx GPIO pin interrupt controller register offsets */
+#define LPC18XX_GPIO_PIN_IC_ISEL 0x00
+#define LPC18XX_GPIO_PIN_IC_IENR 0x04
+#define LPC18XX_GPIO_PIN_IC_SIENR 0x08
+#define LPC18XX_GPIO_PIN_IC_CIENR 0x0c
+#define LPC18XX_GPIO_PIN_IC_IENF 0x10
+#define LPC18XX_GPIO_PIN_IC_SIENF 0x14
+#define LPC18XX_GPIO_PIN_IC_CIENF 0x18
+#define LPC18XX_GPIO_PIN_IC_RISE 0x1c
+#define LPC18XX_GPIO_PIN_IC_FALL 0x20
+#define LPC18XX_GPIO_PIN_IC_IST 0x24
+
+#define NR_LPC18XX_GPIO_PIN_IC_IRQS 8
+
+struct lpc18xx_gpio_pin_ic {
+ void __iomem *base;
+ struct irq_domain *domain;
+ struct raw_spinlock lock;
+};
+
struct lpc18xx_gpio_chip {
struct gpio_chip gpio;
void __iomem *base;
struct clk *clk;
+ struct lpc18xx_gpio_pin_ic *pin_ic;
spinlock_t lock;
};
+static inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic,
+ u32 pin, bool set)
+{
+ u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
+
+ if (set)
+ val &= ~BIT(pin);
+ else
+ val |= BIT(pin);
+
+ writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
+}
+
+static inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic,
+ u32 pin, u32 reg)
+{
+ writel_relaxed(BIT(pin), ic->base + reg);
+}
+
+static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)
+{
+ struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+ u32 type = irqd_get_trigger_type(d);
+
+ raw_spin_lock(&ic->lock);
+
+ if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_CIENR);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_CIENF);
+
+ raw_spin_unlock(&ic->lock);
+
+ irq_chip_mask_parent(d);
+}
+
+static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)
+{
+ struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+ u32 type = irqd_get_trigger_type(d);
+
+ raw_spin_lock(&ic->lock);
+
+ if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_SIENR);
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_SIENF);
+
+ raw_spin_unlock(&ic->lock);
+
+ irq_chip_unmask_parent(d);
+}
+
+static void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d)
+{
+ struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+ u32 type = irqd_get_trigger_type(d);
+
+ raw_spin_lock(&ic->lock);
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_IST);
+
+ raw_spin_unlock(&ic->lock);
+
+ irq_chip_eoi_parent(d);
+}
+
+static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)
+{
+ struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
+
+ raw_spin_lock(&ic->lock);
+
+ if (type & IRQ_TYPE_LEVEL_HIGH) {
+ lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_SIENF);
+ } else if (type & IRQ_TYPE_LEVEL_LOW) {
+ lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
+ lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
+ LPC18XX_GPIO_PIN_IC_CIENF);
+ } else {
+ lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false);
+ }
+
+ raw_spin_unlock(&ic->lock);
+
+ return 0;
+}
+
+static struct irq_chip lpc18xx_gpio_pin_ic = {
+ .name = "LPC18xx GPIO pin",
+ .irq_mask = lpc18xx_gpio_pin_ic_mask,
+ .irq_unmask = lpc18xx_gpio_pin_ic_unmask,
+ .irq_eoi = lpc18xx_gpio_pin_ic_eoi,
+ .irq_set_type = lpc18xx_gpio_pin_ic_set_type,
+ .flags = IRQCHIP_SET_TYPE_MASKED,
+};
+
+static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct irq_fwspec parent_fwspec, *fwspec = data;
+ struct lpc18xx_gpio_pin_ic *ic = domain->host_data;
+ irq_hw_number_t hwirq;
+ int ret;
+
+ if (nr_irqs != 1)
+ return -EINVAL;
+
+ hwirq = fwspec->param[0];
+ if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS)
+ return -EINVAL;
+
+ /*
+ * All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated
+ * into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC
+ */
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ parent_fwspec.param_count = 1;
+ parent_fwspec.param[0] = hwirq + 32;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
+ if (ret < 0) {
+ pr_err("failed to allocate parent irq %u: %d\n",
+ parent_fwspec.param[0], ret);
+ return ret;
+ }
+
+ return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &lpc18xx_gpio_pin_ic, ic);
+}
+
+static const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = {
+ .alloc = lpc18xx_gpio_pin_ic_domain_alloc,
+ .xlate = irq_domain_xlate_twocell,
+ .free = irq_domain_free_irqs_common,
+};
+
+static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)
+{
+ struct device *dev = gc->gpio.parent;
+ struct irq_domain *parent_domain;
+ struct device_node *parent_node;
+ struct lpc18xx_gpio_pin_ic *ic;
+ struct resource res;
+ int ret, index;
+
+ parent_node = of_irq_find_parent(dev->of_node);
+ if (!parent_node)
+ return -ENXIO;
+
+ parent_domain = irq_find_host(parent_node);
+ of_node_put(parent_node);
+ if (!parent_domain)
+ return -ENXIO;
+
+ ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL);
+ if (!ic)
+ return -ENOMEM;
+
+ index = of_property_match_string(dev->of_node, "reg-names",
+ "gpio-pin-ic");
+ if (index < 0) {
+ ret = -ENODEV;
+ goto free_ic;
+ }
+
+ ret = of_address_to_resource(dev->of_node, index, &res);
+ if (ret < 0)
+ goto free_ic;
+
+ ic->base = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(ic->base)) {
+ ret = PTR_ERR(ic->base);
+ goto free_ic;
+ }
+
+ raw_spin_lock_init(&ic->lock);
+
+ ic->domain = irq_domain_add_hierarchy(parent_domain, 0,
+ NR_LPC18XX_GPIO_PIN_IC_IRQS,
+ dev->of_node,
+ &lpc18xx_gpio_pin_ic_domain_ops,
+ ic);
+ if (!ic->domain) {
+ pr_err("unable to add irq domain\n");
+ ret = -ENODEV;
+ goto free_iomap;
+ }
+
+ gc->pin_ic = ic;
+
+ return 0;
+
+free_iomap:
+ devm_iounmap(dev, ic->base);
+free_ic:
+ devm_kfree(dev, ic);
+
+ return ret;
+}
+
static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
@@ -92,45 +326,62 @@ static const struct gpio_chip lpc18xx_chip = {
static int lpc18xx_gpio_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct lpc18xx_gpio_chip *gc;
- struct resource *res;
- int ret;
+ int index, ret;
- gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
return -ENOMEM;
gc->gpio = lpc18xx_chip;
platform_set_drvdata(pdev, gc);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gc->base = devm_ioremap_resource(&pdev->dev, res);
+ index = of_property_match_string(dev->of_node, "reg-names", "gpio");
+ if (index < 0) {
+ /* To support backward compatibility take the first resource */
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gc->base = devm_ioremap_resource(dev, res);
+ } else {
+ struct resource res;
+
+ ret = of_address_to_resource(dev->of_node, index, &res);
+ if (ret < 0)
+ return ret;
+
+ gc->base = devm_ioremap_resource(dev, &res);
+ }
if (IS_ERR(gc->base))
return PTR_ERR(gc->base);
- gc->clk = devm_clk_get(&pdev->dev, NULL);
+ gc->clk = devm_clk_get(dev, NULL);
if (IS_ERR(gc->clk)) {
- dev_err(&pdev->dev, "input clock not found\n");
+ dev_err(dev, "input clock not found\n");
return PTR_ERR(gc->clk);
}
ret = clk_prepare_enable(gc->clk);
if (ret) {
- dev_err(&pdev->dev, "unable to enable clock\n");
+ dev_err(dev, "unable to enable clock\n");
return ret;
}
spin_lock_init(&gc->lock);
- gc->gpio.parent = &pdev->dev;
+ gc->gpio.parent = dev;
- ret = gpiochip_add_data(&gc->gpio, gc);
+ ret = devm_gpiochip_add_data(dev, &gc->gpio, gc);
if (ret) {
- dev_err(&pdev->dev, "failed to add gpio chip\n");
+ dev_err(dev, "failed to add gpio chip\n");
clk_disable_unprepare(gc->clk);
return ret;
}
+ /* On error GPIO pin interrupt controller just won't be registered */
+ lpc18xx_gpio_pin_ic_probe(gc);
+
return 0;
}
@@ -138,7 +389,9 @@ static int lpc18xx_gpio_remove(struct platform_device *pdev)
{
struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
- gpiochip_remove(&gc->gpio);
+ if (gc->pin_ic)
+ irq_domain_remove(gc->pin_ic->domain);
+
clk_disable_unprepare(gc->clk);
return 0;
@@ -161,5 +414,6 @@ static struct platform_driver lpc18xx_gpio_driver = {
module_platform_driver(lpc18xx_gpio_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>");
MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c
index b5b5e500e72c..31b4a091ab60 100644
--- a/drivers/gpio/gpio-lynxpoint.c
+++ b/drivers/gpio/gpio-lynxpoint.c
@@ -1,36 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* GPIO controller driver for Intel Lynxpoint PCH chipset>
* Copyright (c) 2012, Intel Corporation.
*
* Author: Mathias Nyman <mathias.nyman@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
+#include <linux/acpi.h>
#include <linux/bitops.h>
-#include <linux/interrupt.h>
#include <linux/gpio/driver.h>
-#include <linux/slab.h>
-#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/types.h>
/* LynxPoint chipset has support for 94 gpio pins */
@@ -240,21 +226,23 @@ static void lp_gpio_irq_handler(struct irq_desc *desc)
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct lp_gpio *lg = gpiochip_get_data(gc);
struct irq_chip *chip = irq_data_get_irq_chip(data);
- u32 base, pin, mask;
unsigned long reg, ena, pending;
+ u32 base, pin;
/* check from GPIO controller which pin triggered the interrupt */
for (base = 0; base < lg->chip.ngpio; base += 32) {
reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
- while ((pending = (inl(reg) & inl(ena)))) {
+ /* Only interrupts that are enabled */
+ pending = inl(reg) & inl(ena);
+
+ for_each_set_bit(pin, &pending, 32) {
unsigned irq;
- pin = __ffs(pending);
- mask = BIT(pin);
/* Clear before handling so we don't lose an edge */
- outl(mask, reg);
+ outl(BIT(pin), reg);
+
irq = irq_find_mapping(lg->chip.irq.domain, base + pin);
generic_handle_irq(irq);
}
@@ -408,8 +396,7 @@ static int lp_gpio_runtime_resume(struct device *dev)
static int lp_gpio_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct lp_gpio *lg = platform_get_drvdata(pdev);
+ struct lp_gpio *lg = dev_get_drvdata(dev);
unsigned long reg;
int i;
@@ -467,5 +454,5 @@ module_exit(lp_gpio_exit);
MODULE_AUTHOR("Mathias Nyman (Intel)");
MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:lp_gpio");
diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c
index 97421bd4a60f..7c659fdaa6d5 100644
--- a/drivers/gpio/gpio-merrifield.c
+++ b/drivers/gpio/gpio-merrifield.c
@@ -1,18 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Intel Merrifield SoC GPIO driver
*
* Copyright (c) 2016 Intel Corporation.
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.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/acpi.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
-#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c
index d72af6f6cdbd..00e954f22bc9 100644
--- a/drivers/gpio/gpio-mt7621.c
+++ b/drivers/gpio/gpio-mt7621.c
@@ -244,6 +244,8 @@ mediatek_gpio_bank_probe(struct device *dev,
rg->chip.of_xlate = mediatek_gpio_xlate;
rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank);
+ if (!rg->chip.label)
+ return -ENOMEM;
ret = devm_gpiochip_add_data(dev, &rg->chip, mtk);
if (ret < 0) {
@@ -295,6 +297,7 @@ mediatek_gpio_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct mtk *mtk;
int i;
+ int ret;
mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL);
if (!mtk)
@@ -309,8 +312,11 @@ mediatek_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mtk);
mediatek_gpio_irq_chip.name = dev_name(dev);
- for (i = 0; i < MTK_BANK_CNT; i++)
- mediatek_gpio_bank_probe(dev, np, i);
+ for (i = 0; i < MTK_BANK_CNT; i++) {
+ ret = mediatek_gpio_bank_probe(dev, np, i);
+ if (ret)
+ return ret;
+ }
return 0;
}
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index adc768f908f1..7d5c55494ccd 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -608,7 +608,7 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
ret = -EBUSY;
} else {
desc = gpiochip_request_own_desc(&mvchip->chip,
- pwm->hwpwm, "mvebu-pwm");
+ pwm->hwpwm, "mvebu-pwm", 0);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
goto out;
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 995cf0b9e0b1..2d1dfa1e0745 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -17,6 +17,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/syscore_ops.h>
#include <linux/gpio/driver.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -550,33 +551,38 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
}
-static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
+static int mxc_gpio_syscore_suspend(void)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+ struct mxc_gpio_port *port;
- mxc_gpio_save_regs(port);
- clk_disable_unprepare(port->clk);
+ /* walk through all ports */
+ list_for_each_entry(port, &mxc_gpio_ports, node) {
+ mxc_gpio_save_regs(port);
+ clk_disable_unprepare(port->clk);
+ }
return 0;
}
-static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
+static void mxc_gpio_syscore_resume(void)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+ struct mxc_gpio_port *port;
int ret;
- ret = clk_prepare_enable(port->clk);
- if (ret)
- return ret;
- mxc_gpio_restore_regs(port);
-
- return 0;
+ /* walk through all ports */
+ list_for_each_entry(port, &mxc_gpio_ports, node) {
+ ret = clk_prepare_enable(port->clk);
+ if (ret) {
+ pr_err("mxc: failed to enable gpio clock %d\n", ret);
+ return;
+ }
+ mxc_gpio_restore_regs(port);
+ }
}
-static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
+static struct syscore_ops mxc_gpio_syscore_ops = {
+ .suspend = mxc_gpio_syscore_suspend,
+ .resume = mxc_gpio_syscore_resume,
};
static struct platform_driver mxc_gpio_driver = {
@@ -584,7 +590,6 @@ static struct platform_driver mxc_gpio_driver = {
.name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids,
.suppress_bind_attrs = true,
- .pm = &mxc_gpio_dev_pm_ops,
},
.probe = mxc_gpio_probe,
.id_table = mxc_gpio_devtype,
@@ -592,6 +597,8 @@ static struct platform_driver mxc_gpio_driver = {
static int __init gpio_mxc_init(void)
{
+ register_syscore_ops(&mxc_gpio_syscore_ops);
+
return platform_driver_register(&mxc_gpio_driver);
}
subsys_initcall(gpio_mxc_init);
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index ea874fd033a5..5e5437a2c607 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -84,7 +84,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
port->both_edges &= ~pin_mask;
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
- val = port->gc.get(&port->gc, d->hwirq);
+ val = readl(port->base + PINCTRL_DIN(port)) & pin_mask;
if (val)
edge = GPIO_INT_FALL_EDGE;
else
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 5b3e83cd7137..f4e9921fa966 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -936,8 +936,7 @@ omap2_gpio_disable_level_quirk(struct gpio_bank *bank)
static int omap_mpuio_suspend_noirq(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gpio_bank *bank = platform_get_drvdata(pdev);
+ struct gpio_bank *bank = dev_get_drvdata(dev);
void __iomem *mask_reg = bank->base +
OMAP_MPUIO_GPIO_MASKIT / bank->stride;
unsigned long flags;
@@ -951,8 +950,7 @@ static int omap_mpuio_suspend_noirq(struct device *dev)
static int omap_mpuio_resume_noirq(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gpio_bank *bank = platform_get_drvdata(pdev);
+ struct gpio_bank *bank = dev_get_drvdata(dev);
void __iomem *mask_reg = bank->base +
OMAP_MPUIO_GPIO_MASKIT / bank->stride;
unsigned long flags;
@@ -1635,8 +1633,7 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gpio_bank *bank = platform_get_drvdata(pdev);
+ struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags;
int error = 0;
@@ -1656,8 +1653,7 @@ unlock:
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct gpio_bank *bank = platform_get_drvdata(pdev);
+ struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags;
int error = 0;
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 540166443c34..83617fdc661d 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_data/pca953x.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -30,6 +31,8 @@
#define PCA953X_INVERT 0x02
#define PCA953X_DIRECTION 0x03
+#define REG_ADDR_MASK 0x3f
+#define REG_ADDR_EXT 0x40
#define REG_ADDR_AI 0x80
#define PCA957X_IN 0x00
@@ -58,7 +61,7 @@
#define PCA_GPIO_MASK 0x00FF
#define PCAL_GPIO_MASK 0x1f
-#define PCAL_PINCTRL_MASK 0xe0
+#define PCAL_PINCTRL_MASK 0x60
#define PCA_INT 0x0100
#define PCA_PCAL 0x0200
@@ -119,25 +122,27 @@ struct pca953x_reg_config {
int direction;
int output;
int input;
+ int invert;
};
static const struct pca953x_reg_config pca953x_regs = {
.direction = PCA953X_DIRECTION,
.output = PCA953X_OUTPUT,
.input = PCA953X_INPUT,
+ .invert = PCA953X_INVERT,
};
static const struct pca953x_reg_config pca957x_regs = {
.direction = PCA957X_CFG,
.output = PCA957X_OUT,
.input = PCA957X_IN,
+ .invert = PCA957X_INVRT,
};
struct pca953x_chip {
unsigned gpio_start;
- u8 reg_output[MAX_BANK];
- u8 reg_direction[MAX_BANK];
struct mutex i2c_lock;
+ struct regmap *regmap;
#ifdef CONFIG_GPIO_PCA953X_IRQ
struct mutex irq_lock;
@@ -154,87 +159,177 @@ struct pca953x_chip {
struct regulator *regulator;
const struct pca953x_reg_config *regs;
-
- int (*write_regs)(struct pca953x_chip *, int, u8 *);
- int (*read_regs)(struct pca953x_chip *, int, u8 *);
};
-static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
- int off)
+static int pca953x_bank_shift(struct pca953x_chip *chip)
{
- int ret;
- int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
- int offset = off / BANK_SZ;
+ return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+}
- ret = i2c_smbus_read_byte_data(chip->client,
- (reg << bank_shift) + offset);
- *val = ret;
+#define PCA953x_BANK_INPUT BIT(0)
+#define PCA953x_BANK_OUTPUT BIT(1)
+#define PCA953x_BANK_POLARITY BIT(2)
+#define PCA953x_BANK_CONFIG BIT(3)
- if (ret < 0) {
- dev_err(&chip->client->dev, "failed reading register\n");
- return ret;
+#define PCA957x_BANK_INPUT BIT(0)
+#define PCA957x_BANK_POLARITY BIT(1)
+#define PCA957x_BANK_BUSHOLD BIT(2)
+#define PCA957x_BANK_CONFIG BIT(4)
+#define PCA957x_BANK_OUTPUT BIT(5)
+
+#define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2)
+#define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5)
+#define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6)
+
+/*
+ * We care about the following registers:
+ * - Standard set, below 0x40, each port can be replicated up to 8 times
+ * - PCA953x standard
+ * Input port 0x00 + 0 * bank_size R
+ * Output port 0x00 + 1 * bank_size RW
+ * Polarity Inversion port 0x00 + 2 * bank_size RW
+ * Configuration port 0x00 + 3 * bank_size RW
+ * - PCA957x with mixed up registers
+ * Input port 0x00 + 0 * bank_size R
+ * Polarity Inversion port 0x00 + 1 * bank_size RW
+ * Bus hold port 0x00 + 2 * bank_size RW
+ * Configuration port 0x00 + 4 * bank_size RW
+ * Output port 0x00 + 5 * bank_size RW
+ *
+ * - Extended set, above 0x40, often chip specific.
+ * - PCAL6524/PCAL9555A with custom PCAL IRQ handling:
+ * Input latch register 0x40 + 2 * bank_size RW
+ * Interrupt mask register 0x40 + 5 * bank_size RW
+ * Interrupt status register 0x40 + 6 * bank_size R
+ *
+ * - Registers with bit 0x80 set, the AI bit
+ * The bit is cleared and the registers fall into one of the
+ * categories above.
+ */
+
+static bool pca953x_check_register(struct pca953x_chip *chip, unsigned int reg,
+ u32 checkbank)
+{
+ int bank_shift = pca953x_bank_shift(chip);
+ int bank = (reg & REG_ADDR_MASK) >> bank_shift;
+ int offset = reg & (BIT(bank_shift) - 1);
+
+ /* Special PCAL extended register check. */
+ if (reg & REG_ADDR_EXT) {
+ if (!(chip->driver_data & PCA_PCAL))
+ return false;
+ bank += 8;
}
- return 0;
+ /* Register is not in the matching bank. */
+ if (!(BIT(bank) & checkbank))
+ return false;
+
+ /* Register is not within allowed range of bank. */
+ if (offset >= NBANK(chip))
+ return false;
+
+ return true;
}
-static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
- int off)
+static bool pca953x_readable_register(struct device *dev, unsigned int reg)
{
- int ret;
- int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
- int offset = off / BANK_SZ;
+ struct pca953x_chip *chip = dev_get_drvdata(dev);
+ u32 bank;
- ret = i2c_smbus_write_byte_data(chip->client,
- (reg << bank_shift) + offset, val);
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
+ bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT |
+ PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG;
+ } else {
+ bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT |
+ PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG |
+ PCA957x_BANK_BUSHOLD;
+ }
- if (ret < 0) {
- dev_err(&chip->client->dev, "failed writing register\n");
- return ret;
+ if (chip->driver_data & PCA_PCAL) {
+ bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK |
+ PCAL9xxx_BANK_IRQ_STAT;
}
- return 0;
+ return pca953x_check_register(chip, reg, bank);
}
-static int pca953x_write_regs_8(struct pca953x_chip *chip, int reg, u8 *val)
+static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
{
- return i2c_smbus_write_byte_data(chip->client, reg, *val);
-}
+ struct pca953x_chip *chip = dev_get_drvdata(dev);
+ u32 bank;
-static int pca953x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val)
-{
- u16 word = get_unaligned((u16 *)val);
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
+ bank = PCA953x_BANK_OUTPUT | PCA953x_BANK_POLARITY |
+ PCA953x_BANK_CONFIG;
+ } else {
+ bank = PCA957x_BANK_OUTPUT | PCA957x_BANK_POLARITY |
+ PCA957x_BANK_CONFIG | PCA957x_BANK_BUSHOLD;
+ }
- return i2c_smbus_write_word_data(chip->client, reg << 1, word);
+ if (chip->driver_data & PCA_PCAL)
+ bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK;
+
+ return pca953x_check_register(chip, reg, bank);
}
-static int pca957x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val)
+static bool pca953x_volatile_register(struct device *dev, unsigned int reg)
{
- int ret;
+ struct pca953x_chip *chip = dev_get_drvdata(dev);
+ u32 bank;
- ret = i2c_smbus_write_byte_data(chip->client, reg << 1, val[0]);
- if (ret < 0)
- return ret;
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
+ bank = PCA953x_BANK_INPUT;
+ else
+ bank = PCA957x_BANK_INPUT;
+
+ if (chip->driver_data & PCA_PCAL)
+ bank |= PCAL9xxx_BANK_IRQ_STAT;
- return i2c_smbus_write_byte_data(chip->client, (reg << 1) + 1, val[1]);
+ return pca953x_check_register(chip, reg, bank);
}
-static int pca953x_write_regs_24(struct pca953x_chip *chip, int reg, u8 *val)
+const struct regmap_config pca953x_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = pca953x_readable_register,
+ .writeable_reg = pca953x_writeable_register,
+ .volatile_reg = pca953x_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 0x7f,
+};
+
+static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off,
+ bool write, bool addrinc)
{
- int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
+ int bank_shift = pca953x_bank_shift(chip);
int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
+ u8 regaddr = pinctrl | addr | (off / BANK_SZ);
+
+ /* Single byte read doesn't need AI bit set. */
+ if (!addrinc)
+ return regaddr;
+
+ /* Chips with 24 and more GPIOs always support Auto Increment */
+ if (write && NBANK(chip) > 2)
+ regaddr |= REG_ADDR_AI;
- return i2c_smbus_write_i2c_block_data(chip->client,
- pinctrl | addr | REG_ADDR_AI,
- NBANK(chip), val);
+ /* PCA9575 needs address-increment on multi-byte writes */
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE)
+ regaddr |= REG_ADDR_AI;
+
+ return regaddr;
}
static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
{
- int ret = 0;
+ u8 regaddr = pca953x_recalc_addr(chip, reg, 0, true, true);
+ int ret;
- ret = chip->write_regs(chip, reg, val);
+ ret = regmap_bulk_write(chip->regmap, regaddr, val, NBANK(chip));
if (ret < 0) {
dev_err(&chip->client->dev, "failed writing register\n");
return ret;
@@ -243,42 +338,12 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
return 0;
}
-static int pca953x_read_regs_8(struct pca953x_chip *chip, int reg, u8 *val)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(chip->client, reg);
- *val = ret;
-
- return ret;
-}
-
-static int pca953x_read_regs_16(struct pca953x_chip *chip, int reg, u8 *val)
-{
- int ret;
-
- ret = i2c_smbus_read_word_data(chip->client, reg << 1);
- put_unaligned(ret, (u16 *)val);
-
- return ret;
-}
-
-static int pca953x_read_regs_24(struct pca953x_chip *chip, int reg, u8 *val)
-{
- int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
- int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
- int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
-
- return i2c_smbus_read_i2c_block_data(chip->client,
- pinctrl | addr | REG_ADDR_AI,
- NBANK(chip), val);
-}
-
static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
{
+ u8 regaddr = pca953x_recalc_addr(chip, reg, 0, false, true);
int ret;
- ret = chip->read_regs(chip, reg, val);
+ ret = regmap_bulk_read(chip->regmap, regaddr, val, NBANK(chip));
if (ret < 0) {
dev_err(&chip->client->dev, "failed reading register\n");
return ret;
@@ -290,18 +355,13 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 reg_val;
+ u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
+ true, false);
+ u8 bit = BIT(off % BANK_SZ);
int ret;
mutex_lock(&chip->i2c_lock);
- reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ));
-
- ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off);
- if (ret)
- goto exit;
-
- chip->reg_direction[off / BANK_SZ] = reg_val;
-exit:
+ ret = regmap_write_bits(chip->regmap, dirreg, bit, bit);
mutex_unlock(&chip->i2c_lock);
return ret;
}
@@ -310,31 +370,21 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 reg_val;
+ u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
+ true, false);
+ u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
+ true, false);
+ u8 bit = BIT(off % BANK_SZ);
int ret;
mutex_lock(&chip->i2c_lock);
/* set output level */
- if (val)
- reg_val = chip->reg_output[off / BANK_SZ]
- | (1u << (off % BANK_SZ));
- else
- reg_val = chip->reg_output[off / BANK_SZ]
- & ~(1u << (off % BANK_SZ));
-
- ret = pca953x_write_single(chip, chip->regs->output, reg_val, off);
+ ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
if (ret)
goto exit;
- chip->reg_output[off / BANK_SZ] = reg_val;
-
/* then direction */
- reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
- ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off);
- if (ret)
- goto exit;
-
- chip->reg_direction[off / BANK_SZ] = reg_val;
+ ret = regmap_write_bits(chip->regmap, dirreg, bit, 0);
exit:
mutex_unlock(&chip->i2c_lock);
return ret;
@@ -343,11 +393,14 @@ exit:
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
+ u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off,
+ true, false);
+ u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
mutex_lock(&chip->i2c_lock);
- ret = pca953x_read_single(chip, chip->regs->input, &reg_val, off);
+ ret = regmap_read(chip->regmap, inreg, &reg_val);
mutex_unlock(&chip->i2c_lock);
if (ret < 0) {
/* NOTE: diagnostic already emitted; that's all we should
@@ -357,45 +410,37 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
return 0;
}
- return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0;
+ return !!(reg_val & bit);
}
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
- u8 reg_val;
- int ret;
+ u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
+ true, false);
+ u8 bit = BIT(off % BANK_SZ);
mutex_lock(&chip->i2c_lock);
- if (val)
- reg_val = chip->reg_output[off / BANK_SZ]
- | (1u << (off % BANK_SZ));
- else
- reg_val = chip->reg_output[off / BANK_SZ]
- & ~(1u << (off % BANK_SZ));
-
- ret = pca953x_write_single(chip, chip->regs->output, reg_val, off);
- if (ret)
- goto exit;
-
- chip->reg_output[off / BANK_SZ] = reg_val;
-exit:
+ regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
mutex_unlock(&chip->i2c_lock);
}
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
+ u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
+ true, false);
+ u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
mutex_lock(&chip->i2c_lock);
- ret = pca953x_read_single(chip, chip->regs->direction, &reg_val, off);
+ ret = regmap_read(chip->regmap, dirreg, &reg_val);
mutex_unlock(&chip->i2c_lock);
if (ret < 0)
return ret;
- return !!(reg_val & (1u << (off % BANK_SZ)));
+ return !!(reg_val & bit);
}
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
@@ -403,14 +448,15 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
unsigned int bank_mask, bank_val;
- int bank_shift, bank;
+ int bank;
u8 reg_val[MAX_BANK];
int ret;
- bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
-
mutex_lock(&chip->i2c_lock);
- memcpy(reg_val, chip->reg_output, NBANK(chip));
+ ret = pca953x_read_regs(chip, chip->regs->output, reg_val);
+ if (ret)
+ goto exit;
+
for (bank = 0; bank < NBANK(chip); bank++) {
bank_mask = mask[bank / sizeof(*mask)] >>
((bank % sizeof(*mask)) * 8);
@@ -422,13 +468,7 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
}
}
- ret = i2c_smbus_write_i2c_block_data(chip->client,
- chip->regs->output << bank_shift,
- NBANK(chip), reg_val);
- if (ret)
- goto exit;
-
- memcpy(chip->reg_output, reg_val, NBANK(chip));
+ pca953x_write_regs(chip, chip->regs->output, reg_val);
exit:
mutex_unlock(&chip->i2c_lock);
}
@@ -487,6 +527,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
u8 new_irqs;
int level, i;
u8 invert_irq_mask[MAX_BANK];
+ int reg_direction[MAX_BANK];
+
+ regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
+ NBANK(chip));
if (chip->driver_data & PCA_PCAL) {
/* Enable latch on interrupt-enabled inputs */
@@ -502,7 +546,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
/* Look for any newly setup interrupt */
for (i = 0; i < NBANK(chip); i++) {
new_irqs = chip->irq_trig_fall[i] | chip->irq_trig_raise[i];
- new_irqs &= ~chip->reg_direction[i];
+ new_irqs &= reg_direction[i];
while (new_irqs) {
level = __ffs(new_irqs);
@@ -567,6 +611,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
bool pending_seen = false;
bool trigger_seen = false;
u8 trigger[MAX_BANK];
+ int reg_direction[MAX_BANK];
int ret, i;
if (chip->driver_data & PCA_PCAL) {
@@ -597,8 +642,10 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
return false;
/* Remove output pins from the equation */
+ regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
+ NBANK(chip));
for (i = 0; i < NBANK(chip); i++)
- cur_stat[i] &= chip->reg_direction[i];
+ cur_stat[i] &= reg_direction[i];
memcpy(old_stat, chip->irq_stat, NBANK(chip));
@@ -652,6 +699,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
int irq_base)
{
struct i2c_client *client = chip->client;
+ int reg_direction[MAX_BANK];
int ret, i;
if (client->irq && irq_base != -1
@@ -666,8 +714,10 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
* interrupt. We have to rely on the previous read for
* this purpose.
*/
+ regmap_bulk_read(chip->regmap, chip->regs->direction,
+ reg_direction, NBANK(chip));
for (i = 0; i < NBANK(chip); i++)
- chip->irq_stat[i] &= chip->reg_direction[i];
+ chip->irq_stat[i] &= reg_direction[i];
mutex_init(&chip->irq_lock);
ret = devm_request_threaded_irq(&client->dev,
@@ -715,20 +765,19 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
}
#endif
-static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
+static int device_pca95xx_init(struct pca953x_chip *chip, u32 invert)
{
int ret;
u8 val[MAX_BANK];
- chip->regs = &pca953x_regs;
-
- ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output);
- if (ret)
+ ret = regcache_sync_region(chip->regmap, chip->regs->output,
+ chip->regs->output + NBANK(chip));
+ if (ret != 0)
goto out;
- ret = pca953x_read_regs(chip, chip->regs->direction,
- chip->reg_direction);
- if (ret)
+ ret = regcache_sync_region(chip->regmap, chip->regs->direction,
+ chip->regs->direction + NBANK(chip));
+ if (ret != 0)
goto out;
/* set platform specific polarity inversion */
@@ -737,7 +786,7 @@ static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
else
memset(val, 0, NBANK(chip));
- ret = pca953x_write_regs(chip, PCA953X_INVERT, val);
+ ret = pca953x_write_regs(chip, chip->regs->invert, val);
out:
return ret;
}
@@ -747,22 +796,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
int ret;
u8 val[MAX_BANK];
- chip->regs = &pca957x_regs;
-
- ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output);
- if (ret)
- goto out;
- ret = pca953x_read_regs(chip, chip->regs->direction,
- chip->reg_direction);
- if (ret)
- goto out;
-
- /* set platform specific polarity inversion */
- if (invert)
- memset(val, 0xFF, NBANK(chip));
- else
- memset(val, 0, NBANK(chip));
- ret = pca953x_write_regs(chip, PCA957X_INVRT, val);
+ ret = device_pca95xx_init(chip, invert);
if (ret)
goto out;
@@ -853,6 +887,16 @@ static int pca953x_probe(struct i2c_client *client,
}
}
+ i2c_set_clientdata(client, chip);
+
+ chip->regmap = devm_regmap_init_i2c(client, &pca953x_i2c_regmap);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ goto err_exit;
+ }
+
+ regcache_mark_dirty(chip->regmap);
+
mutex_init(&chip->i2c_lock);
/*
* In case we have an i2c-mux controlled by a GPIO provided by an
@@ -878,24 +922,13 @@ static int pca953x_probe(struct i2c_client *client,
*/
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
- if (chip->gpio_chip.ngpio <= 8) {
- chip->write_regs = pca953x_write_regs_8;
- chip->read_regs = pca953x_read_regs_8;
- } else if (chip->gpio_chip.ngpio >= 24) {
- chip->write_regs = pca953x_write_regs_24;
- chip->read_regs = pca953x_read_regs_24;
+ if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
+ chip->regs = &pca953x_regs;
+ ret = device_pca95xx_init(chip, invert);
} else {
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
- chip->write_regs = pca953x_write_regs_16;
- else
- chip->write_regs = pca957x_write_regs_16;
- chip->read_regs = pca953x_read_regs_16;
- }
-
- if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
- ret = device_pca953x_init(chip, invert);
- else
+ chip->regs = &pca957x_regs;
ret = device_pca957x_init(chip, invert);
+ }
if (ret)
goto err_exit;
@@ -914,7 +947,6 @@ static int pca953x_probe(struct i2c_client *client,
dev_warn(&client->dev, "setup failed, %d\n", ret);
}
- i2c_set_clientdata(client, chip);
return 0;
err_exit:
@@ -943,6 +975,91 @@ static int pca953x_remove(struct i2c_client *client)
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static int pca953x_regcache_sync(struct device *dev)
+{
+ struct pca953x_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ /*
+ * The ordering between direction and output is important,
+ * sync these registers first and only then sync the rest.
+ */
+ ret = regcache_sync_region(chip->regmap, chip->regs->direction,
+ chip->regs->direction + NBANK(chip));
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = regcache_sync_region(chip->regmap, chip->regs->output,
+ chip->regs->output + NBANK(chip));
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret);
+ return ret;
+ }
+
+#ifdef CONFIG_GPIO_PCA953X_IRQ
+ if (chip->driver_data & PCA_PCAL) {
+ ret = regcache_sync_region(chip->regmap, PCAL953X_IN_LATCH,
+ PCAL953X_IN_LATCH + NBANK(chip));
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync INT latch registers: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regcache_sync_region(chip->regmap, PCAL953X_INT_MASK,
+ PCAL953X_INT_MASK + NBANK(chip));
+ if (ret != 0) {
+ dev_err(dev, "Failed to sync INT mask registers: %d\n",
+ ret);
+ return ret;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+static int pca953x_suspend(struct device *dev)
+{
+ struct pca953x_chip *chip = dev_get_drvdata(dev);
+
+ regcache_cache_only(chip->regmap, true);
+
+ regulator_disable(chip->regulator);
+
+ return 0;
+}
+
+static int pca953x_resume(struct device *dev)
+{
+ struct pca953x_chip *chip = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(chip->regulator);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable regulator: %d\n", ret);
+ return 0;
+ }
+
+ regcache_cache_only(chip->regmap, false);
+ regcache_mark_dirty(chip->regmap);
+ ret = pca953x_regcache_sync(dev);
+ if (ret)
+ return ret;
+
+ ret = regcache_sync(chip->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register map: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
/* convenience to stop overlong match-table lines */
#define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int)
#define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
@@ -986,9 +1103,12 @@ static const struct of_device_id pca953x_dt_ids[] = {
MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
+static SIMPLE_DEV_PM_OPS(pca953x_pm_ops, pca953x_suspend, pca953x_resume);
+
static struct i2c_driver pca953x_driver = {
.driver = {
.name = "pca953x",
+ .pm = &pca953x_pm_ops,
.of_match_table = pca953x_dt_ids,
.acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
},
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index ffce0ab912ed..ee79e5f88b5a 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
- *
- * 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; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
#include <linux/slab.h>
#define PCH_EDGE_FALLING 0
@@ -171,11 +159,10 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
return 0;
}
-#ifdef CONFIG_PM
/*
* Save register configuration and disable interrupts.
*/
-static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
+static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip)
{
chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
@@ -185,14 +172,13 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
if (chip->ioh == INTEL_EG20T_PCH)
chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1);
if (chip->ioh == OKISEMI_ML7223n_IOH)
- chip->pch_gpio_reg.gpio_use_sel_reg =\
- ioread32(&chip->reg->gpio_use_sel);
+ chip->pch_gpio_reg.gpio_use_sel_reg = ioread32(&chip->reg->gpio_use_sel);
}
/*
* This function restores the register configuration of the GPIO device.
*/
-static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
+static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip)
{
iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
@@ -204,10 +190,8 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
if (chip->ioh == INTEL_EG20T_PCH)
iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1);
if (chip->ioh == OKISEMI_ML7223n_IOH)
- iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
- &chip->reg->gpio_use_sel);
+ iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, &chip->reg->gpio_use_sel);
}
-#endif
static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
{
@@ -226,7 +210,6 @@ static void pch_gpio_setup(struct pch_gpio *chip)
gpio->get = pch_gpio_get;
gpio->direction_output = pch_gpio_direction_output;
gpio->set = pch_gpio_set;
- gpio->dbg_show = NULL;
gpio->base = -1;
gpio->ngpio = gpio_pins[chip->ioh];
gpio->can_sleep = false;
@@ -250,8 +233,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)
im_reg = &chip->reg->im1;
im_pos = ch - 8;
}
- dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n",
- __func__, irq, type, ch, im_pos);
+ dev_dbg(chip->dev, "irq=%d type=%d ch=%d pos=%d\n", irq, type, ch, im_pos);
spin_lock_irqsave(&chip->spinlock, flags);
@@ -317,16 +299,13 @@ static void pch_irq_ack(struct irq_data *d)
static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
{
struct pch_gpio *chip = dev_id;
- u32 reg_val = ioread32(&chip->reg->istatus);
+ unsigned long reg_val = ioread32(&chip->reg->istatus);
int i, ret = IRQ_NONE;
- for (i = 0; i < gpio_pins[chip->ioh]; i++) {
- if (reg_val & BIT(i)) {
- dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n",
- __func__, i, irq, reg_val);
- generic_handle_irq(chip->irq_base + i);
- ret = IRQ_HANDLED;
- }
+ for_each_set_bit(i, &reg_val, gpio_pins[chip->ioh]) {
+ dev_dbg(chip->dev, "[%d]:irq=%d status=0x%lx\n", i, irq, reg_val);
+ generic_handle_irq(chip->irq_base + i);
+ ret = IRQ_HANDLED;
}
return ret;
}
@@ -367,29 +346,24 @@ static int pch_gpio_probe(struct pci_dev *pdev,
int irq_base;
u32 msk;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->dev = &pdev->dev;
- ret = pci_enable_device(pdev);
+ ret = pcim_enable_device(pdev);
if (ret) {
- dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__);
- goto err_pci_enable;
+ dev_err(&pdev->dev, "pci_enable_device FAILED");
+ return ret;
}
- ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ ret = pcim_iomap_regions(pdev, 1 << 1, KBUILD_MODNAME);
if (ret) {
dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
- goto err_request_regions;
+ return ret;
}
- chip->base = pci_iomap(pdev, 1, 0);
- if (!chip->base) {
- dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__);
- ret = -ENOMEM;
- goto err_iomap;
- }
+ chip->base = pcim_iomap_table(pdev)[1];
if (pdev->device == 0x8803)
chip->ioh = INTEL_EG20T_PCH;
@@ -402,13 +376,11 @@ static int pch_gpio_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, chip);
spin_lock_init(&chip->spinlock);
pch_gpio_setup(chip);
-#ifdef CONFIG_OF_GPIO
- chip->gpio.of_node = pdev->dev.of_node;
-#endif
- ret = gpiochip_add_data(&chip->gpio, chip);
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip);
if (ret) {
dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n");
- goto err_gpiochip_add;
+ return ret;
}
irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0,
@@ -416,7 +388,7 @@ static int pch_gpio_probe(struct pci_dev *pdev,
if (irq_base < 0) {
dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n");
chip->irq_base = -1;
- goto end;
+ return 0;
}
chip->irq_base = irq_base;
@@ -427,53 +399,17 @@ static int pch_gpio_probe(struct pci_dev *pdev,
ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler,
IRQF_SHARED, KBUILD_MODNAME, chip);
- if (ret != 0) {
- dev_err(&pdev->dev,
- "%s request_irq failed\n", __func__);
- goto err_request_irq;
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ return ret;
}
- ret = pch_gpio_alloc_generic_chip(chip, irq_base,
- gpio_pins[chip->ioh]);
- if (ret)
- goto err_request_irq;
-
-end:
- return 0;
-
-err_request_irq:
- gpiochip_remove(&chip->gpio);
-
-err_gpiochip_add:
- pci_iounmap(pdev, chip->base);
-
-err_iomap:
- pci_release_regions(pdev);
-
-err_request_regions:
- pci_disable_device(pdev);
-
-err_pci_enable:
- kfree(chip);
- dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret);
- return ret;
-}
-
-static void pch_gpio_remove(struct pci_dev *pdev)
-{
- struct pch_gpio *chip = pci_get_drvdata(pdev);
-
- gpiochip_remove(&chip->gpio);
- pci_iounmap(pdev, chip->base);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- kfree(chip);
+ return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
}
-#ifdef CONFIG_PM
-static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
+static int __maybe_unused pch_gpio_suspend(struct device *dev)
{
- s32 ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct pch_gpio *chip = pci_get_drvdata(pdev);
unsigned long flags;
@@ -481,36 +417,15 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
pch_gpio_save_reg_conf(chip);
spin_unlock_irqrestore(&chip->spinlock, flags);
- ret = pci_save_state(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret);
- return ret;
- }
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D0);
- ret = pci_enable_wake(pdev, PCI_D0, 1);
- if (ret)
- dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret);
-
return 0;
}
-static int pch_gpio_resume(struct pci_dev *pdev)
+static int __maybe_unused pch_gpio_resume(struct device *dev)
{
- s32 ret;
+ struct pci_dev *pdev = to_pci_dev(dev);
struct pch_gpio *chip = pci_get_drvdata(pdev);
unsigned long flags;
- ret = pci_enable_wake(pdev, PCI_D0, 0);
-
- pci_set_power_state(pdev, PCI_D0);
- ret = pci_enable_device(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret);
- return ret;
- }
- pci_restore_state(pdev);
-
spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->reset);
iowrite32(0x00, &chip->reg->reset);
@@ -519,10 +434,8 @@ static int pch_gpio_resume(struct pci_dev *pdev)
return 0;
}
-#else
-#define pch_gpio_suspend NULL
-#define pch_gpio_resume NULL
-#endif
+
+static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
#define PCI_VENDOR_ID_ROHM 0x10DB
static const struct pci_device_id pch_gpio_pcidev_id[] = {
@@ -538,12 +451,12 @@ static struct pci_driver pch_gpio_driver = {
.name = "pch_gpio",
.id_table = pch_gpio_pcidev_id,
.probe = pch_gpio_probe,
- .remove = pch_gpio_remove,
- .suspend = pch_gpio_suspend,
- .resume = pch_gpio_resume
+ .driver = {
+ .pm = &pch_gpio_pm_ops,
+ },
};
module_pci_driver(pch_gpio_driver);
MODULE_DESCRIPTION("PCH GPIO PCI Driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c
index 25d16b2af1c3..6b7349783223 100644
--- a/drivers/gpio/gpio-pci-idio-16.c
+++ b/drivers/gpio/gpio-pci-idio-16.c
@@ -146,7 +146,7 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
port_state = ioread8(ports[i]);
/* store acquired bits at respective bits array offset */
- bits[word_index] |= port_state << word_offset;
+ bits[word_index] |= (port_state << word_offset) & word_mask;
}
return 0;
diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index f953541e7890..52f1647a46fd 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -243,7 +243,7 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
/* store acquired bits at respective bits array offset */
- bits[word_index] |= port_state << word_offset;
+ bits[word_index] |= (port_state << word_offset) & word_mask;
}
return 0;
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 2afd9de84a0d..dc42571e6fdc 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -54,6 +54,7 @@ struct pl061 {
void __iomem *base;
struct gpio_chip gc;
+ struct irq_chip irq_chip;
int parent_irq;
#ifdef CONFIG_PM
@@ -281,15 +282,6 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
return irq_set_irq_wake(pl061->parent_irq, state);
}
-static struct irq_chip pl061_irqchip = {
- .name = "pl061",
- .irq_ack = pl061_irq_ack,
- .irq_mask = pl061_irq_mask,
- .irq_unmask = pl061_irq_unmask,
- .irq_set_type = pl061_irq_type,
- .irq_set_wake = pl061_irq_set_wake,
-};
-
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
@@ -328,6 +320,13 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
/*
* irq_chip support
*/
+ pl061->irq_chip.name = dev_name(dev);
+ pl061->irq_chip.irq_ack = pl061_irq_ack;
+ pl061->irq_chip.irq_mask = pl061_irq_mask;
+ pl061->irq_chip.irq_unmask = pl061_irq_unmask;
+ pl061->irq_chip.irq_set_type = pl061_irq_type;
+ pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
+
writeb(0, pl061->base + GPIOIE); /* disable irqs */
irq = adev->irq[0];
if (irq < 0) {
@@ -336,14 +335,14 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
}
pl061->parent_irq = irq;
- ret = gpiochip_irqchip_add(&pl061->gc, &pl061_irqchip,
+ ret = gpiochip_irqchip_add(&pl061->gc, &pl061->irq_chip,
0, handle_bad_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_info(&adev->dev, "could not add irqchip\n");
return ret;
}
- gpiochip_set_chained_irqchip(&pl061->gc, &pl061_irqchip,
+ gpiochip_set_chained_irqchip(&pl061->gc, &pl061->irq_chip,
irq, pl061_irq_handler);
amba_set_drvdata(adev, pl061);
diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c
index d6d36d537e37..b77ea16ffa03 100644
--- a/drivers/gpio/gpio-raspberrypi-exp.c
+++ b/drivers/gpio/gpio-raspberrypi-exp.c
@@ -206,6 +206,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
}
fw = rpi_firmware_get(fw_node);
+ of_node_put(fw_node);
if (!fw)
return -EPROBE_DEFER;
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 3c82bb3c2030..068ce25ffd28 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car GPIO Support
*
* Copyright (C) 2014 Renesas Electronics Corporation
* Copyright (C) 2013 Magnus Damm
- *
- * 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
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/err.h>
@@ -43,7 +35,7 @@ struct gpio_rcar_bank_info {
struct gpio_rcar_priv {
void __iomem *base;
spinlock_t lock;
- struct platform_device *pdev;
+ struct device *dev;
struct gpio_chip gpio_chip;
struct irq_chip irq_chip;
unsigned int irq_parent;
@@ -148,7 +140,7 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
struct gpio_rcar_priv *p = gpiochip_get_data(gc);
unsigned int hwirq = irqd_to_hwirq(d);
- dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type);
+ dev_dbg(p->dev, "sense irq = %d, type = %d\n", hwirq, type);
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_LEVEL_HIGH:
@@ -188,8 +180,7 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
if (p->irq_parent) {
error = irq_set_irq_wake(p->irq_parent, on);
if (error) {
- dev_dbg(&p->pdev->dev,
- "irq %u doesn't support irq_set_wake\n",
+ dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n",
p->irq_parent);
p->irq_parent = 0;
}
@@ -252,13 +243,13 @@ static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
struct gpio_rcar_priv *p = gpiochip_get_data(chip);
int error;
- error = pm_runtime_get_sync(&p->pdev->dev);
+ error = pm_runtime_get_sync(p->dev);
if (error < 0)
return error;
error = pinctrl_gpio_request(chip->base + offset);
if (error)
- pm_runtime_put(&p->pdev->dev);
+ pm_runtime_put(p->dev);
return error;
}
@@ -275,7 +266,7 @@ static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset)
*/
gpio_rcar_config_general_input_output_mode(chip, offset, false);
- pm_runtime_put(&p->pdev->dev);
+ pm_runtime_put(p->dev);
}
static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset)
@@ -406,21 +397,20 @@ MODULE_DEVICE_TABLE(of, gpio_rcar_of_table);
static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
{
- struct device_node *np = p->pdev->dev.of_node;
+ struct device_node *np = p->dev->of_node;
const struct gpio_rcar_info *info;
struct of_phandle_args args;
int ret;
- info = of_device_get_match_data(&p->pdev->dev);
+ info = of_device_get_match_data(p->dev);
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
*npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
p->has_both_edge_trigger = info->has_both_edge_trigger;
if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) {
- dev_warn(&p->pdev->dev,
- "Invalid number of gpio lines %u, using %u\n", *npins,
- RCAR_MAX_GPIO_PER_BANK);
+ dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",
+ *npins, RCAR_MAX_GPIO_PER_BANK);
*npins = RCAR_MAX_GPIO_PER_BANK;
}
@@ -442,7 +432,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
if (!p)
return -ENOMEM;
- p->pdev = pdev;
+ p->dev = dev;
spin_lock_init(&p->lock);
/* Get device configuration from DT node */
diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c
new file mode 100644
index 000000000000..03a000659fa1
--- /dev/null
+++ b/drivers/gpio/gpio-sama5d2-piobu.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SAMA5D2 PIOBU GPIO controller
+ *
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Andrei Stefanescu <andrei.stefanescu@microchip.com>
+ *
+ */
+#include <linux/bits.h>
+#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define PIOBU_NUM 8
+#define PIOBU_REG_SIZE 4
+
+/*
+ * backup mode protection register for tamper detection
+ * normal mode protection register for tamper detection
+ * wakeup signal generation
+ */
+#define PIOBU_BMPR 0x7C
+#define PIOBU_NMPR 0x80
+#define PIOBU_WKPR 0x90
+
+#define PIOBU_BASE 0x18 /* PIOBU offset from SECUMOD base register address. */
+
+#define PIOBU_DET_OFFSET 16
+
+/* In the datasheet this bit is called OUTPUT */
+#define PIOBU_DIRECTION BIT(8)
+#define PIOBU_OUT BIT(8)
+#define PIOBU_IN 0
+
+#define PIOBU_SOD BIT(9)
+#define PIOBU_PDS BIT(10)
+
+#define PIOBU_HIGH BIT(9)
+#define PIOBU_LOW 0
+
+struct sama5d2_piobu {
+ struct gpio_chip chip;
+ struct regmap *regmap;
+};
+
+/**
+ * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call
+ *
+ * Do not consider pin for tamper detection (normal and backup modes)
+ * Do not consider pin as tamper wakeup interrupt source
+ */
+static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
+{
+ int ret;
+ struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
+ chip);
+ unsigned int mask = BIT(PIOBU_DET_OFFSET + pin);
+
+ ret = regmap_update_bits(piobu->regmap, PIOBU_BMPR, mask, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(piobu->regmap, PIOBU_NMPR, mask, 0);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0);
+}
+
+/**
+ * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register
+ */
+static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
+ unsigned int mask, unsigned int value)
+{
+ int reg;
+ struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
+ chip);
+
+ reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
+
+ return regmap_update_bits(piobu->regmap, reg, mask, value);
+}
+
+/**
+ * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU
+ * register
+ */
+static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
+ unsigned int mask)
+{
+ struct sama5d2_piobu *piobu = container_of(chip, struct sama5d2_piobu,
+ chip);
+ unsigned int val, reg;
+ int ret;
+
+ reg = PIOBU_BASE + pin * PIOBU_REG_SIZE;
+ ret = regmap_read(piobu->regmap, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ return val & mask;
+}
+
+/**
+ * sama5d2_piobu_set_direction() - mark pin as input or output
+ */
+static int sama5d2_piobu_set_direction(struct gpio_chip *chip,
+ unsigned int direction,
+ unsigned int pin)
+{
+ return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, direction);
+}
+
+/**
+ * sama5d2_piobu_get_direction() - gpiochip get_direction
+ */
+static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
+ unsigned int pin)
+{
+ int ret = sama5d2_piobu_read_value(chip, pin, PIOBU_DIRECTION);
+
+ if (ret < 0)
+ return ret;
+
+ return (ret == PIOBU_IN) ? 1 : 0;
+}
+
+/**
+ * sama5d2_piobu_direction_input() - gpiochip direction_input
+ */
+static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
+ unsigned int pin)
+{
+ return sama5d2_piobu_set_direction(chip, PIOBU_IN, pin);
+}
+
+/**
+ * sama5d2_piobu_direction_output() - gpiochip direction_output
+ */
+static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
+ unsigned int pin, int value)
+{
+ return sama5d2_piobu_set_direction(chip, PIOBU_OUT, pin);
+}
+
+/**
+ * sama5d2_piobu_get() - gpiochip get
+ */
+static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
+{
+ /* if pin is input, read value from PDS else read from SOD */
+ int ret = sama5d2_piobu_get_direction(chip, pin);
+
+ if (ret == 1)
+ ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS);
+ else if (!ret)
+ ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD);
+
+ if (ret < 0)
+ return ret;
+
+ return !!ret;
+}
+
+/**
+ * sama5d2_piobu_set() - gpiochip set
+ */
+static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin,
+ int value)
+{
+ if (!value)
+ value = PIOBU_LOW;
+ else
+ value = PIOBU_HIGH;
+
+ sama5d2_piobu_write_value(chip, pin, PIOBU_SOD, value);
+}
+
+static int sama5d2_piobu_probe(struct platform_device *pdev)
+{
+ struct sama5d2_piobu *piobu;
+ int ret, i;
+
+ piobu = devm_kzalloc(&pdev->dev, sizeof(*piobu), GFP_KERNEL);
+ if (!piobu)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, piobu);
+ piobu->chip.label = pdev->name;
+ piobu->chip.parent = &pdev->dev;
+ piobu->chip.of_node = pdev->dev.of_node;
+ piobu->chip.owner = THIS_MODULE,
+ piobu->chip.get_direction = sama5d2_piobu_get_direction,
+ piobu->chip.direction_input = sama5d2_piobu_direction_input,
+ piobu->chip.direction_output = sama5d2_piobu_direction_output,
+ piobu->chip.get = sama5d2_piobu_get,
+ piobu->chip.set = sama5d2_piobu_set,
+ piobu->chip.base = -1,
+ piobu->chip.ngpio = PIOBU_NUM,
+ piobu->chip.can_sleep = 0,
+
+ piobu->regmap = syscon_node_to_regmap(pdev->dev.of_node);
+ if (IS_ERR(piobu->regmap)) {
+ dev_err(&pdev->dev, "Failed to get syscon regmap %ld\n",
+ PTR_ERR(piobu->regmap));
+ return PTR_ERR(piobu->regmap);
+ }
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &piobu->chip, piobu);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add gpiochip %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < PIOBU_NUM; ++i) {
+ ret = sama5d2_piobu_setup_pin(&piobu->chip, i);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup pin: %d %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id sama5d2_piobu_ids[] = {
+ { .compatible = "atmel,sama5d2-secumod" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sama5d2_piobu_ids);
+
+static struct platform_driver sama5d2_piobu_driver = {
+ .driver = {
+ .name = "sama5d2-piobu",
+ .of_match_table = of_match_ptr(sama5d2_piobu_ids)
+ },
+ .probe = sama5d2_piobu_probe,
+};
+
+module_platform_driver(sama5d2_piobu_driver);
+
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SAMA5D2 PIOBU controller driver");
+MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@microchip.com>");
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index e9878f6ede67..c333046d02b8 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -1,32 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* GPIO interface for Intel Poulsbo SCH
*
* Copyright (c) 2010 CompuLab Ltd
* Author: Denis Turischev <denis@compulab.co.il>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 2 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/errno.h>
-#include <linux/acpi.h>
-#include <linux/platform_device.h>
#include <linux/pci_ids.h>
-#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
#define GEN 0x00
#define GIO 0x04
@@ -235,5 +222,5 @@ module_platform_driver(sch_gpio_driver);
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sch_gpio");
diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c
index 5497f0a88cf0..4df5335469fd 100644
--- a/drivers/gpio/gpio-sch311x.c
+++ b/drivers/gpio/gpio-sch311x.c
@@ -188,7 +188,7 @@ static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset,
struct sch311x_gpio_block *block = gpiochip_get_data(chip);
spin_lock(&block->lock);
- __sch311x_gpio_set(block, offset, value);
+ __sch311x_gpio_set(block, offset, value);
spin_unlock(&block->lock);
}
diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c
index f60da83349ef..aed988e78251 100644
--- a/drivers/gpio/gpio-sodaville.c
+++ b/drivers/gpio/gpio-sodaville.c
@@ -1,26 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* GPIO interface for Intel Sodaville SoCs.
*
* Copyright (c) 2010, 2011 Intel Corporation
*
* Author: Hans J. Koch <hjk@linutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License 2 as published
- * by the Free Software Foundation.
- *
*/
#include <linux/errno.h>
+#include <linux/gpio/driver.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
-#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/of_irq.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
-#include <linux/of_irq.h>
-#include <linux/gpio/driver.h>
#define DRV_NAME "sdv_gpio"
#define SDV_NUM_PUB_GPIOS 12
@@ -80,18 +76,15 @@ static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type)
static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data)
{
struct sdv_gpio_chip_data *sd = data;
- u32 irq_stat = readl(sd->gpio_pub_base + GPSTR);
+ unsigned long irq_stat = readl(sd->gpio_pub_base + GPSTR);
+ int irq_bit;
irq_stat &= readl(sd->gpio_pub_base + GPIO_INT);
if (!irq_stat)
return IRQ_NONE;
- while (irq_stat) {
- u32 irq_bit = __fls(irq_stat);
-
- irq_stat &= ~BIT(irq_bit);
+ for_each_set_bit(irq_bit, &irq_stat, 32)
generic_handle_irq(irq_find_mapping(sd->id, irq_bit));
- }
return IRQ_HANDLED;
}
@@ -155,8 +148,10 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
* we unmask & ACK the IRQ before the source of the interrupt is gone
* then the interrupt is active again.
*/
- sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base,
- sd->gpio_pub_base, handle_fasteoi_irq);
+ sd->gc = devm_irq_alloc_generic_chip(&pdev->dev, "sdv-gpio", 1,
+ sd->irq_base,
+ sd->gpio_pub_base,
+ handle_fasteoi_irq);
if (!sd->gc)
return -ENOMEM;
@@ -186,70 +181,52 @@ static int sdv_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_id)
{
struct sdv_gpio_chip_data *sd;
- unsigned long addr;
- const void *prop;
- int len;
int ret;
u32 mux_val;
- sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL);
+ sd = devm_kzalloc(&pdev->dev, sizeof(*sd), GFP_KERNEL);
if (!sd)
return -ENOMEM;
- ret = pci_enable_device(pdev);
+
+ ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "can't enable device.\n");
- goto done;
+ return ret;
}
- ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
+ ret = pcim_iomap_regions(pdev, 1 << GPIO_BAR, DRV_NAME);
if (ret) {
dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
- goto disable_pci;
+ return ret;
}
- addr = pci_resource_start(pdev, GPIO_BAR);
- if (!addr) {
- ret = -ENODEV;
- goto release_reg;
- }
- sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR));
+ sd->gpio_pub_base = pcim_iomap_table(pdev)[GPIO_BAR];
- prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len);
- if (prop && len == 4) {
- mux_val = of_read_number(prop, 1);
+ ret = of_property_read_u32(pdev->dev.of_node, "intel,muxctl", &mux_val);
+ if (!ret)
writel(mux_val, sd->gpio_pub_base + GPMUXCTL);
- }
ret = bgpio_init(&sd->chip, &pdev->dev, 4,
sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR,
NULL, sd->gpio_pub_base + GPOER, NULL, 0);
if (ret)
- goto unmap;
+ return ret;
+
sd->chip.ngpio = SDV_NUM_PUB_GPIOS;
- ret = gpiochip_add_data(&sd->chip, sd);
+ ret = devm_gpiochip_add_data(&pdev->dev, &sd->chip, sd);
if (ret < 0) {
dev_err(&pdev->dev, "gpiochip_add() failed.\n");
- goto unmap;
+ return ret;
}
ret = sdv_register_irqsupport(sd, pdev);
if (ret)
- goto unmap;
+ return ret;
pci_set_drvdata(pdev, sd);
dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n");
return 0;
-
-unmap:
- iounmap(sd->gpio_pub_base);
-release_reg:
- pci_release_region(pdev, GPIO_BAR);
-disable_pci:
- pci_disable_device(pdev);
-done:
- kfree(sd);
- return ret;
}
static const struct pci_device_id sdv_gpio_pci_ids[] = {
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 47dbd19751d0..02f6db925fd5 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -404,8 +404,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
#ifdef CONFIG_PM_SLEEP
static int tegra_gpio_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct tegra_gpio_info *tgi = platform_get_drvdata(pdev);
+ struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
unsigned long flags;
unsigned int b, p;
@@ -444,8 +443,7 @@ static int tegra_gpio_resume(struct device *dev)
static int tegra_gpio_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct tegra_gpio_info *tgi = platform_get_drvdata(pdev);
+ struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
unsigned long flags;
unsigned int b, p;
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 9d0292c8a199..66ec38bb7954 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -279,7 +279,7 @@ static void tegra186_irq_unmask(struct irq_data *data)
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
}
-static int tegra186_irq_set_type(struct irq_data *data, unsigned int flow)
+static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
{
struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
void __iomem *base;
@@ -293,7 +293,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int flow)
value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK;
value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL;
- switch (flow & IRQ_TYPE_SENSE_MASK) {
+ switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_NONE:
break;
@@ -325,7 +325,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int flow)
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
- if ((flow & IRQ_TYPE_EDGE_BOTH) == 0)
+ if ((type & IRQ_TYPE_EDGE_BOTH) == 0)
irq_set_handler_locked(data, handle_level_irq);
else
irq_set_handler_locked(data, handle_edge_irq);
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
index 74551cbdb2e8..0f662b297a95 100644
--- a/drivers/gpio/gpio-uniphier.c
+++ b/drivers/gpio/gpio-uniphier.c
@@ -1,16 +1,7 @@
-/*
- * Copyright (C) 2017 Socionext Inc.
- * Author: Masahiro Yamada <yamada.masahiro@socionext.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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2017 Socionext Inc.
+// Author: Masahiro Yamada <yamada.masahiro@socionext.com>
#include <linux/bits.h>
#include <linux/gpio/driver.h>
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index 5960396c8d9a..1b79ebcfce3e 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -7,6 +7,7 @@
* Author: Stefan Agner <stefan@agner.ch>.
*/
#include <linux/bitops.h>
+#include <linux/clk.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
@@ -32,6 +33,8 @@ struct vf610_gpio_port {
void __iomem *gpio_base;
const struct fsl_gpio_soc_data *sdata;
u8 irqc[VF610_GPIO_PER_PORT];
+ struct clk *clk_port;
+ struct clk *clk_gpio;
int irq;
};
@@ -271,6 +274,33 @@ static int vf610_gpio_probe(struct platform_device *pdev)
if (port->irq < 0)
return port->irq;
+ port->clk_port = devm_clk_get(&pdev->dev, "port");
+ if (!IS_ERR(port->clk_port)) {
+ ret = clk_prepare_enable(port->clk_port);
+ if (ret)
+ return ret;
+ } else if (port->clk_port == ERR_PTR(-EPROBE_DEFER)) {
+ /*
+ * Percolate deferrals, for anything else,
+ * just live without the clocking.
+ */
+ return PTR_ERR(port->clk_port);
+ }
+
+ port->clk_gpio = devm_clk_get(&pdev->dev, "gpio");
+ if (!IS_ERR(port->clk_gpio)) {
+ ret = clk_prepare_enable(port->clk_gpio);
+ if (ret) {
+ clk_disable_unprepare(port->clk_port);
+ return ret;
+ }
+ } else if (port->clk_gpio == ERR_PTR(-EPROBE_DEFER)) {
+ clk_disable_unprepare(port->clk_port);
+ return PTR_ERR(port->clk_gpio);
+ }
+
+ platform_set_drvdata(pdev, port);
+
gc = &port->gc;
gc->of_node = np;
gc->parent = dev;
@@ -305,12 +335,26 @@ static int vf610_gpio_probe(struct platform_device *pdev)
return 0;
}
+static int vf610_gpio_remove(struct platform_device *pdev)
+{
+ struct vf610_gpio_port *port = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&port->gc);
+ if (!IS_ERR(port->clk_port))
+ clk_disable_unprepare(port->clk_port);
+ if (!IS_ERR(port->clk_gpio))
+ clk_disable_unprepare(port->clk_gpio);
+
+ return 0;
+}
+
static struct platform_driver vf610_gpio_driver = {
.driver = {
.name = "gpio-vf610",
.of_match_table = vf610_gpio_dt_ids,
},
.probe = vf610_gpio_probe,
+ .remove = vf610_gpio_remove,
};
builtin_platform_driver(vf610_gpio_driver);
diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c
index c7028eb0b8e1..5cf3697bfb15 100644
--- a/drivers/gpio/gpio-ws16c48.c
+++ b/drivers/gpio/gpio-ws16c48.c
@@ -169,7 +169,7 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
port_state = inb(ws16c48gpio->base + i);
/* store acquired bits at respective bits array offset */
- bits[word_index] |= port_state << word_offset;
+ bits[word_index] |= (port_state << word_offset) & word_mask;
}
return 0;
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 3f5fcdd5a429..b3b4edcdffe0 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -358,6 +358,28 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
}
/**
+ * zynq_gpio_get_direction - Read the direction of the specified GPIO pin
+ * @chip: gpio_chip instance to be worked on
+ * @pin: gpio pin number within the device
+ *
+ * This function returns the direction of the specified GPIO.
+ *
+ * Return: 0 for output, 1 for input
+ */
+static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio *gpio = gpiochip_get_data(chip);
+
+ zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
+
+ reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+
+ return !(reg & BIT(bank_pin_num));
+}
+
+/**
* zynq_gpio_irq_mask - Disable the interrupts for a gpio pin
* @irq_data: per irq and chip data passed down to chip functions
*
@@ -693,8 +715,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+ struct zynq_gpio *gpio = dev_get_drvdata(dev);
clk_disable_unprepare(gpio->clk);
@@ -703,8 +724,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct zynq_gpio *gpio = platform_get_drvdata(pdev);
+ struct zynq_gpio *gpio = dev_get_drvdata(dev);
return clk_prepare_enable(gpio->clk);
}
@@ -827,6 +847,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
chip->free = zynq_gpio_free;
chip->direction_input = zynq_gpio_dir_in;
chip->direction_output = zynq_gpio_dir_out;
+ chip->get_direction = zynq_gpio_get_direction;
chip->base = of_alias_get_id(pdev->dev.of_node, "gpio");
chip->ngpio = gpio->p_data->ngpio;
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 7f93954c58ea..48534bda73d3 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -217,7 +217,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
if (!handler)
return AE_OK;
- desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
+ desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event", 0);
if (IS_ERR(desc)) {
dev_err(chip->parent, "Failed to request GPIO\n");
return AE_ERROR;
@@ -913,23 +913,15 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
if (!found) {
enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio);
const char *label = "ACPI:OpRegion";
- int err;
- desc = gpiochip_request_own_desc(chip, pin, label);
+ desc = gpiochip_request_own_desc(chip, pin, label,
+ flags);
if (IS_ERR(desc)) {
status = AE_ERROR;
mutex_unlock(&achip->conn_lock);
goto out;
}
- err = gpiod_configure_flags(desc, label, 0, flags);
- if (err < 0) {
- status = AE_NOT_CONFIGURED;
- gpiochip_free_own_desc(desc);
- mutex_unlock(&achip->conn_lock);
- goto out;
- }
-
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn) {
status = AE_NO_MEMORY;
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 7f1260c78270..a6e1891217e2 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0+
/*
* OF helpers for the GPIO API
*
@@ -54,10 +54,32 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
}
static void of_gpio_flags_quirks(struct device_node *np,
+ const char *propname,
enum of_gpio_flags *flags,
int index)
{
/*
+ * Handle MMC "cd-inverted" and "wp-inverted" semantics.
+ */
+ if (IS_ENABLED(CONFIG_MMC)) {
+ /*
+ * Active low is the default according to the
+ * SDHCI specification and the device tree
+ * bindings. However the code in the current
+ * kernel was written such that the phandle
+ * flags were always respected, and "cd-inverted"
+ * would invert the flag from the device phandle.
+ */
+ if (!strcmp(propname, "cd-gpios")) {
+ if (of_property_read_bool(np, "cd-inverted"))
+ *flags ^= OF_GPIO_ACTIVE_LOW;
+ }
+ if (!strcmp(propname, "wp-gpios")) {
+ if (of_property_read_bool(np, "wp-inverted"))
+ *flags ^= OF_GPIO_ACTIVE_LOW;
+ }
+ }
+ /*
* Some GPIO fixed regulator quirks.
* Note that active low is the default.
*/
@@ -174,7 +196,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
goto out;
if (flags)
- of_gpio_flags_quirks(np, flags, index);
+ of_gpio_flags_quirks(np, propname, flags, index);
pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
__func__, propname, np, index,
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 985c09ce80fb..1651d7f0a303 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1512,19 +1512,6 @@ static void devm_gpio_chip_release(struct device *dev, void *res)
gpiochip_remove(chip);
}
-static int devm_gpio_chip_match(struct device *dev, void *res, void *data)
-
-{
- struct gpio_chip **r = res;
-
- if (!r || !*r) {
- WARN_ON(!r || !*r);
- return 0;
- }
-
- return *r == data;
-}
-
/**
* devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
* @dev: pointer to the device that gpio_chip belongs to.
@@ -1565,23 +1552,6 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
/**
- * devm_gpiochip_remove() - Resource manager of gpiochip_remove()
- * @dev: device for which which resource was allocated
- * @chip: the chip to remove
- *
- * A gpio_chip with any GPIOs still requested may not be removed.
- */
-void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip)
-{
- int ret;
-
- ret = devres_release(dev, devm_gpio_chip_release,
- devm_gpio_chip_match, chip);
- WARN_ON(ret);
-}
-EXPORT_SYMBOL_GPL(devm_gpiochip_remove);
-
-/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
* @match: Callback function to check gpio_chip
@@ -2299,6 +2269,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
unsigned long flags;
unsigned offset;
+ if (label) {
+ label = kstrdup_const(label, GFP_KERNEL);
+ if (!label)
+ return -ENOMEM;
+ }
+
spin_lock_irqsave(&gpio_lock, flags);
/* NOTE: gpio_request() can be called in early boot,
@@ -2309,6 +2285,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
desc_set_label(desc, label ? : "?");
status = 0;
} else {
+ kfree_const(label);
status = -EBUSY;
goto done;
}
@@ -2325,6 +2302,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (status < 0) {
desc_set_label(desc, NULL);
+ kfree_const(label);
clear_bit(FLAG_REQUESTED, &desc->flags);
goto done;
}
@@ -2420,6 +2398,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
chip->free(chip, gpio_chip_hwgpio(desc));
spin_lock_irqsave(&gpio_lock, flags);
}
+ kfree_const(desc->label);
desc_set_label(desc, NULL);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
@@ -2476,6 +2455,7 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* @chip: GPIO chip
* @hwnum: hardware number of the GPIO for which to request the descriptor
* @label: label for the GPIO
+ * @flags: flags for this GPIO or 0 if default
*
* Function allows GPIO chip drivers to request and use their own GPIO
* descriptors via gpiolib API. Difference to gpiod_request() is that this
@@ -2488,7 +2468,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* code on failure.
*/
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
- const char *label)
+ const char *label,
+ enum gpiod_flags flags)
{
struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
int err;
@@ -2502,6 +2483,13 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
if (err < 0)
return ERR_PTR(err);
+ err = gpiod_configure_flags(desc, label, 0, flags);
+ if (err) {
+ chip_err(chip, "setup of own GPIO %s failed\n", label);
+ gpiod_free_commit(desc);
+ return ERR_PTR(err);
+ }
+
return desc;
}
EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);
@@ -3375,11 +3363,19 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
* @desc: gpio to set the consumer name on
* @name: the new consumer name
*/
-void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
+int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
{
- VALIDATE_DESC_VOID(desc);
- /* Just overwrite whatever the previous name was */
- desc->label = name;
+ VALIDATE_DESC(desc);
+ if (name) {
+ name = kstrdup_const(name, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ }
+
+ kfree_const(desc->label);
+ desc_set_label(desc, name);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
@@ -4348,7 +4344,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
chip = gpiod_to_chip(desc);
hwnum = gpio_chip_hwgpio(desc);
- local_desc = gpiochip_request_own_desc(chip, hwnum, name);
+ /*
+ * FIXME: not very elegant that we call gpiod_configure_flags()
+ * twice here (once inside gpiochip_request_own_desc() and
+ * again here), but the gpiochip_request_own_desc() is external
+ * and cannot really pass the lflags so this is the lesser evil
+ * at the moment. Pass zero as dflags on this first call so we
+ * don't screw anything up.
+ */
+ local_desc = gpiochip_request_own_desc(chip, hwnum, name, 0);
if (IS_ERR(local_desc)) {
status = PTR_ERR(local_desc);
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 271f31461da4..47f65857408d 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -1203,7 +1203,7 @@ static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev,
return -EINVAL;
dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin,
- "HID/I2C:Event");
+ "HID/I2C:Event", 0);
if (IS_ERR(dev->desc[pin])) {
dev_err(dev->gc.parent, "Failed to request GPIO\n");
return PTR_ERR(dev->desc[pin]);
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index c215287e80cf..e1d91e64e008 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -21,6 +21,7 @@
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h> /* GPIO descriptor enum */
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/platform_device.h>
@@ -2170,7 +2171,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
unsigned int wait_pin = gpmc_s.wait_pin;
waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
- wait_pin, "WAITPIN");
+ wait_pin, "WAITPIN",
+ 0);
if (IS_ERR(waitpin_desc)) {
dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
ret = PTR_ERR(waitpin_desc);