1
0
Fork 0

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()
  ...
zero-colors
Linus Torvalds 2017-07-07 12:40:27 -07:00
commit c7d28eca1d
49 changed files with 1514 additions and 466 deletions

View File

@ -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
calling acpi_dev_remove_driver_gpios() on the ACPI device object where that
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.

View File

@ -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:
- 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
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
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
----------------------------------
@ -282,8 +285,8 @@ Example 1:
};
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
pins 50..59.
pinctrl1's pins 20..29, and GPIOs 10..29 routed to pin controller pinctrl2's
pins 50..69.
Example 2:

View File

@ -3,6 +3,7 @@
Required Properties:
- 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-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller.
- "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller.

View File

@ -5747,6 +5747,15 @@ F: include/asm-generic/gpio.h
F: include/uapi/linux/gpio.h
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
M: Dmitry Kozlov <xeb@mail.ru>
L: netdev@vger.kernel.org
@ -12026,6 +12035,13 @@ S: Maintained
F: drivers/media/platform/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
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
@ -14338,6 +14354,14 @@ L: linux-kernel@vger.kernel.org
S: Supported
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
M: Max Filippov <jcmvbkbc@gmail.com>
L: linux-xtensa@linux-xtensa.org

View File

@ -17,7 +17,7 @@
#include <linux/gpio/machine.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/i2c/pcf857x.h>
#include <linux/platform_data/pcf857x.h>
#include <linux/platform_data/at24.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>

View File

@ -14,7 +14,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c/pcf857x.h>
#include <linux/platform_data/pcf857x.h>
#include <linux/platform_data/at24.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>

View File

@ -23,7 +23,7 @@
#include <linux/platform_device.h>
#include <linux/i2c.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/adv7343.h>

View File

@ -27,7 +27,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/types.h>
#include <linux/i2c/pcf857x.h>
#include <linux/platform_data/pcf857x.h>
#include <linux/i2c/pxa-i2c.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/physmap.h>

View File

@ -27,7 +27,7 @@
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/mfd/da903x.h>
#include <linux/i2c/max732x.h>
#include <linux/platform_data/max732x.h>
#include <linux/i2c/pxa-i2c.h>
#include <asm/types.h>

View File

@ -26,7 +26,7 @@
#include <linux/mtd/partitions.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/smc91x.h>
#include <linux/gpio.h>

View File

@ -22,7 +22,7 @@
#include <linux/usb/isp1362.h>
#endif
#include <linux/i2c.h>
#include <linux/i2c/adp5588.h>
#include <linux/platform_data/adp5588.h>
#include <linux/etherdevice.h>
#include <linux/ata_platform.h>
#include <linux/irq.h>

View File

@ -12,7 +12,7 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
#include <linux/i2c/pcf857x.h>
#include <linux/platform_data/pcf857x.h>
#include "machtypes.h"
#include "dev-gpio-buttons.h"

View File

@ -344,7 +344,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
* platform device is released.
*/
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);
}

View File

@ -337,9 +337,10 @@ config GPIO_MPC8XXX
config GPIO_MVEBU
def_bool y
depends on PLAT_ORION
depends on PLAT_ORION || ARCH_MVEBU
depends on OF_GPIO
select GENERIC_IRQ_CHIP
select REGMAP_MMIO
config GPIO_MXC
def_bool y
@ -515,12 +516,13 @@ config GPIO_XILINX
config GPIO_XLP
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
help
This driver provides support for GPIO interface on Netlogic XLP MIPS64
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.
@ -963,6 +965,16 @@ config GPIO_LP873X
This driver can also be built as a module. If so, the module will be
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
tristate "GPIO support for PMIC MAX77620 and MAX20024"
depends on MFD_MAX77620
@ -1236,6 +1248,12 @@ config GPIO_PISOSR
GPIO driver for SPI compatible parallel-in/serial-out shift
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
menu "USB GPIO expanders"

View File

@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.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_MAX730X) += gpio-max730x.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_XILINX) += gpio-xilinx.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_ZEVIO) += gpio-zevio.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o

View File

@ -16,7 +16,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/i2c/adp5588.h>
#include <linux/platform_data/adp5588.h>
#define DRV_NAME "adp5588-gpio"

View File

@ -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 *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,
ARIZONA_GPN_DIR, ARIZONA_GPN_DIR);
ret = regmap_update_bits_check(arizona->regmap,
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)
@ -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 *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)
value = ARIZONA_GPN_LVL;
@ -158,6 +187,8 @@ static int arizona_gpio_probe(struct platform_device *pdev)
else
arizona_gpio->gpio_chip.base = -1;
pm_runtime_enable(&pdev->dev);
ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip,
arizona_gpio);
if (ret < 0) {

View File

@ -437,6 +437,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
{
unsigned gpio, bank;
int irq;
int ret;
struct clk *clk;
u32 binten = 0;
unsigned ngpio, bank_irq;
@ -480,12 +481,15 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
PTR_ERR(clk));
return PTR_ERR(clk);
}
clk_prepare_enable(clk);
ret = clk_prepare_enable(clk);
if (ret)
return ret;
if (!pdata->gpio_unbanked) {
irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0);
if (irq < 0) {
dev_err(dev, "Couldn't allocate IRQ numbers\n");
clk_disable_unprepare(clk);
return irq;
}
@ -494,6 +498,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
chips);
if (!irq_domain) {
dev_err(dev, "Couldn't register an IRQ domain\n");
clk_disable_unprepare(clk);
return -ENODEV;
}
}
@ -562,8 +567,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev)
sizeof(struct
davinci_gpio_irq_data),
GFP_KERNEL);
if (!irqdata)
if (!irqdata) {
clk_disable_unprepare(clk);
return -ENOMEM;
}
irqdata->regs = g;
irqdata->bank_num = bank;

View File

@ -288,7 +288,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
irq_setup_alt_chip(d, type);
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);
return 0;

View File

@ -31,6 +31,7 @@ struct exar_gpio_chip {
int index;
void __iomem *regs;
char name[20];
unsigned int first_pin;
};
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,
unsigned int offset)
{
unsigned int bank = offset / 8;
unsigned int addr;
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
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, offset % 8);
exar_update(chip, addr, direction, bit);
return 0;
}
@ -68,41 +70,38 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg)
value = readb(exar_gpio->regs + reg);
mutex_unlock(&exar_gpio->lock);
return !!value;
return value;
}
static int exar_get_direction(struct gpio_chip *chip, unsigned int offset)
{
unsigned int bank = offset / 8;
unsigned int addr;
int val;
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
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;
val = exar_get(chip, addr) >> (offset % 8);
return !!val;
return !!(exar_get(chip, addr) & BIT(bit));
}
static int exar_get_value(struct gpio_chip *chip, unsigned int offset)
{
unsigned int bank = offset / 8;
unsigned int addr;
int val;
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
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_LO : EXAR_OFFSET_MPIOLVL_HI;
val = exar_get(chip, addr) >> (offset % 8);
return !!val;
return !!(exar_get(chip, addr) & BIT(bit));
}
static void exar_set_value(struct gpio_chip *chip, unsigned int offset,
int value)
{
unsigned int bank = offset / 8;
unsigned int addr;
struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip);
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, offset % 8);
exar_update(chip, addr, value, bit);
}
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)
{
struct pci_dev *pcidev = platform_get_drvdata(pdev);
struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent);
struct exar_gpio_chip *exar_gpio;
u32 first_pin, ngpios;
void __iomem *p;
int index, ret;
if (pcidev->vendor != PCI_VENDOR_ID_EXAR)
return -ENODEV;
/*
* Map the pci device to get the register addresses.
* We will need to read and write those registers to control
* 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.
* The UART driver must have mapped region 0 prior to registering this
* device - use it.
*/
p = pcim_iomap(pcidev, 0, 0);
p = pcim_iomap_table(pcidev)[0];
if (!p)
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)
return -ENOMEM;
@ -149,18 +151,19 @@ static int gpio_exar_probe(struct platform_device *pdev)
sprintf(exar_gpio->name, "exar_gpio%d", index);
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_input = exar_direction_input;
exar_gpio->gpio_chip.get_direction = exar_get_direction;
exar_gpio->gpio_chip.get = exar_get_value;
exar_gpio->gpio_chip.set = exar_set_value;
exar_gpio->gpio_chip.base = -1;
exar_gpio->gpio_chip.ngpio = 16;
exar_gpio->gpio_chip.ngpio = ngpios;
exar_gpio->regs = p;
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);
if (ret)
goto err_destroy;

View File

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

View File

@ -20,7 +20,7 @@
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/max732x.h>
#include <linux/platform_data/max732x.h>
#include <linux/of.h>

View File

@ -11,7 +11,6 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>

View File

@ -385,14 +385,18 @@ static irqreturn_t ioh_gpio_handler(int irq, void *dev_id)
return ret;
}
static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
unsigned int irq_start, unsigned int num)
static int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip,
unsigned int irq_start,
unsigned int num)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base,
handle_simple_irq);
if (!gc)
return -ENOMEM;
gc->private = chip;
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_NOREQUEST | IRQ_NOPROBE, 0);
return 0;
}
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;
}
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;

View File

@ -3,6 +3,7 @@
*
* Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com>
* 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
* 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_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 {
DIR_IN = 0,
DIR_OUT,
GPIO_MOCKUP_DIR_OUT = 0,
GPIO_MOCKUP_DIR_IN = 1,
};
/*
@ -41,6 +47,7 @@ enum {
struct gpio_mockup_line_status {
int dir;
bool value;
bool irq_enabled;
};
struct gpio_mockup_irq_context {
@ -61,7 +68,7 @@ struct gpio_mockup_dbgfs_private {
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;
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);
gpio_mockup_set(gc, offset, value);
chip->lines[offset].dir = DIR_OUT;
chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT;
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);
chip->lines[offset].dir = DIR_IN;
chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN;
return 0;
}
@ -121,7 +128,7 @@ static int gpio_mockup_name_lines(struct device *dev,
char **names;
int i;
names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL);
names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL);
if (!names)
return -ENOMEM;
@ -142,12 +149,21 @@ static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset)
return chip->irq_base + offset;
}
/*
* While we should generally support irqmask and irqunmask, this driver is
* for testing purposes only so we don't care.
*/
static void gpio_mockup_irqmask(struct irq_data *d) { }
static void gpio_mockup_irqunmask(struct irq_data *d) { }
static void gpio_mockup_irqmask(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 = 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 = {
.name = GPIO_MOCKUP_NAME,
@ -178,6 +194,7 @@ static int gpio_mockup_irqchip_setup(struct device *dev,
for (i = 0; i < gc->ngpio; i++) {
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_modify_status(irq_base + i,
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 gpio_desc *desc;
struct gpio_chip *gc;
int val;
char buf;
int rv, val;
rv = kstrtoint_from_user(usr_buf, size, 0, &val);
if (rv)
return rv;
if (val != 0 && val != 1)
return -EINVAL;
sfile = file->private_data;
priv = sfile->private;
@ -206,19 +228,11 @@ static ssize_t gpio_mockup_event_write(struct file *file,
chip = priv->chip;
gc = &chip->gc;
if (copy_from_user(&buf, usr_buf, 1))
return -EFAULT;
if (buf == '0')
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);
if (chip->lines[priv->offset].irq_enabled) {
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;
}
@ -294,8 +308,8 @@ static int gpio_mockup_add(struct device *dev,
gc->get_direction = gpio_mockup_get_direction;
gc->to_irq = gpio_mockup_to_irq;
chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio,
GFP_KERNEL);
chip->lines = devm_kcalloc(dev, gc->ngpio,
sizeof(*chip->lines), GFP_KERNEL);
if (!chip->lines)
return -ENOMEM;
@ -321,23 +335,24 @@ static int gpio_mockup_add(struct device *dev,
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;
int ret, i, base, ngpio;
struct gpio_mockup_chip *chips;
char *chip_name;
if (gpio_mockup_params_nr < 2)
if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2))
return -EINVAL;
chips = devm_kzalloc(dev,
sizeof(*chips) * (gpio_mockup_params_nr >> 1),
GFP_KERNEL);
/* Each chip is described by two values. */
num_chips = gpio_mockup_params_nr / 2;
chips = devm_kcalloc(dev, num_chips, sizeof(*chips), GFP_KERNEL);
if (!chips)
return -ENOMEM;
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];
if (base == -1)
@ -355,18 +370,16 @@ static int gpio_mockup_probe(struct platform_device *pdev)
ret = gpio_mockup_add(dev, &chips[i],
chip_name, base, ngpio);
} else {
ret = -1;
ret = -EINVAL;
}
if (ret) {
dev_err(dev, "gpio<%d..%d> add failed\n",
base, base < 0 ? ngpio : base + ngpio);
dev_err(dev,
"adding gpiochip failed: %d (base: %d, ngpio: %d)\n",
ret, base, base < 0 ? ngpio : base + ngpio);
return ret;
}
dev_info(dev, "gpio<%d..%d> add successful!",
base, base + ngpio);
}
return 0;
@ -420,5 +433,6 @@ module_exit(mock_device_exit);
MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>");
MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
MODULE_DESCRIPTION("GPIO Testing driver");
MODULE_LICENSE("GPL v2");

View File

@ -33,21 +33,23 @@
* 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/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"
@ -87,6 +89,7 @@
#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
#define MVEBU_GPIO_SOC_VARIANT_A8K 0x4
#define MVEBU_MAX_GPIO_PER_BANK 32
@ -106,9 +109,9 @@ struct mvebu_pwm {
struct mvebu_gpio_chip {
struct gpio_chip chip;
spinlock_t lock;
void __iomem *membase;
void __iomem *percpu_membase;
struct regmap *regs;
u32 offset;
struct regmap *percpu_regs;
int irqbase;
struct irq_domain *domain;
int soc_variant;
@ -130,92 +133,152 @@ struct mvebu_gpio_chip {
* Functions returning addresses of individual registers for a given
* 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)
{
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)
static void mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip,
struct regmap **map, unsigned int *offset)
{
int cpu;
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
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:
cpu = smp_processor_id();
return mvchip->percpu_membase +
GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
*map = mvchip->percpu_regs;
*offset = GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
break;
default:
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;
switch (mvchip->soc_variant) {
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:
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:
cpu = smp_processor_id();
return mvchip->percpu_membase +
GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
*map = mvchip->percpu_regs;
*offset = GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
break;
default:
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;
switch (mvchip->soc_variant) {
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:
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:
cpu = smp_processor_id();
return mvchip->percpu_membase +
GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
*map = mvchip->percpu_regs;
*offset = GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
break;
default:
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
* 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)
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
unsigned long flags;
u32 u;
spin_lock_irqsave(&mvchip->lock, flags);
u = readl_relaxed(mvebu_gpioreg_out(mvchip));
if (value)
u |= BIT(pin);
else
u &= ~BIT(pin);
writel_relaxed(u, mvebu_gpioreg_out(mvchip));
spin_unlock_irqrestore(&mvchip->lock, flags);
regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
BIT(pin), value ? BIT(pin) : 0);
}
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);
u32 u;
if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin)) {
u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^
readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
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 {
u = readl_relaxed(mvebu_gpioreg_out(mvchip));
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &u);
}
return (u >> pin) & 1;
@ -268,25 +330,15 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin,
int value)
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
unsigned long flags;
u32 u;
spin_lock_irqsave(&mvchip->lock, flags);
u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
if (value)
u |= BIT(pin);
else
u &= ~BIT(pin);
writel_relaxed(u, mvebu_gpioreg_blink(mvchip));
spin_unlock_irqrestore(&mvchip->lock, flags);
regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset,
BIT(pin), value ? BIT(pin) : 0);
}
static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin)
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
unsigned long flags;
int ret;
u32 u;
/*
* 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)
return ret;
spin_lock_irqsave(&mvchip->lock, flags);
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
u |= BIT(pin);
writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
spin_unlock_irqrestore(&mvchip->lock, flags);
regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
BIT(pin), BIT(pin));
return 0;
}
@ -309,9 +358,7 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
int value)
{
struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
unsigned long flags;
int ret;
u32 u;
/*
* 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_set(chip, pin, value);
spin_lock_irqsave(&mvchip->lock, flags);
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
u &= ~BIT(pin);
writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip));
spin_unlock_irqrestore(&mvchip->lock, flags);
regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
BIT(pin), 0);
return 0;
}
@ -350,7 +394,7 @@ static void mvebu_gpio_irq_ack(struct irq_data *d)
u32 mask = d->mask;
irq_gc_lock(gc);
writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip));
mvebu_gpio_write_edge_cause(mvchip, ~mask);
irq_gc_unlock(gc);
}
@ -363,8 +407,7 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d)
irq_gc_lock(gc);
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);
}
@ -377,7 +420,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d)
irq_gc_lock(gc);
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);
}
@ -390,7 +433,7 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d)
irq_gc_lock(gc);
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);
}
@ -403,7 +446,7 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d)
irq_gc_lock(gc);
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);
}
@ -443,8 +486,8 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type)
pin = d->hwirq;
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin);
if (!u)
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u);
if ((u & BIT(pin)) == 0)
return -EINVAL;
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) {
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
u &= ~BIT(pin);
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
regmap_update_bits(mvchip->regs,
GPIO_IN_POL_OFF + mvchip->offset,
BIT(pin), 0);
break;
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
u |= BIT(pin);
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
regmap_update_bits(mvchip->regs,
GPIO_IN_POL_OFF + mvchip->offset,
BIT(pin), BIT(pin));
break;
case IRQ_TYPE_EDGE_BOTH: {
u32 v;
u32 data_in, in_pol, val;
v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^
readl_relaxed(mvebu_gpioreg_data_in(mvchip));
regmap_read(mvchip->regs,
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
*/
u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
if (v & BIT(pin))
u |= BIT(pin); /* falling */
if ((data_in ^ in_pol) & BIT(pin))
val = BIT(pin); /* falling */
else
u &= ~BIT(pin); /* rising */
writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip));
val = 0; /* raising */
regmap_update_bits(mvchip->regs,
GPIO_IN_POL_OFF + mvchip->offset,
BIT(pin), val);
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 irq_chip *chip = irq_desc_get_chip(desc);
u32 cause, type;
u32 cause, type, data_in, level_mask, edge_cause, edge_mask;
int i;
if (mvchip == NULL)
@ -505,10 +552,12 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
chained_irq_enter(chip, desc);
cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) &
readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) &
readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
level_mask = mvebu_gpio_read_level_mask(mvchip);
edge_cause = mvebu_gpio_read_edge_cause(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++) {
int irq;
@ -523,9 +572,13 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
/* Swap polarity (race with GPIO line) */
u32 polarity;
polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
regmap_read(mvchip->regs,
GPIO_IN_POL_OFF + mvchip->offset,
&polarity);
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);
@ -628,7 +681,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
state->period = 1;
}
u = readl_relaxed(mvebu_gpioreg_blink(mvchip));
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
if (u)
state->enabled = true;
else
@ -691,8 +744,8 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
{
struct mvebu_pwm *mvpwm = mvchip->mvpwm;
mvpwm->blink_select =
readl_relaxed(mvebu_gpioreg_blink_counter_select(mvchip));
regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
&mvpwm->blink_select);
mvpwm->blink_on_duration =
readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm));
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;
writel_relaxed(mvpwm->blink_select,
mvebu_gpioreg_blink_counter_select(mvchip));
regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset,
mvpwm->blink_select);
writel_relaxed(mvpwm->blink_on_duration,
mvebu_pwmreg_blink_on_duration(mvpwm));
writel_relaxed(mvpwm->blink_off_duration,
@ -747,7 +800,8 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
set = U32_MAX;
else
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);
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;
int i;
out = readl_relaxed(mvebu_gpioreg_out(mvchip));
io_conf = readl_relaxed(mvebu_gpioreg_io_conf(mvchip));
blink = readl_relaxed(mvebu_gpioreg_blink(mvchip));
in_pol = readl_relaxed(mvebu_gpioreg_in_pol(mvchip));
data_in = readl_relaxed(mvebu_gpioreg_data_in(mvchip));
cause = readl_relaxed(mvebu_gpioreg_edge_cause(mvchip));
edg_msk = readl_relaxed(mvebu_gpioreg_edge_mask(mvchip));
lvl_msk = readl_relaxed(mvebu_gpioreg_level_mask(mvchip));
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out);
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &io_conf);
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &blink);
regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, &in_pol);
regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in);
cause = mvebu_gpio_read_edge_cause(mvchip);
edg_msk = mvebu_gpio_read_edge_mask(mvchip);
lvl_msk = mvebu_gpio_read_level_mask(mvchip);
for (i = 0; i < chip->ngpio; i++) {
const char *label;
@ -855,6 +909,10 @@ static const struct of_device_id mvebu_gpio_of_match[] = {
.compatible = "marvell,armada-370-gpio",
.data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
},
{
.compatible = "marvell,armada-8k-gpio",
.data = (void *) MVEBU_GPIO_SOC_VARIANT_A8K,
},
{
/* 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);
int i;
mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip));
mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip));
mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip));
mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip));
regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
&mvchip->out_reg);
regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
&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) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
mvchip->edge_mask_regs[0] =
readl(mvchip->membase + GPIO_EDGE_MASK_OFF);
mvchip->level_mask_regs[0] =
readl(mvchip->membase + GPIO_LEVEL_MASK_OFF);
case MVEBU_GPIO_SOC_VARIANT_A8K:
regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
&mvchip->edge_mask_regs[0]);
regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
&mvchip->level_mask_regs[0]);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
for (i = 0; i < 2; i++) {
mvchip->edge_mask_regs[i] =
readl(mvchip->membase +
GPIO_EDGE_MASK_MV78200_OFF(i));
mvchip->level_mask_regs[i] =
readl(mvchip->membase +
GPIO_LEVEL_MASK_MV78200_OFF(i));
regmap_read(mvchip->regs,
GPIO_EDGE_MASK_MV78200_OFF(i),
&mvchip->edge_mask_regs[i]);
regmap_read(mvchip->regs,
GPIO_LEVEL_MASK_MV78200_OFF(i),
&mvchip->level_mask_regs[i]);
}
break;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
for (i = 0; i < 4; i++) {
mvchip->edge_mask_regs[i] =
readl(mvchip->membase +
GPIO_EDGE_MASK_ARMADAXP_OFF(i));
mvchip->level_mask_regs[i] =
readl(mvchip->membase +
GPIO_LEVEL_MASK_ARMADAXP_OFF(i));
regmap_read(mvchip->regs,
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
&mvchip->edge_mask_regs[i]);
regmap_read(mvchip->regs,
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
&mvchip->level_mask_regs[i]);
}
break;
default:
@ -912,35 +975,41 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
int i;
writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip));
writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip));
writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip));
writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip));
regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
mvchip->out_reg);
regmap_write(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset,
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) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
writel(mvchip->edge_mask_regs[0],
mvchip->membase + GPIO_EDGE_MASK_OFF);
writel(mvchip->level_mask_regs[0],
mvchip->membase + GPIO_LEVEL_MASK_OFF);
case MVEBU_GPIO_SOC_VARIANT_A8K:
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
mvchip->edge_mask_regs[0]);
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
mvchip->level_mask_regs[0]);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
for (i = 0; i < 2; i++) {
writel(mvchip->edge_mask_regs[i],
mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i));
writel(mvchip->level_mask_regs[i],
mvchip->membase +
GPIO_LEVEL_MASK_MV78200_OFF(i));
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_MV78200_OFF(i),
mvchip->edge_mask_regs[i]);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_MV78200_OFF(i),
mvchip->level_mask_regs[i]);
}
break;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
for (i = 0; i < 4; i++) {
writel(mvchip->edge_mask_regs[i],
mvchip->membase +
GPIO_EDGE_MASK_ARMADAXP_OFF(i));
writel(mvchip->level_mask_regs[i],
mvchip->membase +
GPIO_LEVEL_MASK_ARMADAXP_OFF(i));
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
mvchip->edge_mask_regs[i]);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
mvchip->level_mask_regs[i]);
}
break;
default:
@ -953,12 +1022,73 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
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)
{
struct mvebu_gpio_chip *mvchip;
const struct of_device_id *match;
struct device_node *np = pdev->dev.of_node;
struct resource *res;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
unsigned int ngpios;
@ -1016,53 +1146,47 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
mvchip->chip.of_node = np;
mvchip->chip.dbg_show = mvebu_gpio_dbg_show;
spin_lock_init(&mvchip->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mvchip->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mvchip->membase))
return PTR_ERR(mvchip->membase);
if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K)
err = mvebu_gpio_probe_syscon(pdev, mvchip);
else
err = mvebu_gpio_probe_raw(pdev, mvchip);
/*
* The Armada XP has a second range of registers for the
* 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);
}
if (err)
return err;
/*
* Mask and clear GPIO interrupts.
*/
switch (soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
case MVEBU_GPIO_SOC_VARIANT_A8K:
regmap_write(mvchip->regs,
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;
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++) {
writel_relaxed(0, mvchip->membase +
GPIO_EDGE_MASK_MV78200_OFF(cpu));
writel_relaxed(0, mvchip->membase +
GPIO_LEVEL_MASK_MV78200_OFF(cpu));
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_MV78200_OFF(cpu), 0);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_MV78200_OFF(cpu), 0);
}
break;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF);
writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF);
writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF);
regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0);
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0);
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0);
for (cpu = 0; cpu < 4; cpu++) {
writel_relaxed(0, mvchip->percpu_membase +
GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu));
writel_relaxed(0, mvchip->percpu_membase +
GPIO_EDGE_MASK_ARMADAXP_OFF(cpu));
writel_relaxed(0, mvchip->percpu_membase +
GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu));
regmap_write(mvchip->percpu_regs,
GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu), 0);
regmap_write(mvchip->percpu_regs,
GPIO_EDGE_MASK_ARMADAXP_OFF(cpu), 0);
regmap_write(mvchip->percpu_regs,
GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu), 0);
}
break;
default:

View File

@ -20,7 +20,7 @@
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c/pcf857x.h>
#include <linux/platform_data/pcf857x.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>

View File

@ -331,14 +331,18 @@ static irqreturn_t pch_gpio_handler(int irq, void *dev_id)
return ret;
}
static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
unsigned int irq_start, unsigned int num)
static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip,
unsigned int irq_start,
unsigned int num)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base,
handle_simple_irq);
if (!gc)
return -ENOMEM;
gc->private = chip;
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_NOREQUEST | IRQ_NOPROBE, 0);
return 0;
}
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;
}
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:
return 0;

View File

@ -344,6 +344,10 @@ static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
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",
.data = &gpio_rcar_info_gen2,
}, {

View File

@ -320,13 +320,16 @@ static irqreturn_t gsta_gpio_handler(int irq, void *dev_id)
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_type *ct;
gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base,
chip->reg_base, handle_simple_irq);
if (!gc)
return -ENOMEM;
gc->private = chip;
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;
}
return 0;
}
/* 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;
}
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,
IRQF_SHARED, KBUILD_MODNAME, chip);

View File

@ -108,19 +108,14 @@ struct wcove_gpio {
static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type)
{
unsigned int reg;
int bank;
if (gpio < BANK0_NR_PINS)
bank = 0;
else if (gpio < BANK0_NR_PINS + BANK1_NR_PINS)
bank = 1;
else
bank = 2;
if (gpio >= WCOVE_GPIO_NUM)
return -EOPNOTSUPP;
if (reg_type == CTRL_IN)
reg = GPIO_IN_CTRL_BASE + bank;
reg = GPIO_IN_CTRL_BASE + gpio;
else
reg = GPIO_OUT_CTRL_BASE + bank;
reg = GPIO_OUT_CTRL_BASE + gpio;
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)
{
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);
}
@ -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)
{
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),
CTLO_INPUT_SET);
if (reg < 0)
return 0;
return regmap_write(wg->regmap, reg, CTLO_INPUT_SET);
}
static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio,
int value)
{
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),
CTLO_OUTPUT_SET | value);
if (reg < 0)
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)
{
struct wcove_gpio *wg = gpiochip_get_data(chip);
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)
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);
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)
return ret;
@ -197,25 +207,33 @@ static void wcove_gpio_set(struct gpio_chip *chip,
unsigned int gpio, int value)
{
struct wcove_gpio *wg = gpiochip_get_data(chip);
int reg = to_reg(gpio, CTRL_OUT);
if (reg < 0)
return;
if (value)
regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 1);
regmap_update_bits(wg->regmap, reg, 1, 1);
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,
unsigned long config)
{
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)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT),
CTLO_DRV_MASK, CTLO_DRV_OD);
return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK,
CTLO_DRV_OD);
case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT),
CTLO_DRV_MASK, CTLO_DRV_CMOS);
return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK,
CTLO_DRV_CMOS);
default:
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 wcove_gpio *wg = gpiochip_get_data(chip);
if (data->hwirq >= WCOVE_GPIO_NUM)
return 0;
switch (type) {
case IRQ_TYPE_NONE:
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 wcove_gpio *wg = gpiochip_get_data(chip);
if (data->hwirq >= WCOVE_GPIO_NUM)
return;
wg->set_irq_mask = false;
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 wcove_gpio *wg = gpiochip_get_data(chip);
if (data->hwirq >= WCOVE_GPIO_NUM)
return;
wg->set_irq_mask = true;
wg->update |= UPDATE_IRQ_MASK;
}

View File

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

View File

@ -96,8 +96,8 @@
/* GPIO upper 16 bit mask */
#define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
/* For GPIO quirks */
#define ZYNQ_GPIO_QUIRK_FOO BIT(0)
/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */
#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0)
/**
* 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_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
* 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)
{
u32 reg;
bool is_zynq_gpio;
unsigned int bank_num, bank_pin_num;
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);
/*
* On zynq bank 0 pins 7 and 8 are special and cannot be used
* 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))
return -EINVAL;
@ -637,7 +646,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = {
static const struct zynq_platform_data zynq_gpio_def = {
.label = "zynq_gpio",
.quirks = ZYNQ_GPIO_QUIRK_FOO,
.quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ,
.ngpio = ZYNQ_GPIO_NR_GPIOS,
.max_bank = ZYNQ_GPIO_MAX_BANK,
.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[] = {
{ .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def },
{ .compatible = "xlnx,zynqmp-gpio-1.0",
.data = (void *)&zynqmp_gpio_def },
{ .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def },
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);

View File

@ -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. */
}
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,
void *context)
{
@ -178,11 +195,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares,
unsigned long irqflags;
int ret, pin, irq;
if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
return AE_OK;
agpio = &ares->data.gpio;
if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT)
if (!acpi_gpio_get_irq_resource(ares, &agpio))
return AE_OK;
handle = ACPI_HANDLE(chip->parent);
@ -423,6 +436,59 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
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_info info;
int index;
@ -460,8 +526,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
*/
if (lookup->info.gpioint) {
lookup->info.flags = GPIOD_IN;
lookup->info.polarity = agpio->polarity;
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,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags,
enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_gpio_info info;
struct gpio_desc *desc;
char propname[32];
int err;
int i;
/* Try first from _DSD */
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",
con_id, gpio_suffixes[i]);
} else {
@ -622,17 +692,21 @@ struct gpio_desc *acpi_find_gpio(struct device *dev,
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
if (IS_ERR(desc))
return desc;
}
if ((flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH) &&
info.gpioint) {
dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
return ERR_PTR(-ENOENT);
}
if (info.gpioint &&
(*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) {
dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
return ERR_PTR(-ENOENT);
}
if (info.polarity == 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;
}
@ -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
* 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.
*/
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
{
int idx, i;
unsigned int irq_flags;
int ret;
for (i = 0, idx = 0; idx <= index; i++) {
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);
if (info.gpioint && idx++ == index) {
char label[32];
int irq;
if (IS_ERR(desc))
@ -713,6 +792,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
if (irq < 0)
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,
info.polarity);
@ -740,7 +824,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
struct acpi_resource *ares;
int pin_index = (int)address;
acpi_status status;
bool pull_up;
int length;
int i;
@ -755,7 +838,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
}
agpio = &ares->data.gpio;
pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP;
if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT &&
function == ACPI_WRITE)) {
@ -806,35 +888,23 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
}
if (!found) {
desc = gpiochip_request_own_desc(chip, pin,
"ACPI:OpRegion");
enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio);
const char *label = "ACPI:OpRegion";
int err;
desc = gpiochip_request_own_desc(chip, pin, label);
if (IS_ERR(desc)) {
status = AE_ERROR;
mutex_unlock(&achip->conn_lock);
goto out;
}
switch (agpio->io_restriction) {
case ACPI_IO_RESTRICT_INPUT:
gpiod_direction_input(desc);
break;
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.
*/
gpiod_direction_output(desc, pull_up);
break;
default:
/*
* Assume that the BIOS has configured the
* direction and pull accordingly.
*/
break;
err = gpiod_configure_flags(desc, label, 0, flags);
if (err < 0) {
status = AE_NOT_CONFIGURED;
gpiochip_free_own_desc(desc);
mutex_unlock(&achip->conn_lock);
goto out;
}
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
@ -1089,7 +1159,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
/* Try first from _DSD */
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",
con_id, gpio_suffixes[i]);
else
@ -1119,6 +1189,9 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
struct list_head resource_list;
unsigned int crs_count = 0;
if (!acpi_can_fallback_to_crs(adev, con_id))
return count;
INIT_LIST_HEAD(&resource_list);
acpi_dev_get_resources(adev, &resource_list,
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;
}
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)
{
struct acpi_crs_lookup *l, *lookup = NULL;
/* Never allow fallback if the device has properties */
if (adev->data.properties || adev->driver_gpios)
return false;
mutex_lock(&acpi_crs_lookup_lock);
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));
return con_id == NULL;
}

View File

@ -153,6 +153,9 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
*flags |= GPIO_OPEN_SOURCE;
}
if (of_flags & OF_GPIO_SLEEP_MAY_LOOSE_VALUE)
*flags |= GPIO_SLEEP_MAY_LOOSE_VALUE;
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
* 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)
{

View File

@ -1,4 +1,4 @@
#include <linux/bitops.h>
#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/module.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)
{
int i;
if (!gpiochip->irq_need_valid_mask)
return 0;
@ -1483,8 +1481,7 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
return -ENOMEM;
/* Assume by default all GPIOs are valid */
for (i = 0; i < gpiochip->ngpio; i++)
set_bit(i, gpiochip->irq_valid_mask);
bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio);
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);
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
* @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);
}
EXPORT_SYMBOL_GPL(gpiod_add_lookup_table);
/**
* 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);
}
EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table);
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
* 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)
{
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);
if (lflags & GPIO_OPEN_SOURCE)
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... */
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);
} else if (ACPI_COMPANION(dev)) {
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;
desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
if (!IS_ERR(desc))
if (!IS_ERR(desc)) {
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))

View File

@ -75,11 +75,13 @@ struct gpio_device {
/**
* 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
* @polarity: interrupt polarity as provided by ACPI
* @triggering: triggering type as provided by ACPI
*/
struct acpi_gpio_info {
enum gpiod_flags flags;
bool gpioint;
int polarity;
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_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,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags,
enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags);
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index,
@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
static inline void
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 *
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)
{
return ERR_PTR(-ENOENT);
@ -190,6 +201,7 @@ struct gpio_desc {
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_SLEEP_MAY_LOOSE_VALUE 12 /* GPIO may loose value in sleep */
/* Connection label */
const char *label;
@ -199,6 +211,8 @@ struct gpio_desc {
int gpiod_request(struct gpio_desc *desc, const char *label);
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,
unsigned long lflags, enum gpiod_flags dflags);

View File

@ -20,7 +20,7 @@
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/i2c/adp5588.h>
#include <linux/platform_data/adp5588.h>
/* Key Event Register xy */
#define KEY_EV_PRESSED (1 << 7)

View File

@ -15,10 +15,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/acpi.h>
@ -149,8 +145,8 @@ static int vendor_resource_matches(struct pnp_dev *dev,
uuid_len == sizeof(match->data) &&
memcmp(uuid, match->data, uuid_len) == 0) {
if (expected_len && expected_len != actual_len) {
dev_err(&dev->dev, "wrong vendor descriptor size; "
"expected %d, found %d bytes\n",
dev_err(&dev->dev,
"wrong vendor descriptor size; expected %d, found %d bytes\n",
expected_len, actual_len);
return 0;
}
@ -180,6 +176,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
struct pnp_dev *dev = data;
struct acpi_resource_dma *dma;
struct acpi_resource_vendor_typed *vendor_typed;
struct acpi_resource_gpio *gpio;
struct resource_win win = {{0}, 0};
struct resource *r = &win.res;
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.
*/
if (pnp_can_write(dev)) {
dev_warn(&dev->dev, "multiple interrupts in "
"_CRS descriptor; configuration can't "
"be changed\n");
dev_warn(&dev->dev,
"multiple interrupts in _CRS descriptor; configuration can't be changed\n");
dev->capabilities &= ~PNP_WRITE;
}
}
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) {
pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED);
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)
__set_bit(p->interrupts[i], map.bits);
else
dev_err(&dev->dev, "ignoring IRQ %d option "
"(too large for %d entry bitmap)\n",
dev_err(&dev->dev,
"ignoring IRQ %d option (too large for %d entry bitmap)\n",
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_GENERIC_REGISTER:
default: /* other type */
dev_warn(&dev->dev, "can't encode unknown resource "
"type %d\n", resource->type);
dev_warn(&dev->dev,
"can't encode unknown resource type %d\n",
resource->type);
return -EINVAL;
}
resource++;

View File

@ -9,10 +9,13 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*/
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/property.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
@ -60,8 +63,50 @@
#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[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_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
* @num_ports: number of serial ports
@ -194,7 +239,8 @@ static void setup_gpio(struct pci_dev *pcidev, u8 __iomem *p)
}
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;
@ -202,8 +248,11 @@ xr17v35x_register_gpio(struct pci_dev *pcidev)
if (!pdev)
return NULL;
platform_set_drvdata(pdev, pcidev);
if (platform_device_add(pdev) < 0) {
pdev->dev.parent = &pcidev->dev;
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);
return NULL;
}
@ -211,17 +260,131 @@ xr17v35x_register_gpio(struct pci_dev *pcidev)
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
pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
struct uart_8250_port *port, int idx)
{
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 baud = 7812500;
u8 __iomem *p;
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.rs485_config = platform->rs485_config;
/*
* Setup the uart clock for the devices on expansion slot to
* 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_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)

View File

@ -28,4 +28,8 @@
#define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN)
#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

View File

@ -964,6 +964,8 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev,
const struct acpi_gpio_mapping *gpios);
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);
#else
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 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)
{
return -ENXIO;

View File

@ -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_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 */
void *gpiochip_get_data(struct gpio_chip *chip);

View File

@ -9,6 +9,8 @@ enum gpio_lookup_flags {
GPIO_ACTIVE_LOW = (1 << 0),
GPIO_OPEN_DRAIN = (1 << 1),
GPIO_OPEN_SOURCE = (1 << 2),
GPIO_SLEEP_MAINTAIN_VALUE = (0 << 3),
GPIO_SLEEP_MAY_LOOSE_VALUE = (1 << 3),
};
/**

View File

@ -31,6 +31,7 @@ enum of_gpio_flags {
OF_GPIO_ACTIVE_LOW = 0x1,
OF_GPIO_SINGLE_ENDED = 0x2,
OF_GPIO_OPEN_DRAIN = 0x4,
OF_GPIO_SLEEP_MAY_LOOSE_VALUE = 0x8,
};
#ifdef CONFIG_OF_GPIO

View File

@ -172,7 +172,7 @@ extern int platform_device_add_resources(struct platform_device *pdev,
extern int platform_device_add_data(struct platform_device *pdev,
const void *data, size_t size);
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 void platform_device_del(struct platform_device *pdev);
extern void platform_device_put(struct platform_device *pdev);