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:
commit
24dc83635f
|
@ -158,14 +158,24 @@ Security Module (SECUMOD)
|
|||
|
||||
The Security Module macrocell provides all necessary secure functions to avoid
|
||||
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:
|
||||
- compatible: Should be "atmel,<chip>-secumod", "syscon".
|
||||
<chip> can be "sama5d2".
|
||||
- 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 {
|
||||
compatible = "atmel,sama5d2-secumod", "syscon";
|
||||
reg = <0xfc040000 0x100>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
|
43
Documentation/devicetree/bindings/gpio/cdns,gpio.txt
Normal file
43
Documentation/devicetree/bindings/gpio/cdns,gpio.txt
Normal 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>;
|
||||
};
|
|
@ -24,6 +24,12 @@ Required properties for GPIO node:
|
|||
4 = active high 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"
|
||||
node.
|
||||
|
||||
|
|
|
@ -3,12 +3,24 @@ NXP LPC18xx/43xx GPIO controller Device Tree Bindings
|
|||
|
||||
Required properties:
|
||||
- compatible : Should be "nxp,lpc1850-gpio"
|
||||
- reg : Address and length of the register set for the device
|
||||
- clocks : Clock specifier (see clock bindings for details)
|
||||
- gpio-controller : Marks the device node as a GPIO controller.
|
||||
- #gpio-cells : Should be two
|
||||
- First cell is the GPIO line number
|
||||
- Second cell is used to specify polarity
|
||||
- reg : List of addresses and lengths of the GPIO controller
|
||||
register sets
|
||||
- reg-names : Should be "gpio", "gpio-pin-ic", "gpio-group0-ic" and
|
||||
"gpio-gpoup1-ic"
|
||||
- clocks : Phandle and clock specifier pair for GPIO controller
|
||||
- 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:
|
||||
- gpio-ranges : Mapping between GPIO and pinctrl
|
||||
|
@ -19,21 +31,29 @@ Example:
|
|||
|
||||
gpio: gpio@400f4000 {
|
||||
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>;
|
||||
resets = <&rgu 28>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
gpio-ranges = <&pinctrl LPC_GPIO(0,0) LPC_PIN(0,0) 2>,
|
||||
...
|
||||
<&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5) 7>;
|
||||
};
|
||||
|
||||
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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ Required Properties:
|
|||
- "renesas,gpio-r8a7745": for R8A7745 (RZ/G1E) 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-r8a774c0": for R8A774C0 (RZ/G2E) 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-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.
|
||||
|
|
|
@ -43,7 +43,7 @@ gpio: gpio@20000 {
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
porta: gpio-controller@0 {
|
||||
porta: gpio@0 {
|
||||
compatible = "snps,dw-apb-gpio-port";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
@ -55,7 +55,7 @@ gpio: gpio@20000 {
|
|||
interrupts = <0>;
|
||||
};
|
||||
|
||||
portb: gpio-controller@1 {
|
||||
portb: gpio@1 {
|
||||
compatible = "snps,dw-apb-gpio-port";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
|
|
@ -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::
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -256,7 +256,6 @@ GPIO
|
|||
devm_gpiod_put()
|
||||
devm_gpiod_unhinge()
|
||||
devm_gpiochip_add_data()
|
||||
devm_gpiochip_remove()
|
||||
devm_gpio_request()
|
||||
devm_gpio_request_one()
|
||||
devm_gpio_free()
|
||||
|
|
|
@ -6412,7 +6412,6 @@ F: drivers/media/rc/gpio-ir-tx.c
|
|||
|
||||
GPIO MOCKUP DRIVER
|
||||
M: Bamvor Jian Zhang <bamv2005@gmail.com>
|
||||
R: Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-mockup.c
|
||||
|
@ -9933,6 +9932,12 @@ M: Nicolas Ferre <nicolas.ferre@microchip.com>
|
|||
S: Supported
|
||||
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
|
||||
M: Nicolas Ferre <nicolas.ferre@microchip.com>
|
||||
S: Supported
|
||||
|
|
|
@ -103,7 +103,7 @@ void __init ams_delta_init_fiq(struct gpio_chip *chip,
|
|||
}
|
||||
|
||||
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)) {
|
||||
pr_err("%s: failed to get GPIO pin %d (%ld)\n",
|
||||
__func__, i, PTR_ERR(gpiod));
|
||||
|
|
|
@ -601,7 +601,7 @@ static void __init modem_assign_irq(struct gpio_chip *chip)
|
|||
struct gpio_desc *gpiod;
|
||||
|
||||
gpiod = gpiochip_request_own_desc(chip, AMS_DELTA_GPIO_PIN_MODEM_IRQ,
|
||||
"modem_irq");
|
||||
"modem_irq", 0);
|
||||
if (IS_ERR(gpiod)) {
|
||||
pr_err("%s: modem IRQ GPIO request failed (%ld)\n", __func__,
|
||||
PTR_ERR(gpiod));
|
||||
|
@ -809,7 +809,7 @@ static void __init ams_delta_led_init(struct gpio_chip *chip)
|
|||
int 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)) {
|
||||
pr_warn("%s: %s GPIO %d request failed (%ld)\n",
|
||||
__func__, LATCH1_LABEL, i, PTR_ERR(gpiod));
|
||||
|
|
|
@ -160,6 +160,14 @@ config GPIO_BRCMSTB
|
|||
help
|
||||
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
|
||||
|
||||
config GPIO_CADENCE
|
||||
tristate "Cadence GPIO support"
|
||||
depends on OF_GPIO
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to enable support for Cadence GPIO controller.
|
||||
|
||||
config GPIO_CLPS711X
|
||||
tristate "CLPS711X GPIO support"
|
||||
depends on ARCH_CLPS711X || COMPILE_TEST
|
||||
|
@ -288,6 +296,7 @@ config GPIO_LPC18XX
|
|||
tristate "NXP LPC18XX/43XX GPIO support"
|
||||
default y if ARCH_LPC18XX
|
||||
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
Select this option to enable GPIO driver for
|
||||
NXP LPC18XX/43XX devices.
|
||||
|
@ -429,6 +438,18 @@ config GPIO_REG
|
|||
A 32-bit single register GPIO fixed in/out implementation. This
|
||||
can be used to represent any register as a set of GPIO signals.
|
||||
|
||||
config GPIO_SAMA5D2_PIOBU
|
||||
tristate "SAMA5D2 PIOBU GPIO support"
|
||||
depends on MFD_SYSCON
|
||||
depends on OF_GPIO
|
||||
select GPIO_SYSCON
|
||||
help
|
||||
Say yes here to use the PIOBU pins as GPIOs.
|
||||
|
||||
PIOBU pins on the SAMA5D2 can be used as GPIOs.
|
||||
The difference from regular GPIOs is that they
|
||||
maintain their value during backup/self-refresh.
|
||||
|
||||
config GPIO_SIOX
|
||||
tristate "SIOX GPIO support"
|
||||
depends on SIOX
|
||||
|
@ -849,6 +870,7 @@ config GPIO_MC9S08DZ60
|
|||
|
||||
config GPIO_PCA953X
|
||||
tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to provide access to several register-oriented
|
||||
SMBus I/O expanders, made mostly by NXP or TI. Compatible
|
||||
|
|
|
@ -37,6 +37,7 @@ obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
|
|||
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
|
||||
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
|
||||
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
|
||||
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
|
||||
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
|
||||
obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o
|
||||
obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o
|
||||
|
@ -108,6 +109,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
|
|||
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
|
||||
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
|
||||
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
||||
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
|
||||
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
|
||||
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
||||
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
|
||||
|
|
109
drivers/gpio/TODO
Normal file
109
drivers/gpio/TODO
Normal 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.
|
|
@ -222,7 +222,7 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
|||
port_state = inb(dio48egpio->base + ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
bits[word_index] |= (port_state << word_offset) & word_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -128,7 +128,7 @@ static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
|||
port_state = inb(idi48gpio->base + ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
bits[word_index] |= (port_state << word_offset) & word_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1185,7 +1185,6 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
gpio->chip.parent = &pdev->dev;
|
||||
gpio->chip.ngpio = gpio->config->nr_gpios;
|
||||
gpio->chip.parent = &pdev->dev;
|
||||
gpio->chip.direction_input = aspeed_gpio_dir_in;
|
||||
gpio->chip.direction_output = aspeed_gpio_dir_out;
|
||||
gpio->chip.get_direction = aspeed_gpio_get_direction;
|
||||
|
|
291
drivers/gpio/gpio-cadence.c
Normal file
291
drivers/gpio/gpio-cadence.c
Normal 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");
|
|
@ -748,8 +748,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwapb_gpio_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
|
||||
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
|
||||
struct gpio_chip *gc = &gpio->ports[0].gc;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
@ -793,8 +792,7 @@ static int dwapb_gpio_suspend(struct device *dev)
|
|||
|
||||
static int dwapb_gpio_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct dwapb_gpio *gpio = platform_get_drvdata(pdev);
|
||||
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
|
||||
struct gpio_chip *gc = &gpio->ports[0].gc;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
|
|
@ -211,7 +211,7 @@ static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
|||
port_state = inb(gpiommgpio->base + ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
bits[word_index] |= (port_state << word_offset) & word_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
|
|
@ -1,32 +1,18 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Intel ICH6-10, Series 5 and 6, Atom C2000 (Avoton/Rangeley) GPIO driver
|
||||
*
|
||||
* Copyright (C) 2010 Extreme Engineering Solutions.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define DRV_NAME "gpio_ich"
|
||||
|
||||
|
@ -100,7 +86,7 @@ struct ichx_desc {
|
|||
|
||||
static struct {
|
||||
spinlock_t lock;
|
||||
struct platform_device *dev;
|
||||
struct device *dev;
|
||||
struct gpio_chip chip;
|
||||
struct resource *gpio_base; /* GPIO IO base */
|
||||
struct resource *pm_base; /* Power Mangagment IO base */
|
||||
|
@ -112,8 +98,7 @@ static struct {
|
|||
|
||||
static int modparam_gpiobase = -1; /* dynamic */
|
||||
module_param_named(gpiobase, modparam_gpiobase, int, 0444);
|
||||
MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, "
|
||||
"which is the default.");
|
||||
MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default.");
|
||||
|
||||
static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
|
||||
{
|
||||
|
@ -121,7 +106,6 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
|
|||
u32 data, tmp;
|
||||
int reg_nr = nr / 32;
|
||||
int bit = nr & 0x1f;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&ichx_priv.lock, flags);
|
||||
|
||||
|
@ -142,12 +126,10 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
|
|||
|
||||
tmp = ICHX_READ(ichx_priv.desc->regs[reg][reg_nr],
|
||||
ichx_priv.gpio_base);
|
||||
if (verify && data != tmp)
|
||||
ret = -EPERM;
|
||||
|
||||
spin_unlock_irqrestore(&ichx_priv.lock, flags);
|
||||
|
||||
return ret;
|
||||
return (verify && data != tmp) ? -EPERM : 0;
|
||||
}
|
||||
|
||||
static int ichx_read_bit(int reg, unsigned nr)
|
||||
|
@ -186,10 +168,7 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
|||
* Try setting pin as an input and verify it worked since many pins
|
||||
* are output-only.
|
||||
*/
|
||||
if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
return ichx_write_bit(GPIO_IO_SEL, nr, 1, 1);
|
||||
}
|
||||
|
||||
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||
|
@ -206,10 +185,7 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
|||
* Try setting pin as an output and verify it worked since many pins
|
||||
* are input-only.
|
||||
*/
|
||||
if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
return ichx_write_bit(GPIO_IO_SEL, nr, 0, 1);
|
||||
}
|
||||
|
||||
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
|
||||
|
@ -284,7 +260,7 @@ static void ichx_gpiolib_setup(struct gpio_chip *chip)
|
|||
{
|
||||
chip->owner = THIS_MODULE;
|
||||
chip->label = DRV_NAME;
|
||||
chip->parent = &ichx_priv.dev->dev;
|
||||
chip->parent = ichx_priv.dev;
|
||||
|
||||
/* Allow chip-specific overrides of request()/get() */
|
||||
chip->request = ichx_priv.desc->request ?
|
||||
|
@ -407,15 +383,14 @@ static int ichx_gpio_request_regions(struct device *dev,
|
|||
|
||||
static int ichx_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct lpc_ich_info *ich_info = dev_get_platdata(dev);
|
||||
struct resource *res_base, *res_pm;
|
||||
int err;
|
||||
struct lpc_ich_info *ich_info = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!ich_info)
|
||||
return -ENODEV;
|
||||
|
||||
ichx_priv.dev = pdev;
|
||||
|
||||
switch (ich_info->gpio_version) {
|
||||
case ICH_I3100_GPIO:
|
||||
ichx_priv.desc = &i3100_desc;
|
||||
|
@ -445,19 +420,21 @@ static int ichx_gpio_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
ichx_priv.dev = dev;
|
||||
spin_lock_init(&ichx_priv.lock);
|
||||
|
||||
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
|
||||
ichx_priv.use_gpio = ich_info->use_gpio;
|
||||
err = ichx_gpio_request_regions(&pdev->dev, res_base, pdev->name,
|
||||
ichx_priv.use_gpio);
|
||||
err = ichx_gpio_request_regions(dev, res_base, pdev->name,
|
||||
ich_info->use_gpio);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ichx_priv.gpio_base = res_base;
|
||||
ichx_priv.use_gpio = ich_info->use_gpio;
|
||||
|
||||
/*
|
||||
* If necessary, determine the I/O address of ACPI/power management
|
||||
* registers which are needed to read the the GPE0 register for GPI pins
|
||||
* registers which are needed to read the GPE0 register for GPI pins
|
||||
* 0 - 15 on some chipsets.
|
||||
*/
|
||||
if (!ichx_priv.desc->uses_gpe0)
|
||||
|
@ -465,13 +442,13 @@ static int ichx_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
|
||||
if (!res_pm) {
|
||||
pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
|
||||
dev_warn(dev, "ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
|
||||
goto init;
|
||||
}
|
||||
|
||||
if (!devm_request_region(&pdev->dev, res_pm->start,
|
||||
resource_size(res_pm), pdev->name)) {
|
||||
pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");
|
||||
if (!devm_request_region(dev, res_pm->start, resource_size(res_pm),
|
||||
pdev->name)) {
|
||||
dev_warn(dev, "ACPI BAR is busy, GPI 0 - 15 unavailable\n");
|
||||
goto init;
|
||||
}
|
||||
|
||||
|
@ -481,12 +458,12 @@ init:
|
|||
ichx_gpiolib_setup(&ichx_priv.chip);
|
||||
err = gpiochip_add_data(&ichx_priv.chip, NULL);
|
||||
if (err) {
|
||||
pr_err("Failed to register GPIOs\n");
|
||||
dev_err(dev, "Failed to register GPIOs\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,
|
||||
ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);
|
||||
dev_info(dev, "GPIO from %d to %d\n", ichx_priv.chip.base,
|
||||
ichx_priv.chip.base + ichx_priv.chip.ngpio - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel MID GPIO driver
|
||||
*
|
||||
* Copyright (c) 2008-2014,2016 Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
|
@ -20,12 +12,11 @@
|
|||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -273,9 +264,8 @@ static const struct pci_device_id intel_gpio_ids[] = {
|
|||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
|
||||
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
|
||||
},
|
||||
{ 0 }
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_gpio_ids);
|
||||
|
||||
static void intel_mid_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
|
|
|
@ -282,22 +282,13 @@ static int ks8695_gpio_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ks8695_gpio_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ks8695_gpio_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations ks8695_gpio_operations = {
|
||||
.open = ks8695_gpio_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(ks8695_gpio);
|
||||
|
||||
static int __init ks8695_gpio_debugfs_init(void)
|
||||
{
|
||||
/* /sys/kernel/debug/ks8695_gpio */
|
||||
(void) debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, &ks8695_gpio_operations);
|
||||
debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL,
|
||||
&ks8695_gpio_fops);
|
||||
return 0;
|
||||
}
|
||||
postcore_initcall(ks8695_gpio_debugfs_init);
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO driver for NXP LPC18xx/43xx.
|
||||
*
|
||||
* Copyright (C) 2018 Vladimir Zapolskiy <vz@mleia.com>
|
||||
* Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
|
@ -24,13 +25,246 @@
|
|||
#define LPC18XX_MAX_PORTS 8
|
||||
#define LPC18XX_PINS_PER_PORT 32
|
||||
|
||||
/* LPC18xx GPIO pin interrupt controller register offsets */
|
||||
#define LPC18XX_GPIO_PIN_IC_ISEL 0x00
|
||||
#define LPC18XX_GPIO_PIN_IC_IENR 0x04
|
||||
#define LPC18XX_GPIO_PIN_IC_SIENR 0x08
|
||||
#define LPC18XX_GPIO_PIN_IC_CIENR 0x0c
|
||||
#define LPC18XX_GPIO_PIN_IC_IENF 0x10
|
||||
#define LPC18XX_GPIO_PIN_IC_SIENF 0x14
|
||||
#define LPC18XX_GPIO_PIN_IC_CIENF 0x18
|
||||
#define LPC18XX_GPIO_PIN_IC_RISE 0x1c
|
||||
#define LPC18XX_GPIO_PIN_IC_FALL 0x20
|
||||
#define LPC18XX_GPIO_PIN_IC_IST 0x24
|
||||
|
||||
#define NR_LPC18XX_GPIO_PIN_IC_IRQS 8
|
||||
|
||||
struct lpc18xx_gpio_pin_ic {
|
||||
void __iomem *base;
|
||||
struct irq_domain *domain;
|
||||
struct raw_spinlock lock;
|
||||
};
|
||||
|
||||
struct lpc18xx_gpio_chip {
|
||||
struct gpio_chip gpio;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct lpc18xx_gpio_pin_ic *pin_ic;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static inline void lpc18xx_gpio_pin_ic_isel(struct lpc18xx_gpio_pin_ic *ic,
|
||||
u32 pin, bool set)
|
||||
{
|
||||
u32 val = readl_relaxed(ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
|
||||
|
||||
if (set)
|
||||
val &= ~BIT(pin);
|
||||
else
|
||||
val |= BIT(pin);
|
||||
|
||||
writel_relaxed(val, ic->base + LPC18XX_GPIO_PIN_IC_ISEL);
|
||||
}
|
||||
|
||||
static inline void lpc18xx_gpio_pin_ic_set(struct lpc18xx_gpio_pin_ic *ic,
|
||||
u32 pin, u32 reg)
|
||||
{
|
||||
writel_relaxed(BIT(pin), ic->base + reg);
|
||||
}
|
||||
|
||||
static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d)
|
||||
{
|
||||
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
|
||||
u32 type = irqd_get_trigger_type(d);
|
||||
|
||||
raw_spin_lock(&ic->lock);
|
||||
|
||||
if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_CIENR);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_CIENF);
|
||||
|
||||
raw_spin_unlock(&ic->lock);
|
||||
|
||||
irq_chip_mask_parent(d);
|
||||
}
|
||||
|
||||
static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d)
|
||||
{
|
||||
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
|
||||
u32 type = irqd_get_trigger_type(d);
|
||||
|
||||
raw_spin_lock(&ic->lock);
|
||||
|
||||
if (type & IRQ_TYPE_LEVEL_MASK || type & IRQ_TYPE_EDGE_RISING)
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_SIENR);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_SIENF);
|
||||
|
||||
raw_spin_unlock(&ic->lock);
|
||||
|
||||
irq_chip_unmask_parent(d);
|
||||
}
|
||||
|
||||
static void lpc18xx_gpio_pin_ic_eoi(struct irq_data *d)
|
||||
{
|
||||
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
|
||||
u32 type = irqd_get_trigger_type(d);
|
||||
|
||||
raw_spin_lock(&ic->lock);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_IST);
|
||||
|
||||
raw_spin_unlock(&ic->lock);
|
||||
|
||||
irq_chip_eoi_parent(d);
|
||||
}
|
||||
|
||||
static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct lpc18xx_gpio_pin_ic *ic = d->chip_data;
|
||||
|
||||
raw_spin_lock(&ic->lock);
|
||||
|
||||
if (type & IRQ_TYPE_LEVEL_HIGH) {
|
||||
lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_SIENF);
|
||||
} else if (type & IRQ_TYPE_LEVEL_LOW) {
|
||||
lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, true);
|
||||
lpc18xx_gpio_pin_ic_set(ic, d->hwirq,
|
||||
LPC18XX_GPIO_PIN_IC_CIENF);
|
||||
} else {
|
||||
lpc18xx_gpio_pin_ic_isel(ic, d->hwirq, false);
|
||||
}
|
||||
|
||||
raw_spin_unlock(&ic->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip lpc18xx_gpio_pin_ic = {
|
||||
.name = "LPC18xx GPIO pin",
|
||||
.irq_mask = lpc18xx_gpio_pin_ic_mask,
|
||||
.irq_unmask = lpc18xx_gpio_pin_ic_unmask,
|
||||
.irq_eoi = lpc18xx_gpio_pin_ic_eoi,
|
||||
.irq_set_type = lpc18xx_gpio_pin_ic_set_type,
|
||||
.flags = IRQCHIP_SET_TYPE_MASKED,
|
||||
};
|
||||
|
||||
static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain,
|
||||
unsigned int virq,
|
||||
unsigned int nr_irqs, void *data)
|
||||
{
|
||||
struct irq_fwspec parent_fwspec, *fwspec = data;
|
||||
struct lpc18xx_gpio_pin_ic *ic = domain->host_data;
|
||||
irq_hw_number_t hwirq;
|
||||
int ret;
|
||||
|
||||
if (nr_irqs != 1)
|
||||
return -EINVAL;
|
||||
|
||||
hwirq = fwspec->param[0];
|
||||
if (hwirq >= NR_LPC18XX_GPIO_PIN_IC_IRQS)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* All LPC18xx/LPC43xx GPIO pin hardware interrupts are translated
|
||||
* into edge interrupts 32...39 on parent Cortex-M3/M4 NVIC
|
||||
*/
|
||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||
parent_fwspec.param_count = 1;
|
||||
parent_fwspec.param[0] = hwirq + 32;
|
||||
|
||||
ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
|
||||
if (ret < 0) {
|
||||
pr_err("failed to allocate parent irq %u: %d\n",
|
||||
parent_fwspec.param[0], ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
|
||||
&lpc18xx_gpio_pin_ic, ic);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops lpc18xx_gpio_pin_ic_domain_ops = {
|
||||
.alloc = lpc18xx_gpio_pin_ic_domain_alloc,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
};
|
||||
|
||||
static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc)
|
||||
{
|
||||
struct device *dev = gc->gpio.parent;
|
||||
struct irq_domain *parent_domain;
|
||||
struct device_node *parent_node;
|
||||
struct lpc18xx_gpio_pin_ic *ic;
|
||||
struct resource res;
|
||||
int ret, index;
|
||||
|
||||
parent_node = of_irq_find_parent(dev->of_node);
|
||||
if (!parent_node)
|
||||
return -ENXIO;
|
||||
|
||||
parent_domain = irq_find_host(parent_node);
|
||||
of_node_put(parent_node);
|
||||
if (!parent_domain)
|
||||
return -ENXIO;
|
||||
|
||||
ic = devm_kzalloc(dev, sizeof(*ic), GFP_KERNEL);
|
||||
if (!ic)
|
||||
return -ENOMEM;
|
||||
|
||||
index = of_property_match_string(dev->of_node, "reg-names",
|
||||
"gpio-pin-ic");
|
||||
if (index < 0) {
|
||||
ret = -ENODEV;
|
||||
goto free_ic;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(dev->of_node, index, &res);
|
||||
if (ret < 0)
|
||||
goto free_ic;
|
||||
|
||||
ic->base = devm_ioremap_resource(dev, &res);
|
||||
if (IS_ERR(ic->base)) {
|
||||
ret = PTR_ERR(ic->base);
|
||||
goto free_ic;
|
||||
}
|
||||
|
||||
raw_spin_lock_init(&ic->lock);
|
||||
|
||||
ic->domain = irq_domain_add_hierarchy(parent_domain, 0,
|
||||
NR_LPC18XX_GPIO_PIN_IC_IRQS,
|
||||
dev->of_node,
|
||||
&lpc18xx_gpio_pin_ic_domain_ops,
|
||||
ic);
|
||||
if (!ic->domain) {
|
||||
pr_err("unable to add irq domain\n");
|
||||
ret = -ENODEV;
|
||||
goto free_iomap;
|
||||
}
|
||||
|
||||
gc->pin_ic = ic;
|
||||
|
||||
return 0;
|
||||
|
||||
free_iomap:
|
||||
devm_iounmap(dev, ic->base);
|
||||
free_ic:
|
||||
devm_kfree(dev, ic);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
|
||||
|
@ -92,45 +326,62 @@ static const struct gpio_chip lpc18xx_chip = {
|
|||
|
||||
static int lpc18xx_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct lpc18xx_gpio_chip *gc;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
int index, ret;
|
||||
|
||||
gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
|
||||
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
|
||||
if (!gc)
|
||||
return -ENOMEM;
|
||||
|
||||
gc->gpio = lpc18xx_chip;
|
||||
platform_set_drvdata(pdev, gc);
|
||||
|
||||
index = of_property_match_string(dev->of_node, "reg-names", "gpio");
|
||||
if (index < 0) {
|
||||
/* To support backward compatibility take the first resource */
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
gc->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
gc->base = devm_ioremap_resource(dev, res);
|
||||
} else {
|
||||
struct resource res;
|
||||
|
||||
ret = of_address_to_resource(dev->of_node, index, &res);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gc->base = devm_ioremap_resource(dev, &res);
|
||||
}
|
||||
if (IS_ERR(gc->base))
|
||||
return PTR_ERR(gc->base);
|
||||
|
||||
gc->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
gc->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(gc->clk)) {
|
||||
dev_err(&pdev->dev, "input clock not found\n");
|
||||
dev_err(dev, "input clock not found\n");
|
||||
return PTR_ERR(gc->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(gc->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable clock\n");
|
||||
dev_err(dev, "unable to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_init(&gc->lock);
|
||||
|
||||
gc->gpio.parent = &pdev->dev;
|
||||
gc->gpio.parent = dev;
|
||||
|
||||
ret = gpiochip_add_data(&gc->gpio, gc);
|
||||
ret = devm_gpiochip_add_data(dev, &gc->gpio, gc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add gpio chip\n");
|
||||
dev_err(dev, "failed to add gpio chip\n");
|
||||
clk_disable_unprepare(gc->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* On error GPIO pin interrupt controller just won't be registered */
|
||||
lpc18xx_gpio_pin_ic_probe(gc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -138,7 +389,9 @@ static int lpc18xx_gpio_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&gc->gpio);
|
||||
if (gc->pin_ic)
|
||||
irq_domain_remove(gc->pin_ic->domain);
|
||||
|
||||
clk_disable_unprepare(gc->clk);
|
||||
|
||||
return 0;
|
||||
|
@ -161,5 +414,6 @@ static struct platform_driver lpc18xx_gpio_driver = {
|
|||
module_platform_driver(lpc18xx_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
|
||||
MODULE_AUTHOR("Vladimir Zapolskiy <vz@mleia.com>");
|
||||
MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,36 +1,22 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO controller driver for Intel Lynxpoint PCH chipset>
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* Author: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/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/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/pm_runtime.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* LynxPoint chipset has support for 94 gpio pins */
|
||||
|
||||
|
@ -240,21 +226,23 @@ static void lp_gpio_irq_handler(struct irq_desc *desc)
|
|||
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
||||
struct lp_gpio *lg = gpiochip_get_data(gc);
|
||||
struct irq_chip *chip = irq_data_get_irq_chip(data);
|
||||
u32 base, pin, mask;
|
||||
unsigned long reg, ena, pending;
|
||||
u32 base, pin;
|
||||
|
||||
/* check from GPIO controller which pin triggered the interrupt */
|
||||
for (base = 0; base < lg->chip.ngpio; base += 32) {
|
||||
reg = lp_gpio_reg(&lg->chip, base, LP_INT_STAT);
|
||||
ena = lp_gpio_reg(&lg->chip, base, LP_INT_ENABLE);
|
||||
|
||||
while ((pending = (inl(reg) & inl(ena)))) {
|
||||
/* Only interrupts that are enabled */
|
||||
pending = inl(reg) & inl(ena);
|
||||
|
||||
for_each_set_bit(pin, &pending, 32) {
|
||||
unsigned irq;
|
||||
|
||||
pin = __ffs(pending);
|
||||
mask = BIT(pin);
|
||||
/* Clear before handling so we don't lose an edge */
|
||||
outl(mask, reg);
|
||||
outl(BIT(pin), reg);
|
||||
|
||||
irq = irq_find_mapping(lg->chip.irq.domain, base + pin);
|
||||
generic_handle_irq(irq);
|
||||
}
|
||||
|
@ -408,8 +396,7 @@ static int lp_gpio_runtime_resume(struct device *dev)
|
|||
|
||||
static int lp_gpio_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct lp_gpio *lg = platform_get_drvdata(pdev);
|
||||
struct lp_gpio *lg = dev_get_drvdata(dev);
|
||||
unsigned long reg;
|
||||
int i;
|
||||
|
||||
|
@ -467,5 +454,5 @@ module_exit(lp_gpio_exit);
|
|||
|
||||
MODULE_AUTHOR("Mathias Nyman (Intel)");
|
||||
MODULE_DESCRIPTION("GPIO interface for Intel Lynxpoint");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:lp_gpio");
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel Merrifield SoC GPIO driver
|
||||
*
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -244,6 +244,8 @@ mediatek_gpio_bank_probe(struct device *dev,
|
|||
rg->chip.of_xlate = mediatek_gpio_xlate;
|
||||
rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
|
||||
dev_name(dev), bank);
|
||||
if (!rg->chip.label)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, &rg->chip, mtk);
|
||||
if (ret < 0) {
|
||||
|
@ -295,6 +297,7 @@ mediatek_gpio_probe(struct platform_device *pdev)
|
|||
struct device_node *np = dev->of_node;
|
||||
struct mtk *mtk;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL);
|
||||
if (!mtk)
|
||||
|
@ -309,8 +312,11 @@ mediatek_gpio_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, mtk);
|
||||
mediatek_gpio_irq_chip.name = dev_name(dev);
|
||||
|
||||
for (i = 0; i < MTK_BANK_CNT; i++)
|
||||
mediatek_gpio_bank_probe(dev, np, i);
|
||||
for (i = 0; i < MTK_BANK_CNT; i++) {
|
||||
ret = mediatek_gpio_bank_probe(dev, np, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -608,7 +608,7 @@ static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
ret = -EBUSY;
|
||||
} else {
|
||||
desc = gpiochip_request_own_desc(&mvchip->chip,
|
||||
pwm->hwpwm, "mvebu-pwm");
|
||||
pwm->hwpwm, "mvebu-pwm", 0);
|
||||
if (IS_ERR(desc)) {
|
||||
ret = PTR_ERR(desc);
|
||||
goto out;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
@ -550,33 +551,38 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
|
|||
writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
|
||||
}
|
||||
|
||||
static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
|
||||
static int mxc_gpio_syscore_suspend(void)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mxc_gpio_port *port = platform_get_drvdata(pdev);
|
||||
struct mxc_gpio_port *port;
|
||||
|
||||
/* walk through all ports */
|
||||
list_for_each_entry(port, &mxc_gpio_ports, node) {
|
||||
mxc_gpio_save_regs(port);
|
||||
clk_disable_unprepare(port->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
|
||||
static void mxc_gpio_syscore_resume(void)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mxc_gpio_port *port = platform_get_drvdata(pdev);
|
||||
struct mxc_gpio_port *port;
|
||||
int ret;
|
||||
|
||||
/* walk through all ports */
|
||||
list_for_each_entry(port, &mxc_gpio_ports, node) {
|
||||
ret = clk_prepare_enable(port->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) {
|
||||
pr_err("mxc: failed to enable gpio clock %d\n", ret);
|
||||
return;
|
||||
}
|
||||
mxc_gpio_restore_regs(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
|
||||
static struct syscore_ops mxc_gpio_syscore_ops = {
|
||||
.suspend = mxc_gpio_syscore_suspend,
|
||||
.resume = mxc_gpio_syscore_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver mxc_gpio_driver = {
|
||||
|
@ -584,7 +590,6 @@ static struct platform_driver mxc_gpio_driver = {
|
|||
.name = "gpio-mxc",
|
||||
.of_match_table = mxc_gpio_dt_ids,
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &mxc_gpio_dev_pm_ops,
|
||||
},
|
||||
.probe = mxc_gpio_probe,
|
||||
.id_table = mxc_gpio_devtype,
|
||||
|
@ -592,6 +597,8 @@ static struct platform_driver mxc_gpio_driver = {
|
|||
|
||||
static int __init gpio_mxc_init(void)
|
||||
{
|
||||
register_syscore_ops(&mxc_gpio_syscore_ops);
|
||||
|
||||
return platform_driver_register(&mxc_gpio_driver);
|
||||
}
|
||||
subsys_initcall(gpio_mxc_init);
|
||||
|
|
|
@ -84,7 +84,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
|
|||
port->both_edges &= ~pin_mask;
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
val = port->gc.get(&port->gc, d->hwirq);
|
||||
val = readl(port->base + PINCTRL_DIN(port)) & pin_mask;
|
||||
if (val)
|
||||
edge = GPIO_INT_FALL_EDGE;
|
||||
else
|
||||
|
|
|
@ -936,8 +936,7 @@ omap2_gpio_disable_level_quirk(struct gpio_bank *bank)
|
|||
|
||||
static int omap_mpuio_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
struct gpio_bank *bank = dev_get_drvdata(dev);
|
||||
void __iomem *mask_reg = bank->base +
|
||||
OMAP_MPUIO_GPIO_MASKIT / bank->stride;
|
||||
unsigned long flags;
|
||||
|
@ -951,8 +950,7 @@ static int omap_mpuio_suspend_noirq(struct device *dev)
|
|||
|
||||
static int omap_mpuio_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
struct gpio_bank *bank = dev_get_drvdata(dev);
|
||||
void __iomem *mask_reg = bank->base +
|
||||
OMAP_MPUIO_GPIO_MASKIT / bank->stride;
|
||||
unsigned long flags;
|
||||
|
@ -1635,8 +1633,7 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
|
|||
|
||||
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
struct gpio_bank *bank = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
|
@ -1656,8 +1653,7 @@ unlock:
|
|||
|
||||
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct gpio_bank *bank = platform_get_drvdata(pdev);
|
||||
struct gpio_bank *bank = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_data/pca953x.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -30,6 +31,8 @@
|
|||
#define PCA953X_INVERT 0x02
|
||||
#define PCA953X_DIRECTION 0x03
|
||||
|
||||
#define REG_ADDR_MASK 0x3f
|
||||
#define REG_ADDR_EXT 0x40
|
||||
#define REG_ADDR_AI 0x80
|
||||
|
||||
#define PCA957X_IN 0x00
|
||||
|
@ -58,7 +61,7 @@
|
|||
#define PCA_GPIO_MASK 0x00FF
|
||||
|
||||
#define PCAL_GPIO_MASK 0x1f
|
||||
#define PCAL_PINCTRL_MASK 0xe0
|
||||
#define PCAL_PINCTRL_MASK 0x60
|
||||
|
||||
#define PCA_INT 0x0100
|
||||
#define PCA_PCAL 0x0200
|
||||
|
@ -119,25 +122,27 @@ struct pca953x_reg_config {
|
|||
int direction;
|
||||
int output;
|
||||
int input;
|
||||
int invert;
|
||||
};
|
||||
|
||||
static const struct pca953x_reg_config pca953x_regs = {
|
||||
.direction = PCA953X_DIRECTION,
|
||||
.output = PCA953X_OUTPUT,
|
||||
.input = PCA953X_INPUT,
|
||||
.invert = PCA953X_INVERT,
|
||||
};
|
||||
|
||||
static const struct pca953x_reg_config pca957x_regs = {
|
||||
.direction = PCA957X_CFG,
|
||||
.output = PCA957X_OUT,
|
||||
.input = PCA957X_IN,
|
||||
.invert = PCA957X_INVRT,
|
||||
};
|
||||
|
||||
struct pca953x_chip {
|
||||
unsigned gpio_start;
|
||||
u8 reg_output[MAX_BANK];
|
||||
u8 reg_direction[MAX_BANK];
|
||||
struct mutex i2c_lock;
|
||||
struct regmap *regmap;
|
||||
|
||||
#ifdef CONFIG_GPIO_PCA953X_IRQ
|
||||
struct mutex irq_lock;
|
||||
|
@ -154,87 +159,177 @@ struct pca953x_chip {
|
|||
struct regulator *regulator;
|
||||
|
||||
const struct pca953x_reg_config *regs;
|
||||
|
||||
int (*write_regs)(struct pca953x_chip *, int, u8 *);
|
||||
int (*read_regs)(struct pca953x_chip *, int, u8 *);
|
||||
};
|
||||
|
||||
static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
|
||||
int off)
|
||||
static int pca953x_bank_shift(struct pca953x_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
int offset = off / BANK_SZ;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
(reg << bank_shift) + offset);
|
||||
*val = ret;
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "failed reading register\n");
|
||||
return ret;
|
||||
return fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define PCA953x_BANK_INPUT BIT(0)
|
||||
#define PCA953x_BANK_OUTPUT BIT(1)
|
||||
#define PCA953x_BANK_POLARITY BIT(2)
|
||||
#define PCA953x_BANK_CONFIG BIT(3)
|
||||
|
||||
static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val,
|
||||
int off)
|
||||
#define PCA957x_BANK_INPUT BIT(0)
|
||||
#define PCA957x_BANK_POLARITY BIT(1)
|
||||
#define PCA957x_BANK_BUSHOLD BIT(2)
|
||||
#define PCA957x_BANK_CONFIG BIT(4)
|
||||
#define PCA957x_BANK_OUTPUT BIT(5)
|
||||
|
||||
#define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2)
|
||||
#define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5)
|
||||
#define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6)
|
||||
|
||||
/*
|
||||
* We care about the following registers:
|
||||
* - Standard set, below 0x40, each port can be replicated up to 8 times
|
||||
* - PCA953x standard
|
||||
* Input port 0x00 + 0 * bank_size R
|
||||
* Output port 0x00 + 1 * bank_size RW
|
||||
* Polarity Inversion port 0x00 + 2 * bank_size RW
|
||||
* Configuration port 0x00 + 3 * bank_size RW
|
||||
* - PCA957x with mixed up registers
|
||||
* Input port 0x00 + 0 * bank_size R
|
||||
* Polarity Inversion port 0x00 + 1 * bank_size RW
|
||||
* Bus hold port 0x00 + 2 * bank_size RW
|
||||
* Configuration port 0x00 + 4 * bank_size RW
|
||||
* Output port 0x00 + 5 * bank_size RW
|
||||
*
|
||||
* - Extended set, above 0x40, often chip specific.
|
||||
* - PCAL6524/PCAL9555A with custom PCAL IRQ handling:
|
||||
* Input latch register 0x40 + 2 * bank_size RW
|
||||
* Interrupt mask register 0x40 + 5 * bank_size RW
|
||||
* Interrupt status register 0x40 + 6 * bank_size R
|
||||
*
|
||||
* - Registers with bit 0x80 set, the AI bit
|
||||
* The bit is cleared and the registers fall into one of the
|
||||
* categories above.
|
||||
*/
|
||||
|
||||
static bool pca953x_check_register(struct pca953x_chip *chip, unsigned int reg,
|
||||
u32 checkbank)
|
||||
{
|
||||
int ret;
|
||||
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
int offset = off / BANK_SZ;
|
||||
int bank_shift = pca953x_bank_shift(chip);
|
||||
int bank = (reg & REG_ADDR_MASK) >> bank_shift;
|
||||
int offset = reg & (BIT(bank_shift) - 1);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
(reg << bank_shift) + offset, val);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "failed writing register\n");
|
||||
return ret;
|
||||
/* 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_regs_8(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
static bool pca953x_readable_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_INPUT | PCA953x_BANK_OUTPUT |
|
||||
PCA953x_BANK_POLARITY | PCA953x_BANK_CONFIG;
|
||||
} else {
|
||||
bank = PCA957x_BANK_INPUT | PCA957x_BANK_OUTPUT |
|
||||
PCA957x_BANK_POLARITY | PCA957x_BANK_CONFIG |
|
||||
PCA957x_BANK_BUSHOLD;
|
||||
}
|
||||
|
||||
static int pca953x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
u16 word = get_unaligned((u16 *)val);
|
||||
|
||||
return i2c_smbus_write_word_data(chip->client, reg << 1, word);
|
||||
if (chip->driver_data & PCA_PCAL) {
|
||||
bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK |
|
||||
PCAL9xxx_BANK_IRQ_STAT;
|
||||
}
|
||||
|
||||
static int pca957x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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]);
|
||||
return pca953x_check_register(chip, reg, bank);
|
||||
}
|
||||
|
||||
static int pca953x_write_regs_24(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
static bool pca953x_writeable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
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 bool pca953x_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct pca953x_chip *chip = dev_get_drvdata(dev);
|
||||
u32 bank;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const struct regmap_config pca953x_i2c_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.readable_reg = pca953x_readable_register,
|
||||
.writeable_reg = pca953x_writeable_register,
|
||||
.volatile_reg = pca953x_volatile_register,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = 0x7f,
|
||||
};
|
||||
|
||||
static u8 pca953x_recalc_addr(struct pca953x_chip *chip, int reg, int off,
|
||||
bool write, bool addrinc)
|
||||
{
|
||||
int bank_shift = pca953x_bank_shift(chip);
|
||||
int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
|
||||
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
|
||||
u8 regaddr = pinctrl | addr | (off / BANK_SZ);
|
||||
|
||||
return i2c_smbus_write_i2c_block_data(chip->client,
|
||||
pinctrl | addr | REG_ADDR_AI,
|
||||
NBANK(chip), val);
|
||||
/* Single byte read doesn't need AI bit set. */
|
||||
if (!addrinc)
|
||||
return regaddr;
|
||||
|
||||
/* Chips with 24 and more GPIOs always support Auto Increment */
|
||||
if (write && NBANK(chip) > 2)
|
||||
regaddr |= REG_ADDR_AI;
|
||||
|
||||
/* PCA9575 needs address-increment on multi-byte writes */
|
||||
if (PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE)
|
||||
regaddr |= REG_ADDR_AI;
|
||||
|
||||
return regaddr;
|
||||
}
|
||||
|
||||
static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 regaddr = pca953x_recalc_addr(chip, reg, 0, true, true);
|
||||
int ret;
|
||||
|
||||
ret = chip->write_regs(chip, reg, val);
|
||||
ret = regmap_bulk_write(chip->regmap, regaddr, val, NBANK(chip));
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "failed writing register\n");
|
||||
return ret;
|
||||
|
@ -243,42 +338,12 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pca953x_read_regs_8(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(chip->client, reg);
|
||||
*val = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pca953x_read_regs_16(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(chip->client, reg << 1);
|
||||
put_unaligned(ret, (u16 *)val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pca953x_read_regs_24(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
int addr = (reg & PCAL_GPIO_MASK) << bank_shift;
|
||||
int pinctrl = (reg & PCAL_PINCTRL_MASK) << 1;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(chip->client,
|
||||
pinctrl | addr | REG_ADDR_AI,
|
||||
NBANK(chip), val);
|
||||
}
|
||||
|
||||
static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
|
||||
{
|
||||
u8 regaddr = pca953x_recalc_addr(chip, reg, 0, false, true);
|
||||
int ret;
|
||||
|
||||
ret = chip->read_regs(chip, reg, val);
|
||||
ret = regmap_bulk_read(chip->regmap, regaddr, val, NBANK(chip));
|
||||
if (ret < 0) {
|
||||
dev_err(&chip->client->dev, "failed reading register\n");
|
||||
return ret;
|
||||
|
@ -290,18 +355,13 @@ static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val)
|
|||
static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
|
||||
{
|
||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||
u8 reg_val;
|
||||
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
|
||||
true, false);
|
||||
u8 bit = BIT(off % BANK_SZ);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->i2c_lock);
|
||||
reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ));
|
||||
|
||||
ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
chip->reg_direction[off / BANK_SZ] = reg_val;
|
||||
exit:
|
||||
ret = regmap_write_bits(chip->regmap, dirreg, bit, bit);
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -310,31 +370,21 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc,
|
|||
unsigned off, int val)
|
||||
{
|
||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||
u8 reg_val;
|
||||
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
|
||||
true, false);
|
||||
u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
|
||||
true, false);
|
||||
u8 bit = BIT(off % BANK_SZ);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->i2c_lock);
|
||||
/* set output level */
|
||||
if (val)
|
||||
reg_val = chip->reg_output[off / BANK_SZ]
|
||||
| (1u << (off % BANK_SZ));
|
||||
else
|
||||
reg_val = chip->reg_output[off / BANK_SZ]
|
||||
& ~(1u << (off % BANK_SZ));
|
||||
|
||||
ret = pca953x_write_single(chip, chip->regs->output, reg_val, off);
|
||||
ret = regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
chip->reg_output[off / BANK_SZ] = reg_val;
|
||||
|
||||
/* then direction */
|
||||
reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ));
|
||||
ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
chip->reg_direction[off / BANK_SZ] = reg_val;
|
||||
ret = regmap_write_bits(chip->regmap, dirreg, bit, 0);
|
||||
exit:
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
return ret;
|
||||
|
@ -343,11 +393,14 @@ exit:
|
|||
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
|
||||
{
|
||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||
u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off,
|
||||
true, false);
|
||||
u8 bit = BIT(off % BANK_SZ);
|
||||
u32 reg_val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->i2c_lock);
|
||||
ret = pca953x_read_single(chip, chip->regs->input, ®_val, off);
|
||||
ret = regmap_read(chip->regmap, inreg, ®_val);
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
if (ret < 0) {
|
||||
/* NOTE: diagnostic already emitted; that's all we should
|
||||
|
@ -357,45 +410,37 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
|
|||
return 0;
|
||||
}
|
||||
|
||||
return (reg_val & (1u << (off % BANK_SZ))) ? 1 : 0;
|
||||
return !!(reg_val & bit);
|
||||
}
|
||||
|
||||
static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
|
||||
{
|
||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||
u8 reg_val;
|
||||
int ret;
|
||||
u8 outreg = pca953x_recalc_addr(chip, chip->regs->output, off,
|
||||
true, false);
|
||||
u8 bit = BIT(off % BANK_SZ);
|
||||
|
||||
mutex_lock(&chip->i2c_lock);
|
||||
if (val)
|
||||
reg_val = chip->reg_output[off / BANK_SZ]
|
||||
| (1u << (off % BANK_SZ));
|
||||
else
|
||||
reg_val = chip->reg_output[off / BANK_SZ]
|
||||
& ~(1u << (off % BANK_SZ));
|
||||
|
||||
ret = pca953x_write_single(chip, chip->regs->output, reg_val, off);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
chip->reg_output[off / BANK_SZ] = reg_val;
|
||||
exit:
|
||||
regmap_write_bits(chip->regmap, outreg, bit, val ? bit : 0);
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
}
|
||||
|
||||
static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off)
|
||||
{
|
||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||
u8 dirreg = pca953x_recalc_addr(chip, chip->regs->direction, off,
|
||||
true, false);
|
||||
u8 bit = BIT(off % BANK_SZ);
|
||||
u32 reg_val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->i2c_lock);
|
||||
ret = pca953x_read_single(chip, chip->regs->direction, ®_val, off);
|
||||
ret = regmap_read(chip->regmap, dirreg, ®_val);
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(reg_val & (1u << (off % BANK_SZ)));
|
||||
return !!(reg_val & bit);
|
||||
}
|
||||
|
||||
static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
|
||||
|
@ -403,14 +448,15 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
|
|||
{
|
||||
struct pca953x_chip *chip = gpiochip_get_data(gc);
|
||||
unsigned int bank_mask, bank_val;
|
||||
int bank_shift, bank;
|
||||
int bank;
|
||||
u8 reg_val[MAX_BANK];
|
||||
int ret;
|
||||
|
||||
bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ);
|
||||
|
||||
mutex_lock(&chip->i2c_lock);
|
||||
memcpy(reg_val, chip->reg_output, NBANK(chip));
|
||||
ret = pca953x_read_regs(chip, chip->regs->output, reg_val);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
for (bank = 0; bank < NBANK(chip); bank++) {
|
||||
bank_mask = mask[bank / sizeof(*mask)] >>
|
||||
((bank % sizeof(*mask)) * 8);
|
||||
|
@ -422,13 +468,7 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc,
|
|||
}
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(chip->client,
|
||||
chip->regs->output << bank_shift,
|
||||
NBANK(chip), reg_val);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
memcpy(chip->reg_output, reg_val, NBANK(chip));
|
||||
pca953x_write_regs(chip, chip->regs->output, reg_val);
|
||||
exit:
|
||||
mutex_unlock(&chip->i2c_lock);
|
||||
}
|
||||
|
@ -487,6 +527,10 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
|
|||
u8 new_irqs;
|
||||
int level, i;
|
||||
u8 invert_irq_mask[MAX_BANK];
|
||||
int reg_direction[MAX_BANK];
|
||||
|
||||
regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
|
||||
NBANK(chip));
|
||||
|
||||
if (chip->driver_data & PCA_PCAL) {
|
||||
/* Enable latch on interrupt-enabled inputs */
|
||||
|
@ -502,7 +546,7 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
|
|||
/* Look for any newly setup interrupt */
|
||||
for (i = 0; i < NBANK(chip); i++) {
|
||||
new_irqs = chip->irq_trig_fall[i] | chip->irq_trig_raise[i];
|
||||
new_irqs &= ~chip->reg_direction[i];
|
||||
new_irqs &= reg_direction[i];
|
||||
|
||||
while (new_irqs) {
|
||||
level = __ffs(new_irqs);
|
||||
|
@ -567,6 +611,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
|
|||
bool pending_seen = false;
|
||||
bool trigger_seen = false;
|
||||
u8 trigger[MAX_BANK];
|
||||
int reg_direction[MAX_BANK];
|
||||
int ret, i;
|
||||
|
||||
if (chip->driver_data & PCA_PCAL) {
|
||||
|
@ -597,8 +642,10 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
|
|||
return false;
|
||||
|
||||
/* Remove output pins from the equation */
|
||||
regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction,
|
||||
NBANK(chip));
|
||||
for (i = 0; i < NBANK(chip); i++)
|
||||
cur_stat[i] &= chip->reg_direction[i];
|
||||
cur_stat[i] &= reg_direction[i];
|
||||
|
||||
memcpy(old_stat, chip->irq_stat, NBANK(chip));
|
||||
|
||||
|
@ -652,6 +699,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|||
int irq_base)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
int reg_direction[MAX_BANK];
|
||||
int ret, i;
|
||||
|
||||
if (client->irq && irq_base != -1
|
||||
|
@ -666,8 +714,10 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|||
* interrupt. We have to rely on the previous read for
|
||||
* this purpose.
|
||||
*/
|
||||
regmap_bulk_read(chip->regmap, chip->regs->direction,
|
||||
reg_direction, NBANK(chip));
|
||||
for (i = 0; i < NBANK(chip); i++)
|
||||
chip->irq_stat[i] &= chip->reg_direction[i];
|
||||
chip->irq_stat[i] &= reg_direction[i];
|
||||
mutex_init(&chip->irq_lock);
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
|
@ -715,20 +765,19 @@ static int pca953x_irq_setup(struct pca953x_chip *chip,
|
|||
}
|
||||
#endif
|
||||
|
||||
static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
|
||||
static int device_pca95xx_init(struct pca953x_chip *chip, u32 invert)
|
||||
{
|
||||
int ret;
|
||||
u8 val[MAX_BANK];
|
||||
|
||||
chip->regs = &pca953x_regs;
|
||||
|
||||
ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output);
|
||||
if (ret)
|
||||
ret = regcache_sync_region(chip->regmap, chip->regs->output,
|
||||
chip->regs->output + NBANK(chip));
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
ret = pca953x_read_regs(chip, chip->regs->direction,
|
||||
chip->reg_direction);
|
||||
if (ret)
|
||||
ret = regcache_sync_region(chip->regmap, chip->regs->direction,
|
||||
chip->regs->direction + NBANK(chip));
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
/* set platform specific polarity inversion */
|
||||
|
@ -737,7 +786,7 @@ static int device_pca953x_init(struct pca953x_chip *chip, u32 invert)
|
|||
else
|
||||
memset(val, 0, NBANK(chip));
|
||||
|
||||
ret = pca953x_write_regs(chip, PCA953X_INVERT, val);
|
||||
ret = pca953x_write_regs(chip, chip->regs->invert, val);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -747,22 +796,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
|
|||
int ret;
|
||||
u8 val[MAX_BANK];
|
||||
|
||||
chip->regs = &pca957x_regs;
|
||||
|
||||
ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = pca953x_read_regs(chip, chip->regs->direction,
|
||||
chip->reg_direction);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* set platform specific polarity inversion */
|
||||
if (invert)
|
||||
memset(val, 0xFF, NBANK(chip));
|
||||
else
|
||||
memset(val, 0, NBANK(chip));
|
||||
ret = pca953x_write_regs(chip, PCA957X_INVRT, val);
|
||||
ret = device_pca95xx_init(chip, invert);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -853,6 +887,16 @@ static int pca953x_probe(struct i2c_client *client,
|
|||
}
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(client, &pca953x_i2c_regmap);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
ret = PTR_ERR(chip->regmap);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
regcache_mark_dirty(chip->regmap);
|
||||
|
||||
mutex_init(&chip->i2c_lock);
|
||||
/*
|
||||
* In case we have an i2c-mux controlled by a GPIO provided by an
|
||||
|
@ -878,24 +922,13 @@ static int pca953x_probe(struct i2c_client *client,
|
|||
*/
|
||||
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
|
||||
|
||||
if (chip->gpio_chip.ngpio <= 8) {
|
||||
chip->write_regs = pca953x_write_regs_8;
|
||||
chip->read_regs = pca953x_read_regs_8;
|
||||
} else if (chip->gpio_chip.ngpio >= 24) {
|
||||
chip->write_regs = pca953x_write_regs_24;
|
||||
chip->read_regs = pca953x_read_regs_24;
|
||||
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) {
|
||||
chip->regs = &pca953x_regs;
|
||||
ret = device_pca95xx_init(chip, invert);
|
||||
} else {
|
||||
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
|
||||
chip->write_regs = pca953x_write_regs_16;
|
||||
else
|
||||
chip->write_regs = pca957x_write_regs_16;
|
||||
chip->read_regs = pca953x_read_regs_16;
|
||||
}
|
||||
|
||||
if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE)
|
||||
ret = device_pca953x_init(chip, invert);
|
||||
else
|
||||
chip->regs = &pca957x_regs;
|
||||
ret = device_pca957x_init(chip, invert);
|
||||
}
|
||||
if (ret)
|
||||
goto err_exit;
|
||||
|
||||
|
@ -914,7 +947,6 @@ static int pca953x_probe(struct i2c_client *client,
|
|||
dev_warn(&client->dev, "setup failed, %d\n", ret);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
|
@ -943,6 +975,91 @@ static int pca953x_remove(struct i2c_client *client)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pca953x_regcache_sync(struct device *dev)
|
||||
{
|
||||
struct pca953x_chip *chip = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The ordering between direction and output is important,
|
||||
* sync these registers first and only then sync the rest.
|
||||
*/
|
||||
ret = regcache_sync_region(chip->regmap, chip->regs->direction,
|
||||
chip->regs->direction + NBANK(chip));
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regcache_sync_region(chip->regmap, chip->regs->output,
|
||||
chip->regs->output + NBANK(chip));
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIO_PCA953X_IRQ
|
||||
if (chip->driver_data & PCA_PCAL) {
|
||||
ret = regcache_sync_region(chip->regmap, PCAL953X_IN_LATCH,
|
||||
PCAL953X_IN_LATCH + NBANK(chip));
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to sync INT latch registers: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regcache_sync_region(chip->regmap, PCAL953X_INT_MASK,
|
||||
PCAL953X_INT_MASK + NBANK(chip));
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to sync INT mask registers: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca953x_suspend(struct device *dev)
|
||||
{
|
||||
struct pca953x_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
regcache_cache_only(chip->regmap, true);
|
||||
|
||||
regulator_disable(chip->regulator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pca953x_resume(struct device *dev)
|
||||
{
|
||||
struct pca953x_chip *chip = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(chip->regulator);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable regulator: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
regcache_cache_only(chip->regmap, false);
|
||||
regcache_mark_dirty(chip->regmap);
|
||||
ret = pca953x_regcache_sync(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regcache_sync(chip->regmap);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to restore register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* convenience to stop overlong match-table lines */
|
||||
#define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int)
|
||||
#define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
|
||||
|
@ -986,9 +1103,12 @@ static const struct of_device_id pca953x_dt_ids[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pca953x_pm_ops, pca953x_suspend, pca953x_resume);
|
||||
|
||||
static struct i2c_driver pca953x_driver = {
|
||||
.driver = {
|
||||
.name = "pca953x",
|
||||
.pm = &pca953x_pm_ops,
|
||||
.of_match_table = pca953x_dt_ids,
|
||||
.acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
|
||||
},
|
||||
|
|
|
@ -1,25 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PCH_EDGE_FALLING 0
|
||||
|
@ -171,11 +159,10 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* Save register configuration and disable interrupts.
|
||||
*/
|
||||
static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
|
||||
static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip)
|
||||
{
|
||||
chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
|
||||
chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
|
||||
|
@ -185,14 +172,13 @@ static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
|
|||
if (chip->ioh == INTEL_EG20T_PCH)
|
||||
chip->pch_gpio_reg.im1_reg = ioread32(&chip->reg->im1);
|
||||
if (chip->ioh == OKISEMI_ML7223n_IOH)
|
||||
chip->pch_gpio_reg.gpio_use_sel_reg =\
|
||||
ioread32(&chip->reg->gpio_use_sel);
|
||||
chip->pch_gpio_reg.gpio_use_sel_reg = ioread32(&chip->reg->gpio_use_sel);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function restores the register configuration of the GPIO device.
|
||||
*/
|
||||
static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
|
||||
static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip)
|
||||
{
|
||||
iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
|
||||
iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
|
||||
|
@ -204,10 +190,8 @@ static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
|
|||
if (chip->ioh == INTEL_EG20T_PCH)
|
||||
iowrite32(chip->pch_gpio_reg.im1_reg, &chip->reg->im1);
|
||||
if (chip->ioh == OKISEMI_ML7223n_IOH)
|
||||
iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg,
|
||||
&chip->reg->gpio_use_sel);
|
||||
iowrite32(chip->pch_gpio_reg.gpio_use_sel_reg, &chip->reg->gpio_use_sel);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pch_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
|
||||
{
|
||||
|
@ -226,7 +210,6 @@ static void pch_gpio_setup(struct pch_gpio *chip)
|
|||
gpio->get = pch_gpio_get;
|
||||
gpio->direction_output = pch_gpio_direction_output;
|
||||
gpio->set = pch_gpio_set;
|
||||
gpio->dbg_show = NULL;
|
||||
gpio->base = -1;
|
||||
gpio->ngpio = gpio_pins[chip->ioh];
|
||||
gpio->can_sleep = false;
|
||||
|
@ -250,8 +233,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)
|
|||
im_reg = &chip->reg->im1;
|
||||
im_pos = ch - 8;
|
||||
}
|
||||
dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d\n",
|
||||
__func__, irq, type, ch, im_pos);
|
||||
dev_dbg(chip->dev, "irq=%d type=%d ch=%d pos=%d\n", irq, type, ch, im_pos);
|
||||
|
||||
spin_lock_irqsave(&chip->spinlock, flags);
|
||||
|
||||
|
@ -317,17 +299,14 @@ static void pch_irq_ack(struct irq_data *d)
|
|||
static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct pch_gpio *chip = dev_id;
|
||||
u32 reg_val = ioread32(&chip->reg->istatus);
|
||||
unsigned long reg_val = ioread32(&chip->reg->istatus);
|
||||
int i, ret = IRQ_NONE;
|
||||
|
||||
for (i = 0; i < gpio_pins[chip->ioh]; i++) {
|
||||
if (reg_val & BIT(i)) {
|
||||
dev_dbg(chip->dev, "%s:[%d]:irq=%d status=0x%x\n",
|
||||
__func__, i, irq, reg_val);
|
||||
for_each_set_bit(i, ®_val, gpio_pins[chip->ioh]) {
|
||||
dev_dbg(chip->dev, "[%d]:irq=%d status=0x%lx\n", i, irq, reg_val);
|
||||
generic_handle_irq(chip->irq_base + i);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -367,29 +346,24 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
|||
int irq_base;
|
||||
u32 msk;
|
||||
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (chip == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dev = &pdev->dev;
|
||||
ret = pci_enable_device(pdev);
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s : pci_enable_device FAILED", __func__);
|
||||
goto err_pci_enable;
|
||||
dev_err(&pdev->dev, "pci_enable_device FAILED");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_request_regions(pdev, KBUILD_MODNAME);
|
||||
ret = pcim_iomap_regions(pdev, 1 << 1, KBUILD_MODNAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "pci_request_regions FAILED-%d", ret);
|
||||
goto err_request_regions;
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->base = pci_iomap(pdev, 1, 0);
|
||||
if (!chip->base) {
|
||||
dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_iomap;
|
||||
}
|
||||
chip->base = pcim_iomap_table(pdev)[1];
|
||||
|
||||
if (pdev->device == 0x8803)
|
||||
chip->ioh = INTEL_EG20T_PCH;
|
||||
|
@ -402,13 +376,11 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
|||
pci_set_drvdata(pdev, chip);
|
||||
spin_lock_init(&chip->spinlock);
|
||||
pch_gpio_setup(chip);
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
chip->gpio.of_node = pdev->dev.of_node;
|
||||
#endif
|
||||
ret = gpiochip_add_data(&chip->gpio, chip);
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &chip->gpio, chip);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "PCH gpio: Failed to register GPIO\n");
|
||||
goto err_gpiochip_add;
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0,
|
||||
|
@ -416,7 +388,7 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
|||
if (irq_base < 0) {
|
||||
dev_warn(&pdev->dev, "PCH gpio: Failed to get IRQ base num\n");
|
||||
chip->irq_base = -1;
|
||||
goto end;
|
||||
return 0;
|
||||
}
|
||||
chip->irq_base = irq_base;
|
||||
|
||||
|
@ -427,53 +399,17 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
|||
|
||||
ret = devm_request_irq(&pdev->dev, pdev->irq, pch_gpio_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, chip);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s request_irq failed\n", __func__);
|
||||
goto err_request_irq;
|
||||
}
|
||||
|
||||
ret = pch_gpio_alloc_generic_chip(chip, irq_base,
|
||||
gpio_pins[chip->ioh]);
|
||||
if (ret)
|
||||
goto err_request_irq;
|
||||
|
||||
end:
|
||||
return 0;
|
||||
|
||||
err_request_irq:
|
||||
gpiochip_remove(&chip->gpio);
|
||||
|
||||
err_gpiochip_add:
|
||||
pci_iounmap(pdev, chip->base);
|
||||
|
||||
err_iomap:
|
||||
pci_release_regions(pdev);
|
||||
|
||||
err_request_regions:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
err_pci_enable:
|
||||
kfree(chip);
|
||||
dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request_irq failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pch_gpio_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct pch_gpio *chip = pci_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&chip->gpio);
|
||||
pci_iounmap(pdev, chip->base);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
kfree(chip);
|
||||
return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
static int __maybe_unused pch_gpio_suspend(struct device *dev)
|
||||
{
|
||||
s32 ret;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pch_gpio *chip = pci_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
|
||||
|
@ -481,36 +417,15 @@ static int pch_gpio_suspend(struct pci_dev *pdev, pm_message_t state)
|
|||
pch_gpio_save_reg_conf(chip);
|
||||
spin_unlock_irqrestore(&chip->spinlock, flags);
|
||||
|
||||
ret = pci_save_state(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
ret = pci_enable_wake(pdev, PCI_D0, 1);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_gpio_resume(struct pci_dev *pdev)
|
||||
static int __maybe_unused pch_gpio_resume(struct device *dev)
|
||||
{
|
||||
s32 ret;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct pch_gpio *chip = pci_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
|
||||
ret = pci_enable_wake(pdev, PCI_D0, 0);
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret);
|
||||
return ret;
|
||||
}
|
||||
pci_restore_state(pdev);
|
||||
|
||||
spin_lock_irqsave(&chip->spinlock, flags);
|
||||
iowrite32(0x01, &chip->reg->reset);
|
||||
iowrite32(0x00, &chip->reg->reset);
|
||||
|
@ -519,10 +434,8 @@ static int pch_gpio_resume(struct pci_dev *pdev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pch_gpio_suspend NULL
|
||||
#define pch_gpio_resume NULL
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
|
||||
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
static const struct pci_device_id pch_gpio_pcidev_id[] = {
|
||||
|
@ -538,12 +451,12 @@ static struct pci_driver pch_gpio_driver = {
|
|||
.name = "pch_gpio",
|
||||
.id_table = pch_gpio_pcidev_id,
|
||||
.probe = pch_gpio_probe,
|
||||
.remove = pch_gpio_remove,
|
||||
.suspend = pch_gpio_suspend,
|
||||
.resume = pch_gpio_resume
|
||||
.driver = {
|
||||
.pm = &pch_gpio_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_pci_driver(pch_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PCH GPIO PCI Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -146,7 +146,7 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
|
|||
port_state = ioread8(ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
bits[word_index] |= (port_state << word_offset) & word_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -243,7 +243,7 @@ static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
|
|||
port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
bits[word_index] |= (port_state << word_offset) & word_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -54,6 +54,7 @@ struct pl061 {
|
|||
|
||||
void __iomem *base;
|
||||
struct gpio_chip gc;
|
||||
struct irq_chip irq_chip;
|
||||
int parent_irq;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -281,15 +282,6 @@ static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
|
|||
return irq_set_irq_wake(pl061->parent_irq, state);
|
||||
}
|
||||
|
||||
static struct irq_chip pl061_irqchip = {
|
||||
.name = "pl061",
|
||||
.irq_ack = pl061_irq_ack,
|
||||
.irq_mask = pl061_irq_mask,
|
||||
.irq_unmask = pl061_irq_unmask,
|
||||
.irq_set_type = pl061_irq_type,
|
||||
.irq_set_wake = pl061_irq_set_wake,
|
||||
};
|
||||
|
||||
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
|
@ -328,6 +320,13 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
/*
|
||||
* irq_chip support
|
||||
*/
|
||||
pl061->irq_chip.name = dev_name(dev);
|
||||
pl061->irq_chip.irq_ack = pl061_irq_ack;
|
||||
pl061->irq_chip.irq_mask = pl061_irq_mask;
|
||||
pl061->irq_chip.irq_unmask = pl061_irq_unmask;
|
||||
pl061->irq_chip.irq_set_type = pl061_irq_type;
|
||||
pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
|
||||
|
||||
writeb(0, pl061->base + GPIOIE); /* disable irqs */
|
||||
irq = adev->irq[0];
|
||||
if (irq < 0) {
|
||||
|
@ -336,14 +335,14 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
}
|
||||
pl061->parent_irq = irq;
|
||||
|
||||
ret = gpiochip_irqchip_add(&pl061->gc, &pl061_irqchip,
|
||||
ret = gpiochip_irqchip_add(&pl061->gc, &pl061->irq_chip,
|
||||
0, handle_bad_irq,
|
||||
IRQ_TYPE_NONE);
|
||||
if (ret) {
|
||||
dev_info(&adev->dev, "could not add irqchip\n");
|
||||
return ret;
|
||||
}
|
||||
gpiochip_set_chained_irqchip(&pl061->gc, &pl061_irqchip,
|
||||
gpiochip_set_chained_irqchip(&pl061->gc, &pl061->irq_chip,
|
||||
irq, pl061_irq_handler);
|
||||
|
||||
amba_set_drvdata(adev, pl061);
|
||||
|
|
|
@ -206,6 +206,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
fw = rpi_firmware_get(fw_node);
|
||||
of_node_put(fw_node);
|
||||
if (!fw)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas R-Car GPIO Support
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
* Copyright (C) 2013 Magnus Damm
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
|
@ -43,7 +35,7 @@ struct gpio_rcar_bank_info {
|
|||
struct gpio_rcar_priv {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
struct gpio_chip gpio_chip;
|
||||
struct irq_chip irq_chip;
|
||||
unsigned int irq_parent;
|
||||
|
@ -148,7 +140,7 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type)
|
|||
struct gpio_rcar_priv *p = gpiochip_get_data(gc);
|
||||
unsigned int hwirq = irqd_to_hwirq(d);
|
||||
|
||||
dev_dbg(&p->pdev->dev, "sense irq = %d, type = %d\n", hwirq, type);
|
||||
dev_dbg(p->dev, "sense irq = %d, type = %d\n", hwirq, type);
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
|
@ -188,8 +180,7 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
|
|||
if (p->irq_parent) {
|
||||
error = irq_set_irq_wake(p->irq_parent, on);
|
||||
if (error) {
|
||||
dev_dbg(&p->pdev->dev,
|
||||
"irq %u doesn't support irq_set_wake\n",
|
||||
dev_dbg(p->dev, "irq %u doesn't support irq_set_wake\n",
|
||||
p->irq_parent);
|
||||
p->irq_parent = 0;
|
||||
}
|
||||
|
@ -252,13 +243,13 @@ static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset)
|
|||
struct gpio_rcar_priv *p = gpiochip_get_data(chip);
|
||||
int error;
|
||||
|
||||
error = pm_runtime_get_sync(&p->pdev->dev);
|
||||
error = pm_runtime_get_sync(p->dev);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = pinctrl_gpio_request(chip->base + offset);
|
||||
if (error)
|
||||
pm_runtime_put(&p->pdev->dev);
|
||||
pm_runtime_put(p->dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -275,7 +266,7 @@ static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset)
|
|||
*/
|
||||
gpio_rcar_config_general_input_output_mode(chip, offset, false);
|
||||
|
||||
pm_runtime_put(&p->pdev->dev);
|
||||
pm_runtime_put(p->dev);
|
||||
}
|
||||
|
||||
static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
|
@ -406,21 +397,20 @@ MODULE_DEVICE_TABLE(of, gpio_rcar_of_table);
|
|||
|
||||
static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
|
||||
{
|
||||
struct device_node *np = p->pdev->dev.of_node;
|
||||
struct device_node *np = p->dev->of_node;
|
||||
const struct gpio_rcar_info *info;
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
info = of_device_get_match_data(&p->pdev->dev);
|
||||
info = of_device_get_match_data(p->dev);
|
||||
|
||||
ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args);
|
||||
*npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
|
||||
p->has_both_edge_trigger = info->has_both_edge_trigger;
|
||||
|
||||
if (*npins == 0 || *npins > RCAR_MAX_GPIO_PER_BANK) {
|
||||
dev_warn(&p->pdev->dev,
|
||||
"Invalid number of gpio lines %u, using %u\n", *npins,
|
||||
RCAR_MAX_GPIO_PER_BANK);
|
||||
dev_warn(p->dev, "Invalid number of gpio lines %u, using %u\n",
|
||||
*npins, RCAR_MAX_GPIO_PER_BANK);
|
||||
*npins = RCAR_MAX_GPIO_PER_BANK;
|
||||
}
|
||||
|
||||
|
@ -442,7 +432,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
|
|||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->pdev = pdev;
|
||||
p->dev = dev;
|
||||
spin_lock_init(&p->lock);
|
||||
|
||||
/* Get device configuration from DT node */
|
||||
|
|
253
drivers/gpio/gpio-sama5d2-piobu.c
Normal file
253
drivers/gpio/gpio-sama5d2-piobu.c
Normal 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>");
|
|
@ -1,32 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO interface for Intel Poulsbo SCH
|
||||
*
|
||||
* Copyright (c) 2010 CompuLab Ltd
|
||||
* Author: Denis Turischev <denis@compulab.co.il>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define GEN 0x00
|
||||
#define GIO 0x04
|
||||
|
@ -235,5 +222,5 @@ module_platform_driver(sch_gpio_driver);
|
|||
|
||||
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
|
||||
MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:sch_gpio");
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO interface for Intel Sodaville SoCs.
|
||||
*
|
||||
* Copyright (c) 2010, 2011 Intel Corporation
|
||||
*
|
||||
* Author: Hans J. Koch <hjk@linutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
#define DRV_NAME "sdv_gpio"
|
||||
#define SDV_NUM_PUB_GPIOS 12
|
||||
|
@ -80,18 +76,15 @@ static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type)
|
|||
static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct sdv_gpio_chip_data *sd = data;
|
||||
u32 irq_stat = readl(sd->gpio_pub_base + GPSTR);
|
||||
unsigned long irq_stat = readl(sd->gpio_pub_base + GPSTR);
|
||||
int irq_bit;
|
||||
|
||||
irq_stat &= readl(sd->gpio_pub_base + GPIO_INT);
|
||||
if (!irq_stat)
|
||||
return IRQ_NONE;
|
||||
|
||||
while (irq_stat) {
|
||||
u32 irq_bit = __fls(irq_stat);
|
||||
|
||||
irq_stat &= ~BIT(irq_bit);
|
||||
for_each_set_bit(irq_bit, &irq_stat, 32)
|
||||
generic_handle_irq(irq_find_mapping(sd->id, irq_bit));
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -155,8 +148,10 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
|
|||
* we unmask & ACK the IRQ before the source of the interrupt is gone
|
||||
* then the interrupt is active again.
|
||||
*/
|
||||
sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base,
|
||||
sd->gpio_pub_base, handle_fasteoi_irq);
|
||||
sd->gc = devm_irq_alloc_generic_chip(&pdev->dev, "sdv-gpio", 1,
|
||||
sd->irq_base,
|
||||
sd->gpio_pub_base,
|
||||
handle_fasteoi_irq);
|
||||
if (!sd->gc)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -186,70 +181,52 @@ static int sdv_gpio_probe(struct pci_dev *pdev,
|
|||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
struct sdv_gpio_chip_data *sd;
|
||||
unsigned long addr;
|
||||
const void *prop;
|
||||
int len;
|
||||
int ret;
|
||||
u32 mux_val;
|
||||
|
||||
sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL);
|
||||
sd = devm_kzalloc(&pdev->dev, sizeof(*sd), GFP_KERNEL);
|
||||
if (!sd)
|
||||
return -ENOMEM;
|
||||
ret = pci_enable_device(pdev);
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't enable device.\n");
|
||||
goto done;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME);
|
||||
ret = pcim_iomap_regions(pdev, 1 << GPIO_BAR, DRV_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR);
|
||||
goto disable_pci;
|
||||
return ret;
|
||||
}
|
||||
|
||||
addr = pci_resource_start(pdev, GPIO_BAR);
|
||||
if (!addr) {
|
||||
ret = -ENODEV;
|
||||
goto release_reg;
|
||||
}
|
||||
sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR));
|
||||
sd->gpio_pub_base = pcim_iomap_table(pdev)[GPIO_BAR];
|
||||
|
||||
prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len);
|
||||
if (prop && len == 4) {
|
||||
mux_val = of_read_number(prop, 1);
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "intel,muxctl", &mux_val);
|
||||
if (!ret)
|
||||
writel(mux_val, sd->gpio_pub_base + GPMUXCTL);
|
||||
}
|
||||
|
||||
ret = bgpio_init(&sd->chip, &pdev->dev, 4,
|
||||
sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR,
|
||||
NULL, sd->gpio_pub_base + GPOER, NULL, 0);
|
||||
if (ret)
|
||||
goto unmap;
|
||||
return ret;
|
||||
|
||||
sd->chip.ngpio = SDV_NUM_PUB_GPIOS;
|
||||
|
||||
ret = gpiochip_add_data(&sd->chip, sd);
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &sd->chip, sd);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "gpiochip_add() failed.\n");
|
||||
goto unmap;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sdv_register_irqsupport(sd, pdev);
|
||||
if (ret)
|
||||
goto unmap;
|
||||
return ret;
|
||||
|
||||
pci_set_drvdata(pdev, sd);
|
||||
dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n");
|
||||
return 0;
|
||||
|
||||
unmap:
|
||||
iounmap(sd->gpio_pub_base);
|
||||
release_reg:
|
||||
pci_release_region(pdev, GPIO_BAR);
|
||||
disable_pci:
|
||||
pci_disable_device(pdev);
|
||||
done:
|
||||
kfree(sd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pci_device_id sdv_gpio_pci_ids[] = {
|
||||
|
|
|
@ -404,8 +404,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_gpio_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tegra_gpio_info *tgi = platform_get_drvdata(pdev);
|
||||
struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
unsigned int b, p;
|
||||
|
||||
|
@ -444,8 +443,7 @@ static int tegra_gpio_resume(struct device *dev)
|
|||
|
||||
static int tegra_gpio_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct tegra_gpio_info *tgi = platform_get_drvdata(pdev);
|
||||
struct tegra_gpio_info *tgi = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
unsigned int b, p;
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ static void tegra186_irq_unmask(struct irq_data *data)
|
|||
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
|
||||
}
|
||||
|
||||
static int tegra186_irq_set_type(struct irq_data *data, unsigned int flow)
|
||||
static int tegra186_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
struct tegra_gpio *gpio = irq_data_get_irq_chip_data(data);
|
||||
void __iomem *base;
|
||||
|
@ -293,7 +293,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int flow)
|
|||
value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK;
|
||||
value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL;
|
||||
|
||||
switch (flow & IRQ_TYPE_SENSE_MASK) {
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_NONE:
|
||||
break;
|
||||
|
||||
|
@ -325,7 +325,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int flow)
|
|||
|
||||
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
|
||||
|
||||
if ((flow & IRQ_TYPE_EDGE_BOTH) == 0)
|
||||
if ((type & IRQ_TYPE_EDGE_BOTH) == 0)
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
else
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Socionext Inc.
|
||||
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (C) 2017 Socionext Inc.
|
||||
// Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* Author: Stefan Agner <stefan@agner.ch>.
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -32,6 +33,8 @@ struct vf610_gpio_port {
|
|||
void __iomem *gpio_base;
|
||||
const struct fsl_gpio_soc_data *sdata;
|
||||
u8 irqc[VF610_GPIO_PER_PORT];
|
||||
struct clk *clk_port;
|
||||
struct clk *clk_gpio;
|
||||
int irq;
|
||||
};
|
||||
|
||||
|
@ -271,6 +274,33 @@ static int vf610_gpio_probe(struct platform_device *pdev)
|
|||
if (port->irq < 0)
|
||||
return port->irq;
|
||||
|
||||
port->clk_port = devm_clk_get(&pdev->dev, "port");
|
||||
if (!IS_ERR(port->clk_port)) {
|
||||
ret = clk_prepare_enable(port->clk_port);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (port->clk_port == ERR_PTR(-EPROBE_DEFER)) {
|
||||
/*
|
||||
* Percolate deferrals, for anything else,
|
||||
* just live without the clocking.
|
||||
*/
|
||||
return PTR_ERR(port->clk_port);
|
||||
}
|
||||
|
||||
port->clk_gpio = devm_clk_get(&pdev->dev, "gpio");
|
||||
if (!IS_ERR(port->clk_gpio)) {
|
||||
ret = clk_prepare_enable(port->clk_gpio);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(port->clk_port);
|
||||
return ret;
|
||||
}
|
||||
} else if (port->clk_gpio == ERR_PTR(-EPROBE_DEFER)) {
|
||||
clk_disable_unprepare(port->clk_port);
|
||||
return PTR_ERR(port->clk_gpio);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, port);
|
||||
|
||||
gc = &port->gc;
|
||||
gc->of_node = np;
|
||||
gc->parent = dev;
|
||||
|
@ -305,12 +335,26 @@ static int vf610_gpio_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_gpio_port *port = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&port->gc);
|
||||
if (!IS_ERR(port->clk_port))
|
||||
clk_disable_unprepare(port->clk_port);
|
||||
if (!IS_ERR(port->clk_gpio))
|
||||
clk_disable_unprepare(port->clk_gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver vf610_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-vf610",
|
||||
.of_match_table = vf610_gpio_dt_ids,
|
||||
},
|
||||
.probe = vf610_gpio_probe,
|
||||
.remove = vf610_gpio_remove,
|
||||
};
|
||||
|
||||
builtin_platform_driver(vf610_gpio_driver);
|
||||
|
|
|
@ -169,7 +169,7 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
|
|||
port_state = inb(ws16c48gpio->base + i);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
bits[word_index] |= (port_state << word_offset) & word_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -357,6 +357,28 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
|
|||
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
|
||||
* @irq_data: per irq and chip data passed down to chip functions
|
||||
|
@ -693,8 +715,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
|
|||
|
||||
static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
|
||||
struct zynq_gpio *gpio = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(gpio->clk);
|
||||
|
||||
|
@ -703,8 +724,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
|
|||
|
||||
static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct zynq_gpio *gpio = platform_get_drvdata(pdev);
|
||||
struct zynq_gpio *gpio = dev_get_drvdata(dev);
|
||||
|
||||
return clk_prepare_enable(gpio->clk);
|
||||
}
|
||||
|
@ -827,6 +847,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
|
|||
chip->free = zynq_gpio_free;
|
||||
chip->direction_input = zynq_gpio_dir_in;
|
||||
chip->direction_output = zynq_gpio_dir_out;
|
||||
chip->get_direction = zynq_gpio_get_direction;
|
||||
chip->base = of_alias_get_id(pdev->dev.of_node, "gpio");
|
||||
chip->ngpio = gpio->p_data->ngpio;
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
|
|||
if (!handler)
|
||||
return AE_OK;
|
||||
|
||||
desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
|
||||
desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event", 0);
|
||||
if (IS_ERR(desc)) {
|
||||
dev_err(chip->parent, "Failed to request GPIO\n");
|
||||
return AE_ERROR;
|
||||
|
@ -913,23 +913,15 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
|
|||
if (!found) {
|
||||
enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio);
|
||||
const char *label = "ACPI:OpRegion";
|
||||
int err;
|
||||
|
||||
desc = gpiochip_request_own_desc(chip, pin, label);
|
||||
desc = gpiochip_request_own_desc(chip, pin, label,
|
||||
flags);
|
||||
if (IS_ERR(desc)) {
|
||||
status = AE_ERROR;
|
||||
mutex_unlock(&achip->conn_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = gpiod_configure_flags(desc, label, 0, flags);
|
||||
if (err < 0) {
|
||||
status = AE_NOT_CONFIGURED;
|
||||
gpiochip_free_own_desc(desc);
|
||||
mutex_unlock(&achip->conn_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
||||
if (!conn) {
|
||||
status = AE_NO_MEMORY;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* 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,
|
||||
const char *propname,
|
||||
enum of_gpio_flags *flags,
|
||||
int index)
|
||||
{
|
||||
/*
|
||||
* Handle MMC "cd-inverted" and "wp-inverted" semantics.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_MMC)) {
|
||||
/*
|
||||
* Active low is the default according to the
|
||||
* SDHCI specification and the device tree
|
||||
* bindings. However the code in the current
|
||||
* kernel was written such that the phandle
|
||||
* flags were always respected, and "cd-inverted"
|
||||
* would invert the flag from the device phandle.
|
||||
*/
|
||||
if (!strcmp(propname, "cd-gpios")) {
|
||||
if (of_property_read_bool(np, "cd-inverted"))
|
||||
*flags ^= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
if (!strcmp(propname, "wp-gpios")) {
|
||||
if (of_property_read_bool(np, "wp-inverted"))
|
||||
*flags ^= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Some GPIO fixed regulator quirks.
|
||||
* Note that active low is the default.
|
||||
|
@ -174,7 +196,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
|||
goto out;
|
||||
|
||||
if (flags)
|
||||
of_gpio_flags_quirks(np, flags, index);
|
||||
of_gpio_flags_quirks(np, propname, flags, index);
|
||||
|
||||
pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
|
||||
__func__, propname, np, index,
|
||||
|
|
|
@ -1512,19 +1512,6 @@ static void devm_gpio_chip_release(struct device *dev, void *res)
|
|||
gpiochip_remove(chip);
|
||||
}
|
||||
|
||||
static int devm_gpio_chip_match(struct device *dev, void *res, void *data)
|
||||
|
||||
{
|
||||
struct gpio_chip **r = res;
|
||||
|
||||
if (!r || !*r) {
|
||||
WARN_ON(!r || !*r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return *r == data;
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
|
||||
* @dev: pointer to the device that gpio_chip belongs to.
|
||||
|
@ -1564,23 +1551,6 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
|
||||
|
||||
/**
|
||||
* devm_gpiochip_remove() - Resource manager of gpiochip_remove()
|
||||
* @dev: device for which which resource was allocated
|
||||
* @chip: the chip to remove
|
||||
*
|
||||
* A gpio_chip with any GPIOs still requested may not be removed.
|
||||
*/
|
||||
void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devres_release(dev, devm_gpio_chip_release,
|
||||
devm_gpio_chip_match, chip);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_gpiochip_remove);
|
||||
|
||||
/**
|
||||
* gpiochip_find() - iterator for locating a specific gpio_chip
|
||||
* @data: data to pass to match function
|
||||
|
@ -2299,6 +2269,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
|
|||
unsigned long flags;
|
||||
unsigned offset;
|
||||
|
||||
if (label) {
|
||||
label = kstrdup_const(label, GFP_KERNEL);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
/* NOTE: gpio_request() can be called in early boot,
|
||||
|
@ -2309,6 +2285,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
|
|||
desc_set_label(desc, label ? : "?");
|
||||
status = 0;
|
||||
} else {
|
||||
kfree_const(label);
|
||||
status = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
@ -2325,6 +2302,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
|
|||
|
||||
if (status < 0) {
|
||||
desc_set_label(desc, NULL);
|
||||
kfree_const(label);
|
||||
clear_bit(FLAG_REQUESTED, &desc->flags);
|
||||
goto done;
|
||||
}
|
||||
|
@ -2420,6 +2398,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
|
|||
chip->free(chip, gpio_chip_hwgpio(desc));
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
}
|
||||
kfree_const(desc->label);
|
||||
desc_set_label(desc, NULL);
|
||||
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
clear_bit(FLAG_REQUESTED, &desc->flags);
|
||||
|
@ -2476,6 +2455,7 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
|
|||
* @chip: GPIO chip
|
||||
* @hwnum: hardware number of the GPIO for which to request the descriptor
|
||||
* @label: label for the GPIO
|
||||
* @flags: flags for this GPIO or 0 if default
|
||||
*
|
||||
* Function allows GPIO chip drivers to request and use their own GPIO
|
||||
* descriptors via gpiolib API. Difference to gpiod_request() is that this
|
||||
|
@ -2488,7 +2468,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
|
|||
* code on failure.
|
||||
*/
|
||||
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
|
||||
const char *label)
|
||||
const char *label,
|
||||
enum gpiod_flags flags)
|
||||
{
|
||||
struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
|
||||
int err;
|
||||
|
@ -2502,6 +2483,13 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
|
|||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
err = gpiod_configure_flags(desc, label, 0, flags);
|
||||
if (err) {
|
||||
chip_err(chip, "setup of own GPIO %s failed\n", label);
|
||||
gpiod_free_commit(desc);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);
|
||||
|
@ -3375,11 +3363,19 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
|
|||
* @desc: gpio to set the consumer name on
|
||||
* @name: the new consumer name
|
||||
*/
|
||||
void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
|
||||
int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
|
||||
{
|
||||
VALIDATE_DESC_VOID(desc);
|
||||
/* Just overwrite whatever the previous name was */
|
||||
desc->label = name;
|
||||
VALIDATE_DESC(desc);
|
||||
if (name) {
|
||||
name = kstrdup_const(name, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kfree_const(desc->label);
|
||||
desc_set_label(desc, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
|
||||
|
||||
|
@ -4348,7 +4344,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
|
|||
chip = gpiod_to_chip(desc);
|
||||
hwnum = gpio_chip_hwgpio(desc);
|
||||
|
||||
local_desc = gpiochip_request_own_desc(chip, hwnum, name);
|
||||
/*
|
||||
* FIXME: not very elegant that we call gpiod_configure_flags()
|
||||
* twice here (once inside gpiochip_request_own_desc() and
|
||||
* again here), but the gpiochip_request_own_desc() is external
|
||||
* and cannot really pass the lflags so this is the lesser evil
|
||||
* at the moment. Pass zero as dflags on this first call so we
|
||||
* don't screw anything up.
|
||||
*/
|
||||
local_desc = gpiochip_request_own_desc(chip, hwnum, name, 0);
|
||||
if (IS_ERR(local_desc)) {
|
||||
status = PTR_ERR(local_desc);
|
||||
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",
|
||||
|
|
|
@ -1203,7 +1203,7 @@ static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev,
|
|||
return -EINVAL;
|
||||
|
||||
dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin,
|
||||
"HID/I2C:Event");
|
||||
"HID/I2C:Event", 0);
|
||||
if (IS_ERR(dev->desc[pin])) {
|
||||
dev_err(dev->gc.parent, "Failed to request GPIO\n");
|
||||
return PTR_ERR(dev->desc[pin]);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/consumer.h> /* GPIO descriptor enum */
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -2170,7 +2171,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
|
|||
unsigned int wait_pin = gpmc_s.wait_pin;
|
||||
|
||||
waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip,
|
||||
wait_pin, "WAITPIN");
|
||||
wait_pin, "WAITPIN",
|
||||
0);
|
||||
if (IS_ERR(waitpin_desc)) {
|
||||
dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin);
|
||||
ret = PTR_ERR(waitpin_desc);
|
||||
|
|
|
@ -14,6 +14,34 @@
|
|||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
/* 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_B 1
|
||||
#define TEGRA_MAIN_GPIO_PORT_C 2
|
||||
|
@ -42,6 +70,19 @@
|
|||
((TEGRA_MAIN_GPIO_PORT_##port * 8) + offset)
|
||||
|
||||
/* 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_U 1
|
||||
#define TEGRA_AON_GPIO_PORT_V 2
|
||||
|
|
|
@ -163,7 +163,7 @@ int gpiod_is_active_low(const struct gpio_desc *desc);
|
|||
int gpiod_cansleep(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 */
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -17,6 +17,7 @@ struct device_node;
|
|||
struct seq_file;
|
||||
struct gpio_device;
|
||||
struct module;
|
||||
enum gpiod_flags;
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
|
||||
|
@ -166,11 +167,6 @@ struct gpio_irq_chip {
|
|||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -422,7 +418,6 @@ static inline int gpiochip_add(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,
|
||||
void *data);
|
||||
extern void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip);
|
||||
|
||||
extern struct gpio_chip *gpiochip_find(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 */
|
||||
|
||||
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);
|
||||
|
||||
#else /* CONFIG_GPIOLIB */
|
||||
|
|
Loading…
Reference in a new issue