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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJcJJcmAAoJEEEQszewGV1zdCoQAMSLPh+4QdYLFS3ZmnsPvZkg
 Zkz94RVT+uLLmHpR8XBl0wl4mmYCiwB50IwsXwlvJGceSXjCn6hLg9BZBP5hFMCb
 yFk3fgXQSh7TOnpjgbCWSssCjpejQ0cOb/2nWHz5s/fbYKYlh3VXckW9hhW+R+b3
 6+Mno1SzmBkpVVQ21ISlZeLoNDynkCW6DfjiKMuHAyxzxa0oZ9Qid+tnVDnQM4rD
 rCnvYLuvxlXw97W+LI7AU9KoCITdO+2M/0dtGVEEYKfHseRReSy2Oo3nlqCglygX
 cNkBe9RYU1CJdas1P/c18wjDAqWp/pbugzHDkYF5Y3tBtB2rQSftjtnQngiVMXMt
 De7ejPvL7/OLECvI2WUD6y9NAduw/HE4FO8BBdn0gP/a9svJpVZ993yPd3F9n7EZ
 ZIBpycDZVtkcE7dwQgek17bFILRY5nBBMB9oCLL2wk48qdmjpcAbxVfGSsYOe6wO
 +cZRZuj/eR3i9gHRugfFx7Evoc7l4erihTpES/YAy3De1s3uV8KjT4zE7Jh+M9LW
 GM797BcXJJeb5toC7HZ/G2UAVMyWEhY0n7PGG616CS8IrzmzIZpJOL9kUVOWPb0F
 9CPt6qhVbUl9rxp1H/Z3TxvYqIw6icn/z5YewZxLU1zaBlpE3jc7PhZb5hOGnFUf
 /DWwe4n99JsjHTrGx1ra
 =05LW
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2018-12-28 20:00:21 -08:00
commit 24dc83635f
57 changed files with 1745 additions and 682 deletions

View File

@ -158,14 +158,24 @@ Security Module (SECUMOD)
The Security Module macrocell provides all necessary secure functions to avoid The Security Module macrocell provides all necessary secure functions to avoid
voltage, temperature, frequency and mechanical attacks on the chip. It also voltage, temperature, frequency and mechanical attacks on the chip. It also
embeds secure memories that can be scrambled embeds secure memories that can be scrambled.
The Security Module also offers the PIOBU pins which can be used as GPIO pins.
Note that they maintain their voltage during Backup/Self-refresh.
required properties: required properties:
- compatible: Should be "atmel,<chip>-secumod", "syscon". - compatible: Should be "atmel,<chip>-secumod", "syscon".
<chip> can be "sama5d2". <chip> can be "sama5d2".
- reg: Should contain registers location and length - reg: Should contain registers location and length
- gpio-controller: Marks the port as GPIO controller.
- #gpio-cells: There are 2. The pin number is the
first, the second represents additional
parameters such as GPIO_ACTIVE_HIGH/LOW.
secumod@fc040000 { secumod@fc040000 {
compatible = "atmel,sama5d2-secumod", "syscon"; compatible = "atmel,sama5d2-secumod", "syscon";
reg = <0xfc040000 0x100>; reg = <0xfc040000 0x100>;
gpio-controller;
#gpio-cells = <2>;
}; };

View File

@ -0,0 +1,43 @@
Cadence GPIO controller bindings
Required properties:
- compatible: should be "cdns,gpio-r1p02".
- reg: the register base address and size.
- #gpio-cells: should be 2.
* first cell is the GPIO number.
* second cell specifies the GPIO flags, as defined in
<dt-bindings/gpio/gpio.h>. Only the GPIO_ACTIVE_HIGH
and GPIO_ACTIVE_LOW flags are supported.
- gpio-controller: marks the device as a GPIO controller.
- clocks: should contain one entry referencing the peripheral clock driving
the GPIO controller.
Optional properties:
- ngpios: integer number of gpio lines supported by this controller, up to 32.
- interrupts: interrupt specifier for the controllers interrupt.
- interrupt-controller: marks the device as an interrupt controller. When
defined, interrupts, interrupt-parent and #interrupt-cells
are required.
- interrupt-cells: should be 2.
* first cell is the GPIO number you want to use as an IRQ source.
* second cell specifies the IRQ type, as defined in
<dt-bindings/interrupt-controller/irq.h>.
Currently only level sensitive IRQs are supported.
Example:
gpio0: gpio-controller@fd060000 {
compatible = "cdns,gpio-r1p02";
reg =<0xfd060000 0x1000>;
clocks = <&gpio_clk>;
interrupt-parent = <&gic>;
interrupts = <0 5 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};

View File

@ -24,6 +24,12 @@ Required properties for GPIO node:
4 = active high level-sensitive. 4 = active high level-sensitive.
8 = active low level-sensitive. 8 = active low level-sensitive.
Optional properties:
-clocks: Must contain an entry for each entry in clock-names.
See common clock-bindings.txt for details.
-clock-names: A list of clock names. For imx7ulp, it must contain
"gpio", "port".
Note: Each GPIO port should have an alias correctly numbered in "aliases" Note: Each GPIO port should have an alias correctly numbered in "aliases"
node. node.

View File

@ -3,12 +3,24 @@ NXP LPC18xx/43xx GPIO controller Device Tree Bindings
Required properties: Required properties:
- compatible : Should be "nxp,lpc1850-gpio" - compatible : Should be "nxp,lpc1850-gpio"
- reg : Address and length of the register set for the device - reg : List of addresses and lengths of the GPIO controller
- clocks : Clock specifier (see clock bindings for details) register sets
- gpio-controller : Marks the device node as a GPIO controller. - reg-names : Should be "gpio", "gpio-pin-ic", "gpio-group0-ic" and
- #gpio-cells : Should be two "gpio-gpoup1-ic"
- First cell is the GPIO line number - clocks : Phandle and clock specifier pair for GPIO controller
- Second cell is used to specify polarity - resets : Phandle and reset specifier pair for GPIO controller
- gpio-controller : Marks the device node as a GPIO controller
- #gpio-cells : Should be two:
- The first cell is the GPIO line number
- The second cell is used to specify polarity
- interrupt-controller : Marks the device node as an interrupt controller
- #interrupt-cells : Should be two:
- The first cell is an interrupt number within
0..9 range, for GPIO pin interrupts it is equal
to 'nxp,gpio-pin-interrupt' property value of
GPIO pin configuration, 8 is for GPIO GROUP0
interrupt, 9 is for GPIO GROUP1 interrupt
- The second cell is used to specify interrupt type
Optional properties: Optional properties:
- gpio-ranges : Mapping between GPIO and pinctrl - gpio-ranges : Mapping between GPIO and pinctrl
@ -19,21 +31,29 @@ Example:
gpio: gpio@400f4000 { gpio: gpio@400f4000 {
compatible = "nxp,lpc1850-gpio"; compatible = "nxp,lpc1850-gpio";
reg = <0x400f4000 0x4000>; reg = <0x400f4000 0x4000>, <0x40087000 0x1000>,
<0x40088000 0x1000>, <0x40089000 0x1000>;
reg-names = "gpio", "gpio-pin-ic",
"gpio-group0-ic", "gpio-gpoup1-ic";
clocks = <&ccu1 CLK_CPU_GPIO>; clocks = <&ccu1 CLK_CPU_GPIO>;
resets = <&rgu 28>;
gpio-controller; gpio-controller;
#gpio-cells = <2>; #gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
gpio-ranges = <&pinctrl LPC_GPIO(0,0) LPC_PIN(0,0) 2>, gpio-ranges = <&pinctrl LPC_GPIO(0,0) LPC_PIN(0,0) 2>,
... ...
<&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5) 7>; <&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5) 7>;
}; };
gpio_joystick { gpio_joystick {
compatible = "gpio-keys-polled"; compatible = "gpio-keys";
... ...
button@0 { button0 {
... ...
interrupt-parent = <&gpio>;
interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>; gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>;
}; };
}; };

View File

@ -8,6 +8,7 @@ Required Properties:
- "renesas,gpio-r8a7745": for R8A7745 (RZ/G1E) compatible GPIO controller. - "renesas,gpio-r8a7745": for R8A7745 (RZ/G1E) compatible GPIO controller.
- "renesas,gpio-r8a77470": for R8A77470 (RZ/G1C) compatible GPIO controller. - "renesas,gpio-r8a77470": for R8A77470 (RZ/G1C) compatible GPIO controller.
- "renesas,gpio-r8a774a1": for R8A774A1 (RZ/G2M) compatible GPIO controller. - "renesas,gpio-r8a774a1": for R8A774A1 (RZ/G2M) compatible GPIO controller.
- "renesas,gpio-r8a774c0": for R8A774C0 (RZ/G2E) compatible GPIO controller.
- "renesas,gpio-r8a7778": for R8A7778 (R-Car M1) compatible GPIO controller. - "renesas,gpio-r8a7778": for R8A7778 (R-Car M1) compatible GPIO controller.
- "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller. - "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller. - "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.

View File

@ -43,7 +43,7 @@ gpio: gpio@20000 {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
porta: gpio-controller@0 { porta: gpio@0 {
compatible = "snps,dw-apb-gpio-port"; compatible = "snps,dw-apb-gpio-port";
gpio-controller; gpio-controller;
#gpio-cells = <2>; #gpio-cells = <2>;
@ -55,7 +55,7 @@ gpio: gpio@20000 {
interrupts = <0>; interrupts = <0>;
}; };
portb: gpio-controller@1 { portb: gpio@1 {
compatible = "snps,dw-apb-gpio-port"; compatible = "snps,dw-apb-gpio-port";
gpio-controller; gpio-controller;
#gpio-cells = <2>; #gpio-cells = <2>;

View File

@ -434,7 +434,9 @@ try_module_get()). A GPIO driver can use the following functions instead
to request and free descriptors without being pinned to the kernel forever:: to request and free descriptors without being pinned to the kernel forever::
struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc, struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc,
const char *label) u16 hwnum,
const char *label,
enum gpiod_flags flags)
void gpiochip_free_own_desc(struct gpio_desc *desc) void gpiochip_free_own_desc(struct gpio_desc *desc)

View File

@ -256,7 +256,6 @@ GPIO
devm_gpiod_put() devm_gpiod_put()
devm_gpiod_unhinge() devm_gpiod_unhinge()
devm_gpiochip_add_data() devm_gpiochip_add_data()
devm_gpiochip_remove()
devm_gpio_request() devm_gpio_request()
devm_gpio_request_one() devm_gpio_request_one()
devm_gpio_free() devm_gpio_free()

View File

@ -6412,7 +6412,6 @@ F: drivers/media/rc/gpio-ir-tx.c
GPIO MOCKUP DRIVER GPIO MOCKUP DRIVER
M: Bamvor Jian Zhang <bamv2005@gmail.com> M: Bamvor Jian Zhang <bamv2005@gmail.com>
R: Bartosz Golaszewski <brgl@bgdev.pl>
L: linux-gpio@vger.kernel.org L: linux-gpio@vger.kernel.org
S: Maintained S: Maintained
F: drivers/gpio/gpio-mockup.c F: drivers/gpio/gpio-mockup.c
@ -9933,6 +9932,12 @@ M: Nicolas Ferre <nicolas.ferre@microchip.com>
S: Supported S: Supported
F: drivers/power/reset/at91-sama5d2_shdwc.c F: drivers/power/reset/at91-sama5d2_shdwc.c
MICROCHIP SAMA5D2-COMPATIBLE PIOBU GPIO
M: Andrei Stefanescu <andrei.stefanescu@microchip.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-gpio@vger.kernel.org
F: drivers/gpio/gpio-sama5d2-piobu.c
MICROCHIP SPI DRIVER MICROCHIP SPI DRIVER
M: Nicolas Ferre <nicolas.ferre@microchip.com> M: Nicolas Ferre <nicolas.ferre@microchip.com>
S: Supported S: Supported

View File

@ -103,7 +103,7 @@ void __init ams_delta_init_fiq(struct gpio_chip *chip,
} }
for (i = 0; i < ARRAY_SIZE(irq_data); i++) { for (i = 0; i < ARRAY_SIZE(irq_data); i++) {
gpiod = gpiochip_request_own_desc(chip, i, pin_name[i]); gpiod = gpiochip_request_own_desc(chip, i, pin_name[i], 0);
if (IS_ERR(gpiod)) { if (IS_ERR(gpiod)) {
pr_err("%s: failed to get GPIO pin %d (%ld)\n", pr_err("%s: failed to get GPIO pin %d (%ld)\n",
__func__, i, PTR_ERR(gpiod)); __func__, i, PTR_ERR(gpiod));

View File

@ -601,7 +601,7 @@ static void __init modem_assign_irq(struct gpio_chip *chip)
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
gpiod = gpiochip_request_own_desc(chip, AMS_DELTA_GPIO_PIN_MODEM_IRQ, gpiod = gpiochip_request_own_desc(chip, AMS_DELTA_GPIO_PIN_MODEM_IRQ,
"modem_irq"); "modem_irq", 0);
if (IS_ERR(gpiod)) { if (IS_ERR(gpiod)) {
pr_err("%s: modem IRQ GPIO request failed (%ld)\n", __func__, pr_err("%s: modem IRQ GPIO request failed (%ld)\n", __func__,
PTR_ERR(gpiod)); PTR_ERR(gpiod));
@ -809,7 +809,7 @@ static void __init ams_delta_led_init(struct gpio_chip *chip)
int i; int i;
for (i = LATCH1_PIN_LED_CAMERA; i < LATCH1_PIN_DOCKIT1; i++) { for (i = LATCH1_PIN_LED_CAMERA; i < LATCH1_PIN_DOCKIT1; i++) {
gpiod = gpiochip_request_own_desc(chip, i, NULL); gpiod = gpiochip_request_own_desc(chip, i, "camera-led", 0);
if (IS_ERR(gpiod)) { if (IS_ERR(gpiod)) {
pr_warn("%s: %s GPIO %d request failed (%ld)\n", pr_warn("%s: %s GPIO %d request failed (%ld)\n",
__func__, LATCH1_LABEL, i, PTR_ERR(gpiod)); __func__, LATCH1_LABEL, i, PTR_ERR(gpiod));

View File

@ -160,6 +160,14 @@ config GPIO_BRCMSTB
help help
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs. 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 config GPIO_CLPS711X
tristate "CLPS711X GPIO support" tristate "CLPS711X GPIO support"
depends on ARCH_CLPS711X || COMPILE_TEST depends on ARCH_CLPS711X || COMPILE_TEST
@ -288,6 +296,7 @@ config GPIO_LPC18XX
tristate "NXP LPC18XX/43XX GPIO support" tristate "NXP LPC18XX/43XX GPIO support"
default y if ARCH_LPC18XX default y if ARCH_LPC18XX
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST) depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
select IRQ_DOMAIN_HIERARCHY
help help
Select this option to enable GPIO driver for Select this option to enable GPIO driver for
NXP LPC18XX/43XX devices. NXP LPC18XX/43XX devices.
@ -429,6 +438,18 @@ config GPIO_REG
A 32-bit single register GPIO fixed in/out implementation. This A 32-bit single register GPIO fixed in/out implementation. This
can be used to represent any register as a set of GPIO signals. 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 config GPIO_SIOX
tristate "SIOX GPIO support" tristate "SIOX GPIO support"
depends on SIOX depends on SIOX
@ -849,6 +870,7 @@ config GPIO_MC9S08DZ60
config GPIO_PCA953X config GPIO_PCA953X
tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports" tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
select REGMAP_I2C
help help
Say yes here to provide access to several register-oriented Say yes here to provide access to several register-oriented
SMBus I/O expanders, made mostly by NXP or TI. Compatible SMBus I/O expanders, made mostly by NXP or TI. Compatible

View File

@ -37,6 +37,7 @@ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.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_CLPS711X) += gpio-clps711x.o
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.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_RCAR) += gpio-rcar.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.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_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o

109
drivers/gpio/TODO Normal file
View File

@ -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.

View File

@ -222,7 +222,7 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
port_state = inb(dio48egpio->base + ports[i]); port_state = inb(dio48egpio->base + ports[i]);
/* store acquired bits at respective bits array offset */ /* 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; return 0;

View File

@ -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]); port_state = inb(idi48gpio->base + ports[i]);
/* store acquired bits at respective bits array offset */ /* 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; return 0;

View File

@ -1185,7 +1185,6 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
gpio->chip.parent = &pdev->dev; gpio->chip.parent = &pdev->dev;
gpio->chip.ngpio = gpio->config->nr_gpios; gpio->chip.ngpio = gpio->config->nr_gpios;
gpio->chip.parent = &pdev->dev;
gpio->chip.direction_input = aspeed_gpio_dir_in; gpio->chip.direction_input = aspeed_gpio_dir_in;
gpio->chip.direction_output = aspeed_gpio_dir_out; gpio->chip.direction_output = aspeed_gpio_dir_out;
gpio->chip.get_direction = aspeed_gpio_get_direction; gpio->chip.get_direction = aspeed_gpio_get_direction;

291
drivers/gpio/gpio-cadence.c Normal file
View File

@ -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");

View File

@ -748,8 +748,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int dwapb_gpio_suspend(struct device *dev) static int dwapb_gpio_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
struct gpio_chip *gc = &gpio->ports[0].gc; struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags; unsigned long flags;
int i; int i;
@ -793,8 +792,7 @@ static int dwapb_gpio_suspend(struct device *dev)
static int dwapb_gpio_resume(struct device *dev) static int dwapb_gpio_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct dwapb_gpio *gpio = dev_get_drvdata(dev);
struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
struct gpio_chip *gc = &gpio->ports[0].gc; struct gpio_chip *gc = &gpio->ports[0].gc;
unsigned long flags; unsigned long flags;
int i; int i;

View File

@ -211,7 +211,7 @@ static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
port_state = inb(gpiommgpio->base + ports[i]); port_state = inb(gpiommgpio->base + ports[i]);
/* store acquired bits at respective bits array offset */ /* 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; return 0;

View File

@ -30,7 +30,6 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>

View File

@ -1,32 +1,18 @@
// SPDX-License-Identifier: GPL-2.0+
/* /*
* Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver * Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
* *
* Copyright (C) 2010 Extreme Engineering Solutions. * 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/ioport.h>
#include <linux/mfd/lpc_ich.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/lpc_ich.h>
#include <linux/bitops.h>
#define DRV_NAME "gpio_ich" #define DRV_NAME "gpio_ich"
@ -100,7 +86,7 @@ struct ichx_desc {
static struct { static struct {
spinlock_t lock; spinlock_t lock;
struct platform_device *dev; struct device *dev;
struct gpio_chip chip; struct gpio_chip chip;
struct resource *gpio_base; /* GPIO IO base */ struct resource *gpio_base; /* GPIO IO base */
struct resource *pm_base; /* Power Mangagment IO base */ struct resource *pm_base; /* Power Mangagment IO base */
@ -112,8 +98,7 @@ static struct {
static int modparam_gpiobase = -1; /* dynamic */ static int modparam_gpiobase = -1; /* dynamic */
module_param_named(gpiobase, modparam_gpiobase, int, 0444); module_param_named(gpiobase, modparam_gpiobase, int, 0444);
MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, " MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default.");
"which is the default.");
static int ichx_write_bit(int reg, unsigned nr, int val, int verify) 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; u32 data, tmp;
int reg_nr = nr / 32; int reg_nr = nr / 32;
int bit = nr & 0x1f; int bit = nr & 0x1f;
int ret = 0;
spin_lock_irqsave(&ichx_priv.lock, flags); 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], tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
ichx_priv.gpio_base); ichx_priv.gpio_base);
if (verify && data != tmp)
ret = -EPERM;
spin_unlock_irqrestore(&ichx_priv.lock, flags); spin_unlock_irqrestore(&ichx_priv.lock, flags);
return ret; return (verify && data != tmp) ? -EPERM : 0;
} }
static int ichx_read_bit(int reg, unsigned nr) 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 * Try setting pin as an input and verify it worked since many pins
* are output-only. * are output-only.
*/ */
if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1)) return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1);
return -EINVAL;
return 0;
} }
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, 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 * Try setting pin as an output and verify it worked since many pins
* are input-only. * are input-only.
*/ */
if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1)) return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1);
return -EINVAL;
return 0;
} }
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr) 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->owner = THIS_MODULE;
chip->label = DRV_NAME; chip->label = DRV_NAME;
chip->parent = &ichx_priv.dev->dev; chip->parent = ichx_priv.dev;
/* Allow chip-specific overrides of request()/get() */ /* Allow chip-specific overrides of request()/get() */
chip->request = ichx_priv.desc->request ? 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) 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; struct resource *res_base, *res_pm;
int err; int err;
struct lpc_ich_info *ich_info = dev_get_platdata(&pdev->dev);
if (!ich_info) if (!ich_info)
return -ENODEV; return -ENODEV;
ichx_priv.dev = pdev;
switch (ich_info->gpio_version) { switch (ich_info->gpio_version) {
case ICH_I3100_GPIO: case ICH_I3100_GPIO:
ichx_priv.desc = &i3100_desc; ichx_priv.desc = &i3100_desc;
@ -445,19 +420,21 @@ static int ichx_gpio_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
ichx_priv.dev = dev;
spin_lock_init(&ichx_priv.lock); spin_lock_init(&ichx_priv.lock);
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO); res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
ichx_priv.use_gpio = ich_info->use_gpio; err = ichx_gpio_request_regions(dev, res_base, pdev->name,
err = ichx_gpio_request_regions(&pdev->dev, res_base, pdev->name, ich_info->use_gpio);
ichx_priv.use_gpio);
if (err) if (err)
return err; return err;
ichx_priv.gpio_base = res_base; 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 * 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. * 0 - 15 on some chipsets.
*/ */
if (!ichx_priv.desc->uses_gpe0) 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); res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
if (!res_pm) { 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; goto init;
} }
if (!devm_request_region(&pdev->dev, res_pm->start, if (!devm_request_region(dev, res_pm->start, resource_size(res_pm),
resource_size(res_pm), pdev->name)) { pdev->name)) {
pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n"); dev_warn(dev, "ACPI BAR is busy, GPI 0 - 15 unavailable\n");
goto init; goto init;
} }
@ -481,12 +458,12 @@ init:
ichx_gpiolib_setup(&ichx_priv.chip); ichx_gpiolib_setup(&ichx_priv.chip);
err = gpiochip_add_data(&ichx_priv.chip, NULL); err = gpiochip_add_data(&ichx_priv.chip, NULL);
if (err) { if (err) {
pr_err("Failed to register GPIOs\n"); dev_err(dev, "Failed to register GPIOs\n");
return err; return err;
} }
pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base, dev_info(dev, "GPIO from %d to %d\n", ichx_priv.chip.base,
ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME); ichx_priv.chip.base + ichx_priv.chip.ngpio - 1);
return 0; return 0;
} }

View File

@ -1,16 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Intel MID GPIO driver * Intel MID GPIO driver
* *
* Copyright (c) 2008-2014,2016 Intel Corporation. * 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: /* Supports:
@ -20,12 +12,11 @@
*/ */
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio/driver.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio/driver.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.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), PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
.driver_data = (kernel_ulong_t)&gpio_cloverview_core, .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) static void intel_mid_irq_handler(struct irq_desc *desc)
{ {

View File

@ -282,22 +282,13 @@ static int ks8695_gpio_show(struct seq_file *s, void *unused)
return 0; return 0;
} }
static int ks8695_gpio_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(ks8695_gpio);
{
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,
};
static int __init ks8695_gpio_debugfs_init(void) static int __init ks8695_gpio_debugfs_init(void)
{ {
/* /sys/kernel/debug/ks8695_gpio */ /* /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; return 0;
} }
postcore_initcall(ks8695_gpio_debugfs_init); postcore_initcall(ks8695_gpio_debugfs_init);

View File

@ -1,20 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* GPIO driver for NXP LPC18xx/43xx. * GPIO driver for NXP LPC18xx/43xx.
* *
* Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com>
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.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/clk.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -24,13 +25,246 @@
#define LPC18XX_MAX_PORTS 8 #define LPC18XX_MAX_PORTS 8
#define LPC18XX_PINS_PER_PORT 32 #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 lpc18xx_gpio_chip {
struct gpio_chip gpio; struct gpio_chip gpio;
void __iomem *base; void __iomem *base;
struct clk *clk; struct clk *clk;
struct lpc18xx_gpio_pin_ic *pin_ic;
spinlock_t lock; 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) static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{ {
struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip); 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) static int lpc18xx_gpio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct lpc18xx_gpio_chip *gc; struct lpc18xx_gpio_chip *gc;
struct resource *res; int index, ret;
int ret;
gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc) if (!gc)
return -ENOMEM; return -ENOMEM;
gc->gpio = lpc18xx_chip; gc->gpio = lpc18xx_chip;
platform_set_drvdata(pdev, gc); platform_set_drvdata(pdev, gc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); index = of_property_match_string(dev->of_node, "reg-names", "gpio");
gc->base = devm_ioremap_resource(&pdev->dev, res); 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)) if (IS_ERR(gc->base))
return PTR_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)) { 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); return PTR_ERR(gc->clk);
} }
ret = clk_prepare_enable(gc->clk); ret = clk_prepare_enable(gc->clk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "unable to enable clock\n"); dev_err(dev, "unable to enable clock\n");
return ret; return ret;
} }
spin_lock_init(&gc->lock); 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) { 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); clk_disable_unprepare(gc->clk);
return ret; return ret;
} }
/* On error GPIO pin interrupt controller just won't be registered */
lpc18xx_gpio_pin_ic_probe(gc);
return 0; return 0;
} }
@ -138,7 +389,9 @@ static int lpc18xx_gpio_remove(struct platform_device *pdev)
{ {
struct lpc18xx_gpio_chip *gc = platform_get_drvdata(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); clk_disable_unprepare(gc->clk);
return 0; return 0;
@ -161,5 +414,6 @@ static struct platform_driver lpc18xx_gpio_driver = {
module_platform_driver(lpc18xx_gpio_driver); module_platform_driver(lpc18xx_gpio_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>");
MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx"); MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -1,36 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* GPIO controller driver for Intel Lynxpoint PCH chipset> * GPIO controller driver for Intel Lynxpoint PCH chipset>
* Copyright (c) 2012, Intel Corporation. * Copyright (c) 2012, Intel Corporation.
* *
* Author: Mathias Nyman <mathias.nyman@linux.intel.com> * 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/acpi.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.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 */ /* 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 gpio_chip *gc = irq_desc_get_handler_data(desc);
struct lp_gpio *lg = gpiochip_get_data(gc); struct lp_gpio *lg = gpiochip_get_data(gc);
struct irq_chip *chip = irq_data_get_irq_chip(data); struct irq_chip *chip = irq_data_get_irq_chip(data);
u32 base, pin, mask;
unsigned long reg, ena, pending; unsigned long reg, ena, pending;
u32 base, pin;
/* check from GPIO controller which pin triggered the interrupt */ /* check from GPIO controller which pin triggered the interrupt */
for (base = 0; base < lg->chip.ngpio; base += 32) { for (base = 0; base < lg->chip.ngpio; base += 32) {
reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT); reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE); 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; unsigned irq;
pin = __ffs(pending);
mask = BIT(pin);
/* Clear before handling so we don't lose an edge */ /* 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); irq = irq_find_mapping(lg->chip.irq.domain, base + pin);
generic_handle_irq(irq); 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) static int lp_gpio_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct lp_gpio *lg = dev_get_drvdata(dev);
struct lp_gpio *lg = platform_get_drvdata(pdev);
unsigned long reg; unsigned long reg;
int i; int i;
@ -467,5 +454,5 @@ module_exit(lp_gpio_exit);
MODULE_AUTHOR("Mathias Nyman (Intel)"); MODULE_AUTHOR("Mathias Nyman (Intel)");
MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint"); MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:lp_gpio"); MODULE_ALIAS("platform:lp_gpio");

View File

@ -1,18 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Intel Merrifield SoC GPIO driver * Intel Merrifield SoC GPIO driver
* *
* Copyright (c) 2016 Intel Corporation. * Copyright (c) 2016 Intel Corporation.
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> * 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/acpi.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>

View File

@ -244,6 +244,8 @@ mediatek_gpio_bank_probe(struct device *dev,
rg->chip.of_xlate = mediatek_gpio_xlate; rg->chip.of_xlate = mediatek_gpio_xlate;
rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank); dev_name(dev), bank);
if (!rg->chip.label)
return -ENOMEM;
ret = devm_gpiochip_add_data(dev, &rg->chip, mtk); ret = devm_gpiochip_add_data(dev, &rg->chip, mtk);
if (ret < 0) { if (ret < 0) {
@ -295,6 +297,7 @@ mediatek_gpio_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct mtk *mtk; struct mtk *mtk;
int i; int i;
int ret;
mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL); mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL);
if (!mtk) if (!mtk)
@ -309,8 +312,11 @@ mediatek_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mtk); platform_set_drvdata(pdev, mtk);
mediatek_gpio_irq_chip.name = dev_name(dev); mediatek_gpio_irq_chip.name = dev_name(dev);
for (i = 0; i < MTK_BANK_CNT; i++) for (i = 0; i < MTK_BANK_CNT; i++) {
mediatek_gpio_bank_probe(dev, np, i); ret = mediatek_gpio_bank_probe(dev, np, i);
if (ret)
return ret;
}
return 0; return 0;
} }

View File

@ -608,7 +608,7 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
ret = -EBUSY; ret = -EBUSY;
} else { } else {
desc = gpiochip_request_own_desc(&mvchip->chip, desc = gpiochip_request_own_desc(&mvchip->chip,
pwm->hwpwm, "mvebu-pwm"); pwm->hwpwm, "mvebu-pwm", 0);
if (IS_ERR(desc)) { if (IS_ERR(desc)) {
ret = PTR_ERR(desc); ret = PTR_ERR(desc);
goto out; goto out;

View File

@ -17,6 +17,7 @@
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/syscore_ops.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.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); 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;
struct mxc_gpio_port *port = platform_get_drvdata(pdev);
mxc_gpio_save_regs(port); /* walk through all ports */
clk_disable_unprepare(port->clk); list_for_each_entry(port, &mxc_gpio_ports, node) {
mxc_gpio_save_regs(port);
clk_disable_unprepare(port->clk);
}
return 0; 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;
struct mxc_gpio_port *port = platform_get_drvdata(pdev);
int ret; int ret;
ret = clk_prepare_enable(port->clk); /* walk through all ports */
if (ret) list_for_each_entry(port, &mxc_gpio_ports, node) {
return ret; ret = clk_prepare_enable(port->clk);
mxc_gpio_restore_regs(port); if (ret) {
pr_err("mxc: failed to enable gpio clock %d\n", ret);
return 0; return;
}
mxc_gpio_restore_regs(port);
}
} }
static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { static struct syscore_ops mxc_gpio_syscore_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) .suspend = mxc_gpio_syscore_suspend,
.resume = mxc_gpio_syscore_resume,
}; };
static struct platform_driver mxc_gpio_driver = { static struct platform_driver mxc_gpio_driver = {
@ -584,7 +590,6 @@ static struct platform_driver mxc_gpio_driver = {
.name = "gpio-mxc", .name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids, .of_match_table = mxc_gpio_dt_ids,
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
.pm = &mxc_gpio_dev_pm_ops,
}, },
.probe = mxc_gpio_probe, .probe = mxc_gpio_probe,
.id_table = mxc_gpio_devtype, .id_table = mxc_gpio_devtype,
@ -592,6 +597,8 @@ static struct platform_driver mxc_gpio_driver = {
static int __init gpio_mxc_init(void) static int __init gpio_mxc_init(void)
{ {
register_syscore_ops(&mxc_gpio_syscore_ops);
return platform_driver_register(&mxc_gpio_driver); return platform_driver_register(&mxc_gpio_driver);
} }
subsys_initcall(gpio_mxc_init); subsys_initcall(gpio_mxc_init);

View File

@ -84,7 +84,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
port->both_edges &= ~pin_mask; port->both_edges &= ~pin_mask;
switch (type) { switch (type) {
case IRQ_TYPE_EDGE_BOTH: case IRQ_TYPE_EDGE_BOTH:
val = port->gc.get(&port->gc, d->hwirq); val = readl(port->base + PINCTRL_DIN(port)) & pin_mask;
if (val) if (val)
edge = GPIO_INT_FALL_EDGE; edge = GPIO_INT_FALL_EDGE;
else else

View File

@ -936,8 +936,7 @@ omap2_gpio_disable_level_quirk(struct gpio_bank *bank)
static int omap_mpuio_suspend_noirq(struct device *dev) static int omap_mpuio_suspend_noirq(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
void __iomem *mask_reg = bank->base + void __iomem *mask_reg = bank->base +
OMAP_MPUIO_GPIO_MASKIT / bank->stride; OMAP_MPUIO_GPIO_MASKIT / bank->stride;
unsigned long flags; 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) static int omap_mpuio_resume_noirq(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
void __iomem *mask_reg = bank->base + void __iomem *mask_reg = bank->base +
OMAP_MPUIO_GPIO_MASKIT / bank->stride; OMAP_MPUIO_GPIO_MASKIT / bank->stride;
unsigned long flags; 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) static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
unsigned long flags; unsigned long flags;
int error = 0; int error = 0;
@ -1656,8 +1653,7 @@ unlock:
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct gpio_bank *bank = dev_get_drvdata(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
unsigned long flags; unsigned long flags;
int error = 0; int error = 0;

View File

@ -20,6 +20,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_data/pca953x.h> #include <linux/platform_data/pca953x.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -30,6 +31,8 @@
#define PCA953X_INVERT 0x02 #define PCA953X_INVERT 0x02
#define PCA953X_DIRECTION 0x03 #define PCA953X_DIRECTION 0x03
#define REG_ADDR_MASK 0x3f
#define REG_ADDR_EXT 0x40
#define REG_ADDR_AI 0x80 #define REG_ADDR_AI 0x80
#define PCA957X_IN 0x00 #define PCA957X_IN 0x00
@ -58,7 +61,7 @@
#define PCA_GPIO_MASK 0x00FF #define PCA_GPIO_MASK 0x00FF
#define PCAL_GPIO_MASK 0x1f #define PCAL_GPIO_MASK 0x1f
#define PCAL_PINCTRL_MASK 0xe0 #define PCAL_PINCTRL_MASK 0x60
#define PCA_INT 0x0100 #define PCA_INT 0x0100
#define PCA_PCAL 0x0200 #define PCA_PCAL 0x0200
@ -119,25 +122,27 @@ struct pca953x_reg_config {
int direction; int direction;
int output; int output;
int input; int input;
int invert;
}; };
static const struct pca953x_reg_config pca953x_regs = { static const struct pca953x_reg_config pca953x_regs = {
.direction = PCA953X_DIRECTION, .direction = PCA953X_DIRECTION,
.output = PCA953X_OUTPUT, .output = PCA953X_OUTPUT,
.input = PCA953X_INPUT, .input = PCA953X_INPUT,
.invert = PCA953X_INVERT,
}; };
static const struct pca953x_reg_config pca957x_regs = { static const struct pca953x_reg_config pca957x_regs = {
.direction = PCA957X_CFG, .direction = PCA957X_CFG,
.output = PCA957X_OUT, .output = PCA957X_OUT,
.input = PCA957X_IN, .input = PCA957X_IN,
.invert = PCA957X_INVRT,
}; };
struct pca953x_chip { struct pca953x_chip {
unsigned gpio_start; unsigned gpio_start;
u8 reg_output[MAX_BANK];
u8 reg_direction[MAX_BANK];
struct mutex i2c_lock; struct mutex i2c_lock;
struct regmap *regmap;
#ifdef CONFIG_GPIO_PCA953X_IRQ #ifdef CONFIG_GPIO_PCA953X_IRQ
struct mutex irq_lock; struct mutex irq_lock;
@ -154,87 +159,177 @@ struct pca953x_chip {
struct regulator *regulator; struct regulator *regulator;
const struct pca953x_reg_config *regs; 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, static int pca953x_bank_shift(struct pca953x_chip *chip)
int off)
{ {
int ret; return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); }
int offset = off / BANK_SZ;
ret = i2c_smbus_read_byte_data(chip->client, #define PCA953x_BANK_INPUT BIT(0)
(reg << bank_shift) + offset); #define PCA953x_BANK_OUTPUT BIT(1)
*val = ret; #define PCA953x_BANK_POLARITY BIT(2)
#define PCA953x_BANK_CONFIG BIT(3)
if (ret < 0) { #define PCA957x_BANK_INPUT BIT(0)
dev_err(&chip->client->dev, "failed reading register\n"); #define PCA957x_BANK_POLARITY BIT(1)
return ret; #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, static bool pca953x_readable_register(struct device *dev, unsigned int reg)
int off)
{ {
int ret; struct pca953x_chip *chip = dev_get_drvdata(dev);
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); u32 bank;
int offset = off / BANK_SZ;
ret = i2c_smbus_write_byte_data(chip->client, if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
(reg << bank_shift) + offset, val); bank = PCA953x_BANK_INPUT | PCA953x_BANK_OUTPUT |
PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG;
if (ret < 0) { } else {
dev_err(&chip->client->dev, "failed writing register\n"); bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT |
return ret; PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG |
PCA957x_BANK_BUSHOLD;
} }
return 0; if (chip->driver_data & PCA_PCAL) {
bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK |
PCAL9xxx_BANK_IRQ_STAT;
}
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;
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;
}
if (chip->driver_data & PCA_PCAL)
bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK;
return pca953x_check_register(chip, reg, bank);
} }
static int pca953x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val) static bool pca953x_volatile_register(struct device *dev, unsigned int reg)
{ {
u16 word = get_unaligned((u16 *)val); struct pca953x_chip *chip = dev_get_drvdata(dev);
u32 bank;
return i2c_smbus_write_word_data(chip->client, reg << 1, word); 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 pca953x_check_register(chip, reg, bank);
} }
static int pca957x_write_regs_16(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 ret; int bank_shift = pca953x_bank_shift(chip);
ret = i2c_smbus_write_byte_data(chip->client, reg << 1, val[0]);
if (ret < 0)
return ret;
return i2c_smbus_write_byte_data(chip->client, (reg << 1) + 1, val[1]);
}
static int pca953x_write_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 addr = (reg & PCAL_GPIO_MASK) << bank_shift;
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1; int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
u8 regaddr = pinctrl | addr | (off / BANK_SZ);
return i2c_smbus_write_i2c_block_data(chip->client, /* Single byte read doesn't need AI bit set. */
pinctrl | addr | REG_ADDR_AI, if (!addrinc)
NBANK(chip), val); return regaddr;
/* Chips with 24 and more GPIOs always support Auto Increment */
if (write && NBANK(chip) > 2)
regaddr |= REG_ADDR_AI;
/* 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) 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) { if (ret < 0) {
dev_err(&chip->client->dev, "failed writing register\n"); dev_err(&chip->client->dev, "failed writing register\n");
return ret; return ret;
@ -243,42 +338,12 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
return 0; 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) 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; int ret;
ret = chip->read_regs(chip, reg, val); ret = regmap_bulk_read(chip->regmap, regaddr, val, NBANK(chip));
if (ret < 0) { if (ret < 0) {
dev_err(&chip->client->dev, "failed reading register\n"); dev_err(&chip->client->dev, "failed reading register\n");
return ret; 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) static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{ {
struct pca953x_chip *chip = gpiochip_get_data(gc); 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; int ret;
mutex_lock(&chip->i2c_lock); mutex_lock(&chip->i2c_lock);
reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ)); ret = regmap_write_bits(chip->regmap, dirreg, bit, bit);
ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off);
if (ret)
goto exit;
chip->reg_direction[off / BANK_SZ] = reg_val;
exit:
mutex_unlock(&chip->i2c_lock); mutex_unlock(&chip->i2c_lock);
return ret; return ret;
} }
@ -310,31 +370,21 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val) unsigned off, int val)
{ {
struct pca953x_chip *chip = gpiochip_get_data(gc); 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; int ret;
mutex_lock(&chip->i2c_lock); mutex_lock(&chip->i2c_lock);
/* set output level */ /* set output level */
if (val) ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
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) if (ret)
goto exit; goto exit;
chip->reg_output[off / BANK_SZ] = reg_val;
/* then direction */ /* then direction */
reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ)); ret = regmap_write_bits(chip->regmap, dirreg, bit, 0);
ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off);
if (ret)
goto exit;
chip->reg_direction[off / BANK_SZ] = reg_val;
exit: exit:
mutex_unlock(&chip->i2c_lock); mutex_unlock(&chip->i2c_lock);
return ret; return ret;
@ -343,11 +393,14 @@ exit:
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{ {
struct pca953x_chip *chip = gpiochip_get_data(gc); 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; u32 reg_val;
int ret; int ret;
mutex_lock(&chip->i2c_lock); 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); mutex_unlock(&chip->i2c_lock);
if (ret < 0) { if (ret < 0) {
/* NOTE: diagnostic already emitted; that's all we should /* 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 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) static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
{ {
struct pca953x_chip *chip = gpiochip_get_data(gc); struct pca953x_chip *chip = gpiochip_get_data(gc);
u8 reg_val; u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
int ret; true, false);
u8 bit = BIT(off % BANK_SZ);
mutex_lock(&chip->i2c_lock); mutex_lock(&chip->i2c_lock);
if (val) regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
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:
mutex_unlock(&chip->i2c_lock); mutex_unlock(&chip->i2c_lock);
} }
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
{ {
struct pca953x_chip *chip = gpiochip_get_data(gc); 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; u32 reg_val;
int ret; int ret;
mutex_lock(&chip->i2c_lock); 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); mutex_unlock(&chip->i2c_lock);
if (ret < 0) if (ret < 0)
return ret; return ret;
return !!(reg_val & (1u << (off % BANK_SZ))); return !!(reg_val & bit);
} }
static void pca953x_gpio_set_multiple(struct gpio_chip *gc, 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); struct pca953x_chip *chip = gpiochip_get_data(gc);
unsigned int bank_mask, bank_val; unsigned int bank_mask, bank_val;
int bank_shift, bank; int bank;
u8 reg_val[MAX_BANK]; u8 reg_val[MAX_BANK];
int ret; int ret;
bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
mutex_lock(&chip->i2c_lock); 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++) { for (bank = 0; bank < NBANK(chip); bank++) {
bank_mask = mask[bank / sizeof(*mask)] >> bank_mask = mask[bank / sizeof(*mask)] >>
((bank % sizeof(*mask)) * 8); ((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, pca953x_write_regs(chip, chip->regs->output, reg_val);
chip->regs->output << bank_shift,
NBANK(chip), reg_val);
if (ret)
goto exit;
memcpy(chip->reg_output, reg_val, NBANK(chip));
exit: exit:
mutex_unlock(&chip->i2c_lock); mutex_unlock(&chip->i2c_lock);
} }
@ -487,6 +527,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
u8 new_irqs; u8 new_irqs;
int level, i; int level, i;
u8 invert_irq_mask[MAX_BANK]; 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) { if (chip->driver_data & PCA_PCAL) {
/* Enable latch on interrupt-enabled inputs */ /* 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 */ /* Look for any newly setup interrupt */
for (i = 0; i < NBANK(chip); i++) { for (i = 0; i < NBANK(chip); i++) {
new_irqs = chip->irq_trig_fall[i] | chip->irq_trig_raise[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) { while (new_irqs) {
level = __ffs(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 pending_seen = false;
bool trigger_seen = false; bool trigger_seen = false;
u8 trigger[MAX_BANK]; u8 trigger[MAX_BANK];
int reg_direction[MAX_BANK];
int ret, i; int ret, i;
if (chip->driver_data & PCA_PCAL) { if (chip->driver_data & PCA_PCAL) {
@ -597,8 +642,10 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
return false; return false;
/* Remove output pins from the equation */ /* 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++) 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)); memcpy(old_stat, chip->irq_stat, NBANK(chip));
@ -652,6 +699,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
int irq_base) int irq_base)
{ {
struct i2c_client *client = chip->client; struct i2c_client *client = chip->client;
int reg_direction[MAX_BANK];
int ret, i; int ret, i;
if (client->irq && irq_base != -1 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 * interrupt. We have to rely on the previous read for
* this purpose. * this purpose.
*/ */
regmap_bulk_read(chip->regmap, chip->regs->direction,
reg_direction, NBANK(chip));
for (i = 0; i < NBANK(chip); i++) 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); mutex_init(&chip->irq_lock);
ret = devm_request_threaded_irq(&client->dev, ret = devm_request_threaded_irq(&client->dev,
@ -715,20 +765,19 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
} }
#endif #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; int ret;
u8 val[MAX_BANK]; u8 val[MAX_BANK];
chip->regs = &pca953x_regs; ret = regcache_sync_region(chip->regmap, chip->regs->output,
chip->regs->output + NBANK(chip));
ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output); if (ret != 0)
if (ret)
goto out; goto out;
ret = pca953x_read_regs(chip, chip->regs->direction, ret = regcache_sync_region(chip->regmap, chip->regs->direction,
chip->reg_direction); chip->regs->direction + NBANK(chip));
if (ret) if (ret != 0)
goto out; goto out;
/* set platform specific polarity inversion */ /* set platform specific polarity inversion */
@ -737,7 +786,7 @@ static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
else else
memset(val, 0, NBANK(chip)); memset(val, 0, NBANK(chip));
ret = pca953x_write_regs(chip, PCA953X_INVERT, val); ret = pca953x_write_regs(chip, chip->regs->invert, val);
out: out:
return ret; return ret;
} }
@ -747,22 +796,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
int ret; int ret;
u8 val[MAX_BANK]; u8 val[MAX_BANK];
chip->regs = &pca957x_regs; ret = device_pca95xx_init(chip, invert);
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);
if (ret) if (ret)
goto out; 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); mutex_init(&chip->i2c_lock);
/* /*
* In case we have an i2c-mux controlled by a GPIO provided by an * 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); pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
if (chip->gpio_chip.ngpio <= 8) { if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
chip->write_regs = pca953x_write_regs_8; chip->regs = &pca953x_regs;
chip->read_regs = pca953x_read_regs_8; ret = device_pca95xx_init(chip, invert);
} else if (chip->gpio_chip.ngpio >= 24) {
chip->write_regs = pca953x_write_regs_24;
chip->read_regs = pca953x_read_regs_24;
} else { } else {
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) chip->regs = &pca957x_regs;
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
ret = device_pca957x_init(chip, invert); ret = device_pca957x_init(chip, invert);
}
if (ret) if (ret)
goto err_exit; goto err_exit;
@ -914,7 +947,6 @@ static int pca953x_probe(struct i2c_client *client,
dev_warn(&client->dev, "setup failed, %d\n", ret); dev_warn(&client->dev, "setup failed, %d\n", ret);
} }
i2c_set_clientdata(client, chip);
return 0; return 0;
err_exit: err_exit:
@ -943,6 +975,91 @@ static int pca953x_remove(struct i2c_client *client)
return ret; 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 */ /* convenience to stop overlong match-table lines */
#define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int) #define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int)
#define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_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); 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 = { static struct i2c_driver pca953x_driver = {
.driver = { .driver = {
.name = "pca953x", .name = "pca953x",
.pm = &pca953x_pm_ops,
.of_match_table = pca953x_dt_ids, .of_match_table = pca953x_dt_ids,
.acpi_match_table = ACPI_PTR(pca953x_acpi_ids), .acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
}, },

View File

@ -1,25 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. * 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/gpio/driver.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h> #include <linux/slab.h>
#define PCH_EDGE_FALLING 0 #define PCH_EDGE_FALLING 0
@ -171,11 +159,10 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
return 0; return 0;
} }
#ifdef CONFIG_PM
/* /*
* Save register configuration and disable interrupts. * 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.ien_reg = ioread32(&chip->reg->ien);
chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask); 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) if (chip->ioh == INTEL_EG20T_PCH)
chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1); chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1);
if (chip->ioh == OKISEMI_ML7223n_IOH) if (chip->ioh == OKISEMI_ML7223n_IOH)
chip->pch_gpio_reg.gpio_use_sel_reg =\ chip->pch_gpio_reg.gpio_use_sel_reg = ioread32(&chip->reg->gpio_use_sel);
ioread32(&chip->reg->gpio_use_sel);
} }
/* /*
* This function restores the register configuration of the GPIO device. * 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.ien_reg, &chip->reg->ien);
iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask); 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) if (chip->ioh == INTEL_EG20T_PCH)
iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1); iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1);
if (chip->ioh == OKISEMI_ML7223n_IOH) if (chip->ioh == OKISEMI_ML7223n_IOH)
iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, &chip->reg->gpio_use_sel);
&chip->reg->gpio_use_sel);
} }
#endif
static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) 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->get = pch_gpio_get;
gpio->direction_output = pch_gpio_direction_output; gpio->direction_output = pch_gpio_direction_output;
gpio->set = pch_gpio_set; gpio->set = pch_gpio_set;
gpio->dbg_show = NULL;
gpio->base = -1; gpio->base = -1;
gpio->ngpio = gpio_pins[chip->ioh]; gpio->ngpio = gpio_pins[chip->ioh];
gpio->can_sleep = false; 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_reg = &chip->reg->im1;
im_pos = ch - 8; im_pos = ch - 8;
} }
dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n", dev_dbg(chip->dev, "irq=%d type=%d ch=%d pos=%d\n", irq, type, ch, im_pos);
__func__, irq, type, ch, im_pos);
spin_lock_irqsave(&chip->spinlock, flags); 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) static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
{ {
struct pch_gpio *chip = 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; int i, ret = IRQ_NONE;
for (i = 0; i < gpio_pins[chip->ioh]; i++) { for_each_set_bit(i, &reg_val, gpio_pins[chip->ioh]) {
if (reg_val & BIT(i)) { dev_dbg(chip->dev, "[%d]:irq=%d status=0x%lx\n", i, irq, reg_val);
dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n", generic_handle_irq(chip->irq_base + i);
__func__, i, irq, reg_val); ret = IRQ_HANDLED;
generic_handle_irq(chip->irq_base + i);
ret = IRQ_HANDLED;
}
} }
return ret; return ret;
} }
@ -367,29 +346,24 @@ static int pch_gpio_probe(struct pci_dev *pdev,
int irq_base; int irq_base;
u32 msk; u32 msk;
chip = kzalloc(sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL) if (chip == NULL)
return -ENOMEM; return -ENOMEM;
chip->dev = &pdev->dev; chip->dev = &pdev->dev;
ret = pci_enable_device(pdev); ret = pcim_enable_device(pdev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__); dev_err(&pdev->dev, "pci_enable_device FAILED");
goto err_pci_enable; return ret;
} }
ret = pci_request_regions(pdev, KBUILD_MODNAME); ret = pcim_iomap_regions(pdev, 1 << 1, KBUILD_MODNAME);
if (ret) { if (ret) {
dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret); dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
goto err_request_regions; return ret;
} }
chip->base = pci_iomap(pdev, 1, 0); chip->base = pcim_iomap_table(pdev)[1];
if (!chip->base) {
dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__);
ret = -ENOMEM;
goto err_iomap;
}
if (pdev->device == 0x8803) if (pdev->device == 0x8803)
chip->ioh = INTEL_EG20T_PCH; chip->ioh = INTEL_EG20T_PCH;
@ -402,13 +376,11 @@ static int pch_gpio_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, chip); pci_set_drvdata(pdev, chip);
spin_lock_init(&chip->spinlock); spin_lock_init(&chip->spinlock);
pch_gpio_setup(chip); pch_gpio_setup(chip);
#ifdef CONFIG_OF_GPIO
chip->gpio.of_node = pdev->dev.of_node; ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip);
#endif
ret = gpiochip_add_data(&chip->gpio, chip);
if (ret) { if (ret) {
dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n"); 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, 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) { if (irq_base < 0) {
dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n"); dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n");
chip->irq_base = -1; chip->irq_base = -1;
goto end; return 0;
} }
chip->irq_base = irq_base; 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, ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler,
IRQF_SHARED, KBUILD_MODNAME, chip); IRQF_SHARED, KBUILD_MODNAME, chip);
if (ret != 0) { if (ret) {
dev_err(&pdev->dev, dev_err(&pdev->dev, "request_irq failed\n");
"%s request_irq failed\n", __func__); return ret;
goto err_request_irq;
} }
ret = pch_gpio_alloc_generic_chip(chip, irq_base, return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
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) static int __maybe_unused pch_gpio_suspend(struct device *dev)
{ {
struct pch_gpio *chip = pci_get_drvdata(pdev); struct pci_dev *pdev = to_pci_dev(dev);
gpiochip_remove(&chip->gpio);
pci_iounmap(pdev, chip->base);
pci_release_regions(pdev);
pci_disable_device(pdev);
kfree(chip);
}
#ifdef CONFIG_PM
static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
{
s32 ret;
struct pch_gpio *chip = pci_get_drvdata(pdev); struct pch_gpio *chip = pci_get_drvdata(pdev);
unsigned long flags; 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); pch_gpio_save_reg_conf(chip);
spin_unlock_irqrestore(&chip->spinlock, flags); 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; 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); struct pch_gpio *chip = pci_get_drvdata(pdev);
unsigned long flags; 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); spin_lock_irqsave(&chip->spinlock, flags);
iowrite32(0x01, &chip->reg->reset); iowrite32(0x01, &chip->reg->reset);
iowrite32(0x00, &chip->reg->reset); iowrite32(0x00, &chip->reg->reset);
@ -519,10 +434,8 @@ static int pch_gpio_resume(struct pci_dev *pdev)
return 0; return 0;
} }
#else
#define pch_gpio_suspend NULL static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
#define pch_gpio_resume NULL
#endif
#define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_VENDOR_ID_ROHM 0x10DB
static const struct pci_device_id pch_gpio_pcidev_id[] = { static const struct pci_device_id pch_gpio_pcidev_id[] = {
@ -538,12 +451,12 @@ static struct pci_driver pch_gpio_driver = {
.name = "pch_gpio", .name = "pch_gpio",
.id_table = pch_gpio_pcidev_id, .id_table = pch_gpio_pcidev_id,
.probe = pch_gpio_probe, .probe = pch_gpio_probe,
.remove = pch_gpio_remove, .driver = {
.suspend = pch_gpio_suspend, .pm = &pch_gpio_pm_ops,
.resume = pch_gpio_resume },
}; };
module_pci_driver(pch_gpio_driver); module_pci_driver(pch_gpio_driver);
MODULE_DESCRIPTION("PCH GPIO PCI Driver"); MODULE_DESCRIPTION("PCH GPIO PCI Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");

View File

@ -146,7 +146,7 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
port_state = ioread8(ports[i]); port_state = ioread8(ports[i]);
/* store acquired bits at respective bits array offset */ /* 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; return 0;

View File

@ -243,7 +243,7 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
port_state = ioread8(&idio24gpio->reg->ttl_in0_7); port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
/* store acquired bits at respective bits array offset */ /* 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; return 0;

View File

@ -54,6 +54,7 @@ struct pl061 {
void __iomem *base; void __iomem *base;
struct gpio_chip gc; struct gpio_chip gc;
struct irq_chip irq_chip;
int parent_irq; int parent_irq;
#ifdef CONFIG_PM #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); 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) static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{ {
struct device *dev = &adev->dev; 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 * 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 */ writeb(0, pl061->base + GPIOIE); /* disable irqs */
irq = adev->irq[0]; irq = adev->irq[0];
if (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; 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, 0, handle_bad_irq,
IRQ_TYPE_NONE); IRQ_TYPE_NONE);
if (ret) { if (ret) {
dev_info(&adev->dev, "could not add irqchip\n"); dev_info(&adev->dev, "could not add irqchip\n");
return ret; return ret;
} }
gpiochip_set_chained_irqchip(&pl061->gc, &pl061_irqchip, gpiochip_set_chained_irqchip(&pl061->gc, &pl061->irq_chip,
irq, pl061_irq_handler); irq, pl061_irq_handler);
amba_set_drvdata(adev, pl061); amba_set_drvdata(adev, pl061);

View File

@ -206,6 +206,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
} }
fw = rpi_firmware_get(fw_node); fw = rpi_firmware_get(fw_node);
of_node_put(fw_node);
if (!fw) if (!fw)
return -EPROBE_DEFER; return -EPROBE_DEFER;

View File

@ -1,17 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Renesas R-Car GPIO Support * Renesas R-Car GPIO Support
* *
* Copyright (C) 2014 Renesas Electronics Corporation * Copyright (C) 2014 Renesas Electronics Corporation
* Copyright (C) 2013 Magnus Damm * 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> #include <linux/err.h>
@ -43,7 +35,7 @@ struct gpio_rcar_bank_info {
struct gpio_rcar_priv { struct gpio_rcar_priv {
void __iomem *base; void __iomem *base;
spinlock_t lock; spinlock_t lock;
struct platform_device *pdev; struct device *dev;
struct gpio_chip gpio_chip; struct gpio_chip gpio_chip;
struct irq_chip irq_chip; struct irq_chip irq_chip;
unsigned int irq_parent; 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); struct gpio_rcar_priv *p = gpiochip_get_data(gc);
unsigned int hwirq = irqd_to_hwirq(d); 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) { switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_LEVEL_HIGH: 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) { if (p->irq_parent) {
error = irq_set_irq_wake(p->irq_parent, on); error = irq_set_irq_wake(p->irq_parent, on);
if (error) { if (error) {
dev_dbg(&p->pdev->dev, dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n",
"irq %u doesn't support irq_set_wake\n",
p->irq_parent); p->irq_parent);
p->irq_parent = 0; 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); struct gpio_rcar_priv *p = gpiochip_get_data(chip);
int error; int error;
error = pm_runtime_get_sync(&p->pdev->dev); error = pm_runtime_get_sync(p->dev);
if (error < 0) if (error < 0)
return error; return error;
error = pinctrl_gpio_request(chip->base + offset); error = pinctrl_gpio_request(chip->base + offset);
if (error) if (error)
pm_runtime_put(&p->pdev->dev); pm_runtime_put(p->dev);
return error; 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); 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) 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) 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; const struct gpio_rcar_info *info;
struct of_phandle_args args; struct of_phandle_args args;
int ret; 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); ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
*npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
p->has_both_edge_trigger = info->has_both_edge_trigger; p->has_both_edge_trigger = info->has_both_edge_trigger;
if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) { if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) {
dev_warn(&p->pdev->dev, dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",
"Invalid number of gpio lines %u, using %u\n", *npins, *npins, RCAR_MAX_GPIO_PER_BANK);
RCAR_MAX_GPIO_PER_BANK);
*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) if (!p)
return -ENOMEM; return -ENOMEM;
p->pdev = pdev; p->dev = dev;
spin_lock_init(&p->lock); spin_lock_init(&p->lock);
/* Get device configuration from DT node */ /* Get device configuration from DT node */

View File

@ -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>");

View File

@ -1,32 +1,19 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* GPIO interface for Intel Poulsbo SCH * GPIO interface for Intel Poulsbo SCH
* *
* Copyright (c) 2010 CompuLab Ltd * Copyright (c) 2010 CompuLab Ltd
* Author: Denis Turischev <denis@compulab.co.il> * 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/kernel.h>
#include <linux/module.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/pci_ids.h>
#include <linux/gpio/driver.h> #include <linux/platform_device.h>
#define GEN 0x00 #define GEN 0x00
#define GIO 0x04 #define GIO 0x04
@ -235,5 +222,5 @@ module_platform_driver(sch_gpio_driver);
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sch_gpio"); MODULE_ALIAS("platform:sch_gpio");

View File

@ -188,7 +188,7 @@ static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset,
struct sch311x_gpio_block *block = gpiochip_get_data(chip); struct sch311x_gpio_block *block = gpiochip_get_data(chip);
spin_lock(&block->lock); spin_lock(&block->lock);
__sch311x_gpio_set(block, offset, value); __sch311x_gpio_set(block, offset, value);
spin_unlock(&block->lock); spin_unlock(&block->lock);
} }

View File

@ -1,26 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* GPIO interface for Intel Sodaville SoCs. * GPIO interface for Intel Sodaville SoCs.
* *
* Copyright (c) 2010, 2011 Intel Corporation * Copyright (c) 2010, 2011 Intel Corporation
* *
* Author: Hans J. Koch <hjk@linutronix.de> * 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/errno.h>
#include <linux/gpio/driver.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/of_irq.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of_irq.h>
#include <linux/gpio/driver.h>
#define DRV_NAME "sdv_gpio" #define DRV_NAME "sdv_gpio"
#define SDV_NUM_PUB_GPIOS 12 #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) static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data)
{ {
struct sdv_gpio_chip_data *sd = 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); irq_stat &= readl(sd->gpio_pub_base + GPIO_INT);
if (!irq_stat) if (!irq_stat)
return IRQ_NONE; return IRQ_NONE;
while (irq_stat) { for_each_set_bit(irq_bit, &irq_stat, 32)
u32 irq_bit = __fls(irq_stat);
irq_stat &= ~BIT(irq_bit);
generic_handle_irq(irq_find_mapping(sd->id, irq_bit)); generic_handle_irq(irq_find_mapping(sd->id, irq_bit));
}
return IRQ_HANDLED; 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 * we unmask & ACK the IRQ before the source of the interrupt is gone
* then the interrupt is active again. * then the interrupt is active again.
*/ */
sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base, sd->gc = devm_irq_alloc_generic_chip(&pdev->dev, "sdv-gpio", 1,
sd->gpio_pub_base, handle_fasteoi_irq); sd->irq_base,
sd->gpio_pub_base,
handle_fasteoi_irq);
if (!sd->gc) if (!sd->gc)
return -ENOMEM; return -ENOMEM;
@ -186,70 +181,52 @@ static int sdv_gpio_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_id) const struct pci_device_id *pci_id)
{ {
struct sdv_gpio_chip_data *sd; struct sdv_gpio_chip_data *sd;
unsigned long addr;
const void *prop;
int len;
int ret; int ret;
u32 mux_val; u32 mux_val;
sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL); sd = devm_kzalloc(&pdev->dev, sizeof(*sd), GFP_KERNEL);
if (!sd) if (!sd)
return -ENOMEM; return -ENOMEM;
ret = pci_enable_device(pdev);
ret = pcim_enable_device(pdev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "can't enable device.\n"); 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) { if (ret) {
dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR); 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); sd->gpio_pub_base = pcim_iomap_table(pdev)[GPIO_BAR];
if (!addr) {
ret = -ENODEV;
goto release_reg;
}
sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR));
prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len); ret = of_property_read_u32(pdev->dev.of_node, "intel,muxctl", &mux_val);
if (prop && len == 4) { if (!ret)
mux_val = of_read_number(prop, 1);
writel(mux_val, sd->gpio_pub_base + GPMUXCTL); writel(mux_val, sd->gpio_pub_base + GPMUXCTL);
}
ret = bgpio_init(&sd->chip, &pdev->dev, 4, ret = bgpio_init(&sd->chip, &pdev->dev, 4,
sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR, sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR,
NULL, sd->gpio_pub_base + GPOER, NULL, 0); NULL, sd->gpio_pub_base + GPOER, NULL, 0);
if (ret) if (ret)
goto unmap; return ret;
sd->chip.ngpio = SDV_NUM_PUB_GPIOS; 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) { if (ret < 0) {
dev_err(&pdev->dev, "gpiochip_add() failed.\n"); dev_err(&pdev->dev, "gpiochip_add() failed.\n");
goto unmap; return ret;
} }
ret = sdv_register_irqsupport(sd, pdev); ret = sdv_register_irqsupport(sd, pdev);
if (ret) if (ret)
goto unmap; return ret;
pci_set_drvdata(pdev, sd); pci_set_drvdata(pdev, sd);
dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n"); dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n");
return 0; 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[] = { static const struct pci_device_id sdv_gpio_pci_ids[] = {

View File

@ -404,8 +404,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int tegra_gpio_resume(struct device *dev) static int tegra_gpio_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
struct tegra_gpio_info *tgi = platform_get_drvdata(pdev);
unsigned long flags; unsigned long flags;
unsigned int b, p; unsigned int b, p;
@ -444,8 +443,7 @@ static int tegra_gpio_resume(struct device *dev)
static int tegra_gpio_suspend(struct device *dev) static int tegra_gpio_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
struct tegra_gpio_info *tgi = platform_get_drvdata(pdev);
unsigned long flags; unsigned long flags;
unsigned int b, p; unsigned int b, p;

View File

@ -279,7 +279,7 @@ static void tegra186_irq_unmask(struct irq_data *data)
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); 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); struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
void __iomem *base; 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_TYPE_MASK;
value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL;
switch (flow & IRQ_TYPE_SENSE_MASK) { switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_NONE: case IRQ_TYPE_NONE:
break; 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); 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); irq_set_handler_locked(data, handle_level_irq);
else else
irq_set_handler_locked(data, handle_edge_irq); irq_set_handler_locked(data, handle_edge_irq);

View File

@ -1,16 +1,7 @@
/* // SPDX-License-Identifier: GPL-2.0
* Copyright (C) 2017 Socionext Inc. //
* Author: Masahiro Yamada <yamada.masahiro@socionext.com> // 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.
*/
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>

View File

@ -7,6 +7,7 @@
* Author: Stefan Agner <stefan@agner.ch>. * Author: Stefan Agner <stefan@agner.ch>.
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/init.h> #include <linux/init.h>
@ -32,6 +33,8 @@ struct vf610_gpio_port {
void __iomem *gpio_base; void __iomem *gpio_base;
const struct fsl_gpio_soc_data *sdata; const struct fsl_gpio_soc_data *sdata;
u8 irqc[VF610_GPIO_PER_PORT]; u8 irqc[VF610_GPIO_PER_PORT];
struct clk *clk_port;
struct clk *clk_gpio;
int irq; int irq;
}; };
@ -271,6 +274,33 @@ static int vf610_gpio_probe(struct platform_device *pdev)
if (port->irq < 0) if (port->irq < 0)
return port->irq; 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 = &port->gc;
gc->of_node = np; gc->of_node = np;
gc->parent = dev; gc->parent = dev;
@ -305,12 +335,26 @@ static int vf610_gpio_probe(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver vf610_gpio_driver = {
.driver = { .driver = {
.name = "gpio-vf610", .name = "gpio-vf610",
.of_match_table = vf610_gpio_dt_ids, .of_match_table = vf610_gpio_dt_ids,
}, },
.probe = vf610_gpio_probe, .probe = vf610_gpio_probe,
.remove = vf610_gpio_remove,
}; };
builtin_platform_driver(vf610_gpio_driver); builtin_platform_driver(vf610_gpio_driver);

View File

@ -169,7 +169,7 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
port_state = inb(ws16c48gpio->base + i); port_state = inb(ws16c48gpio->base + i);
/* store acquired bits at respective bits array offset */ /* 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; return 0;

View File

@ -357,6 +357,28 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
return 0; return 0;
} }
/**
* 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 * zynq_gpio_irq_mask - Disable the interrupts for a gpio pin
* @irq_data: per irq and chip data passed down to chip functions * @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) static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
clk_disable_unprepare(gpio->clk); 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) static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
return clk_prepare_enable(gpio->clk); 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->free = zynq_gpio_free;
chip->direction_input = zynq_gpio_dir_in; chip->direction_input = zynq_gpio_dir_in;
chip->direction_output = zynq_gpio_dir_out; 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->base = of_alias_get_id(pdev->dev.of_node, "gpio");
chip->ngpio = gpio->p_data->ngpio; chip->ngpio = gpio->p_data->ngpio;

View File

@ -217,7 +217,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
if (!handler) if (!handler)
return AE_OK; 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)) { if (IS_ERR(desc)) {
dev_err(chip->parent, "Failed to request GPIO\n"); dev_err(chip->parent, "Failed to request GPIO\n");
return AE_ERROR; return AE_ERROR;
@ -913,23 +913,15 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
if (!found) { if (!found) {
enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio); enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio);
const char *label = "ACPI:OpRegion"; 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)) { if (IS_ERR(desc)) {
status = AE_ERROR; status = AE_ERROR;
mutex_unlock(&achip->conn_lock); mutex_unlock(&achip->conn_lock);
goto out; 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); conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn) { if (!conn) {
status = AE_NO_MEMORY; status = AE_NO_MEMORY;

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0+
/* /*
* OF helpers for the GPIO API * OF helpers for the GPIO API
* *
@ -54,9 +54,31 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
} }
static void of_gpio_flags_quirks(struct device_node *np, static void of_gpio_flags_quirks(struct device_node *np,
const char *propname,
enum of_gpio_flags *flags, enum of_gpio_flags *flags,
int index) 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. * Some GPIO fixed regulator quirks.
* Note that active low is the default. * 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; goto out;
if (flags) 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", pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
__func__, propname, np, index, __func__, propname, np, index,

View File

@ -1512,19 +1512,6 @@ static void devm_gpio_chip_release(struct device *dev, void *res)
gpiochip_remove(chip); 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() * devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
* @dev: pointer to the device that gpio_chip belongs to. * @dev: pointer to the device that gpio_chip belongs to.
@ -1564,23 +1551,6 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
} }
EXPORT_SYMBOL_GPL(devm_gpiochip_add_data); 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 * gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function * @data: data to pass to match function
@ -2299,6 +2269,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
unsigned long flags; unsigned long flags;
unsigned offset; unsigned offset;
if (label) {
label = kstrdup_const(label, GFP_KERNEL);
if (!label)
return -ENOMEM;
}
spin_lock_irqsave(&gpio_lock, flags); spin_lock_irqsave(&gpio_lock, flags);
/* NOTE: gpio_request() can be called in early boot, /* 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 ? : "?"); desc_set_label(desc, label ? : "?");
status = 0; status = 0;
} else { } else {
kfree_const(label);
status = -EBUSY; status = -EBUSY;
goto done; goto done;
} }
@ -2325,6 +2302,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (status < 0) { if (status < 0) {
desc_set_label(desc, NULL); desc_set_label(desc, NULL);
kfree_const(label);
clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_REQUESTED, &desc->flags);
goto done; goto done;
} }
@ -2420,6 +2398,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
chip->free(chip, gpio_chip_hwgpio(desc)); chip->free(chip, gpio_chip_hwgpio(desc));
spin_lock_irqsave(&gpio_lock, flags); spin_lock_irqsave(&gpio_lock, flags);
} }
kfree_const(desc->label);
desc_set_label(desc, NULL); desc_set_label(desc, NULL);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags); clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_REQUESTED, &desc->flags);
@ -2476,6 +2455,7 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* @chip: GPIO chip * @chip: GPIO chip
* @hwnum: hardware number of the GPIO for which to request the descriptor * @hwnum: hardware number of the GPIO for which to request the descriptor
* @label: label for the GPIO * @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 * Function allows GPIO chip drivers to request and use their own GPIO
* descriptors via gpiolib API. Difference to gpiod_request() is that this * descriptors via gpiolib API. Difference to gpiod_request() is that this
@ -2488,7 +2468,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* code on failure. * code on failure.
*/ */
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, 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); struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
int err; int err;
@ -2502,6 +2483,13 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
if (err < 0) if (err < 0)
return ERR_PTR(err); 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; return desc;
} }
EXPORT_SYMBOL_GPL(gpiochip_request_own_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 * @desc: gpio to set the consumer name on
* @name: the new consumer name * @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); VALIDATE_DESC(desc);
/* Just overwrite whatever the previous name was */ if (name) {
desc->label = 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); 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); chip = gpiod_to_chip(desc);
hwnum = gpio_chip_hwgpio(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)) { if (IS_ERR(local_desc)) {
status = PTR_ERR(local_desc); status = PTR_ERR(local_desc);
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",

View File

@ -1203,7 +1203,7 @@ static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev,
return -EINVAL; return -EINVAL;
dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin, dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin,
"HID/I2C:Event"); "HID/I2C:Event", 0);
if (IS_ERR(dev->desc[pin])) { if (IS_ERR(dev->desc[pin])) {
dev_err(dev->gc.parent, "Failed to request GPIO\n"); dev_err(dev->gc.parent, "Failed to request GPIO\n");
return PTR_ERR(dev->desc[pin]); return PTR_ERR(dev->desc[pin]);

View File

@ -21,6 +21,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h> /* GPIO descriptor enum */
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/platform_device.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; unsigned int wait_pin = gpmc_s.wait_pin;
waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip, waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
wait_pin, "WAITPIN"); wait_pin, "WAITPIN",
0);
if (IS_ERR(waitpin_desc)) { if (IS_ERR(waitpin_desc)) {
dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin); dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
ret = PTR_ERR(waitpin_desc); ret = PTR_ERR(waitpin_desc);

View File

@ -14,6 +14,34 @@
#include <dt-bindings/gpio/gpio.h> #include <dt-bindings/gpio/gpio.h>
/* GPIOs implemented by main GPIO controller */ /* GPIOs implemented by main GPIO controller */
#define TEGRA186_MAIN_GPIO_PORT_A 0
#define TEGRA186_MAIN_GPIO_PORT_B 1
#define TEGRA186_MAIN_GPIO_PORT_C 2
#define TEGRA186_MAIN_GPIO_PORT_D 3
#define TEGRA186_MAIN_GPIO_PORT_E 4
#define TEGRA186_MAIN_GPIO_PORT_F 5
#define TEGRA186_MAIN_GPIO_PORT_G 6
#define TEGRA186_MAIN_GPIO_PORT_H 7
#define TEGRA186_MAIN_GPIO_PORT_I 8
#define TEGRA186_MAIN_GPIO_PORT_J 9
#define TEGRA186_MAIN_GPIO_PORT_K 10
#define TEGRA186_MAIN_GPIO_PORT_L 11
#define TEGRA186_MAIN_GPIO_PORT_M 12
#define TEGRA186_MAIN_GPIO_PORT_N 13
#define TEGRA186_MAIN_GPIO_PORT_O 14
#define TEGRA186_MAIN_GPIO_PORT_P 15
#define TEGRA186_MAIN_GPIO_PORT_Q 16
#define TEGRA186_MAIN_GPIO_PORT_R 17
#define TEGRA186_MAIN_GPIO_PORT_T 18
#define TEGRA186_MAIN_GPIO_PORT_X 19
#define TEGRA186_MAIN_GPIO_PORT_Y 20
#define TEGRA186_MAIN_GPIO_PORT_BB 21
#define TEGRA186_MAIN_GPIO_PORT_CC 22
#define TEGRA186_MAIN_GPIO(port, offset) \
((TEGRA186_MAIN_GPIO_PORT_##port * 8) + offset)
/* need to keep these for backwards-compatibility */
#define TEGRA_MAIN_GPIO_PORT_A 0 #define TEGRA_MAIN_GPIO_PORT_A 0
#define TEGRA_MAIN_GPIO_PORT_B 1 #define TEGRA_MAIN_GPIO_PORT_B 1
#define TEGRA_MAIN_GPIO_PORT_C 2 #define TEGRA_MAIN_GPIO_PORT_C 2
@ -42,6 +70,19 @@
((TEGRA_MAIN_GPIO_PORT_##port * 8) + offset) ((TEGRA_MAIN_GPIO_PORT_##port * 8) + offset)
/* GPIOs implemented by AON GPIO controller */ /* GPIOs implemented by AON GPIO controller */
#define TEGRA186_AON_GPIO_PORT_S 0
#define TEGRA186_AON_GPIO_PORT_U 1
#define TEGRA186_AON_GPIO_PORT_V 2
#define TEGRA186_AON_GPIO_PORT_W 3
#define TEGRA186_AON_GPIO_PORT_Z 4
#define TEGRA186_AON_GPIO_PORT_AA 5
#define TEGRA186_AON_GPIO_PORT_EE 6
#define TEGRA186_AON_GPIO_PORT_FF 7
#define TEGRA186_AON_GPIO(port, offset) \
((TEGRA186_AON_GPIO_PORT_##port * 8) + offset)
/* need to keep these for backwards-compatibility */
#define TEGRA_AON_GPIO_PORT_S 0 #define TEGRA_AON_GPIO_PORT_S 0
#define TEGRA_AON_GPIO_PORT_U 1 #define TEGRA_AON_GPIO_PORT_U 1
#define TEGRA_AON_GPIO_PORT_V 2 #define TEGRA_AON_GPIO_PORT_V 2

View File

@ -163,7 +163,7 @@ int gpiod_is_active_low(const struct gpio_desc *desc);
int gpiod_cansleep(const struct gpio_desc *desc); int gpiod_cansleep(const struct gpio_desc *desc);
int gpiod_to_irq(const struct gpio_desc *desc); int gpiod_to_irq(const struct gpio_desc *desc);
void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name); int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name);
/* Convert between the old gpio_ and new gpiod_ interfaces */ /* Convert between the old gpio_ and new gpiod_ interfaces */
struct gpio_desc *gpio_to_desc(unsigned gpio); struct gpio_desc *gpio_to_desc(unsigned gpio);
@ -509,15 +509,17 @@ static inline int gpiod_to_irq(const struct gpio_desc *desc)
return -EINVAL; return -EINVAL;
} }
static inline void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) static inline int gpiod_set_consumer_name(struct gpio_desc *desc,
const char *name)
{ {
/* GPIO can never have been requested */ /* GPIO can never have been requested */
WARN_ON(1); WARN_ON(1);
return -EINVAL;
} }
static inline struct gpio_desc *gpio_to_desc(unsigned gpio) static inline struct gpio_desc *gpio_to_desc(unsigned gpio)
{ {
return ERR_PTR(-EINVAL); return NULL;
} }
static inline int desc_to_gpio(const struct gpio_desc *desc) static inline int desc_to_gpio(const struct gpio_desc *desc)

View File

@ -17,6 +17,7 @@ struct device_node;
struct seq_file; struct seq_file;
struct gpio_device; struct gpio_device;
struct module; struct module;
enum gpiod_flags;
#ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB
@ -166,11 +167,6 @@ struct gpio_irq_chip {
*/ */
void (*irq_disable)(struct irq_data *data); void (*irq_disable)(struct irq_data *data);
}; };
static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip)
{
return container_of(chip, struct gpio_irq_chip, chip);
}
#endif #endif
/** /**
@ -422,7 +418,6 @@ static inline int gpiochip_add(struct gpio_chip *chip)
extern void gpiochip_remove(struct gpio_chip *chip); extern void gpiochip_remove(struct gpio_chip *chip);
extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip, extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
void *data); void *data);
extern void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip);
extern struct gpio_chip *gpiochip_find(void *data, extern struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *chip, void *data)); int (*match)(struct gpio_chip *chip, void *data));
@ -610,7 +605,8 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip)
#endif /* CONFIG_PINCTRL */ #endif /* CONFIG_PINCTRL */
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
const char *label); const char *label,
enum gpiod_flags flags);
void gpiochip_free_own_desc(struct gpio_desc *desc); void gpiochip_free_own_desc(struct gpio_desc *desc);
#else /* CONFIG_GPIOLIB */ #else /* CONFIG_GPIOLIB */