This is the bulk of GPIO changes for the v4.13 series:
Core: - Export add/remove for lookup tables so that modules can export GPIO descriptor tables. - Handle GPIO sleep states: it is now possible to flag that a GPIO line may loose its state during suspend/resume of the system to save power. This is used in the Wolfson Micro Arizona driver. - ACPI-based GPIO was tightened up a lot around the edges. - Use bitmap_fill() to speed up a loop. New drivers: - Exar XRA1403 SPI-based GPIO. - MVEBU driver now supports Armada 7K and 8K. - LP87565 PMIC GPIO. - Renesas R-CAR R8A7743 (RZ/G1M). - The new IOT2040 8250 serial/GPIO also comes in through this changeset. Substantial driver changes: - Seriously fix the Exar 8250 GPIO portions to work. - The MCP23S08 was moved out to a pin control driver. - Convert MEVEBU to use regmap for register access. - Drop Vulcan support from the Broadcom driver. - Serious cleanup and improvement of the mockup driver, giving us a better test coverage. Misc: - Lots of janitorial clean up. - A bunch of documentation fixes. -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJZX1MjAAoJEEEQszewGV1zEYUQALFsjJH7D2mRN4TSSEeVAcYr Uz52uupsou8tgW0IupRb/khO+V6zgd7j+kHDJLMxX+rCTw3pTq5+XGyi5+iNpxof TIIT1XBx4eq7Q/n4nWdGodHbHN9BXw7cGsNmTb1TS/G/6h1wOKxfzjvUNhDAC+2v idPy6B5G+WrDsYpBtTWlKHKQKVqbUlhLFyJYoglzqIeM5L9Ry/UoZ6sGleho3hKn Vlg/hMtkCexnVO9zopBe5CuEfseLrkcCgCvtQ713egzVXApryp4hqm3Xti20Ntgy OxnKhmVyloqd0kU0qLSpvDAf7B1invbHHbeZsag6wluTMrxgUYJONuonrqGeGiwB FBDtw9SGn2GlEXcs7sg8ANmAyr2XxxezKXD9XLBL5jadNB2KCY5yKMv1IK3VnYdq gEpFAiZ5cmlpZxIXqlyeZP6LKHNTci4amb33x1I/ghH2BTkGQ/3E3anXEbPNWF8G DDE6nrSgU0oQcNqRHyZaWNZpUIz4aFUgJtOEO4lYYP4+VzYSKTdrHseTiiJ91J7E WBz9p5JvSnB22+60RhyTAPjVjXgWa30nidf7WGCK0UHiIYffihCxGZRTlrhoEEUB fXgveJpqxLopYvxpUxi1OqlPYYo7zKRF5BzHsjKMpdVYXfdMdvs7eq2g/X889i1D WpbE9LyAH9FY5BM8YjFX =TpW1 -----END PGP SIGNATURE----- Merge tag 'gpio-v4.13-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.13 series. Some administrativa: I have a slew of 8250 serial patches and the new IOT2040 serial+GPIO driver coming in through this tree, along with a whole bunch of Exar 8250 fixes. These are ACKed by Greg and also hit drivers/platform/* where they are ACKed by Andy Shevchenko. Speaking about drivers/platform/* there is also a bunch of ACPI stuff coming through that route, again ACKed by Andy. The MCP23S08 changes are coming in here as well. You already have the commits in your tree, so this is just a result of sharing an immutable branch between pin control and GPIO. Core: - Export add/remove for lookup tables so that modules can export GPIO descriptor tables. - Handle GPIO sleep states: it is now possible to flag that a GPIO line may loose its state during suspend/resume of the system to save power. This is used in the Wolfson Micro Arizona driver. - ACPI-based GPIO was tightened up a lot around the edges. - Use bitmap_fill() to speed up a loop. New drivers: - Exar XRA1403 SPI-based GPIO. - MVEBU driver now supports Armada 7K and 8K. - LP87565 PMIC GPIO. - Renesas R-CAR R8A7743 (RZ/G1M). - The new IOT2040 8250 serial/GPIO also comes in through this changeset. Substantial driver changes: - Seriously fix the Exar 8250 GPIO portions to work. - The MCP23S08 was moved out to a pin control driver. - Convert MEVEBU to use regmap for register access. - Drop Vulcan support from the Broadcom driver. - Serious cleanup and improvement of the mockup driver, giving us a better test coverage. Misc: - Lots of janitorial clean up. - A bunch of documentation fixes" * tag 'gpio-v4.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (70 commits) serial: exar: Add support for IOT2040 device gpio-exar/8250-exar: Make set of exported GPIOs configurable platform: Accept const properties serial: exar: Factor out platform hooks gpio-exar/8250-exar: Rearrange gpiochip parenthood gpio: exar: Fix iomap request gpio-exar/8250-exar: Do not even instantiate a GPIO device for Commtech cards serial: uapi: Add support for bus termination gpio: rcar: Add R8A7743 (RZ/G1M) support gpio: gpio-wcove: Fix GPIO control register offset calculation gpio: lp87565: Add support for GPIO gpio: dwapb: fix missing first irq for edgeboth irq type MAINTAINERS: Take maintainership for GPIO ACPI support gpio: exar: Fix reading of directions and values gpio: exar: Allocate resources on behalf of the platform device gpio-exar/8250-exar: Fix passing in of parent PCI device gpio: mockup: use devm_kcalloc() where applicable gpio: mockup: add myself as author gpio: mockup: improve the error message gpio: mockup: don't return magic numbers from probe() ...
This commit is contained in:
commit
c7d28eca1d
|
@ -156,3 +156,68 @@ pointed to by its first argument. That should be done in the driver's .probe()
|
||||||
routine. On removal, the driver should unregister its GPIO mapping table by
|
routine. On removal, the driver should unregister its GPIO mapping table by
|
||||||
calling acpi_dev_remove_driver_gpios() on the ACPI device object where that
|
calling acpi_dev_remove_driver_gpios() on the ACPI device object where that
|
||||||
table was previously registered.
|
table was previously registered.
|
||||||
|
|
||||||
|
Using the _CRS fallback
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
If a device does not have _DSD or the driver does not create ACPI GPIO
|
||||||
|
mapping, the Linux GPIO framework refuses to return any GPIOs. This is
|
||||||
|
because the driver does not know what it actually gets. For example if we
|
||||||
|
have a device like below:
|
||||||
|
|
||||||
|
Device (BTH)
|
||||||
|
{
|
||||||
|
Name (_HID, ...)
|
||||||
|
|
||||||
|
Name (_CRS, ResourceTemplate () {
|
||||||
|
GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone,
|
||||||
|
"\\_SB.GPO0", 0, ResourceConsumer) {15}
|
||||||
|
GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone,
|
||||||
|
"\\_SB.GPO0", 0, ResourceConsumer) {27}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
The driver might expect to get the right GPIO when it does:
|
||||||
|
|
||||||
|
desc = gpiod_get(dev, "reset", GPIOD_OUT_LOW);
|
||||||
|
|
||||||
|
but since there is no way to know the mapping between "reset" and
|
||||||
|
the GpioIo() in _CRS desc will hold ERR_PTR(-ENOENT).
|
||||||
|
|
||||||
|
The driver author can solve this by passing the mapping explictly
|
||||||
|
(the recommended way and documented in the above chapter).
|
||||||
|
|
||||||
|
The ACPI GPIO mapping tables should not contaminate drivers that are not
|
||||||
|
knowing about which exact device they are servicing on. It implies that
|
||||||
|
the ACPI GPIO mapping tables are hardly linked to ACPI ID and certain
|
||||||
|
objects, as listed in the above chapter, of the device in question.
|
||||||
|
|
||||||
|
Getting GPIO descriptor
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
There are two main approaches to get GPIO resource from ACPI:
|
||||||
|
desc = gpiod_get(dev, connection_id, flags);
|
||||||
|
desc = gpiod_get_index(dev, connection_id, index, flags);
|
||||||
|
|
||||||
|
We may consider two different cases here, i.e. when connection ID is
|
||||||
|
provided and otherwise.
|
||||||
|
|
||||||
|
Case 1:
|
||||||
|
desc = gpiod_get(dev, "non-null-connection-id", flags);
|
||||||
|
desc = gpiod_get_index(dev, "non-null-connection-id", index, flags);
|
||||||
|
|
||||||
|
Case 2:
|
||||||
|
desc = gpiod_get(dev, NULL, flags);
|
||||||
|
desc = gpiod_get_index(dev, NULL, index, flags);
|
||||||
|
|
||||||
|
Case 1 assumes that corresponding ACPI device description must have
|
||||||
|
defined device properties and will prevent to getting any GPIO resources
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
Case 2 explicitly tells GPIO core to look for resources in _CRS.
|
||||||
|
|
||||||
|
Be aware that gpiod_get_index() in cases 1 and 2, assuming that there
|
||||||
|
are two versions of ACPI device description provided and no mapping is
|
||||||
|
present in the driver, will return different resources. That's why a
|
||||||
|
certain driver has to handle them carefully as explained in previous
|
||||||
|
chapter.
|
||||||
|
|
|
@ -74,11 +74,14 @@ GPIO pin number, and GPIO flags as accepted by the "qe_pio_e" gpio-controller.
|
||||||
Optional standard bitfield specifiers for the last cell:
|
Optional standard bitfield specifiers for the last cell:
|
||||||
|
|
||||||
- Bit 0: 0 means active high, 1 means active low
|
- Bit 0: 0 means active high, 1 means active low
|
||||||
- Bit 1: 1 means single-ended wiring, see:
|
- Bit 1: 0 mean push-pull wiring, see:
|
||||||
|
https://en.wikipedia.org/wiki/Push-pull_output
|
||||||
|
1 means single-ended wiring, see:
|
||||||
https://en.wikipedia.org/wiki/Single-ended_triode
|
https://en.wikipedia.org/wiki/Single-ended_triode
|
||||||
When used with active-low, this means open drain/collector, see:
|
- Bit 2: 0 means open-source, 1 means open drain, see:
|
||||||
https://en.wikipedia.org/wiki/Open_collector
|
https://en.wikipedia.org/wiki/Open_collector
|
||||||
When used with active-high, this means open source/emitter
|
- Bit 3: 0 means the output should be maintained during sleep/low-power mode
|
||||||
|
1 means the output state can be lost during sleep/low-power mode
|
||||||
|
|
||||||
1.1) GPIO specifier best practices
|
1.1) GPIO specifier best practices
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
@ -282,8 +285,8 @@ Example 1:
|
||||||
};
|
};
|
||||||
|
|
||||||
Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
|
Here, a single GPIO controller has GPIOs 0..9 routed to pin controller
|
||||||
pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's
|
pinctrl1's pins 20..29, and GPIOs 10..29 routed to pin controller pinctrl2's
|
||||||
pins 50..59.
|
pins 50..69.
|
||||||
|
|
||||||
Example 2:
|
Example 2:
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
Required Properties:
|
Required Properties:
|
||||||
|
|
||||||
- compatible: should contain one of the following.
|
- compatible: should contain one of the following.
|
||||||
|
- "renesas,gpio-r8a7743": for R8A7743 (RZ/G1M) compatible GPIO controller.
|
||||||
- "renesas,gpio-r8a7778": for R8A7778 (R-Mobile M1) compatible GPIO controller.
|
- "renesas,gpio-r8a7778": for R8A7778 (R-Mobile M1) compatible GPIO controller.
|
||||||
- "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
|
- "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
|
||||||
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.
|
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.
|
||||||
|
|
24
MAINTAINERS
24
MAINTAINERS
|
@ -5747,6 +5747,15 @@ F: include/asm-generic/gpio.h
|
||||||
F: include/uapi/linux/gpio.h
|
F: include/uapi/linux/gpio.h
|
||||||
F: tools/gpio/
|
F: tools/gpio/
|
||||||
|
|
||||||
|
GPIO ACPI SUPPORT
|
||||||
|
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||||
|
M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||||
|
L: linux-gpio@vger.kernel.org
|
||||||
|
L: linux-acpi@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/acpi/gpio-properties.txt
|
||||||
|
F: drivers/gpio/gpiolib-acpi.c
|
||||||
|
|
||||||
GRE DEMULTIPLEXER DRIVER
|
GRE DEMULTIPLEXER DRIVER
|
||||||
M: Dmitry Kozlov <xeb@mail.ru>
|
M: Dmitry Kozlov <xeb@mail.ru>
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
|
@ -12026,6 +12035,13 @@ S: Maintained
|
||||||
F: drivers/media/platform/davinci/
|
F: drivers/media/platform/davinci/
|
||||||
F: include/media/davinci/
|
F: include/media/davinci/
|
||||||
|
|
||||||
|
TI DAVINCI SERIES GPIO DRIVER
|
||||||
|
M: Keerthy <j-keerthy@ti.com>
|
||||||
|
L: linux-gpio@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/gpio/gpio-davinci.txt
|
||||||
|
F: drivers/gpio/gpio-davinci.c
|
||||||
|
|
||||||
TI AM437X VPFE DRIVER
|
TI AM437X VPFE DRIVER
|
||||||
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
|
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
|
@ -14338,6 +14354,14 @@ L: linux-kernel@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/char/xillybus/
|
F: drivers/char/xillybus/
|
||||||
|
|
||||||
|
XRA1403 GPIO EXPANDER
|
||||||
|
M: Nandor Han <nandor.han@ge.com>
|
||||||
|
M: Semi Malinen <semi.malinen@ge.com>
|
||||||
|
L: linux-gpio@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/gpio/gpio-xra1403.c
|
||||||
|
F: Documentation/devicetree/bindings/gpio/gpio-xra1403.txt
|
||||||
|
|
||||||
XTENSA XTFPGA PLATFORM SUPPORT
|
XTENSA XTFPGA PLATFORM SUPPORT
|
||||||
M: Max Filippov <jcmvbkbc@gmail.com>
|
M: Max Filippov <jcmvbkbc@gmail.com>
|
||||||
L: linux-xtensa@linux-xtensa.org
|
L: linux-xtensa@linux-xtensa.org
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <linux/gpio/machine.h>
|
#include <linux/gpio/machine.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
#include <linux/platform_data/at24.h>
|
#include <linux/platform_data/at24.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
#include <linux/platform_data/at24.h>
|
#include <linux/platform_data/at24.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/platform_data/at24.h>
|
#include <linux/platform_data/at24.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
|
|
||||||
#include <media/i2c/tvp514x.h>
|
#include <media/i2c/tvp514x.h>
|
||||||
#include <media/i2c/adv7343.h>
|
#include <media/i2c/adv7343.h>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
#include <linux/i2c/pxa-i2c.h>
|
#include <linux/i2c/pxa-i2c.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/mtd/physmap.h>
|
#include <linux/mtd/physmap.h>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
#include <linux/mfd/da903x.h>
|
#include <linux/mfd/da903x.h>
|
||||||
#include <linux/i2c/max732x.h>
|
#include <linux/platform_data/max732x.h>
|
||||||
#include <linux/i2c/pxa-i2c.h>
|
#include <linux/i2c/pxa-i2c.h>
|
||||||
|
|
||||||
#include <asm/types.h>
|
#include <asm/types.h>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
#include <linux/i2c/pxa-i2c.h>
|
#include <linux/i2c/pxa-i2c.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
#include <linux/platform_data/at24.h>
|
#include <linux/platform_data/at24.h>
|
||||||
#include <linux/smc91x.h>
|
#include <linux/smc91x.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include <linux/usb/isp1362.h>
|
#include <linux/usb/isp1362.h>
|
||||||
#endif
|
#endif
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/adp5588.h>
|
#include <linux/platform_data/adp5588.h>
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include <linux/ata_platform.h>
|
#include <linux/ata_platform.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c-gpio.h>
|
#include <linux/i2c-gpio.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
|
|
||||||
#include "machtypes.h"
|
#include "machtypes.h"
|
||||||
#include "dev-gpio-buttons.h"
|
#include "dev-gpio-buttons.h"
|
||||||
|
|
|
@ -344,7 +344,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
|
||||||
* platform device is released.
|
* platform device is released.
|
||||||
*/
|
*/
|
||||||
int platform_device_add_properties(struct platform_device *pdev,
|
int platform_device_add_properties(struct platform_device *pdev,
|
||||||
struct property_entry *properties)
|
const struct property_entry *properties)
|
||||||
{
|
{
|
||||||
return device_add_properties(&pdev->dev, properties);
|
return device_add_properties(&pdev->dev, properties);
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,9 +337,10 @@ config GPIO_MPC8XXX
|
||||||
|
|
||||||
config GPIO_MVEBU
|
config GPIO_MVEBU
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on PLAT_ORION
|
depends on PLAT_ORION || ARCH_MVEBU
|
||||||
depends on OF_GPIO
|
depends on OF_GPIO
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
|
select REGMAP_MMIO
|
||||||
|
|
||||||
config GPIO_MXC
|
config GPIO_MXC
|
||||||
def_bool y
|
def_bool y
|
||||||
|
@ -515,12 +516,13 @@ config GPIO_XILINX
|
||||||
|
|
||||||
config GPIO_XLP
|
config GPIO_XLP
|
||||||
tristate "Netlogic XLP GPIO support"
|
tristate "Netlogic XLP GPIO support"
|
||||||
depends on OF_GPIO && (CPU_XLP || ARCH_VULCAN || ARCH_THUNDER2 || COMPILE_TEST)
|
depends on OF_GPIO && (CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST)
|
||||||
select GPIOLIB_IRQCHIP
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
help
|
||||||
This driver provides support for GPIO interface on Netlogic XLP MIPS64
|
This driver provides support for GPIO interface on Netlogic XLP MIPS64
|
||||||
SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX,
|
SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX,
|
||||||
XLP9XX and XLP5XX.
|
XLP9XX and XLP5XX. The same GPIO controller block is also present in
|
||||||
|
Cavium's ThunderX2 CN99XX SoCs.
|
||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
@ -963,6 +965,16 @@ config GPIO_LP873X
|
||||||
This driver can also be built as a module. If so, the module will be
|
This driver can also be built as a module. If so, the module will be
|
||||||
called gpio-lp873x.
|
called gpio-lp873x.
|
||||||
|
|
||||||
|
config GPIO_LP87565
|
||||||
|
tristate "TI LP87565 GPIO"
|
||||||
|
depends on MFD_TI_LP87565
|
||||||
|
help
|
||||||
|
This driver supports the GPIO on TI Lp873565 PMICs. 3 GPIOs are present
|
||||||
|
on LP87565 PMICs.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module will be
|
||||||
|
called gpio-lp87565.
|
||||||
|
|
||||||
config GPIO_MAX77620
|
config GPIO_MAX77620
|
||||||
tristate "GPIO support for PMIC MAX77620 and MAX20024"
|
tristate "GPIO support for PMIC MAX77620 and MAX20024"
|
||||||
depends on MFD_MAX77620
|
depends on MFD_MAX77620
|
||||||
|
@ -1236,6 +1248,12 @@ config GPIO_PISOSR
|
||||||
GPIO driver for SPI compatible parallel-in/serial-out shift
|
GPIO driver for SPI compatible parallel-in/serial-out shift
|
||||||
registers. These are input only devices.
|
registers. These are input only devices.
|
||||||
|
|
||||||
|
config GPIO_XRA1403
|
||||||
|
tristate "EXAR XRA1403 16-bit GPIO expander"
|
||||||
|
select REGMAP_SPI
|
||||||
|
help
|
||||||
|
GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
menu "USB GPIO expanders"
|
menu "USB GPIO expanders"
|
||||||
|
|
|
@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
|
||||||
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
|
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
|
||||||
obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
|
obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o
|
||||||
obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
|
obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
|
||||||
|
obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o
|
||||||
obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
|
obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o
|
||||||
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
|
obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o
|
||||||
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
|
obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o
|
||||||
|
@ -141,6 +142,7 @@ obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o
|
||||||
obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o
|
obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o
|
||||||
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
|
obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o
|
||||||
obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o
|
obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o
|
||||||
|
obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o
|
||||||
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
|
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
|
||||||
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
||||||
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
|
||||||
#include <linux/i2c/adp5588.h>
|
#include <linux/platform_data/adp5588.h>
|
||||||
|
|
||||||
#define DRV_NAME "adp5588-gpio"
|
#define DRV_NAME "adp5588-gpio"
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,23 @@ static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
|
||||||
{
|
{
|
||||||
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
|
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
|
||||||
struct arizona *arizona = arizona_gpio->arizona;
|
struct arizona *arizona = arizona_gpio->arizona;
|
||||||
|
bool persistent = gpiochip_line_is_persistent(chip, offset);
|
||||||
|
bool change;
|
||||||
|
int ret;
|
||||||
|
|
||||||
return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
|
ret = regmap_update_bits_check(arizona->regmap,
|
||||||
ARIZONA_GPN_DIR, ARIZONA_GPN_DIR);
|
ARIZONA_GPIO1_CTRL + offset,
|
||||||
|
ARIZONA_GPN_DIR, ARIZONA_GPN_DIR,
|
||||||
|
&change);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (change && persistent) {
|
||||||
|
pm_runtime_mark_last_busy(chip->parent);
|
||||||
|
pm_runtime_put_autosuspend(chip->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
|
static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||||
|
@ -85,6 +99,21 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip,
|
||||||
{
|
{
|
||||||
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
|
struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip);
|
||||||
struct arizona *arizona = arizona_gpio->arizona;
|
struct arizona *arizona = arizona_gpio->arizona;
|
||||||
|
bool persistent = gpiochip_line_is_persistent(chip, offset);
|
||||||
|
unsigned int val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if ((val & ARIZONA_GPN_DIR) && persistent) {
|
||||||
|
ret = pm_runtime_get_sync(chip->parent);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(chip->parent, "Failed to resume: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
value = ARIZONA_GPN_LVL;
|
value = ARIZONA_GPN_LVL;
|
||||||
|
@ -158,6 +187,8 @@ static int arizona_gpio_probe(struct platform_device *pdev)
|
||||||
else
|
else
|
||||||
arizona_gpio->gpio_chip.base = -1;
|
arizona_gpio->gpio_chip.base = -1;
|
||||||
|
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
|
ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
|
||||||
arizona_gpio);
|
arizona_gpio);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
|
@ -437,6 +437,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
unsigned gpio, bank;
|
unsigned gpio, bank;
|
||||||
int irq;
|
int irq;
|
||||||
|
int ret;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u32 binten = 0;
|
u32 binten = 0;
|
||||||
unsigned ngpio, bank_irq;
|
unsigned ngpio, bank_irq;
|
||||||
|
@ -480,12 +481,15 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
|
||||||
PTR_ERR(clk));
|
PTR_ERR(clk));
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
}
|
}
|
||||||
clk_prepare_enable(clk);
|
ret = clk_prepare_enable(clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (!pdata->gpio_unbanked) {
|
if (!pdata->gpio_unbanked) {
|
||||||
irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0);
|
irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
dev_err(dev, "Couldn't allocate IRQ numbers\n");
|
dev_err(dev, "Couldn't allocate IRQ numbers\n");
|
||||||
|
clk_disable_unprepare(clk);
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +498,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
|
||||||
chips);
|
chips);
|
||||||
if (!irq_domain) {
|
if (!irq_domain) {
|
||||||
dev_err(dev, "Couldn't register an IRQ domain\n");
|
dev_err(dev, "Couldn't register an IRQ domain\n");
|
||||||
|
clk_disable_unprepare(clk);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,8 +567,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
|
||||||
sizeof(struct
|
sizeof(struct
|
||||||
davinci_gpio_irq_data),
|
davinci_gpio_irq_data),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!irqdata)
|
if (!irqdata) {
|
||||||
|
clk_disable_unprepare(clk);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
irqdata->regs = g;
|
irqdata->regs = g;
|
||||||
irqdata->bank_num = bank;
|
irqdata->bank_num = bank;
|
||||||
|
|
|
@ -288,7 +288,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
|
||||||
irq_setup_alt_chip(d, type);
|
irq_setup_alt_chip(d, type);
|
||||||
|
|
||||||
dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
|
dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level);
|
||||||
dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
|
if (type != IRQ_TYPE_EDGE_BOTH)
|
||||||
|
dwapb_write(gpio, GPIO_INT_POLARITY, polarity);
|
||||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct exar_gpio_chip {
|
||||||
int index;
|
int index;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
char name[20];
|
char name[20];
|
||||||
|
unsigned int first_pin;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void exar_update(struct gpio_chip *chip, unsigned int reg, int val,
|
static void exar_update(struct gpio_chip *chip, unsigned int reg, int val,
|
||||||
|
@ -51,11 +52,12 @@ static void exar_update(struct gpio_chip *chip, unsigned int reg, int val,
|
||||||
static int exar_set_direction(struct gpio_chip *chip, int direction,
|
static int exar_set_direction(struct gpio_chip *chip, int direction,
|
||||||
unsigned int offset)
|
unsigned int offset)
|
||||||
{
|
{
|
||||||
unsigned int bank = offset / 8;
|
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
|
||||||
unsigned int addr;
|
unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
|
||||||
|
EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
|
||||||
|
unsigned int bit = (offset + exar_gpio->first_pin) % 8;
|
||||||
|
|
||||||
addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
|
exar_update(chip, addr, direction, bit);
|
||||||
exar_update(chip, addr, direction, offset % 8);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,41 +70,38 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg)
|
||||||
value = readb(exar_gpio->regs + reg);
|
value = readb(exar_gpio->regs + reg);
|
||||||
mutex_unlock(&exar_gpio->lock);
|
mutex_unlock(&exar_gpio->lock);
|
||||||
|
|
||||||
return !!value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exar_get_direction(struct gpio_chip *chip, unsigned int offset)
|
static int exar_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||||
{
|
{
|
||||||
unsigned int bank = offset / 8;
|
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
|
||||||
unsigned int addr;
|
unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
|
||||||
int val;
|
EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
|
||||||
|
unsigned int bit = (offset + exar_gpio->first_pin) % 8;
|
||||||
|
|
||||||
addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO;
|
return !!(exar_get(chip, addr) & BIT(bit));
|
||||||
val = exar_get(chip, addr) >> (offset % 8);
|
|
||||||
|
|
||||||
return !!val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exar_get_value(struct gpio_chip *chip, unsigned int offset)
|
static int exar_get_value(struct gpio_chip *chip, unsigned int offset)
|
||||||
{
|
{
|
||||||
unsigned int bank = offset / 8;
|
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
|
||||||
unsigned int addr;
|
unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
|
||||||
int val;
|
EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO;
|
||||||
|
unsigned int bit = (offset + exar_gpio->first_pin) % 8;
|
||||||
|
|
||||||
addr = bank ? EXAR_OFFSET_MPIOLVL_LO : EXAR_OFFSET_MPIOLVL_HI;
|
return !!(exar_get(chip, addr) & BIT(bit));
|
||||||
val = exar_get(chip, addr) >> (offset % 8);
|
|
||||||
|
|
||||||
return !!val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exar_set_value(struct gpio_chip *chip, unsigned int offset,
|
static void exar_set_value(struct gpio_chip *chip, unsigned int offset,
|
||||||
int value)
|
int value)
|
||||||
{
|
{
|
||||||
unsigned int bank = offset / 8;
|
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
|
||||||
unsigned int addr;
|
unsigned int addr = (offset + exar_gpio->first_pin) / 8 ?
|
||||||
|
EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO;
|
||||||
|
unsigned int bit = (offset + exar_gpio->first_pin) % 8;
|
||||||
|
|
||||||
addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO;
|
exar_update(chip, addr, value, bit);
|
||||||
exar_update(chip, addr, value, offset % 8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exar_direction_output(struct gpio_chip *chip, unsigned int offset,
|
static int exar_direction_output(struct gpio_chip *chip, unsigned int offset,
|
||||||
|
@ -119,27 +118,30 @@ static int exar_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
|
||||||
static int gpio_exar_probe(struct platform_device *pdev)
|
static int gpio_exar_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct pci_dev *pcidev = platform_get_drvdata(pdev);
|
struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent);
|
||||||
struct exar_gpio_chip *exar_gpio;
|
struct exar_gpio_chip *exar_gpio;
|
||||||
|
u32 first_pin, ngpios;
|
||||||
void __iomem *p;
|
void __iomem *p;
|
||||||
int index, ret;
|
int index, ret;
|
||||||
|
|
||||||
if (pcidev->vendor != PCI_VENDOR_ID_EXAR)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Map the pci device to get the register addresses.
|
* The UART driver must have mapped region 0 prior to registering this
|
||||||
* We will need to read and write those registers to control
|
* device - use it.
|
||||||
* the GPIO pins.
|
|
||||||
* Using managed functions will save us from unmaping on exit.
|
|
||||||
* As the device is enabled using managed functions by the
|
|
||||||
* UART driver we can also use managed functions here.
|
|
||||||
*/
|
*/
|
||||||
p = pcim_iomap(pcidev, 0, 0);
|
p = pcim_iomap_table(pcidev)[0];
|
||||||
if (!p)
|
if (!p)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
exar_gpio = devm_kzalloc(&pcidev->dev, sizeof(*exar_gpio), GFP_KERNEL);
|
ret = device_property_read_u32(&pdev->dev, "linux,first-pin",
|
||||||
|
&first_pin);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = device_property_read_u32(&pdev->dev, "ngpios", &ngpios);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL);
|
||||||
if (!exar_gpio)
|
if (!exar_gpio)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -149,18 +151,19 @@ static int gpio_exar_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
sprintf(exar_gpio->name, "exar_gpio%d", index);
|
sprintf(exar_gpio->name, "exar_gpio%d", index);
|
||||||
exar_gpio->gpio_chip.label = exar_gpio->name;
|
exar_gpio->gpio_chip.label = exar_gpio->name;
|
||||||
exar_gpio->gpio_chip.parent = &pcidev->dev;
|
exar_gpio->gpio_chip.parent = &pdev->dev;
|
||||||
exar_gpio->gpio_chip.direction_output = exar_direction_output;
|
exar_gpio->gpio_chip.direction_output = exar_direction_output;
|
||||||
exar_gpio->gpio_chip.direction_input = exar_direction_input;
|
exar_gpio->gpio_chip.direction_input = exar_direction_input;
|
||||||
exar_gpio->gpio_chip.get_direction = exar_get_direction;
|
exar_gpio->gpio_chip.get_direction = exar_get_direction;
|
||||||
exar_gpio->gpio_chip.get = exar_get_value;
|
exar_gpio->gpio_chip.get = exar_get_value;
|
||||||
exar_gpio->gpio_chip.set = exar_set_value;
|
exar_gpio->gpio_chip.set = exar_set_value;
|
||||||
exar_gpio->gpio_chip.base = -1;
|
exar_gpio->gpio_chip.base = -1;
|
||||||
exar_gpio->gpio_chip.ngpio = 16;
|
exar_gpio->gpio_chip.ngpio = ngpios;
|
||||||
exar_gpio->regs = p;
|
exar_gpio->regs = p;
|
||||||
exar_gpio->index = index;
|
exar_gpio->index = index;
|
||||||
|
exar_gpio->first_pin = first_pin;
|
||||||
|
|
||||||
ret = devm_gpiochip_add_data(&pcidev->dev,
|
ret = devm_gpiochip_add_data(&pdev->dev,
|
||||||
&exar_gpio->gpio_chip, exar_gpio);
|
&exar_gpio->gpio_chip, exar_gpio);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_destroy;
|
goto err_destroy;
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
|
||||||
|
* Keerthy <j-keerthy@ti.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 "as is" WITHOUT ANY WARRANTY of any
|
||||||
|
* kind, whether expressed or implied; without even the implied warranty
|
||||||
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License version 2 for more details.
|
||||||
|
*
|
||||||
|
* Based on the LP873X driver
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#include <linux/mfd/lp87565.h>
|
||||||
|
|
||||||
|
struct lp87565_gpio {
|
||||||
|
struct gpio_chip chip;
|
||||||
|
struct regmap *map;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lp87565_gpio_get_direction(struct gpio_chip *chip,
|
||||||
|
unsigned int offset)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
int ret, val;
|
||||||
|
|
||||||
|
ret = regmap_read(gpio->map, LP87565_REG_GPIO_CONFIG, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !(val & BIT(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lp87565_gpio_direction_input(struct gpio_chip *chip,
|
||||||
|
unsigned int offset)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return regmap_update_bits(gpio->map,
|
||||||
|
LP87565_REG_GPIO_CONFIG,
|
||||||
|
BIT(offset), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lp87565_gpio_direction_output(struct gpio_chip *chip,
|
||||||
|
unsigned int offset, int value)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return regmap_update_bits(gpio->map,
|
||||||
|
LP87565_REG_GPIO_CONFIG,
|
||||||
|
BIT(offset), !value ? BIT(offset) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
int ret, val;
|
||||||
|
|
||||||
|
ret = regmap_read(gpio->map, LP87565_REG_GPIO_IN, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !!(val & BIT(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT,
|
||||||
|
BIT(offset), value ? BIT(offset) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lp87565_gpio_request(struct gpio_chip *gc, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(gc);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (offset) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
/*
|
||||||
|
* MUX can program the pin to be in EN1/2/3 pin mode
|
||||||
|
* Or GPIO1/2/3 mode.
|
||||||
|
* Setup the GPIO*_SEL MUX to GPIO mode
|
||||||
|
*/
|
||||||
|
ret = regmap_update_bits(gpio->map,
|
||||||
|
LP87565_REG_PIN_FUNCTION,
|
||||||
|
BIT(offset), BIT(offset));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lp87565_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
||||||
|
unsigned long config)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio = gpiochip_get_data(gc);
|
||||||
|
|
||||||
|
switch (pinconf_to_config_param(config)) {
|
||||||
|
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
|
||||||
|
return regmap_update_bits(gpio->map,
|
||||||
|
LP87565_REG_GPIO_CONFIG,
|
||||||
|
BIT(offset +
|
||||||
|
__ffs(LP87565_GOIO1_OD)),
|
||||||
|
BIT(offset +
|
||||||
|
__ffs(LP87565_GOIO1_OD)));
|
||||||
|
case PIN_CONFIG_DRIVE_PUSH_PULL:
|
||||||
|
return regmap_update_bits(gpio->map,
|
||||||
|
LP87565_REG_GPIO_CONFIG,
|
||||||
|
BIT(offset +
|
||||||
|
__ffs(LP87565_GOIO1_OD)), 0);
|
||||||
|
default:
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct gpio_chip template_chip = {
|
||||||
|
.label = "lp87565-gpio",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.request = lp87565_gpio_request,
|
||||||
|
.get_direction = lp87565_gpio_get_direction,
|
||||||
|
.direction_input = lp87565_gpio_direction_input,
|
||||||
|
.direction_output = lp87565_gpio_direction_output,
|
||||||
|
.get = lp87565_gpio_get,
|
||||||
|
.set = lp87565_gpio_set,
|
||||||
|
.set_config = lp87565_gpio_set_config,
|
||||||
|
.base = -1,
|
||||||
|
.ngpio = 3,
|
||||||
|
.can_sleep = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lp87565_gpio_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct lp87565_gpio *gpio;
|
||||||
|
struct lp87565 *lp87565;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||||
|
if (!gpio)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
lp87565 = dev_get_drvdata(pdev->dev.parent);
|
||||||
|
gpio->chip = template_chip;
|
||||||
|
gpio->chip.parent = lp87565->dev;
|
||||||
|
gpio->map = lp87565->regmap;
|
||||||
|
|
||||||
|
ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct platform_device_id lp87565_gpio_id_table[] = {
|
||||||
|
{ "lp87565-q1-gpio", },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table);
|
||||||
|
|
||||||
|
static struct platform_driver lp87565_gpio_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "lp87565-gpio",
|
||||||
|
},
|
||||||
|
.probe = lp87565_gpio_probe,
|
||||||
|
.id_table = lp87565_gpio_id_table,
|
||||||
|
};
|
||||||
|
module_platform_driver(lp87565_gpio_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Keerthy <j-keerthy@ti.com>");
|
||||||
|
MODULE_DESCRIPTION("LP87565 GPIO driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -20,7 +20,7 @@
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/max732x.h>
|
#include <linux/platform_data/max732x.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/gpio/driver.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
|
@ -385,14 +385,18 @@ static irqreturn_t ioh_gpio_handler(int irq, void *dev_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
|
static int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
|
||||||
unsigned int irq_start, unsigned int num)
|
unsigned int irq_start,
|
||||||
|
unsigned int num)
|
||||||
{
|
{
|
||||||
struct irq_chip_generic *gc;
|
struct irq_chip_generic *gc;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
|
|
||||||
gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base,
|
gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base,
|
||||||
handle_simple_irq);
|
handle_simple_irq);
|
||||||
|
if (!gc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
gc->private = chip;
|
gc->private = chip;
|
||||||
ct = gc->chip_types;
|
ct = gc->chip_types;
|
||||||
|
|
||||||
|
@ -404,6 +408,8 @@ static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
|
||||||
|
|
||||||
irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
|
irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
|
||||||
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ioh_gpio_probe(struct pci_dev *pdev,
|
static int ioh_gpio_probe(struct pci_dev *pdev,
|
||||||
|
@ -468,7 +474,11 @@ static int ioh_gpio_probe(struct pci_dev *pdev,
|
||||||
goto err_gpiochip_add;
|
goto err_gpiochip_add;
|
||||||
}
|
}
|
||||||
chip->irq_base = irq_base;
|
chip->irq_base = irq_base;
|
||||||
ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]);
|
|
||||||
|
ret = ioh_gpio_alloc_generic_chip(chip,
|
||||||
|
irq_base, num_ports[j]);
|
||||||
|
if (ret)
|
||||||
|
goto err_gpiochip_add;
|
||||||
}
|
}
|
||||||
|
|
||||||
chip = chip_save;
|
chip = chip_save;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com>
|
* Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com>
|
||||||
* Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
|
* Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
|
||||||
|
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* 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
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
@ -27,10 +28,15 @@
|
||||||
|
|
||||||
#define GPIO_MOCKUP_NAME "gpio-mockup"
|
#define GPIO_MOCKUP_NAME "gpio-mockup"
|
||||||
#define GPIO_MOCKUP_MAX_GC 10
|
#define GPIO_MOCKUP_MAX_GC 10
|
||||||
|
/*
|
||||||
|
* We're storing two values per chip: the GPIO base and the number
|
||||||
|
* of GPIO lines.
|
||||||
|
*/
|
||||||
|
#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DIR_IN = 0,
|
GPIO_MOCKUP_DIR_OUT = 0,
|
||||||
DIR_OUT,
|
GPIO_MOCKUP_DIR_IN = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -41,6 +47,7 @@ enum {
|
||||||
struct gpio_mockup_line_status {
|
struct gpio_mockup_line_status {
|
||||||
int dir;
|
int dir;
|
||||||
bool value;
|
bool value;
|
||||||
|
bool irq_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct gpio_mockup_irq_context {
|
struct gpio_mockup_irq_context {
|
||||||
|
@ -61,7 +68,7 @@ struct gpio_mockup_dbgfs_private {
|
||||||
int offset;
|
int offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1];
|
static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES];
|
||||||
static int gpio_mockup_params_nr;
|
static int gpio_mockup_params_nr;
|
||||||
module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400);
|
module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400);
|
||||||
|
|
||||||
|
@ -93,7 +100,7 @@ static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset,
|
||||||
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
||||||
|
|
||||||
gpio_mockup_set(gc, offset, value);
|
gpio_mockup_set(gc, offset, value);
|
||||||
chip->lines[offset].dir = DIR_OUT;
|
chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -102,7 +109,7 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset)
|
||||||
{
|
{
|
||||||
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
||||||
|
|
||||||
chip->lines[offset].dir = DIR_IN;
|
chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +128,7 @@ static int gpio_mockup_name_lines(struct device *dev,
|
||||||
char **names;
|
char **names;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL);
|
names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL);
|
||||||
if (!names)
|
if (!names)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -142,12 +149,21 @@ static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset)
|
||||||
return chip->irq_base + offset;
|
return chip->irq_base + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void gpio_mockup_irqmask(struct irq_data *data)
|
||||||
* While we should generally support irqmask and irqunmask, this driver is
|
{
|
||||||
* for testing purposes only so we don't care.
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
|
||||||
*/
|
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
||||||
static void gpio_mockup_irqmask(struct irq_data *d) { }
|
|
||||||
static void gpio_mockup_irqunmask(struct irq_data *d) { }
|
chip->lines[data->irq - gc->irq_base].irq_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpio_mockup_irqunmask(struct irq_data *data)
|
||||||
|
{
|
||||||
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
|
||||||
|
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
||||||
|
|
||||||
|
chip->lines[data->irq - gc->irq_base].irq_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
static struct irq_chip gpio_mockup_irqchip = {
|
static struct irq_chip gpio_mockup_irqchip = {
|
||||||
.name = GPIO_MOCKUP_NAME,
|
.name = GPIO_MOCKUP_NAME,
|
||||||
|
@ -178,6 +194,7 @@ static int gpio_mockup_irqchip_setup(struct device *dev,
|
||||||
|
|
||||||
for (i = 0; i < gc->ngpio; i++) {
|
for (i = 0; i < gc->ngpio; i++) {
|
||||||
irq_set_chip(irq_base + i, gc->irqchip);
|
irq_set_chip(irq_base + i, gc->irqchip);
|
||||||
|
irq_set_chip_data(irq_base + i, gc);
|
||||||
irq_set_handler(irq_base + i, &handle_simple_irq);
|
irq_set_handler(irq_base + i, &handle_simple_irq);
|
||||||
irq_modify_status(irq_base + i,
|
irq_modify_status(irq_base + i,
|
||||||
IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
|
IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
|
||||||
|
@ -197,8 +214,13 @@ static ssize_t gpio_mockup_event_write(struct file *file,
|
||||||
struct seq_file *sfile;
|
struct seq_file *sfile;
|
||||||
struct gpio_desc *desc;
|
struct gpio_desc *desc;
|
||||||
struct gpio_chip *gc;
|
struct gpio_chip *gc;
|
||||||
int val;
|
int rv, val;
|
||||||
char buf;
|
|
||||||
|
rv = kstrtoint_from_user(usr_buf, size, 0, &val);
|
||||||
|
if (rv)
|
||||||
|
return rv;
|
||||||
|
if (val != 0 && val != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
sfile = file->private_data;
|
sfile = file->private_data;
|
||||||
priv = sfile->private;
|
priv = sfile->private;
|
||||||
|
@ -206,19 +228,11 @@ static ssize_t gpio_mockup_event_write(struct file *file,
|
||||||
chip = priv->chip;
|
chip = priv->chip;
|
||||||
gc = &chip->gc;
|
gc = &chip->gc;
|
||||||
|
|
||||||
if (copy_from_user(&buf, usr_buf, 1))
|
if (chip->lines[priv->offset].irq_enabled) {
|
||||||
return -EFAULT;
|
gpiod_set_value_cansleep(desc, val);
|
||||||
|
priv->chip->irq_ctx.irq = gc->irq_base + priv->offset;
|
||||||
if (buf == '0')
|
irq_work_queue(&priv->chip->irq_ctx.work);
|
||||||
val = 0;
|
}
|
||||||
else if (buf == '1')
|
|
||||||
val = 1;
|
|
||||||
else
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
gpiod_set_value_cansleep(desc, val);
|
|
||||||
priv->chip->irq_ctx.irq = gc->irq_base + priv->offset;
|
|
||||||
irq_work_queue(&priv->chip->irq_ctx.work);
|
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
@ -294,8 +308,8 @@ static int gpio_mockup_add(struct device *dev,
|
||||||
gc->get_direction = gpio_mockup_get_direction;
|
gc->get_direction = gpio_mockup_get_direction;
|
||||||
gc->to_irq = gpio_mockup_to_irq;
|
gc->to_irq = gpio_mockup_to_irq;
|
||||||
|
|
||||||
chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
|
chip->lines = devm_kcalloc(dev, gc->ngpio,
|
||||||
GFP_KERNEL);
|
sizeof(*chip->lines), GFP_KERNEL);
|
||||||
if (!chip->lines)
|
if (!chip->lines)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -321,23 +335,24 @@ static int gpio_mockup_add(struct device *dev,
|
||||||
|
|
||||||
static int gpio_mockup_probe(struct platform_device *pdev)
|
static int gpio_mockup_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct gpio_mockup_chip *chips;
|
int ret, i, base, ngpio, num_chips;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
int ret, i, base, ngpio;
|
struct gpio_mockup_chip *chips;
|
||||||
char *chip_name;
|
char *chip_name;
|
||||||
|
|
||||||
if (gpio_mockup_params_nr < 2)
|
if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
chips = devm_kzalloc(dev,
|
/* Each chip is described by two values. */
|
||||||
sizeof(*chips) * (gpio_mockup_params_nr >> 1),
|
num_chips = gpio_mockup_params_nr / 2;
|
||||||
GFP_KERNEL);
|
|
||||||
|
chips = devm_kcalloc(dev, num_chips, sizeof(*chips), GFP_KERNEL);
|
||||||
if (!chips)
|
if (!chips)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, chips);
|
platform_set_drvdata(pdev, chips);
|
||||||
|
|
||||||
for (i = 0; i < gpio_mockup_params_nr >> 1; i++) {
|
for (i = 0; i < num_chips; i++) {
|
||||||
base = gpio_mockup_ranges[i * 2];
|
base = gpio_mockup_ranges[i * 2];
|
||||||
|
|
||||||
if (base == -1)
|
if (base == -1)
|
||||||
|
@ -355,18 +370,16 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
||||||
ret = gpio_mockup_add(dev, &chips[i],
|
ret = gpio_mockup_add(dev, &chips[i],
|
||||||
chip_name, base, ngpio);
|
chip_name, base, ngpio);
|
||||||
} else {
|
} else {
|
||||||
ret = -1;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "gpio<%d..%d> add failed\n",
|
dev_err(dev,
|
||||||
base, base < 0 ? ngpio : base + ngpio);
|
"adding gpiochip failed: %d (base: %d, ngpio: %d)\n",
|
||||||
|
ret, base, base < 0 ? ngpio : base + ngpio);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "gpio<%d..%d> add successful!",
|
|
||||||
base, base + ngpio);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -420,5 +433,6 @@ module_exit(mock_device_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
|
MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
|
||||||
MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
|
MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
|
||||||
|
MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
|
||||||
MODULE_DESCRIPTION("GPIO Testing driver");
|
MODULE_DESCRIPTION("GPIO Testing driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
@ -33,21 +33,23 @@
|
||||||
* interrupts.
|
* interrupts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/err.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/irqdomain.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/of_irq.h>
|
|
||||||
#include <linux/of_device.h>
|
|
||||||
#include <linux/pwm.h>
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/pinctrl/consumer.h>
|
|
||||||
#include <linux/irqchip/chained_irq.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/irqchip/chained_irq.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pwm.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include "gpiolib.h"
|
#include "gpiolib.h"
|
||||||
|
|
||||||
|
@ -87,6 +89,7 @@
|
||||||
#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
|
#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
|
||||||
#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
|
#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
|
||||||
#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
|
#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
|
||||||
|
#define MVEBU_GPIO_SOC_VARIANT_A8K 0x4
|
||||||
|
|
||||||
#define MVEBU_MAX_GPIO_PER_BANK 32
|
#define MVEBU_MAX_GPIO_PER_BANK 32
|
||||||
|
|
||||||
|
@ -106,9 +109,9 @@ struct mvebu_pwm {
|
||||||
|
|
||||||
struct mvebu_gpio_chip {
|
struct mvebu_gpio_chip {
|
||||||
struct gpio_chip chip;
|
struct gpio_chip chip;
|
||||||
spinlock_t lock;
|
struct regmap *regs;
|
||||||
void __iomem *membase;
|
u32 offset;
|
||||||
void __iomem *percpu_membase;
|
struct regmap *percpu_regs;
|
||||||
int irqbase;
|
int irqbase;
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
int soc_variant;
|
int soc_variant;
|
||||||
|
@ -130,92 +133,152 @@ struct mvebu_gpio_chip {
|
||||||
* Functions returning addresses of individual registers for a given
|
* Functions returning addresses of individual registers for a given
|
||||||
* GPIO controller.
|
* GPIO controller.
|
||||||
*/
|
*/
|
||||||
static void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip)
|
|
||||||
{
|
|
||||||
return mvchip->membase + GPIO_OUT_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip)
|
static void mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip,
|
||||||
{
|
struct regmap **map, unsigned int *offset)
|
||||||
return mvchip->membase + GPIO_BLINK_EN_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_blink_counter_select(struct mvebu_gpio_chip
|
|
||||||
*mvchip)
|
|
||||||
{
|
|
||||||
return mvchip->membase + GPIO_BLINK_CNT_SELECT_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
|
|
||||||
{
|
|
||||||
return mvchip->membase + GPIO_IO_CONF_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip)
|
|
||||||
{
|
|
||||||
return mvchip->membase + GPIO_IN_POL_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
|
|
||||||
{
|
|
||||||
return mvchip->membase + GPIO_DATA_IN_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
|
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
switch (mvchip->soc_variant) {
|
switch (mvchip->soc_variant) {
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||||
return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
|
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||||
|
*map = mvchip->regs;
|
||||||
|
*offset = GPIO_EDGE_CAUSE_OFF + mvchip->offset;
|
||||||
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||||
cpu = smp_processor_id();
|
cpu = smp_processor_id();
|
||||||
return mvchip->percpu_membase +
|
*map = mvchip->percpu_regs;
|
||||||
GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
|
*offset = GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
|
static u32
|
||||||
|
mvebu_gpio_read_edge_cause(struct mvebu_gpio_chip *mvchip)
|
||||||
|
{
|
||||||
|
struct regmap *map;
|
||||||
|
unsigned int offset;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
mvebu_gpioreg_edge_cause(mvchip, &map, &offset);
|
||||||
|
regmap_read(map, offset, &val);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mvebu_gpio_write_edge_cause(struct mvebu_gpio_chip *mvchip, u32 val)
|
||||||
|
{
|
||||||
|
struct regmap *map;
|
||||||
|
unsigned int offset;
|
||||||
|
|
||||||
|
mvebu_gpioreg_edge_cause(mvchip, &map, &offset);
|
||||||
|
regmap_write(map, offset, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip,
|
||||||
|
struct regmap **map, unsigned int *offset)
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
switch (mvchip->soc_variant) {
|
switch (mvchip->soc_variant) {
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||||
return mvchip->membase + GPIO_EDGE_MASK_OFF;
|
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||||
|
*map = mvchip->regs;
|
||||||
|
*offset = GPIO_EDGE_MASK_OFF + mvchip->offset;
|
||||||
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||||
cpu = smp_processor_id();
|
cpu = smp_processor_id();
|
||||||
return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu);
|
*map = mvchip->regs;
|
||||||
|
*offset = GPIO_EDGE_MASK_MV78200_OFF(cpu);
|
||||||
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||||
cpu = smp_processor_id();
|
cpu = smp_processor_id();
|
||||||
return mvchip->percpu_membase +
|
*map = mvchip->percpu_regs;
|
||||||
GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
|
*offset = GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip)
|
static u32
|
||||||
|
mvebu_gpio_read_edge_mask(struct mvebu_gpio_chip *mvchip)
|
||||||
|
{
|
||||||
|
struct regmap *map;
|
||||||
|
unsigned int offset;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
mvebu_gpioreg_edge_mask(mvchip, &map, &offset);
|
||||||
|
regmap_read(map, offset, &val);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mvebu_gpio_write_edge_mask(struct mvebu_gpio_chip *mvchip, u32 val)
|
||||||
|
{
|
||||||
|
struct regmap *map;
|
||||||
|
unsigned int offset;
|
||||||
|
|
||||||
|
mvebu_gpioreg_edge_mask(mvchip, &map, &offset);
|
||||||
|
regmap_write(map, offset, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip,
|
||||||
|
struct regmap **map, unsigned int *offset)
|
||||||
{
|
{
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
switch (mvchip->soc_variant) {
|
switch (mvchip->soc_variant) {
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||||
return mvchip->membase + GPIO_LEVEL_MASK_OFF;
|
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||||
|
*map = mvchip->regs;
|
||||||
|
*offset = GPIO_LEVEL_MASK_OFF + mvchip->offset;
|
||||||
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||||
cpu = smp_processor_id();
|
cpu = smp_processor_id();
|
||||||
return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu);
|
*map = mvchip->regs;
|
||||||
|
*offset = GPIO_LEVEL_MASK_MV78200_OFF(cpu);
|
||||||
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||||
cpu = smp_processor_id();
|
cpu = smp_processor_id();
|
||||||
return mvchip->percpu_membase +
|
*map = mvchip->percpu_regs;
|
||||||
GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
|
*offset = GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32
|
||||||
|
mvebu_gpio_read_level_mask(struct mvebu_gpio_chip *mvchip)
|
||||||
|
{
|
||||||
|
struct regmap *map;
|
||||||
|
unsigned int offset;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
mvebu_gpioreg_level_mask(mvchip, &map, &offset);
|
||||||
|
regmap_read(map, offset, &val);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val)
|
||||||
|
{
|
||||||
|
struct regmap *map;
|
||||||
|
unsigned int offset;
|
||||||
|
|
||||||
|
mvebu_gpioreg_level_mask(mvchip, &map, &offset);
|
||||||
|
regmap_write(map, offset, val);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions returning addresses of individual registers for a given
|
* Functions returning addresses of individual registers for a given
|
||||||
* PWM controller.
|
* PWM controller.
|
||||||
|
@ -236,17 +299,9 @@ static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
|
||||||
static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
|
static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value)
|
||||||
{
|
{
|
||||||
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
||||||
unsigned long flags;
|
|
||||||
u32 u;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&mvchip->lock, flags);
|
regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
|
||||||
u = readl_relaxed(mvebu_gpioreg_out(mvchip));
|
BIT(pin), value ? BIT(pin) : 0);
|
||||||
if (value)
|
|
||||||
u |= BIT(pin);
|
|
||||||
else
|
|
||||||
u &= ~BIT(pin);
|
|
||||||
writel_relaxed(u, mvebu_gpioreg_out(mvchip));
|
|
||||||
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
||||||
|
@ -254,11 +309,18 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
||||||
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
||||||
u32 u;
|
u32 u;
|
||||||
|
|
||||||
if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin)) {
|
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
|
||||||
u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^
|
|
||||||
readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
if (u & BIT(pin)) {
|
||||||
|
u32 data_in, in_pol;
|
||||||
|
|
||||||
|
regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset,
|
||||||
|
&data_in);
|
||||||
|
regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
|
&in_pol);
|
||||||
|
u = data_in ^ in_pol;
|
||||||
} else {
|
} else {
|
||||||
u = readl_relaxed(mvebu_gpioreg_out(mvchip));
|
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &u);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (u >> pin) & 1;
|
return (u >> pin) & 1;
|
||||||
|
@ -268,25 +330,15 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin,
|
||||||
int value)
|
int value)
|
||||||
{
|
{
|
||||||
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
||||||
unsigned long flags;
|
|
||||||
u32 u;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&mvchip->lock, flags);
|
regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
|
||||||
u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
|
BIT(pin), value ? BIT(pin) : 0);
|
||||||
if (value)
|
|
||||||
u |= BIT(pin);
|
|
||||||
else
|
|
||||||
u &= ~BIT(pin);
|
|
||||||
writel_relaxed(u, mvebu_gpioreg_blink(mvchip));
|
|
||||||
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
|
static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
|
||||||
{
|
{
|
||||||
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
||||||
unsigned long flags;
|
|
||||||
int ret;
|
int ret;
|
||||||
u32 u;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check with the pinctrl driver whether this pin is usable as
|
* Check with the pinctrl driver whether this pin is usable as
|
||||||
|
@ -296,11 +348,8 @@ static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&mvchip->lock, flags);
|
regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
|
||||||
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
|
BIT(pin), BIT(pin));
|
||||||
u |= BIT(pin);
|
|
||||||
writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
|
|
||||||
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -309,9 +358,7 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
|
||||||
int value)
|
int value)
|
||||||
{
|
{
|
||||||
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
|
||||||
unsigned long flags;
|
|
||||||
int ret;
|
int ret;
|
||||||
u32 u;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check with the pinctrl driver whether this pin is usable as
|
* Check with the pinctrl driver whether this pin is usable as
|
||||||
|
@ -324,11 +371,8 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
|
||||||
mvebu_gpio_blink(chip, pin, 0);
|
mvebu_gpio_blink(chip, pin, 0);
|
||||||
mvebu_gpio_set(chip, pin, value);
|
mvebu_gpio_set(chip, pin, value);
|
||||||
|
|
||||||
spin_lock_irqsave(&mvchip->lock, flags);
|
regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
|
||||||
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
|
BIT(pin), 0);
|
||||||
u &= ~BIT(pin);
|
|
||||||
writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
|
|
||||||
spin_unlock_irqrestore(&mvchip->lock, flags);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +394,7 @@ static void mvebu_gpio_irq_ack(struct irq_data *d)
|
||||||
u32 mask = d->mask;
|
u32 mask = d->mask;
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip));
|
mvebu_gpio_write_edge_cause(mvchip, ~mask);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,8 +407,7 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d)
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
ct->mask_cache_priv &= ~mask;
|
ct->mask_cache_priv &= ~mask;
|
||||||
|
mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv);
|
||||||
writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip));
|
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +420,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
ct->mask_cache_priv |= mask;
|
ct->mask_cache_priv |= mask;
|
||||||
writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip));
|
mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +433,7 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d)
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
ct->mask_cache_priv &= ~mask;
|
ct->mask_cache_priv &= ~mask;
|
||||||
writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip));
|
mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +446,7 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d)
|
||||||
|
|
||||||
irq_gc_lock(gc);
|
irq_gc_lock(gc);
|
||||||
ct->mask_cache_priv |= mask;
|
ct->mask_cache_priv |= mask;
|
||||||
writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip));
|
mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv);
|
||||||
irq_gc_unlock(gc);
|
irq_gc_unlock(gc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,8 +486,8 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||||
|
|
||||||
pin = d->hwirq;
|
pin = d->hwirq;
|
||||||
|
|
||||||
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin);
|
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
|
||||||
if (!u)
|
if ((u & BIT(pin)) == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
type &= IRQ_TYPE_SENSE_MASK;
|
type &= IRQ_TYPE_SENSE_MASK;
|
||||||
|
@ -462,31 +505,35 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IRQ_TYPE_EDGE_RISING:
|
case IRQ_TYPE_EDGE_RISING:
|
||||||
case IRQ_TYPE_LEVEL_HIGH:
|
case IRQ_TYPE_LEVEL_HIGH:
|
||||||
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
regmap_update_bits(mvchip->regs,
|
||||||
u &= ~BIT(pin);
|
GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
|
BIT(pin), 0);
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_EDGE_FALLING:
|
case IRQ_TYPE_EDGE_FALLING:
|
||||||
case IRQ_TYPE_LEVEL_LOW:
|
case IRQ_TYPE_LEVEL_LOW:
|
||||||
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
regmap_update_bits(mvchip->regs,
|
||||||
u |= BIT(pin);
|
GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
|
BIT(pin), BIT(pin));
|
||||||
break;
|
break;
|
||||||
case IRQ_TYPE_EDGE_BOTH: {
|
case IRQ_TYPE_EDGE_BOTH: {
|
||||||
u32 v;
|
u32 data_in, in_pol, val;
|
||||||
|
|
||||||
v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^
|
regmap_read(mvchip->regs,
|
||||||
readl_relaxed(mvebu_gpioreg_data_in(mvchip));
|
GPIO_IN_POL_OFF + mvchip->offset, &in_pol);
|
||||||
|
regmap_read(mvchip->regs,
|
||||||
|
GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set initial polarity based on current input level
|
* set initial polarity based on current input level
|
||||||
*/
|
*/
|
||||||
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
if ((data_in ^ in_pol) & BIT(pin))
|
||||||
if (v & BIT(pin))
|
val = BIT(pin); /* falling */
|
||||||
u |= BIT(pin); /* falling */
|
|
||||||
else
|
else
|
||||||
u &= ~BIT(pin); /* rising */
|
val = 0; /* raising */
|
||||||
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
|
|
||||||
|
regmap_update_bits(mvchip->regs,
|
||||||
|
GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
|
BIT(pin), val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,7 +544,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
||||||
{
|
{
|
||||||
struct mvebu_gpio_chip *mvchip = irq_desc_get_handler_data(desc);
|
struct mvebu_gpio_chip *mvchip = irq_desc_get_handler_data(desc);
|
||||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||||
u32 cause, type;
|
u32 cause, type, data_in, level_mask, edge_cause, edge_mask;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (mvchip == NULL)
|
if (mvchip == NULL)
|
||||||
|
@ -505,10 +552,12 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
||||||
|
|
||||||
chained_irq_enter(chip, desc);
|
chained_irq_enter(chip, desc);
|
||||||
|
|
||||||
cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) &
|
regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
|
||||||
readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
|
level_mask = mvebu_gpio_read_level_mask(mvchip);
|
||||||
cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) &
|
edge_cause = mvebu_gpio_read_edge_cause(mvchip);
|
||||||
readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
|
edge_mask = mvebu_gpio_read_edge_mask(mvchip);
|
||||||
|
|
||||||
|
cause = (data_in ^ level_mask) | (edge_cause & edge_mask);
|
||||||
|
|
||||||
for (i = 0; i < mvchip->chip.ngpio; i++) {
|
for (i = 0; i < mvchip->chip.ngpio; i++) {
|
||||||
int irq;
|
int irq;
|
||||||
|
@ -523,9 +572,13 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
|
||||||
/* Swap polarity (race with GPIO line) */
|
/* Swap polarity (race with GPIO line) */
|
||||||
u32 polarity;
|
u32 polarity;
|
||||||
|
|
||||||
polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
regmap_read(mvchip->regs,
|
||||||
|
GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
|
&polarity);
|
||||||
polarity ^= BIT(i);
|
polarity ^= BIT(i);
|
||||||
writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip));
|
regmap_write(mvchip->regs,
|
||||||
|
GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
|
polarity);
|
||||||
}
|
}
|
||||||
|
|
||||||
generic_handle_irq(irq);
|
generic_handle_irq(irq);
|
||||||
|
@ -628,7 +681,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
|
||||||
state->period = 1;
|
state->period = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
|
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
|
||||||
if (u)
|
if (u)
|
||||||
state->enabled = true;
|
state->enabled = true;
|
||||||
else
|
else
|
||||||
|
@ -691,8 +744,8 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
|
||||||
{
|
{
|
||||||
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
|
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
|
||||||
|
|
||||||
mvpwm->blink_select =
|
regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
|
||||||
readl_relaxed(mvebu_gpioreg_blink_counter_select(mvchip));
|
&mvpwm->blink_select);
|
||||||
mvpwm->blink_on_duration =
|
mvpwm->blink_on_duration =
|
||||||
readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
|
readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
|
||||||
mvpwm->blink_off_duration =
|
mvpwm->blink_off_duration =
|
||||||
|
@ -703,8 +756,8 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
|
||||||
{
|
{
|
||||||
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
|
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
|
||||||
|
|
||||||
writel_relaxed(mvpwm->blink_select,
|
regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
|
||||||
mvebu_gpioreg_blink_counter_select(mvchip));
|
mvpwm->blink_select);
|
||||||
writel_relaxed(mvpwm->blink_on_duration,
|
writel_relaxed(mvpwm->blink_on_duration,
|
||||||
mvebu_pwmreg_blink_on_duration(mvpwm));
|
mvebu_pwmreg_blink_on_duration(mvpwm));
|
||||||
writel_relaxed(mvpwm->blink_off_duration,
|
writel_relaxed(mvpwm->blink_off_duration,
|
||||||
|
@ -747,7 +800,8 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
|
||||||
set = U32_MAX;
|
set = U32_MAX;
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
writel_relaxed(set, mvebu_gpioreg_blink_counter_select(mvchip));
|
regmap_write(mvchip->regs,
|
||||||
|
GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
|
||||||
|
|
||||||
mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
|
mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
|
||||||
if (!mvpwm)
|
if (!mvpwm)
|
||||||
|
@ -790,14 +844,14 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||||
u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
|
u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
out = readl_relaxed(mvebu_gpioreg_out(mvchip));
|
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out);
|
||||||
io_conf = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
|
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &io_conf);
|
||||||
blink = readl_relaxed(mvebu_gpioreg_blink(mvchip));
|
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &blink);
|
||||||
in_pol = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
|
regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, &in_pol);
|
||||||
data_in = readl_relaxed(mvebu_gpioreg_data_in(mvchip));
|
regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
|
||||||
cause = readl_relaxed(mvebu_gpioreg_edge_cause(mvchip));
|
cause = mvebu_gpio_read_edge_cause(mvchip);
|
||||||
edg_msk = readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
|
edg_msk = mvebu_gpio_read_edge_mask(mvchip);
|
||||||
lvl_msk = readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
|
lvl_msk = mvebu_gpio_read_level_mask(mvchip);
|
||||||
|
|
||||||
for (i = 0; i < chip->ngpio; i++) {
|
for (i = 0; i < chip->ngpio; i++) {
|
||||||
const char *label;
|
const char *label;
|
||||||
|
@ -855,6 +909,10 @@ static const struct of_device_id mvebu_gpio_of_match[] = {
|
||||||
.compatible = "marvell,armada-370-gpio",
|
.compatible = "marvell,armada-370-gpio",
|
||||||
.data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
|
.data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "marvell,armada-8k-gpio",
|
||||||
|
.data = (void *) MVEBU_GPIO_SOC_VARIANT_A8K,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
},
|
},
|
||||||
|
@ -865,36 +923,41 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
|
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip));
|
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
|
||||||
mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip));
|
&mvchip->out_reg);
|
||||||
mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip));
|
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
|
||||||
mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip));
|
&mvchip->io_conf_reg);
|
||||||
|
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
|
||||||
|
&mvchip->blink_en_reg);
|
||||||
|
regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
|
&mvchip->in_pol_reg);
|
||||||
|
|
||||||
switch (mvchip->soc_variant) {
|
switch (mvchip->soc_variant) {
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||||
mvchip->edge_mask_regs[0] =
|
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||||
readl(mvchip->membase + GPIO_EDGE_MASK_OFF);
|
regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
|
||||||
mvchip->level_mask_regs[0] =
|
&mvchip->edge_mask_regs[0]);
|
||||||
readl(mvchip->membase + GPIO_LEVEL_MASK_OFF);
|
regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
|
||||||
|
&mvchip->level_mask_regs[0]);
|
||||||
break;
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
mvchip->edge_mask_regs[i] =
|
regmap_read(mvchip->regs,
|
||||||
readl(mvchip->membase +
|
GPIO_EDGE_MASK_MV78200_OFF(i),
|
||||||
GPIO_EDGE_MASK_MV78200_OFF(i));
|
&mvchip->edge_mask_regs[i]);
|
||||||
mvchip->level_mask_regs[i] =
|
regmap_read(mvchip->regs,
|
||||||
readl(mvchip->membase +
|
GPIO_LEVEL_MASK_MV78200_OFF(i),
|
||||||
GPIO_LEVEL_MASK_MV78200_OFF(i));
|
&mvchip->level_mask_regs[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
mvchip->edge_mask_regs[i] =
|
regmap_read(mvchip->regs,
|
||||||
readl(mvchip->membase +
|
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
|
||||||
GPIO_EDGE_MASK_ARMADAXP_OFF(i));
|
&mvchip->edge_mask_regs[i]);
|
||||||
mvchip->level_mask_regs[i] =
|
regmap_read(mvchip->regs,
|
||||||
readl(mvchip->membase +
|
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
|
||||||
GPIO_LEVEL_MASK_ARMADAXP_OFF(i));
|
&mvchip->level_mask_regs[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -912,35 +975,41 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
|
||||||
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
|
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip));
|
regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
|
||||||
writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip));
|
mvchip->out_reg);
|
||||||
writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip));
|
regmap_write(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
|
||||||
writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip));
|
mvchip->io_conf_reg);
|
||||||
|
regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
|
||||||
|
mvchip->blink_en_reg);
|
||||||
|
regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
|
||||||
|
mvchip->in_pol_reg);
|
||||||
|
|
||||||
switch (mvchip->soc_variant) {
|
switch (mvchip->soc_variant) {
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||||
writel(mvchip->edge_mask_regs[0],
|
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||||
mvchip->membase + GPIO_EDGE_MASK_OFF);
|
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
|
||||||
writel(mvchip->level_mask_regs[0],
|
mvchip->edge_mask_regs[0]);
|
||||||
mvchip->membase + GPIO_LEVEL_MASK_OFF);
|
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
|
||||||
|
mvchip->level_mask_regs[0]);
|
||||||
break;
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||||
for (i = 0; i < 2; i++) {
|
for (i = 0; i < 2; i++) {
|
||||||
writel(mvchip->edge_mask_regs[i],
|
regmap_write(mvchip->regs,
|
||||||
mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i));
|
GPIO_EDGE_MASK_MV78200_OFF(i),
|
||||||
writel(mvchip->level_mask_regs[i],
|
mvchip->edge_mask_regs[i]);
|
||||||
mvchip->membase +
|
regmap_write(mvchip->regs,
|
||||||
GPIO_LEVEL_MASK_MV78200_OFF(i));
|
GPIO_LEVEL_MASK_MV78200_OFF(i),
|
||||||
|
mvchip->level_mask_regs[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
writel(mvchip->edge_mask_regs[i],
|
regmap_write(mvchip->regs,
|
||||||
mvchip->membase +
|
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
|
||||||
GPIO_EDGE_MASK_ARMADAXP_OFF(i));
|
mvchip->edge_mask_regs[i]);
|
||||||
writel(mvchip->level_mask_regs[i],
|
regmap_write(mvchip->regs,
|
||||||
mvchip->membase +
|
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
|
||||||
GPIO_LEVEL_MASK_ARMADAXP_OFF(i));
|
mvchip->level_mask_regs[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -953,12 +1022,73 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct regmap_config mvebu_gpio_regmap_config = {
|
||||||
|
.reg_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.val_bits = 32,
|
||||||
|
.fast_io = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mvebu_gpio_probe_raw(struct platform_device *pdev,
|
||||||
|
struct mvebu_gpio_chip *mvchip)
|
||||||
|
{
|
||||||
|
struct resource *res;
|
||||||
|
void __iomem *base;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base,
|
||||||
|
&mvebu_gpio_regmap_config);
|
||||||
|
if (IS_ERR(mvchip->regs))
|
||||||
|
return PTR_ERR(mvchip->regs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the legacy SoCs, the regmap directly maps to the GPIO
|
||||||
|
* registers, so no offset is needed.
|
||||||
|
*/
|
||||||
|
mvchip->offset = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Armada XP has a second range of registers for the
|
||||||
|
* per-CPU registers
|
||||||
|
*/
|
||||||
|
if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
base = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
|
mvchip->percpu_regs =
|
||||||
|
devm_regmap_init_mmio(&pdev->dev, base,
|
||||||
|
&mvebu_gpio_regmap_config);
|
||||||
|
if (IS_ERR(mvchip->percpu_regs))
|
||||||
|
return PTR_ERR(mvchip->percpu_regs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mvebu_gpio_probe_syscon(struct platform_device *pdev,
|
||||||
|
struct mvebu_gpio_chip *mvchip)
|
||||||
|
{
|
||||||
|
mvchip->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
|
||||||
|
if (IS_ERR(mvchip->regs))
|
||||||
|
return PTR_ERR(mvchip->regs);
|
||||||
|
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "offset", &mvchip->offset))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mvebu_gpio_probe(struct platform_device *pdev)
|
static int mvebu_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct mvebu_gpio_chip *mvchip;
|
struct mvebu_gpio_chip *mvchip;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
struct resource *res;
|
|
||||||
struct irq_chip_generic *gc;
|
struct irq_chip_generic *gc;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
unsigned int ngpios;
|
unsigned int ngpios;
|
||||||
|
@ -1016,53 +1146,47 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
||||||
mvchip->chip.of_node = np;
|
mvchip->chip.of_node = np;
|
||||||
mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
|
mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
|
||||||
|
|
||||||
spin_lock_init(&mvchip->lock);
|
if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K)
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
err = mvebu_gpio_probe_syscon(pdev, mvchip);
|
||||||
mvchip->membase = devm_ioremap_resource(&pdev->dev, res);
|
else
|
||||||
if (IS_ERR(mvchip->membase))
|
err = mvebu_gpio_probe_raw(pdev, mvchip);
|
||||||
return PTR_ERR(mvchip->membase);
|
|
||||||
|
|
||||||
/*
|
if (err)
|
||||||
* The Armada XP has a second range of registers for the
|
return err;
|
||||||
* per-CPU registers
|
|
||||||
*/
|
|
||||||
if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) {
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
||||||
mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev,
|
|
||||||
res);
|
|
||||||
if (IS_ERR(mvchip->percpu_membase))
|
|
||||||
return PTR_ERR(mvchip->percpu_membase);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mask and clear GPIO interrupts.
|
* Mask and clear GPIO interrupts.
|
||||||
*/
|
*/
|
||||||
switch (soc_variant) {
|
switch (soc_variant) {
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
case MVEBU_GPIO_SOC_VARIANT_ORION:
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
|
case MVEBU_GPIO_SOC_VARIANT_A8K:
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
|
regmap_write(mvchip->regs,
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
|
GPIO_EDGE_CAUSE_OFF + mvchip->offset, 0);
|
||||||
|
regmap_write(mvchip->regs,
|
||||||
|
GPIO_EDGE_MASK_OFF + mvchip->offset, 0);
|
||||||
|
regmap_write(mvchip->regs,
|
||||||
|
GPIO_LEVEL_MASK_OFF + mvchip->offset, 0);
|
||||||
break;
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
case MVEBU_GPIO_SOC_VARIANT_MV78200:
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
|
regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0);
|
||||||
for (cpu = 0; cpu < 2; cpu++) {
|
for (cpu = 0; cpu < 2; cpu++) {
|
||||||
writel_relaxed(0, mvchip->membase +
|
regmap_write(mvchip->regs,
|
||||||
GPIO_EDGE_MASK_MV78200_OFF(cpu));
|
GPIO_EDGE_MASK_MV78200_OFF(cpu), 0);
|
||||||
writel_relaxed(0, mvchip->membase +
|
regmap_write(mvchip->regs,
|
||||||
GPIO_LEVEL_MASK_MV78200_OFF(cpu));
|
GPIO_LEVEL_MASK_MV78200_OFF(cpu), 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
|
regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0);
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
|
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0);
|
||||||
writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
|
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0);
|
||||||
for (cpu = 0; cpu < 4; cpu++) {
|
for (cpu = 0; cpu < 4; cpu++) {
|
||||||
writel_relaxed(0, mvchip->percpu_membase +
|
regmap_write(mvchip->percpu_regs,
|
||||||
GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu));
|
GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu), 0);
|
||||||
writel_relaxed(0, mvchip->percpu_membase +
|
regmap_write(mvchip->percpu_regs,
|
||||||
GPIO_EDGE_MASK_ARMADAXP_OFF(cpu));
|
GPIO_EDGE_MASK_ARMADAXP_OFF(cpu), 0);
|
||||||
writel_relaxed(0, mvchip->percpu_membase +
|
regmap_write(mvchip->percpu_regs,
|
||||||
GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu));
|
GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu), 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/pcf857x.h>
|
#include <linux/platform_data/pcf857x.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
|
|
|
@ -331,14 +331,18 @@ static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
|
static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
|
||||||
unsigned int irq_start, unsigned int num)
|
unsigned int irq_start,
|
||||||
|
unsigned int num)
|
||||||
{
|
{
|
||||||
struct irq_chip_generic *gc;
|
struct irq_chip_generic *gc;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
|
|
||||||
gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base,
|
gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base,
|
||||||
handle_simple_irq);
|
handle_simple_irq);
|
||||||
|
if (!gc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
gc->private = chip;
|
gc->private = chip;
|
||||||
ct = gc->chip_types;
|
ct = gc->chip_types;
|
||||||
|
|
||||||
|
@ -349,6 +353,8 @@ static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
|
||||||
|
|
||||||
irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
|
irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE,
|
||||||
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pch_gpio_probe(struct pci_dev *pdev,
|
static int pch_gpio_probe(struct pci_dev *pdev,
|
||||||
|
@ -425,7 +431,10 @@ static int pch_gpio_probe(struct pci_dev *pdev,
|
||||||
goto err_request_irq;
|
goto err_request_irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
|
ret = pch_gpio_alloc_generic_chip(chip, irq_base,
|
||||||
|
gpio_pins[chip->ioh]);
|
||||||
|
if (ret)
|
||||||
|
goto err_request_irq;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -344,6 +344,10 @@ static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
|
||||||
|
|
||||||
static const struct of_device_id gpio_rcar_of_table[] = {
|
static const struct of_device_id gpio_rcar_of_table[] = {
|
||||||
{
|
{
|
||||||
|
.compatible = "renesas,gpio-r8a7743",
|
||||||
|
/* RZ/G1 GPIO is identical to R-Car Gen2. */
|
||||||
|
.data = &gpio_rcar_info_gen2,
|
||||||
|
}, {
|
||||||
.compatible = "renesas,gpio-r8a7790",
|
.compatible = "renesas,gpio-r8a7790",
|
||||||
.data = &gpio_rcar_info_gen2,
|
.data = &gpio_rcar_info_gen2,
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -320,13 +320,16 @@ static irqreturn_t gsta_gpio_handler(int irq, void *dev_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gsta_alloc_irq_chip(struct gsta_gpio *chip)
|
static int gsta_alloc_irq_chip(struct gsta_gpio *chip)
|
||||||
{
|
{
|
||||||
struct irq_chip_generic *gc;
|
struct irq_chip_generic *gc;
|
||||||
struct irq_chip_type *ct;
|
struct irq_chip_type *ct;
|
||||||
|
|
||||||
gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base,
|
gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base,
|
||||||
chip->reg_base, handle_simple_irq);
|
chip->reg_base, handle_simple_irq);
|
||||||
|
if (!gc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
gc->private = chip;
|
gc->private = chip;
|
||||||
ct = gc->chip_types;
|
ct = gc->chip_types;
|
||||||
|
|
||||||
|
@ -350,6 +353,8 @@ static void gsta_alloc_irq_chip(struct gsta_gpio *chip)
|
||||||
}
|
}
|
||||||
gc->irq_cnt = i - gc->irq_base;
|
gc->irq_cnt = i - gc->irq_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The platform device used here is instantiated by the MFD device */
|
/* The platform device used here is instantiated by the MFD device */
|
||||||
|
@ -400,7 +405,10 @@ static int gsta_probe(struct platform_device *dev)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
chip->irq_base = err;
|
chip->irq_base = err;
|
||||||
gsta_alloc_irq_chip(chip);
|
|
||||||
|
err = gsta_alloc_irq_chip(chip);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = devm_request_irq(&dev->dev, pdev->irq, gsta_gpio_handler,
|
err = devm_request_irq(&dev->dev, pdev->irq, gsta_gpio_handler,
|
||||||
IRQF_SHARED, KBUILD_MODNAME, chip);
|
IRQF_SHARED, KBUILD_MODNAME, chip);
|
||||||
|
|
|
@ -108,19 +108,14 @@ struct wcove_gpio {
|
||||||
static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type)
|
static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type)
|
||||||
{
|
{
|
||||||
unsigned int reg;
|
unsigned int reg;
|
||||||
int bank;
|
|
||||||
|
|
||||||
if (gpio < BANK0_NR_PINS)
|
if (gpio >= WCOVE_GPIO_NUM)
|
||||||
bank = 0;
|
return -EOPNOTSUPP;
|
||||||
else if (gpio < BANK0_NR_PINS + BANK1_NR_PINS)
|
|
||||||
bank = 1;
|
|
||||||
else
|
|
||||||
bank = 2;
|
|
||||||
|
|
||||||
if (reg_type == CTRL_IN)
|
if (reg_type == CTRL_IN)
|
||||||
reg = GPIO_IN_CTRL_BASE + bank;
|
reg = GPIO_IN_CTRL_BASE + gpio;
|
||||||
else
|
else
|
||||||
reg = GPIO_OUT_CTRL_BASE + bank;
|
reg = GPIO_OUT_CTRL_BASE + gpio;
|
||||||
|
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +140,10 @@ static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio)
|
||||||
|
|
||||||
static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)
|
static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)
|
||||||
{
|
{
|
||||||
unsigned int reg = to_reg(gpio, CTRL_IN);
|
int reg = to_reg(gpio, CTRL_IN);
|
||||||
|
|
||||||
|
if (reg < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt);
|
regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt);
|
||||||
}
|
}
|
||||||
|
@ -153,27 +151,36 @@ static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)
|
||||||
static int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio)
|
static int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio)
|
||||||
{
|
{
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
int reg = to_reg(gpio, CTRL_OUT);
|
||||||
|
|
||||||
return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT),
|
if (reg < 0)
|
||||||
CTLO_INPUT_SET);
|
return 0;
|
||||||
|
|
||||||
|
return regmap_write(wg->regmap, reg, CTLO_INPUT_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio,
|
static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio,
|
||||||
int value)
|
int value)
|
||||||
{
|
{
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
int reg = to_reg(gpio, CTRL_OUT);
|
||||||
|
|
||||||
return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT),
|
if (reg < 0)
|
||||||
CTLO_OUTPUT_SET | value);
|
return 0;
|
||||||
|
|
||||||
|
return regmap_write(wg->regmap, reg, CTLO_OUTPUT_SET | value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio)
|
static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio)
|
||||||
{
|
{
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
int ret;
|
int ret, reg = to_reg(gpio, CTRL_OUT);
|
||||||
|
|
||||||
ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &val);
|
if (reg < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = regmap_read(wg->regmap, reg, &val);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -184,9 +191,12 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio)
|
||||||
{
|
{
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
int ret;
|
int ret, reg = to_reg(gpio, CTRL_IN);
|
||||||
|
|
||||||
ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &val);
|
if (reg < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = regmap_read(wg->regmap, reg, &val);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -197,25 +207,33 @@ static void wcove_gpio_set(struct gpio_chip *chip,
|
||||||
unsigned int gpio, int value)
|
unsigned int gpio, int value)
|
||||||
{
|
{
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
int reg = to_reg(gpio, CTRL_OUT);
|
||||||
|
|
||||||
|
if (reg < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 1);
|
regmap_update_bits(wg->regmap, reg, 1, 1);
|
||||||
else
|
else
|
||||||
regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 0);
|
regmap_update_bits(wg->regmap, reg, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio,
|
static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio,
|
||||||
unsigned long config)
|
unsigned long config)
|
||||||
{
|
{
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
int reg = to_reg(gpio, CTRL_OUT);
|
||||||
|
|
||||||
|
if (reg < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
switch (pinconf_to_config_param(config)) {
|
switch (pinconf_to_config_param(config)) {
|
||||||
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
|
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
|
||||||
return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT),
|
return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK,
|
||||||
CTLO_DRV_MASK, CTLO_DRV_OD);
|
CTLO_DRV_OD);
|
||||||
case PIN_CONFIG_DRIVE_PUSH_PULL:
|
case PIN_CONFIG_DRIVE_PUSH_PULL:
|
||||||
return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT),
|
return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK,
|
||||||
CTLO_DRV_MASK, CTLO_DRV_CMOS);
|
CTLO_DRV_CMOS);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +246,9 @@ static int wcove_irq_type(struct irq_data *data, unsigned int type)
|
||||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
if (data->hwirq >= WCOVE_GPIO_NUM)
|
||||||
|
return 0;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IRQ_TYPE_NONE:
|
case IRQ_TYPE_NONE:
|
||||||
wg->intcnt = CTLI_INTCNT_DIS;
|
wg->intcnt = CTLI_INTCNT_DIS;
|
||||||
|
@ -278,6 +299,9 @@ static void wcove_irq_unmask(struct irq_data *data)
|
||||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
if (data->hwirq >= WCOVE_GPIO_NUM)
|
||||||
|
return;
|
||||||
|
|
||||||
wg->set_irq_mask = false;
|
wg->set_irq_mask = false;
|
||||||
wg->update |= UPDATE_IRQ_MASK;
|
wg->update |= UPDATE_IRQ_MASK;
|
||||||
}
|
}
|
||||||
|
@ -287,6 +311,9 @@ static void wcove_irq_mask(struct irq_data *data)
|
||||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||||
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
struct wcove_gpio *wg = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
if (data->hwirq >= WCOVE_GPIO_NUM)
|
||||||
|
return;
|
||||||
|
|
||||||
wg->set_irq_mask = true;
|
wg->set_irq_mask = true;
|
||||||
wg->update |= UPDATE_IRQ_MASK;
|
wg->update |= UPDATE_IRQ_MASK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* GPIO driver for EXAR XRA1403 16-bit GPIO expander
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017, General Electric Company
|
||||||
|
*
|
||||||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
/* XRA1403 registers */
|
||||||
|
#define XRA_GSR 0x00 /* GPIO State */
|
||||||
|
#define XRA_OCR 0x02 /* Output Control */
|
||||||
|
#define XRA_PIR 0x04 /* Input Polarity Inversion */
|
||||||
|
#define XRA_GCR 0x06 /* GPIO Configuration */
|
||||||
|
#define XRA_PUR 0x08 /* Input Internal Pull-up Resistor Enable/Disable */
|
||||||
|
#define XRA_IER 0x0A /* Input Interrupt Enable */
|
||||||
|
#define XRA_TSCR 0x0C /* Output Three-State Control */
|
||||||
|
#define XRA_ISR 0x0E /* Input Interrupt Status */
|
||||||
|
#define XRA_REIR 0x10 /* Input Rising Edge Interrupt Enable */
|
||||||
|
#define XRA_FEIR 0x12 /* Input Falling Edge Interrupt Enable */
|
||||||
|
#define XRA_IFR 0x14 /* Input Filter Enable/Disable */
|
||||||
|
|
||||||
|
struct xra1403 {
|
||||||
|
struct gpio_chip chip;
|
||||||
|
struct regmap *regmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config xra1403_regmap_cfg = {
|
||||||
|
.reg_bits = 7,
|
||||||
|
.pad_bits = 1,
|
||||||
|
.val_bits = 8,
|
||||||
|
|
||||||
|
.max_register = XRA_IFR | 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int to_reg(unsigned int reg, unsigned int offset)
|
||||||
|
{
|
||||||
|
return reg + (offset > 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xra1403_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
return regmap_update_bits(xra->regmap, to_reg(XRA_GCR, offset),
|
||||||
|
BIT(offset % 8), BIT(offset % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xra1403_direction_output(struct gpio_chip *chip, unsigned int offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
ret = regmap_update_bits(xra->regmap, to_reg(XRA_GCR, offset),
|
||||||
|
BIT(offset % 8), 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset),
|
||||||
|
BIT(offset % 8), value ? BIT(offset % 8) : 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xra1403_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int val;
|
||||||
|
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
ret = regmap_read(xra->regmap, to_reg(XRA_GCR, offset), &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !!(val & BIT(offset % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xra1403_get(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int val;
|
||||||
|
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
ret = regmap_read(xra->regmap, to_reg(XRA_GSR, offset), &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return !!(val & BIT(offset % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xra1403_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset),
|
||||||
|
BIT(offset % 8), value ? BIT(offset % 8) : 0);
|
||||||
|
if (ret)
|
||||||
|
dev_err(chip->parent, "Failed to set pin: %d, ret: %d\n",
|
||||||
|
offset, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||||
|
{
|
||||||
|
int reg;
|
||||||
|
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||||
|
int value[xra1403_regmap_cfg.max_register];
|
||||||
|
int i;
|
||||||
|
unsigned int gcr;
|
||||||
|
unsigned int gsr;
|
||||||
|
|
||||||
|
seq_puts(s, "xra reg:");
|
||||||
|
for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++)
|
||||||
|
seq_printf(s, " %2.2x", reg);
|
||||||
|
seq_puts(s, "\n value:");
|
||||||
|
for (reg = 0; reg < xra1403_regmap_cfg.max_register; reg++) {
|
||||||
|
regmap_read(xra->regmap, reg, &value[reg]);
|
||||||
|
seq_printf(s, " %2.2x", value[reg]);
|
||||||
|
}
|
||||||
|
seq_puts(s, "\n");
|
||||||
|
|
||||||
|
gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR];
|
||||||
|
gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR];
|
||||||
|
for (i = 0; i < chip->ngpio; i++) {
|
||||||
|
const char *label = gpiochip_is_requested(chip, i);
|
||||||
|
|
||||||
|
if (!label)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
seq_printf(s, " gpio-%-3d (%-12s) %s %s\n",
|
||||||
|
chip->base + i, label,
|
||||||
|
(gcr & BIT(i)) ? "in" : "out",
|
||||||
|
(gsr & BIT(i)) ? "hi" : "lo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define xra1403_dbg_show NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int xra1403_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct xra1403 *xra;
|
||||||
|
struct gpio_desc *reset_gpio;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
xra = devm_kzalloc(&spi->dev, sizeof(*xra), GFP_KERNEL);
|
||||||
|
if (!xra)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* bring the chip out of reset if reset pin is provided*/
|
||||||
|
reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
|
||||||
|
if (IS_ERR(reset_gpio))
|
||||||
|
dev_warn(&spi->dev, "Could not get reset-gpios\n");
|
||||||
|
|
||||||
|
xra->chip.direction_input = xra1403_direction_input;
|
||||||
|
xra->chip.direction_output = xra1403_direction_output;
|
||||||
|
xra->chip.get_direction = xra1403_get_direction;
|
||||||
|
xra->chip.get = xra1403_get;
|
||||||
|
xra->chip.set = xra1403_set;
|
||||||
|
|
||||||
|
xra->chip.dbg_show = xra1403_dbg_show;
|
||||||
|
|
||||||
|
xra->chip.ngpio = 16;
|
||||||
|
xra->chip.label = "xra1403";
|
||||||
|
|
||||||
|
xra->chip.base = -1;
|
||||||
|
xra->chip.can_sleep = true;
|
||||||
|
xra->chip.parent = &spi->dev;
|
||||||
|
xra->chip.owner = THIS_MODULE;
|
||||||
|
|
||||||
|
xra->regmap = devm_regmap_init_spi(spi, &xra1403_regmap_cfg);
|
||||||
|
if (IS_ERR(xra->regmap)) {
|
||||||
|
ret = PTR_ERR(xra->regmap);
|
||||||
|
dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_gpiochip_add_data(&spi->dev, &xra->chip, xra);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&spi->dev, "Unable to register gpiochip\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_set_drvdata(spi, xra);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct spi_device_id xra1403_ids[] = {
|
||||||
|
{ "xra1403" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, xra1403_ids);
|
||||||
|
|
||||||
|
static const struct of_device_id xra1403_spi_of_match[] = {
|
||||||
|
{ .compatible = "exar,xra1403" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, xra1403_spi_of_match);
|
||||||
|
|
||||||
|
static struct spi_driver xra1403_driver = {
|
||||||
|
.probe = xra1403_probe,
|
||||||
|
.id_table = xra1403_ids,
|
||||||
|
.driver = {
|
||||||
|
.name = "xra1403",
|
||||||
|
.of_match_table = of_match_ptr(xra1403_spi_of_match),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_spi_driver(xra1403_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Nandor Han <nandor.han@ge.com>");
|
||||||
|
MODULE_AUTHOR("Semi Malinen <semi.malinen@ge.com>");
|
||||||
|
MODULE_DESCRIPTION("GPIO expander driver for EXAR XRA1403");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -96,8 +96,8 @@
|
||||||
/* GPIO upper 16 bit mask */
|
/* GPIO upper 16 bit mask */
|
||||||
#define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
|
#define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
|
||||||
|
|
||||||
/* For GPIO quirks */
|
/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */
|
||||||
#define ZYNQ_GPIO_QUIRK_FOO BIT(0)
|
#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct zynq_gpio - gpio device private data structure
|
* struct zynq_gpio - gpio device private data structure
|
||||||
|
@ -135,6 +135,17 @@ struct zynq_platform_data {
|
||||||
static struct irq_chip zynq_gpio_level_irqchip;
|
static struct irq_chip zynq_gpio_level_irqchip;
|
||||||
static struct irq_chip zynq_gpio_edge_irqchip;
|
static struct irq_chip zynq_gpio_edge_irqchip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* zynq_gpio_is_zynq - test if HW is zynq or zynqmp
|
||||||
|
* @gpio: Pointer to driver data struct
|
||||||
|
*
|
||||||
|
* Return: 0 if zynqmp, 1 if zynq.
|
||||||
|
*/
|
||||||
|
static int zynq_gpio_is_zynq(struct zynq_gpio *gpio)
|
||||||
|
{
|
||||||
|
return !!(gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_IS_ZYNQ);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
|
* zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
|
||||||
* for a given pin in the GPIO device
|
* for a given pin in the GPIO device
|
||||||
|
@ -242,18 +253,16 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
|
||||||
static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
|
static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
bool is_zynq_gpio;
|
|
||||||
unsigned int bank_num, bank_pin_num;
|
unsigned int bank_num, bank_pin_num;
|
||||||
struct zynq_gpio *gpio = gpiochip_get_data(chip);
|
struct zynq_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
|
||||||
is_zynq_gpio = gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_FOO;
|
|
||||||
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
|
zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On zynq bank 0 pins 7 and 8 are special and cannot be used
|
* On zynq bank 0 pins 7 and 8 are special and cannot be used
|
||||||
* as inputs.
|
* as inputs.
|
||||||
*/
|
*/
|
||||||
if (is_zynq_gpio && bank_num == 0 &&
|
if (zynq_gpio_is_zynq(gpio) && bank_num == 0 &&
|
||||||
(bank_pin_num == 7 || bank_pin_num == 8))
|
(bank_pin_num == 7 || bank_pin_num == 8))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -637,7 +646,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = {
|
||||||
|
|
||||||
static const struct zynq_platform_data zynq_gpio_def = {
|
static const struct zynq_platform_data zynq_gpio_def = {
|
||||||
.label = "zynq_gpio",
|
.label = "zynq_gpio",
|
||||||
.quirks = ZYNQ_GPIO_QUIRK_FOO,
|
.quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ,
|
||||||
.ngpio = ZYNQ_GPIO_NR_GPIOS,
|
.ngpio = ZYNQ_GPIO_NR_GPIOS,
|
||||||
.max_bank = ZYNQ_GPIO_MAX_BANK,
|
.max_bank = ZYNQ_GPIO_MAX_BANK,
|
||||||
.bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
|
.bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
|
||||||
|
@ -651,9 +660,8 @@ static const struct zynq_platform_data zynq_gpio_def = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id zynq_gpio_of_match[] = {
|
static const struct of_device_id zynq_gpio_of_match[] = {
|
||||||
{ .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def },
|
{ .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def },
|
||||||
{ .compatible = "xlnx,zynqmp-gpio-1.0",
|
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
|
||||||
.data = (void *)&zynqmp_gpio_def },
|
|
||||||
{ /* end of table */ }
|
{ /* end of table */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
|
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
|
||||||
|
|
|
@ -165,6 +165,23 @@ static void acpi_gpio_chip_dh(acpi_handle handle, void *data)
|
||||||
/* The address of this function is used as a key. */
|
/* The address of this function is used as a key. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
|
||||||
|
struct acpi_resource_gpio **agpio)
|
||||||
|
{
|
||||||
|
struct acpi_resource_gpio *gpio;
|
||||||
|
|
||||||
|
if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gpio = &ares->data.gpio;
|
||||||
|
if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*agpio = gpio;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource);
|
||||||
|
|
||||||
static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
|
static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
|
||||||
void *context)
|
void *context)
|
||||||
{
|
{
|
||||||
|
@ -178,11 +195,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
|
||||||
unsigned long irqflags;
|
unsigned long irqflags;
|
||||||
int ret, pin, irq;
|
int ret, pin, irq;
|
||||||
|
|
||||||
if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
|
if (!acpi_gpio_get_irq_resource(ares, &agpio))
|
||||||
return AE_OK;
|
|
||||||
|
|
||||||
agpio = &ares->data.gpio;
|
|
||||||
if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT)
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
|
||||||
handle = ACPI_HANDLE(chip->parent);
|
handle = ACPI_HANDLE(chip->parent);
|
||||||
|
@ -423,6 +436,59 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum gpiod_flags
|
||||||
|
acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio)
|
||||||
|
{
|
||||||
|
bool pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP;
|
||||||
|
|
||||||
|
switch (agpio->io_restriction) {
|
||||||
|
case ACPI_IO_RESTRICT_INPUT:
|
||||||
|
return GPIOD_IN;
|
||||||
|
case ACPI_IO_RESTRICT_OUTPUT:
|
||||||
|
/*
|
||||||
|
* ACPI GPIO resources don't contain an initial value for the
|
||||||
|
* GPIO. Therefore we deduce that value from the pull field
|
||||||
|
* instead. If the pin is pulled up we assume default to be
|
||||||
|
* high, otherwise low.
|
||||||
|
*/
|
||||||
|
return pull_up ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* Assume that the BIOS has configured the direction and pull
|
||||||
|
* accordingly.
|
||||||
|
*/
|
||||||
|
return GPIOD_ASIS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the BIOS has IoRestriction with explicitly set direction
|
||||||
|
* and update @flags accordingly. Otherwise use whatever caller asked
|
||||||
|
* for.
|
||||||
|
*/
|
||||||
|
if (update & GPIOD_FLAGS_BIT_DIR_SET) {
|
||||||
|
enum gpiod_flags diff = *flags ^ update;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if caller supplied incompatible GPIO initialization
|
||||||
|
* flags.
|
||||||
|
*
|
||||||
|
* Return %-EINVAL to notify that firmware has different
|
||||||
|
* settings and we are going to use them.
|
||||||
|
*/
|
||||||
|
if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) ||
|
||||||
|
((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL)))
|
||||||
|
ret = -EINVAL;
|
||||||
|
*flags = update;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct acpi_gpio_lookup {
|
struct acpi_gpio_lookup {
|
||||||
struct acpi_gpio_info info;
|
struct acpi_gpio_info info;
|
||||||
int index;
|
int index;
|
||||||
|
@ -460,8 +526,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
|
||||||
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
|
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
|
||||||
*/
|
*/
|
||||||
if (lookup->info.gpioint) {
|
if (lookup->info.gpioint) {
|
||||||
|
lookup->info.flags = GPIOD_IN;
|
||||||
lookup->info.polarity = agpio->polarity;
|
lookup->info.polarity = agpio->polarity;
|
||||||
lookup->info.triggering = agpio->triggering;
|
lookup->info.triggering = agpio->triggering;
|
||||||
|
} else {
|
||||||
|
lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -588,18 +657,19 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
||||||
struct gpio_desc *acpi_find_gpio(struct device *dev,
|
struct gpio_desc *acpi_find_gpio(struct device *dev,
|
||||||
const char *con_id,
|
const char *con_id,
|
||||||
unsigned int idx,
|
unsigned int idx,
|
||||||
enum gpiod_flags flags,
|
enum gpiod_flags *dflags,
|
||||||
enum gpio_lookup_flags *lookupflags)
|
enum gpio_lookup_flags *lookupflags)
|
||||||
{
|
{
|
||||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||||
struct acpi_gpio_info info;
|
struct acpi_gpio_info info;
|
||||||
struct gpio_desc *desc;
|
struct gpio_desc *desc;
|
||||||
char propname[32];
|
char propname[32];
|
||||||
|
int err;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Try first from _DSD */
|
/* Try first from _DSD */
|
||||||
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
||||||
if (con_id && strcmp(con_id, "gpios")) {
|
if (con_id) {
|
||||||
snprintf(propname, sizeof(propname), "%s-%s",
|
snprintf(propname, sizeof(propname), "%s-%s",
|
||||||
con_id, gpio_suffixes[i]);
|
con_id, gpio_suffixes[i]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -622,17 +692,21 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
|
||||||
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
||||||
if (IS_ERR(desc))
|
if (IS_ERR(desc))
|
||||||
return desc;
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
if ((flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH) &&
|
if (info.gpioint &&
|
||||||
info.gpioint) {
|
(*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) {
|
||||||
dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
|
dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.polarity == GPIO_ACTIVE_LOW)
|
if (info.polarity == GPIO_ACTIVE_LOW)
|
||||||
*lookupflags |= GPIO_ACTIVE_LOW;
|
*lookupflags |= GPIO_ACTIVE_LOW;
|
||||||
|
|
||||||
|
err = acpi_gpio_update_gpiod_flags(dflags, info.flags);
|
||||||
|
if (err)
|
||||||
|
dev_dbg(dev, "Override GPIO initialization flags\n");
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,12 +760,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
||||||
* used to translate from the GPIO offset in the resource to the Linux IRQ
|
* used to translate from the GPIO offset in the resource to the Linux IRQ
|
||||||
* number.
|
* number.
|
||||||
*
|
*
|
||||||
|
* The function is idempotent, though each time it runs it will configure GPIO
|
||||||
|
* pin direction according to the flags in GpioInt resource.
|
||||||
|
*
|
||||||
* Return: Linux IRQ number (>%0) on success, negative errno on failure.
|
* Return: Linux IRQ number (>%0) on success, negative errno on failure.
|
||||||
*/
|
*/
|
||||||
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
||||||
{
|
{
|
||||||
int idx, i;
|
int idx, i;
|
||||||
unsigned int irq_flags;
|
unsigned int irq_flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
for (i = 0, idx = 0; idx <= index; i++) {
|
for (i = 0, idx = 0; idx <= index; i++) {
|
||||||
struct acpi_gpio_info info;
|
struct acpi_gpio_info info;
|
||||||
|
@ -704,6 +782,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
||||||
return PTR_ERR(desc);
|
return PTR_ERR(desc);
|
||||||
|
|
||||||
if (info.gpioint && idx++ == index) {
|
if (info.gpioint && idx++ == index) {
|
||||||
|
char label[32];
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
if (IS_ERR(desc))
|
if (IS_ERR(desc))
|
||||||
|
@ -713,6 +792,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
return irq;
|
return irq;
|
||||||
|
|
||||||
|
snprintf(label, sizeof(label), "GpioInt() %d", index);
|
||||||
|
ret = gpiod_configure_flags(desc, label, 0, info.flags);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
irq_flags = acpi_dev_get_irq_type(info.triggering,
|
irq_flags = acpi_dev_get_irq_type(info.triggering,
|
||||||
info.polarity);
|
info.polarity);
|
||||||
|
|
||||||
|
@ -740,7 +824,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
|
||||||
struct acpi_resource *ares;
|
struct acpi_resource *ares;
|
||||||
int pin_index = (int)address;
|
int pin_index = (int)address;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
bool pull_up;
|
|
||||||
int length;
|
int length;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -755,7 +838,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
|
||||||
}
|
}
|
||||||
|
|
||||||
agpio = &ares->data.gpio;
|
agpio = &ares->data.gpio;
|
||||||
pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP;
|
|
||||||
|
|
||||||
if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
|
if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
|
||||||
function == ACPI_WRITE)) {
|
function == ACPI_WRITE)) {
|
||||||
|
@ -806,35 +888,23 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
desc = gpiochip_request_own_desc(chip, pin,
|
enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio);
|
||||||
"ACPI:OpRegion");
|
const char *label = "ACPI:OpRegion";
|
||||||
|
int err;
|
||||||
|
|
||||||
|
desc = gpiochip_request_own_desc(chip, pin, label);
|
||||||
if (IS_ERR(desc)) {
|
if (IS_ERR(desc)) {
|
||||||
status = AE_ERROR;
|
status = AE_ERROR;
|
||||||
mutex_unlock(&achip->conn_lock);
|
mutex_unlock(&achip->conn_lock);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (agpio->io_restriction) {
|
err = gpiod_configure_flags(desc, label, 0, flags);
|
||||||
case ACPI_IO_RESTRICT_INPUT:
|
if (err < 0) {
|
||||||
gpiod_direction_input(desc);
|
status = AE_NOT_CONFIGURED;
|
||||||
break;
|
gpiochip_free_own_desc(desc);
|
||||||
case ACPI_IO_RESTRICT_OUTPUT:
|
mutex_unlock(&achip->conn_lock);
|
||||||
/*
|
goto out;
|
||||||
* ACPI GPIO resources don't contain an
|
|
||||||
* initial value for the GPIO. Therefore we
|
|
||||||
* deduce that value from the pull field
|
|
||||||
* instead. If the pin is pulled up we
|
|
||||||
* assume default to be high, otherwise
|
|
||||||
* low.
|
|
||||||
*/
|
|
||||||
gpiod_direction_output(desc, pull_up);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/*
|
|
||||||
* Assume that the BIOS has configured the
|
|
||||||
* direction and pull accordingly.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
||||||
|
@ -1089,7 +1159,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||||
|
|
||||||
/* Try first from _DSD */
|
/* Try first from _DSD */
|
||||||
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
|
||||||
if (con_id && strcmp(con_id, "gpios"))
|
if (con_id)
|
||||||
snprintf(propname, sizeof(propname), "%s-%s",
|
snprintf(propname, sizeof(propname), "%s-%s",
|
||||||
con_id, gpio_suffixes[i]);
|
con_id, gpio_suffixes[i]);
|
||||||
else
|
else
|
||||||
|
@ -1119,6 +1189,9 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||||
struct list_head resource_list;
|
struct list_head resource_list;
|
||||||
unsigned int crs_count = 0;
|
unsigned int crs_count = 0;
|
||||||
|
|
||||||
|
if (!acpi_can_fallback_to_crs(adev, con_id))
|
||||||
|
return count;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&resource_list);
|
INIT_LIST_HEAD(&resource_list);
|
||||||
acpi_dev_get_resources(adev, &resource_list,
|
acpi_dev_get_resources(adev, &resource_list,
|
||||||
acpi_find_gpio_count, &crs_count);
|
acpi_find_gpio_count, &crs_count);
|
||||||
|
@ -1129,45 +1202,11 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||||
return count ? count : -ENOENT;
|
return count ? count : -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct acpi_crs_lookup {
|
|
||||||
struct list_head node;
|
|
||||||
struct acpi_device *adev;
|
|
||||||
const char *con_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
static DEFINE_MUTEX(acpi_crs_lookup_lock);
|
|
||||||
static LIST_HEAD(acpi_crs_lookup_list);
|
|
||||||
|
|
||||||
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
|
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
|
||||||
{
|
{
|
||||||
struct acpi_crs_lookup *l, *lookup = NULL;
|
|
||||||
|
|
||||||
/* Never allow fallback if the device has properties */
|
/* Never allow fallback if the device has properties */
|
||||||
if (adev->data.properties || adev->driver_gpios)
|
if (adev->data.properties || adev->driver_gpios)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mutex_lock(&acpi_crs_lookup_lock);
|
return con_id == NULL;
|
||||||
|
|
||||||
list_for_each_entry(l, &acpi_crs_lookup_list, node) {
|
|
||||||
if (l->adev == adev) {
|
|
||||||
lookup = l;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lookup) {
|
|
||||||
lookup = kmalloc(sizeof(*lookup), GFP_KERNEL);
|
|
||||||
if (lookup) {
|
|
||||||
lookup->adev = adev;
|
|
||||||
lookup->con_id = kstrdup(con_id, GFP_KERNEL);
|
|
||||||
list_add_tail(&lookup->node, &acpi_crs_lookup_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&acpi_crs_lookup_lock);
|
|
||||||
|
|
||||||
return lookup &&
|
|
||||||
((!lookup->con_id && !con_id) ||
|
|
||||||
(lookup->con_id && con_id &&
|
|
||||||
strcmp(lookup->con_id, con_id) == 0));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,9 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
|
||||||
*flags |= GPIO_OPEN_SOURCE;
|
*flags |= GPIO_OPEN_SOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (of_flags & OF_GPIO_SLEEP_MAY_LOOSE_VALUE)
|
||||||
|
*flags |= GPIO_SLEEP_MAY_LOOSE_VALUE;
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +239,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
||||||
*
|
*
|
||||||
* This is only used by of_gpiochip_add to request/set GPIO initial
|
* This is only used by of_gpiochip_add to request/set GPIO initial
|
||||||
* configuration.
|
* configuration.
|
||||||
* It retures error if it fails otherwise 0 on success.
|
* It returns error if it fails otherwise 0 on success.
|
||||||
*/
|
*/
|
||||||
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitmap.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
@ -1472,8 +1472,6 @@ static struct gpio_chip *find_chip_by_name(const char *name)
|
||||||
|
|
||||||
static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
|
static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!gpiochip->irq_need_valid_mask)
|
if (!gpiochip->irq_need_valid_mask)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -1483,8 +1481,7 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Assume by default all GPIOs are valid */
|
/* Assume by default all GPIOs are valid */
|
||||||
for (i = 0; i < gpiochip->ngpio; i++)
|
bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio);
|
||||||
set_bit(i, gpiochip->irq_valid_mask);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2870,6 +2867,16 @@ bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
|
EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source);
|
||||||
|
|
||||||
|
bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset)
|
||||||
|
{
|
||||||
|
if (offset >= chip->ngpio)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !test_bit(FLAG_SLEEP_MAY_LOOSE_VALUE,
|
||||||
|
&chip->gpiodev->descs[offset].flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiod_get_raw_value_cansleep() - return a gpio's raw value
|
* gpiod_get_raw_value_cansleep() - return a gpio's raw value
|
||||||
* @desc: gpio whose value will be returned
|
* @desc: gpio whose value will be returned
|
||||||
|
@ -3009,6 +3016,7 @@ void gpiod_add_lookup_table(struct gpiod_lookup_table *table)
|
||||||
|
|
||||||
mutex_unlock(&gpio_lookup_lock);
|
mutex_unlock(&gpio_lookup_lock);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiod_add_lookup_table);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gpiod_remove_lookup_table() - unregister GPIO device consumers
|
* gpiod_remove_lookup_table() - unregister GPIO device consumers
|
||||||
|
@ -3022,6 +3030,7 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
|
||||||
|
|
||||||
mutex_unlock(&gpio_lookup_lock);
|
mutex_unlock(&gpio_lookup_lock);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table);
|
||||||
|
|
||||||
static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev)
|
static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev)
|
||||||
{
|
{
|
||||||
|
@ -3213,7 +3222,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional);
|
||||||
* requested function and/or index, or another IS_ERR() code if an error
|
* requested function and/or index, or another IS_ERR() code if an error
|
||||||
* occurred while trying to acquire the GPIO.
|
* occurred while trying to acquire the GPIO.
|
||||||
*/
|
*/
|
||||||
static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
||||||
unsigned long lflags, enum gpiod_flags dflags)
|
unsigned long lflags, enum gpiod_flags dflags)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
@ -3224,6 +3233,8 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
||||||
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
||||||
if (lflags & GPIO_OPEN_SOURCE)
|
if (lflags & GPIO_OPEN_SOURCE)
|
||||||
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
||||||
|
if (lflags & GPIO_SLEEP_MAY_LOOSE_VALUE)
|
||||||
|
set_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, &desc->flags);
|
||||||
|
|
||||||
/* No particular flag request, return here... */
|
/* No particular flag request, return here... */
|
||||||
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
|
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
|
||||||
|
@ -3273,7 +3284,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
||||||
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
|
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
|
||||||
} else if (ACPI_COMPANION(dev)) {
|
} else if (ACPI_COMPANION(dev)) {
|
||||||
dev_dbg(dev, "using ACPI for GPIO lookup\n");
|
dev_dbg(dev, "using ACPI for GPIO lookup\n");
|
||||||
desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags);
|
desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3354,8 +3365,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
||||||
struct acpi_gpio_info info;
|
struct acpi_gpio_info info;
|
||||||
|
|
||||||
desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
|
desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
|
||||||
if (!IS_ERR(desc))
|
if (!IS_ERR(desc)) {
|
||||||
active_low = info.polarity == GPIO_ACTIVE_LOW;
|
active_low = info.polarity == GPIO_ACTIVE_LOW;
|
||||||
|
ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags);
|
||||||
|
if (ret)
|
||||||
|
pr_debug("Override GPIO initialization flags\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ERR(desc))
|
if (IS_ERR(desc))
|
||||||
|
|
|
@ -75,11 +75,13 @@ struct gpio_device {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct acpi_gpio_info - ACPI GPIO specific information
|
* struct acpi_gpio_info - ACPI GPIO specific information
|
||||||
|
* @flags: GPIO initialization flags
|
||||||
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
||||||
* @polarity: interrupt polarity as provided by ACPI
|
* @polarity: interrupt polarity as provided by ACPI
|
||||||
* @triggering: triggering type as provided by ACPI
|
* @triggering: triggering type as provided by ACPI
|
||||||
*/
|
*/
|
||||||
struct acpi_gpio_info {
|
struct acpi_gpio_info {
|
||||||
|
enum gpiod_flags flags;
|
||||||
bool gpioint;
|
bool gpioint;
|
||||||
int polarity;
|
int polarity;
|
||||||
int triggering;
|
int triggering;
|
||||||
|
@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip);
|
||||||
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
|
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
|
||||||
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
|
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
|
||||||
|
|
||||||
|
int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
|
||||||
|
enum gpiod_flags update);
|
||||||
|
|
||||||
struct gpio_desc *acpi_find_gpio(struct device *dev,
|
struct gpio_desc *acpi_find_gpio(struct device *dev,
|
||||||
const char *con_id,
|
const char *con_id,
|
||||||
unsigned int idx,
|
unsigned int idx,
|
||||||
enum gpiod_flags flags,
|
enum gpiod_flags *dflags,
|
||||||
enum gpio_lookup_flags *lookupflags);
|
enum gpio_lookup_flags *lookupflags);
|
||||||
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
||||||
const char *propname, int index,
|
const char *propname, int index,
|
||||||
|
@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
|
||||||
static inline void
|
static inline void
|
||||||
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
|
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct gpio_desc *
|
static inline struct gpio_desc *
|
||||||
acpi_find_gpio(struct device *dev, const char *con_id,
|
acpi_find_gpio(struct device *dev, const char *con_id,
|
||||||
unsigned int idx, enum gpiod_flags flags,
|
unsigned int idx, enum gpiod_flags *dflags,
|
||||||
enum gpio_lookup_flags *lookupflags)
|
enum gpio_lookup_flags *lookupflags)
|
||||||
{
|
{
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
|
@ -190,6 +201,7 @@ struct gpio_desc {
|
||||||
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
|
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
|
||||||
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
|
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
|
||||||
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
|
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
|
||||||
|
#define FLAG_SLEEP_MAY_LOOSE_VALUE 12 /* GPIO may loose value in sleep */
|
||||||
|
|
||||||
/* Connection label */
|
/* Connection label */
|
||||||
const char *label;
|
const char *label;
|
||||||
|
@ -199,6 +211,8 @@ struct gpio_desc {
|
||||||
|
|
||||||
int gpiod_request(struct gpio_desc *desc, const char *label);
|
int gpiod_request(struct gpio_desc *desc, const char *label);
|
||||||
void gpiod_free(struct gpio_desc *desc);
|
void gpiod_free(struct gpio_desc *desc);
|
||||||
|
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
||||||
|
unsigned long lflags, enum gpiod_flags dflags);
|
||||||
int gpiod_hog(struct gpio_desc *desc, const char *name,
|
int gpiod_hog(struct gpio_desc *desc, const char *name,
|
||||||
unsigned long lflags, enum gpiod_flags dflags);
|
unsigned long lflags, enum gpiod_flags dflags);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include <linux/i2c/adp5588.h>
|
#include <linux/platform_data/adp5588.h>
|
||||||
|
|
||||||
/* Key Event Register xy */
|
/* Key Event Register xy */
|
||||||
#define KEY_EV_PRESSED (1 << 7)
|
#define KEY_EV_PRESSED (1 << 7)
|
||||||
|
|
|
@ -15,10 +15,6 @@
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
* General Public License for more details.
|
* 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/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
@ -149,8 +145,8 @@ static int vendor_resource_matches(struct pnp_dev *dev,
|
||||||
uuid_len == sizeof(match->data) &&
|
uuid_len == sizeof(match->data) &&
|
||||||
memcmp(uuid, match->data, uuid_len) == 0) {
|
memcmp(uuid, match->data, uuid_len) == 0) {
|
||||||
if (expected_len && expected_len != actual_len) {
|
if (expected_len && expected_len != actual_len) {
|
||||||
dev_err(&dev->dev, "wrong vendor descriptor size; "
|
dev_err(&dev->dev,
|
||||||
"expected %d, found %d bytes\n",
|
"wrong vendor descriptor size; expected %d, found %d bytes\n",
|
||||||
expected_len, actual_len);
|
expected_len, actual_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -180,6 +176,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
||||||
struct pnp_dev *dev = data;
|
struct pnp_dev *dev = data;
|
||||||
struct acpi_resource_dma *dma;
|
struct acpi_resource_dma *dma;
|
||||||
struct acpi_resource_vendor_typed *vendor_typed;
|
struct acpi_resource_vendor_typed *vendor_typed;
|
||||||
|
struct acpi_resource_gpio *gpio;
|
||||||
struct resource_win win = {{0}, 0};
|
struct resource_win win = {{0}, 0};
|
||||||
struct resource *r = &win.res;
|
struct resource *r = &win.res;
|
||||||
int i, flags;
|
int i, flags;
|
||||||
|
@ -203,13 +200,27 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
||||||
* one interrupt, we won't be able to re-encode it.
|
* one interrupt, we won't be able to re-encode it.
|
||||||
*/
|
*/
|
||||||
if (pnp_can_write(dev)) {
|
if (pnp_can_write(dev)) {
|
||||||
dev_warn(&dev->dev, "multiple interrupts in "
|
dev_warn(&dev->dev,
|
||||||
"_CRS descriptor; configuration can't "
|
"multiple interrupts in _CRS descriptor; configuration can't be changed\n");
|
||||||
"be changed\n");
|
|
||||||
dev->capabilities &= ~PNP_WRITE;
|
dev->capabilities &= ~PNP_WRITE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
} else if (acpi_gpio_get_irq_resource(res, &gpio)) {
|
||||||
|
/*
|
||||||
|
* If the resource is GpioInt() type then extract the IRQ
|
||||||
|
* from GPIO resource and fill it into IRQ resource type.
|
||||||
|
*/
|
||||||
|
i = acpi_dev_gpio_irq_get(dev->data, 0);
|
||||||
|
if (i >= 0) {
|
||||||
|
flags = acpi_dev_irq_flags(gpio->triggering,
|
||||||
|
gpio->polarity,
|
||||||
|
gpio->sharable);
|
||||||
|
} else {
|
||||||
|
flags = IORESOURCE_DISABLED;
|
||||||
|
}
|
||||||
|
pnp_add_irq_resource(dev, i, flags);
|
||||||
|
return AE_OK;
|
||||||
} else if (r->flags & IORESOURCE_DISABLED) {
|
} else if (r->flags & IORESOURCE_DISABLED) {
|
||||||
pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED);
|
pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED);
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
@ -331,8 +342,8 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
|
||||||
if (p->interrupts[i] < PNP_IRQ_NR)
|
if (p->interrupts[i] < PNP_IRQ_NR)
|
||||||
__set_bit(p->interrupts[i], map.bits);
|
__set_bit(p->interrupts[i], map.bits);
|
||||||
else
|
else
|
||||||
dev_err(&dev->dev, "ignoring IRQ %d option "
|
dev_err(&dev->dev,
|
||||||
"(too large for %d entry bitmap)\n",
|
"ignoring IRQ %d option (too large for %d entry bitmap)\n",
|
||||||
p->interrupts[i], PNP_IRQ_NR);
|
p->interrupts[i], PNP_IRQ_NR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -933,8 +944,9 @@ int pnpacpi_encode_resources(struct pnp_dev *dev, struct acpi_buffer *buffer)
|
||||||
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
||||||
case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
|
case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
|
||||||
default: /* other type */
|
default: /* other type */
|
||||||
dev_warn(&dev->dev, "can't encode unknown resource "
|
dev_warn(&dev->dev,
|
||||||
"type %d\n", resource->type);
|
"can't encode unknown resource type %d\n",
|
||||||
|
resource->type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
resource++;
|
resource++;
|
||||||
|
|
|
@ -9,10 +9,13 @@
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation; either version 2 of the License.
|
* the Free Software Foundation; either version 2 of the License.
|
||||||
*/
|
*/
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
#include <linux/property.h>
|
||||||
#include <linux/serial_core.h>
|
#include <linux/serial_core.h>
|
||||||
#include <linux/serial_reg.h>
|
#include <linux/serial_reg.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -60,8 +63,50 @@
|
||||||
#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */
|
#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */
|
||||||
#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */
|
#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */
|
||||||
|
|
||||||
|
#define UART_EXAR_RS485_DLY(x) ((x) << 4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IOT2040 MPIO wiring semantics:
|
||||||
|
*
|
||||||
|
* MPIO Port Function
|
||||||
|
* ---- ---- --------
|
||||||
|
* 0 2 Mode bit 0
|
||||||
|
* 1 2 Mode bit 1
|
||||||
|
* 2 2 Terminate bus
|
||||||
|
* 3 - <reserved>
|
||||||
|
* 4 3 Mode bit 0
|
||||||
|
* 5 3 Mode bit 1
|
||||||
|
* 6 3 Terminate bus
|
||||||
|
* 7 - <reserved>
|
||||||
|
* 8 2 Enable
|
||||||
|
* 9 3 Enable
|
||||||
|
* 10 - Red LED
|
||||||
|
* 11..15 - <unused>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* IOT2040 MPIOs 0..7 */
|
||||||
|
#define IOT2040_UART_MODE_RS232 0x01
|
||||||
|
#define IOT2040_UART_MODE_RS485 0x02
|
||||||
|
#define IOT2040_UART_MODE_RS422 0x03
|
||||||
|
#define IOT2040_UART_TERMINATE_BUS 0x04
|
||||||
|
|
||||||
|
#define IOT2040_UART1_MASK 0x0f
|
||||||
|
#define IOT2040_UART2_SHIFT 4
|
||||||
|
|
||||||
|
#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */
|
||||||
|
#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */
|
||||||
|
|
||||||
|
/* IOT2040 MPIOs 8..15 */
|
||||||
|
#define IOT2040_UARTS_ENABLE 0x03
|
||||||
|
#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */
|
||||||
|
|
||||||
struct exar8250;
|
struct exar8250;
|
||||||
|
|
||||||
|
struct exar8250_platform {
|
||||||
|
int (*rs485_config)(struct uart_port *, struct serial_rs485 *);
|
||||||
|
int (*register_gpio)(struct pci_dev *, struct uart_8250_port *);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct exar8250_board - board information
|
* struct exar8250_board - board information
|
||||||
* @num_ports: number of serial ports
|
* @num_ports: number of serial ports
|
||||||
|
@ -194,7 +239,8 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
xr17v35x_register_gpio(struct pci_dev *pcidev)
|
__xr17v35x_register_gpio(struct pci_dev *pcidev,
|
||||||
|
const struct property_entry *properties)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
|
||||||
|
@ -202,8 +248,11 @@ xr17v35x_register_gpio(struct pci_dev *pcidev)
|
||||||
if (!pdev)
|
if (!pdev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, pcidev);
|
pdev->dev.parent = &pcidev->dev;
|
||||||
if (platform_device_add(pdev) < 0) {
|
ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev));
|
||||||
|
|
||||||
|
if (platform_device_add_properties(pdev, properties) < 0 ||
|
||||||
|
platform_device_add(pdev) < 0) {
|
||||||
platform_device_put(pdev);
|
platform_device_put(pdev);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -211,17 +260,131 @@ xr17v35x_register_gpio(struct pci_dev *pcidev)
|
||||||
return pdev;
|
return pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct property_entry exar_gpio_properties[] = {
|
||||||
|
PROPERTY_ENTRY_U32("linux,first-pin", 0),
|
||||||
|
PROPERTY_ENTRY_U32("ngpios", 16),
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int xr17v35x_register_gpio(struct pci_dev *pcidev,
|
||||||
|
struct uart_8250_port *port)
|
||||||
|
{
|
||||||
|
if (pcidev->vendor == PCI_VENDOR_ID_EXAR)
|
||||||
|
port->port.private_data =
|
||||||
|
__xr17v35x_register_gpio(pcidev, exar_gpio_properties);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct exar8250_platform exar8250_default_platform = {
|
||||||
|
.register_gpio = xr17v35x_register_gpio,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int iot2040_rs485_config(struct uart_port *port,
|
||||||
|
struct serial_rs485 *rs485)
|
||||||
|
{
|
||||||
|
bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED);
|
||||||
|
u8 __iomem *p = port->membase;
|
||||||
|
u8 mask = IOT2040_UART1_MASK;
|
||||||
|
u8 mode, value;
|
||||||
|
|
||||||
|
if (is_rs485) {
|
||||||
|
if (rs485->flags & SER_RS485_RX_DURING_TX)
|
||||||
|
mode = IOT2040_UART_MODE_RS422;
|
||||||
|
else
|
||||||
|
mode = IOT2040_UART_MODE_RS485;
|
||||||
|
|
||||||
|
if (rs485->flags & SER_RS485_TERMINATE_BUS)
|
||||||
|
mode |= IOT2040_UART_TERMINATE_BUS;
|
||||||
|
} else {
|
||||||
|
mode = IOT2040_UART_MODE_RS232;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port->line == 3) {
|
||||||
|
mask <<= IOT2040_UART2_SHIFT;
|
||||||
|
mode <<= IOT2040_UART2_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = readb(p + UART_EXAR_MPIOLVL_7_0);
|
||||||
|
value &= ~mask;
|
||||||
|
value |= mode;
|
||||||
|
writeb(value, p + UART_EXAR_MPIOLVL_7_0);
|
||||||
|
|
||||||
|
value = readb(p + UART_EXAR_FCTR);
|
||||||
|
if (is_rs485)
|
||||||
|
value |= UART_FCTR_EXAR_485;
|
||||||
|
else
|
||||||
|
value &= ~UART_FCTR_EXAR_485;
|
||||||
|
writeb(value, p + UART_EXAR_FCTR);
|
||||||
|
|
||||||
|
if (is_rs485)
|
||||||
|
writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR);
|
||||||
|
|
||||||
|
port->rs485 = *rs485;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct property_entry iot2040_gpio_properties[] = {
|
||||||
|
PROPERTY_ENTRY_U32("linux,first-pin", 10),
|
||||||
|
PROPERTY_ENTRY_U32("ngpios", 1),
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int iot2040_register_gpio(struct pci_dev *pcidev,
|
||||||
|
struct uart_8250_port *port)
|
||||||
|
{
|
||||||
|
u8 __iomem *p = port->port.membase;
|
||||||
|
|
||||||
|
writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0);
|
||||||
|
writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0);
|
||||||
|
writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8);
|
||||||
|
writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8);
|
||||||
|
|
||||||
|
port->port.private_data =
|
||||||
|
__xr17v35x_register_gpio(pcidev, iot2040_gpio_properties);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct exar8250_platform iot2040_platform = {
|
||||||
|
.rs485_config = iot2040_rs485_config,
|
||||||
|
.register_gpio = iot2040_register_gpio,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct dmi_system_id exar_platforms[] = {
|
||||||
|
{
|
||||||
|
.matches = {
|
||||||
|
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
|
||||||
|
DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
|
||||||
|
"6ES7647-0AA00-1YA2"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)&iot2040_platform,
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
|
pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
|
||||||
struct uart_8250_port *port, int idx)
|
struct uart_8250_port *port, int idx)
|
||||||
{
|
{
|
||||||
const struct exar8250_board *board = priv->board;
|
const struct exar8250_board *board = priv->board;
|
||||||
|
const struct exar8250_platform *platform;
|
||||||
|
const struct dmi_system_id *dmi_match;
|
||||||
unsigned int offset = idx * 0x400;
|
unsigned int offset = idx * 0x400;
|
||||||
unsigned int baud = 7812500;
|
unsigned int baud = 7812500;
|
||||||
u8 __iomem *p;
|
u8 __iomem *p;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
dmi_match = dmi_first_match(exar_platforms);
|
||||||
|
if (dmi_match)
|
||||||
|
platform = dmi_match->driver_data;
|
||||||
|
else
|
||||||
|
platform = &exar8250_default_platform;
|
||||||
|
|
||||||
port->port.uartclk = baud * 16;
|
port->port.uartclk = baud * 16;
|
||||||
|
port->port.rs485_config = platform->rs485_config;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup the uart clock for the devices on expansion slot to
|
* Setup the uart clock for the devices on expansion slot to
|
||||||
* half the clock speed of the main chip (which is 125MHz)
|
* half the clock speed of the main chip (which is 125MHz)
|
||||||
|
@ -244,10 +407,10 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
|
||||||
/* Setup Multipurpose Input/Output pins. */
|
/* Setup Multipurpose Input/Output pins. */
|
||||||
setup_gpio(pcidev, p);
|
setup_gpio(pcidev, p);
|
||||||
|
|
||||||
port->port.private_data = xr17v35x_register_gpio(pcidev);
|
ret = platform->register_gpio(pcidev, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_xr17v35x_exit(struct pci_dev *pcidev)
|
static void pci_xr17v35x_exit(struct pci_dev *pcidev)
|
||||||
|
|
|
@ -28,4 +28,8 @@
|
||||||
#define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN)
|
#define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN)
|
||||||
#define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE)
|
#define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE)
|
||||||
|
|
||||||
|
/* Bit 3 express GPIO suspend/resume persistence */
|
||||||
|
#define GPIO_SLEEP_MAINTAIN_VALUE 0
|
||||||
|
#define GPIO_SLEEP_MAY_LOOSE_VALUE 8
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -964,6 +964,8 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev,
|
||||||
const struct acpi_gpio_mapping *gpios);
|
const struct acpi_gpio_mapping *gpios);
|
||||||
void devm_acpi_dev_remove_driver_gpios(struct device *dev);
|
void devm_acpi_dev_remove_driver_gpios(struct device *dev);
|
||||||
|
|
||||||
|
bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
|
||||||
|
struct acpi_resource_gpio **agpio);
|
||||||
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index);
|
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index);
|
||||||
#else
|
#else
|
||||||
static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
||||||
|
@ -980,6 +982,11 @@ static inline int devm_acpi_dev_add_driver_gpios(struct device *dev,
|
||||||
}
|
}
|
||||||
static inline void devm_acpi_dev_remove_driver_gpios(struct device *dev) {}
|
static inline void devm_acpi_dev_remove_driver_gpios(struct device *dev) {}
|
||||||
|
|
||||||
|
static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares,
|
||||||
|
struct acpi_resource_gpio **agpio)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
||||||
{
|
{
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
|
@ -213,6 +213,9 @@ bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset);
|
||||||
bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset);
|
||||||
bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset);
|
||||||
|
|
||||||
|
/* Sleep persistence inquiry for drivers */
|
||||||
|
bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset);
|
||||||
|
|
||||||
/* get driver data */
|
/* get driver data */
|
||||||
void *gpiochip_get_data(struct gpio_chip *chip);
|
void *gpiochip_get_data(struct gpio_chip *chip);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ enum gpio_lookup_flags {
|
||||||
GPIO_ACTIVE_LOW = (1 << 0),
|
GPIO_ACTIVE_LOW = (1 << 0),
|
||||||
GPIO_OPEN_DRAIN = (1 << 1),
|
GPIO_OPEN_DRAIN = (1 << 1),
|
||||||
GPIO_OPEN_SOURCE = (1 << 2),
|
GPIO_OPEN_SOURCE = (1 << 2),
|
||||||
|
GPIO_SLEEP_MAINTAIN_VALUE = (0 << 3),
|
||||||
|
GPIO_SLEEP_MAY_LOOSE_VALUE = (1 << 3),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,6 +31,7 @@ enum of_gpio_flags {
|
||||||
OF_GPIO_ACTIVE_LOW = 0x1,
|
OF_GPIO_ACTIVE_LOW = 0x1,
|
||||||
OF_GPIO_SINGLE_ENDED = 0x2,
|
OF_GPIO_SINGLE_ENDED = 0x2,
|
||||||
OF_GPIO_OPEN_DRAIN = 0x4,
|
OF_GPIO_OPEN_DRAIN = 0x4,
|
||||||
|
OF_GPIO_SLEEP_MAY_LOOSE_VALUE = 0x8,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_OF_GPIO
|
#ifdef CONFIG_OF_GPIO
|
||||||
|
|
|
@ -172,7 +172,7 @@ extern int platform_device_add_resources(struct platform_device *pdev,
|
||||||
extern int platform_device_add_data(struct platform_device *pdev,
|
extern int platform_device_add_data(struct platform_device *pdev,
|
||||||
const void *data, size_t size);
|
const void *data, size_t size);
|
||||||
extern int platform_device_add_properties(struct platform_device *pdev,
|
extern int platform_device_add_properties(struct platform_device *pdev,
|
||||||
struct property_entry *properties);
|
const struct property_entry *properties);
|
||||||
extern int platform_device_add(struct platform_device *pdev);
|
extern int platform_device_add(struct platform_device *pdev);
|
||||||
extern void platform_device_del(struct platform_device *pdev);
|
extern void platform_device_del(struct platform_device *pdev);
|
||||||
extern void platform_device_put(struct platform_device *pdev);
|
extern void platform_device_put(struct platform_device *pdev);
|
||||||
|
|
Loading…
Reference in New Issue