Commit 882d6edf authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'gpio-updates-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio updates from Bartosz Golaszewski:
 "It's been a relatively calm release cycle and we're actually removing
  more code than we're adding.

  Summary:

   - new driver for the Toshiba Visconti platform

   - rework of interrupt handling in gpio-tegra

   - updates for GPIO selftests: we're now using the character device to
     perform the subsystem checks

   - support for a new rcar variant + some code refactoring

   - refactoring of gpio-ep93xx

   - SPDX License identifier has been updated in the uapi header so that
     userspace programs bundling it can become fully REUSE-compliant

   - improvements to pwm handling in gpio-mvebu

   - support for interrupt handling and power management for gpio-xilinx
     as well as some code refactoring

   - support for a new chip variant in gpio-pca953x

   - removal of drivers: zte xs & intel-mid and removal of leftovers
     from intel-msic

   - impovements to intel drivers pulled from Andy Shevchenko

   - improvements to the gpio-aggregator virtual GPIO driver

   - and several minor tweaks and fixes to code and documentation all
     over the place"

* tag 'gpio-updates-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (71 commits)
  gpio: pcf857x: Fix missing first interrupt
  gpio: ep93xx: refactor base IRQ number
  gpio: ep93xx: refactor ep93xx_gpio_add_bank
  gpio: ep93xx: Fix typo s/hierarchial/hierarchical
  gpio: ep93xx: drop to_irq binding
  gpio: ep93xx: Fix wrong irq numbers in port F
  gpio: uapi: use the preferred SPDX license identifier
  gpio: gpio-xilinx: Add check if width exceeds 32
  gpio: gpio-xilinx: Add support for suspend and resume
  gpio: gpio-xilinx: Add interrupt support
  gpio: gpio-xilinx: Reduce spinlock array to array
  gpio: gpio-xilinx: Simplify with dev_err_probe()
  gpio: msic: Drop driver from Makefile
  gpio: wcove: Split out to_ireg() helper and deduplicate the code
  gpio: wcove: Switch to use regmap_set_bits(), regmap_clear_bits()
  gpio: wcove: Get rid of error prone casting in IRQ handler
  gpio: intel-mid: Remove driver for deprecated platform
  gpio: msic: Remove driver for deprecated platform
  gpio: aggregator: Remove trailing comma in terminator entries
  gpio: aggregator: Use compound literal from the header
  ...
parents 0328b5f2 a8002a35
...@@ -80,6 +80,11 @@ Required properties: ...@@ -80,6 +80,11 @@ Required properties:
- offset: offset address inside the syscon block - offset: offset address inside the syscon block
Optional properties:
- marvell,pwm-offset: offset address of PWM duration control registers inside
the syscon block
Example: Example:
ap_syscon: system-controller@6f4000 { ap_syscon: system-controller@6f4000 {
compatible = "syscon", "simple-mfd"; compatible = "syscon", "simple-mfd";
...@@ -101,6 +106,9 @@ ap_syscon: system-controller@6f4000 { ...@@ -101,6 +106,9 @@ ap_syscon: system-controller@6f4000 {
gpio-controller; gpio-controller;
#gpio-cells = <2>; #gpio-cells = <2>;
gpio-ranges = <&ap_pinctrl 0 0 19>; gpio-ranges = <&ap_pinctrl 0 0 19>;
marvell,pwm-offset = <0x10c0>;
#pwm-cells = <2>;
clocks = <&ap_clk 3>;
}; };
}; };
......
...@@ -7,6 +7,7 @@ Required Properties: ...@@ -7,6 +7,7 @@ Required Properties:
"ti,k2g-gpio", "ti,keystone-gpio": for 66AK2G "ti,k2g-gpio", "ti,keystone-gpio": for 66AK2G
"ti,am654-gpio", "ti,keystone-gpio": for TI K3 AM654 "ti,am654-gpio", "ti,keystone-gpio": for TI K3 AM654
"ti,j721e-gpio", "ti,keystone-gpio": for J721E SoCs "ti,j721e-gpio", "ti,keystone-gpio": for J721E SoCs
"ti,am64-gpio", "ti,keystone-gpio": for AM64 SoCs
- reg: Physical base address of the controller and the size of memory mapped - reg: Physical base address of the controller and the size of memory mapped
registers. registers.
......
...@@ -32,6 +32,7 @@ properties: ...@@ -32,6 +32,7 @@ properties:
- maxim,max7327 - maxim,max7327
- nxp,pca6416 - nxp,pca6416
- nxp,pca9505 - nxp,pca9505
- nxp,pca9506
- nxp,pca9534 - nxp,pca9534
- nxp,pca9535 - nxp,pca9535
- nxp,pca9536 - nxp,pca9536
...@@ -70,7 +71,7 @@ properties: ...@@ -70,7 +71,7 @@ properties:
gpio-line-names: gpio-line-names:
minItems: 1 minItems: 1
maxItems: 32 maxItems: 40
interrupts: interrupts:
maxItems: 1 maxItems: 1
......
...@@ -48,6 +48,9 @@ properties: ...@@ -48,6 +48,9 @@ properties:
- renesas,gpio-r8a77995 # R-Car D3 - renesas,gpio-r8a77995 # R-Car D3
- const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2 - const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2
- items:
- const: renesas,gpio-r8a779a0 # R-Car V3U
reg: reg:
maxItems: 1 maxItems: 1
......
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/toshiba,gpio-visconti.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Toshiba Visconti ARM SoCs GPIO controller
maintainers:
- Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
properties:
compatible:
items:
- const: toshiba,gpio-tmpv7708
reg:
maxItems: 1
"#gpio-cells":
const: 2
gpio-ranges: true
gpio-controller: true
interrupt-controller: true
"#interrupt-cells":
const: 2
interrupts:
description:
interrupt mapping one per GPIO.
minItems: 16
maxItems: 16
required:
- compatible
- reg
- "#gpio-cells"
- gpio-ranges
- gpio-controller
- interrupt-controller
- "#interrupt-cells"
- interrupt-parent
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
gpio: gpio@28020000 {
compatible = "toshiba,gpio-tmpv7708";
reg = <0 0x28020000 0 0x1000>;
#gpio-cells = <0x2>;
gpio-ranges = <&pmux 0 0 32>;
gpio-controller;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
};
};
...
ZTE ZX296702 GPIO controller
Required properties:
- compatible : "zte,zx296702-gpio"
- #gpio-cells : Should be two. The first cell is the pin number and the
second cell is used to specify optional parameters:
- bit 0 specifies polarity (0 for normal, 1 for inverted)
- gpio-controller : Marks the device node as a GPIO controller.
- interrupts : Interrupt mapping for GPIO IRQ.
- gpio-ranges : Interaction with the PINCTRL subsystem.
gpio1: gpio@b008040 {
compatible = "zte,zx296702-gpio";
reg = <0xb008040 0x40>;
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = < &pmx0 0 54 2 &pmx0 2 59 14>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&intc>;
interrupt-controller;
#interrupt-cells = <2>;
clock-names = "gpio_pclk";
clocks = <&lsp0clk ZX296702_GPIO_CLK>;
};
...@@ -361,7 +361,8 @@ corresponding chip driver. In that case a significantly improved performance ...@@ -361,7 +361,8 @@ corresponding chip driver. In that case a significantly improved performance
can be expected. If simultaneous access is not possible the GPIOs will be can be expected. If simultaneous access is not possible the GPIOs will be
accessed sequentially. accessed sequentially.
The functions take three arguments: The functions take four arguments:
* array_size - the number of array elements * array_size - the number of array elements
* desc_array - an array of GPIO descriptors * desc_array - an array of GPIO descriptors
* array_info - optional information obtained from gpiod_get_array() * array_info - optional information obtained from gpiod_get_array()
......
...@@ -106,11 +106,11 @@ don't. When you need open drain signaling but your hardware doesn't directly ...@@ -106,11 +106,11 @@ don't. When you need open drain signaling but your hardware doesn't directly
support it, there's a common idiom you can use to emulate it with any GPIO pin support it, there's a common idiom you can use to emulate it with any GPIO pin
that can be used as either an input or an output: that can be used as either an input or an output:
LOW: gpiod_direction_output(gpio, 0) ... this drives the signal and overrides **LOW**: ``gpiod_direction_output(gpio, 0)`` ... this drives the signal and
the pullup. overrides the pullup.
HIGH: gpiod_direction_input(gpio) ... this turns off the output, so the pullup **HIGH**: ``gpiod_direction_input(gpio)`` ... this turns off the output, so
(or some other device) controls the signal. the pullup (or some other device) controls the signal.
The same logic can be applied to emulate open source signaling, by driving the The same logic can be applied to emulate open source signaling, by driving the
high signal and configuring the GPIO as input for low. This open drain/open high signal and configuring the GPIO as input for low. This open drain/open
......
...@@ -2609,10 +2609,12 @@ S: Supported ...@@ -2609,10 +2609,12 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwamatsu/linux-visconti.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwamatsu/linux-visconti.git
F: Documentation/devicetree/bindings/arm/toshiba.yaml F: Documentation/devicetree/bindings/arm/toshiba.yaml
F: Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml F: Documentation/devicetree/bindings/net/toshiba,visconti-dwmac.yaml
F: Documentation/devicetree/bindings/gpio/toshiba,gpio-visconti.yaml
F: Documentation/devicetree/bindings/pinctrl/toshiba,tmpv7700-pinctrl.yaml F: Documentation/devicetree/bindings/pinctrl/toshiba,tmpv7700-pinctrl.yaml
F: Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml F: Documentation/devicetree/bindings/watchdog/toshiba,visconti-wdt.yaml
F: arch/arm64/boot/dts/toshiba/ F: arch/arm64/boot/dts/toshiba/
F: drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c F: drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
F: drivers/gpio/gpio-visconti.c
F: drivers/pinctrl/visconti/ F: drivers/pinctrl/visconti/
F: drivers/watchdog/visconti_wdt.c F: drivers/watchdog/visconti_wdt.c
N: visconti N: visconti
......
...@@ -487,11 +487,11 @@ config GPIO_PXA ...@@ -487,11 +487,11 @@ config GPIO_PXA
Say yes here to support the PXA GPIO device Say yes here to support the PXA GPIO device
config GPIO_RCAR config GPIO_RCAR
tristate "Renesas R-Car GPIO" tristate "Renesas R-Car and RZ/G GPIO support"
depends on ARCH_RENESAS || COMPILE_TEST depends on ARCH_RENESAS || COMPILE_TEST
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
help help
Say yes here to support GPIO on Renesas R-Car SoCs. Say yes here to support GPIO on Renesas R-Car or RZ/G SoCs.
config GPIO_RDA config GPIO_RDA
bool "RDA Micro GPIO controller support" bool "RDA Micro GPIO controller support"
...@@ -595,7 +595,7 @@ config GPIO_TB10X ...@@ -595,7 +595,7 @@ config GPIO_TB10X
select OF_GPIO select OF_GPIO
config GPIO_TEGRA config GPIO_TEGRA
bool "NVIDIA Tegra GPIO support" tristate "NVIDIA Tegra GPIO support"
default ARCH_TEGRA default ARCH_TEGRA
depends on ARCH_TEGRA || COMPILE_TEST depends on ARCH_TEGRA || COMPILE_TEST
depends on OF_GPIO depends on OF_GPIO
...@@ -648,6 +648,16 @@ config GPIO_VF610 ...@@ -648,6 +648,16 @@ config GPIO_VF610
help help
Say yes here to support Vybrid vf610 GPIOs. Say yes here to support Vybrid vf610 GPIOs.
config GPIO_VISCONTI
tristate "Toshiba Visconti GPIO support"
depends on ARCH_VISCONTI || COMPILE_TEST
depends on OF_GPIO
select GPIOLIB_IRQCHIP
select GPIO_GENERIC
select IRQ_DOMAIN_HIERARCHY
help
Say yes here to support GPIO on Tohisba Visconti.
config GPIO_VR41XX config GPIO_VR41XX
tristate "NEC VR4100 series General-purpose I/O Uint support" tristate "NEC VR4100 series General-purpose I/O Uint support"
depends on CPU_VR41XX depends on CPU_VR41XX
...@@ -670,7 +680,7 @@ config GPIO_WCD934X ...@@ -670,7 +680,7 @@ config GPIO_WCD934X
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver" tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver"
depends on MFD_WCD934X && OF_GPIO depends on MFD_WCD934X && OF_GPIO
help help
This driver is to supprot GPIO block found on the Qualcomm Technologies This driver is to support GPIO block found on the Qualcomm Technologies
Inc WCD9340/WCD9341 Audio Codec. Inc WCD9340/WCD9341 Audio Codec.
config GPIO_XGENE config GPIO_XGENE
...@@ -694,6 +704,8 @@ config GPIO_XGENE_SB ...@@ -694,6 +704,8 @@ config GPIO_XGENE_SB
config GPIO_XILINX config GPIO_XILINX
tristate "Xilinx GPIO support" tristate "Xilinx GPIO support"
select GPIOLIB_IRQCHIP
depends on OF_GPIO
help help
Say yes here to support the Xilinx FPGA GPIO device Say yes here to support the Xilinx FPGA GPIO device
...@@ -731,13 +743,6 @@ config GPIO_ZYNQ ...@@ -731,13 +743,6 @@ config GPIO_ZYNQ
help help
Say yes here to support Xilinx Zynq GPIO controller. Say yes here to support Xilinx Zynq GPIO controller.
config GPIO_ZX
bool "ZTE ZX GPIO support"
depends on ARCH_ZX || COMPILE_TEST
select GPIOLIB_IRQCHIP
help
Say yes here to support the GPIO device on ZTE ZX SoCs.
config GPIO_LOONGSON1 config GPIO_LOONGSON1
tristate "Loongson1 GPIO support" tristate "Loongson1 GPIO support"
depends on MACH_LOONGSON32 depends on MACH_LOONGSON32
...@@ -1623,8 +1628,7 @@ config GPIO_MOCKUP ...@@ -1623,8 +1628,7 @@ config GPIO_MOCKUP
select IRQ_SIM select IRQ_SIM
help help
This enables GPIO Testing driver, which provides a way to test GPIO This enables GPIO Testing driver, which provides a way to test GPIO
subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS subsystem through sysfs (or char device) and debugfs.
must be selected for this test.
User could use it through the script in User could use it through the script in
tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in
it. it.
......
...@@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o ...@@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o
obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o
obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
...@@ -163,6 +162,7 @@ obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o ...@@ -163,6 +162,7 @@ obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
...@@ -179,5 +179,4 @@ obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o ...@@ -179,5 +179,4 @@ obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o
obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
...@@ -62,34 +62,6 @@ static char *get_arg(char **args) ...@@ -62,34 +62,6 @@ static char *get_arg(char **args)
return start; return start;
} }
static bool isrange(const char *s)
{
size_t n;
if (IS_ERR_OR_NULL(s))
return false;
while (1) {
n = strspn(s, "0123456789");
if (!n)
return false;
s += n;
switch (*s++) {
case '\0':
return true;
case '-':
case ',':
break;
default:
return false;
}
}
}
static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
int hwnum, unsigned int *n) int hwnum, unsigned int *n)
{ {
...@@ -100,8 +72,7 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, ...@@ -100,8 +72,7 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
if (!lookups) if (!lookups)
return -ENOMEM; return -ENOMEM;
lookups->table[*n] = lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
(struct gpiod_lookup)GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
(*n)++; (*n)++;
memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); memset(&lookups->table[*n], 0, sizeof(lookups->table[*n]));
...@@ -112,10 +83,10 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, ...@@ -112,10 +83,10 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
static int aggr_parse(struct gpio_aggregator *aggr) static int aggr_parse(struct gpio_aggregator *aggr)
{ {
char *name, *offsets, *p;
char *args = aggr->args; char *args = aggr->args;
unsigned long *bitmap; unsigned long *bitmap;
unsigned int i, n = 0; unsigned int i, n = 0;
char *name, *offsets;
int error = 0; int error = 0;
bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL); bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL);
...@@ -130,7 +101,8 @@ static int aggr_parse(struct gpio_aggregator *aggr) ...@@ -130,7 +101,8 @@ static int aggr_parse(struct gpio_aggregator *aggr)
goto free_bitmap; goto free_bitmap;
} }
if (!isrange(offsets)) { p = get_options(offsets, 0, &error);
if (error == 0 || *p) {
/* Named GPIO line */ /* Named GPIO line */
error = aggr_add_gpio(aggr, name, U16_MAX, &n); error = aggr_add_gpio(aggr, name, U16_MAX, &n);
if (error) if (error)
...@@ -271,7 +243,7 @@ static DRIVER_ATTR_WO(delete_device); ...@@ -271,7 +243,7 @@ static DRIVER_ATTR_WO(delete_device);
static struct attribute *gpio_aggregator_attrs[] = { static struct attribute *gpio_aggregator_attrs[] = {
&driver_attr_new_device.attr, &driver_attr_new_device.attr,
&driver_attr_delete_device.attr, &driver_attr_delete_device.attr,
NULL, NULL
}; };
ATTRIBUTE_GROUPS(gpio_aggregator); ATTRIBUTE_GROUPS(gpio_aggregator);
...@@ -545,7 +517,7 @@ static const struct of_device_id gpio_aggregator_dt_ids[] = { ...@@ -545,7 +517,7 @@ static const struct of_device_id gpio_aggregator_dt_ids[] = {
* Add GPIO-operated devices controlled from userspace below, * Add GPIO-operated devices controlled from userspace below,
* or use "driver_override" in sysfs * or use "driver_override" in sysfs
*/ */
{}, {}
}; };
MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids); MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
#endif #endif
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
#define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2) #define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2)
struct bd70528_gpio { struct bd70528_gpio {
struct rohm_regmap_dev chip; struct regmap *regmap;
struct device *dev;
struct gpio_chip gpio; struct gpio_chip gpio;
}; };
...@@ -35,11 +36,11 @@ static int bd70528_set_debounce(struct bd70528_gpio *bdgpio, ...@@ -35,11 +36,11 @@ static int bd70528_set_debounce(struct bd70528_gpio *bdgpio,
val = BD70528_DEBOUNCE_50MS; val = BD70528_DEBOUNCE_50MS;
break; break;
default: default:
dev_err(bdgpio->chip.dev, dev_err(bdgpio->dev,
"Invalid debounce value %u\n", debounce); "Invalid debounce value %u\n", debounce);
return -EINVAL; return -EINVAL;
} }
return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset), return regmap_update_bits(bdgpio->regmap, GPIO_IN_REG(offset),
BD70528_DEBOUNCE_MASK, val); BD70528_DEBOUNCE_MASK, val);
} }
...@@ -49,9 +50,9 @@ static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset) ...@@ -49,9 +50,9 @@ static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset)
int val, ret; int val, ret;
/* Do we need to do something to IRQs here? */ /* Do we need to do something to IRQs here? */
ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset), &val);
if (ret) { if (ret) {
dev_err(bdgpio->chip.dev, "Could not read gpio direction\n"); dev_err(bdgpio->dev, "Could not read gpio direction\n");
return ret; return ret;
} }
if (val & BD70528_GPIO_OUT_EN_MASK) if (val & BD70528_GPIO_OUT_EN_MASK)
...@@ -67,13 +68,13 @@ static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset, ...@@ -67,13 +68,13 @@ static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
switch (pinconf_to_config_param(config)) { switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN: case PIN_CONFIG_DRIVE_OPEN_DRAIN:
return regmap_update_bits(bdgpio->chip.regmap, return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset), GPIO_OUT_REG(offset),
BD70528_GPIO_DRIVE_MASK, BD70528_GPIO_DRIVE_MASK,
BD70528_GPIO_OPEN_DRAIN); BD70528_GPIO_OPEN_DRAIN);
break; break;
case PIN_CONFIG_DRIVE_PUSH_PULL: case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(bdgpio->chip.regmap, return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset), GPIO_OUT_REG(offset),
BD70528_GPIO_DRIVE_MASK, BD70528_GPIO_DRIVE_MASK,
BD70528_GPIO_PUSH_PULL); BD70528_GPIO_PUSH_PULL);
...@@ -93,7 +94,7 @@ static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset) ...@@ -93,7 +94,7 @@ static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset)
struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
/* Do we need to do something to IRQs here? */ /* Do we need to do something to IRQs here? */
return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset),
BD70528_GPIO_OUT_EN_MASK, BD70528_GPIO_OUT_EN_MASK,
BD70528_GPIO_OUT_DISABLE); BD70528_GPIO_OUT_DISABLE);
} }
...@@ -105,10 +106,10 @@ static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset, ...@@ -105,10 +106,10 @@ static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset,
struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO; u8 val = (value) ? BD70528_GPIO_OUT_HI : BD70528_GPIO_OUT_LO;
ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), ret = regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset),
BD70528_GPIO_OUT_MASK, val); BD70528_GPIO_OUT_MASK, val);
if (ret) if (ret)
dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value); dev_err(bdgpio->dev, "Could not set gpio to %d\n", value);
} }
static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset, static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset,
...@@ -117,7 +118,7 @@ static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset, ...@@ -117,7 +118,7 @@ static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset,
struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
bd70528_gpio_set(chip, offset, value); bd70528_gpio_set(chip, offset, value);
return regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), return regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset),
BD70528_GPIO_OUT_EN_MASK, BD70528_GPIO_OUT_EN_MASK,
BD70528_GPIO_OUT_ENABLE); BD70528_GPIO_OUT_ENABLE);
} }
...@@ -129,11 +130,11 @@ static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset) ...@@ -129,11 +130,11 @@ static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset)
int ret; int ret;
unsigned int val; unsigned int val;
ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), &val); ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset), &val);
if (!ret) if (!ret)
ret = !!(val & BD70528_GPIO_OUT_MASK); ret = !!(val & BD70528_GPIO_OUT_MASK);
else else
dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n"); dev_err(bdgpio->dev, "GPIO (out) state read failed\n");
return ret; return ret;
} }
...@@ -143,12 +144,12 @@ static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset) ...@@ -143,12 +144,12 @@ static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset)
unsigned int val; unsigned int val;
int ret; int ret;
ret = regmap_read(bdgpio->chip.regmap, BD70528_REG_GPIO_STATE, &val); ret = regmap_read(bdgpio->regmap, BD70528_REG_GPIO_STATE, &val);
if (!ret) if (!ret)
ret = !(val & GPIO_IN_STATE_MASK(offset)); ret = !(val & GPIO_IN_STATE_MASK(offset));
else else
dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n"); dev_err(bdgpio->dev, "GPIO (in) state read failed\n");
return ret; return ret;
} }
...@@ -173,29 +174,22 @@ static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset) ...@@ -173,29 +174,22 @@ static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset)
else if (ret == GPIO_LINE_DIRECTION_IN) else if (ret == GPIO_LINE_DIRECTION_IN)
ret = bd70528_gpio_get_i(bdgpio, offset); ret = bd70528_gpio_get_i(bdgpio, offset);
else else
dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n"); dev_err(bdgpio->dev, "failed to read GPIO direction\n");
return ret; return ret;
} }
static int bd70528_probe(struct platform_device *pdev) static int bd70528_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct bd70528_gpio *bdgpio; struct bd70528_gpio *bdgpio;
struct rohm_regmap_dev *bd70528;
int ret; int ret;
bd70528 = dev_get_drvdata(pdev->dev.parent); bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL);
if (!bd70528) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
GFP_KERNEL);
if (!bdgpio) if (!bdgpio)
return -ENOMEM; return -ENOMEM;
bdgpio->chip.dev = &pdev->dev; bdgpio->dev = dev;
bdgpio->gpio.parent = pdev->dev.parent; bdgpio->gpio.parent = dev->parent;
bdgpio->gpio.label = "bd70528-gpio"; bdgpio->gpio.label = "bd70528-gpio";
bdgpio->gpio.owner = THIS_MODULE; bdgpio->gpio.owner = THIS_MODULE;
bdgpio->gpio.get_direction = bd70528_get_direction; bdgpio->gpio.get_direction = bd70528_get_direction;
...@@ -208,14 +202,15 @@ static int bd70528_probe(struct platform_device *pdev) ...@@ -208,14 +202,15 @@ static int bd70528_probe(struct platform_device *pdev)
bdgpio->gpio.ngpio = 4; bdgpio->gpio.ngpio = 4;
bdgpio->gpio.base = -1; bdgpio->gpio.base = -1;
#ifdef CONFIG_OF_GPIO #ifdef CONFIG_OF_GPIO
bdgpio->gpio.of_node = pdev->dev.parent->of_node; bdgpio->gpio.of_node = dev->parent->of_node;
#endif #endif
bdgpio->chip.regmap = bd70528->regmap; bdgpio->regmap = dev_get_regmap(dev->parent, NULL);
if (!bdgpio->regmap)
return -ENODEV;
ret = devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio, ret = devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio);
bdgpio);
if (ret) if (ret)
dev_err(&pdev->dev, "gpio_init: Failed to add bd70528-gpio\n"); dev_err(dev, "gpio_init: Failed to add bd70528-gpio\n");
return ret; return ret;
} }
......
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
#define HALL_GPIO_OFFSET 3 #define HALL_GPIO_OFFSET 3
struct bd71828_gpio { struct bd71828_gpio {
struct rohm_regmap_dev chip; struct regmap *regmap;
struct device *dev;
struct gpio_chip gpio; struct gpio_chip gpio;
}; };
...@@ -29,10 +30,10 @@ static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset, ...@@ -29,10 +30,10 @@ static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset,
if (offset == HALL_GPIO_OFFSET) if (offset == HALL_GPIO_OFFSET)
return; return;
ret = regmap_update_bits(bdgpio->chip.regmap, GPIO_OUT_REG(offset), ret = regmap_update_bits(bdgpio->regmap, GPIO_OUT_REG(offset),
BD71828_GPIO_OUT_MASK, val); BD71828_GPIO_OUT_MASK, val);
if (ret) if (ret)
dev_err(bdgpio->chip.dev, "Could not set gpio to %d\n", value); dev_err(bdgpio->dev, "Could not set gpio to %d\n", value);
} }
static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset) static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset)
...@@ -42,10 +43,10 @@ static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset) ...@@ -42,10 +43,10 @@ static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset)
struct bd71828_gpio *bdgpio = gpiochip_get_data(chip); struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
if (offset == HALL_GPIO_OFFSET) if (offset == HALL_GPIO_OFFSET)
ret = regmap_read(bdgpio->chip.regmap, BD71828_REG_IO_STAT, ret = regmap_read(bdgpio->regmap, BD71828_REG_IO_STAT,
&val); &val);
else else
ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset), ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset),
&val); &val);
if (!ret) if (!ret)
ret = (val & BD71828_GPIO_OUT_MASK); ret = (val & BD71828_GPIO_OUT_MASK);
...@@ -63,12 +64,12 @@ static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset, ...@@ -63,12 +64,12 @@ static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
switch (pinconf_to_config_param(config)) { switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN: case PIN_CONFIG_DRIVE_OPEN_DRAIN:
return regmap_update_bits(bdgpio->chip.regmap, return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset), GPIO_OUT_REG(offset),
BD71828_GPIO_DRIVE_MASK, BD71828_GPIO_DRIVE_MASK,
BD71828_GPIO_OPEN_DRAIN); BD71828_GPIO_OPEN_DRAIN);
case PIN_CONFIG_DRIVE_PUSH_PULL: case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(bdgpio->chip.regmap, return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset), GPIO_OUT_REG(offset),
BD71828_GPIO_DRIVE_MASK, BD71828_GPIO_DRIVE_MASK,
BD71828_GPIO_PUSH_PULL); BD71828_GPIO_PUSH_PULL);
...@@ -96,22 +97,15 @@ static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset) ...@@ -96,22 +97,15 @@ static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset)
static int bd71828_probe(struct platform_device *pdev) static int bd71828_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct bd71828_gpio *bdgpio; struct bd71828_gpio *bdgpio;
struct rohm_regmap_dev *bd71828;
bd71828 = dev_get_drvdata(pdev->dev.parent); bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL);
if (!bd71828) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
GFP_KERNEL);
if (!bdgpio) if (!bdgpio)
return -ENOMEM; return -ENOMEM;
bdgpio->chip.dev = &pdev->dev; bdgpio->dev = dev;
bdgpio->gpio.parent = pdev->dev.parent; bdgpio->gpio.parent = dev->parent;
bdgpio->gpio.label = "bd71828-gpio"; bdgpio->gpio.label = "bd71828-gpio";
bdgpio->gpio.owner = THIS_MODULE; bdgpio->gpio.owner = THIS_MODULE;
bdgpio->gpio.get_direction = bd71828_get_direction; bdgpio->gpio.get_direction = bd71828_get_direction;
...@@ -127,11 +121,12 @@ static int bd71828_probe(struct platform_device *pdev) ...@@ -127,11 +121,12 @@ static int bd71828_probe(struct platform_device *pdev)
* "gpio-reserved-ranges" and exclude them from control * "gpio-reserved-ranges" and exclude them from control
*/ */
bdgpio->gpio.ngpio = 4; bdgpio->gpio.ngpio = 4;
bdgpio->gpio.of_node = pdev->dev.parent->of_node; bdgpio->gpio.of_node = dev->parent->of_node;
bdgpio->chip.regmap = bd71828->regmap; bdgpio->regmap = dev_get_regmap(dev->parent, NULL);
if (!bdgpio->regmap)
return -ENODEV;
return devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio, return devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio);
bdgpio);
} }
static struct platform_driver bd71828_gpio = { static struct platform_driver bd71828_gpio = {
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
/* Maximum value for irq capable line identifiers */ /* Maximum value for irq capable line identifiers */
#define EP93XX_GPIO_LINE_MAX_IRQ 23 #define EP93XX_GPIO_LINE_MAX_IRQ 23
#define EP93XX_GPIO_A_IRQ_BASE 64
#define EP93XX_GPIO_B_IRQ_BASE 72
/* /*
* Static mapping of GPIO bank F IRQS: * Static mapping of GPIO bank F IRQS:
* F0..F7 (16..24) to irq 80..87. * F0..F7 (16..24) to irq 80..87.
...@@ -292,14 +294,14 @@ struct ep93xx_gpio_bank { ...@@ -292,14 +294,14 @@ struct ep93xx_gpio_bank {
static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
/* Bank A has 8 IRQs */ /* Bank A has 8 IRQs */
EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, 64), EP93XX_GPIO_BANK("A", 0x00, 0x10, 0x90, 0, true, false, EP93XX_GPIO_A_IRQ_BASE),
/* Bank B has 8 IRQs */ /* Bank B has 8 IRQs */
EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, 72), EP93XX_GPIO_BANK("B", 0x04, 0x14, 0xac, 8, true, false, EP93XX_GPIO_B_IRQ_BASE),
EP93XX_GPIO_BANK("C", 0x08, 0x18, 0x00, 40, false, false, 0), EP93XX_GPIO_BANK("C", 0x08, 0x18, 0x00, 40, false, false, 0),
EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 0x00, 24, false, false, 0), EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 0x00, 24, false, false, 0),
EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0), EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0),
/* Bank F has 8 IRQs */ /* Bank F has 8 IRQs */
EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, 0), EP93XX_GPIO_BANK("F", 0x30, 0x34, 0x4c, 16, false, true, EP93XX_GPIO_F_IRQ_BASE),
EP93XX_GPIO_BANK("G", 0x38, 0x3c, 0x00, 48, false, false, 0), EP93XX_GPIO_BANK("G", 0x38, 0x3c, 0x00, 48, false, false, 0),
EP93XX_GPIO_BANK("H", 0x40, 0x44, 0x00, 56, false, false, 0), EP93XX_GPIO_BANK("H", 0x40, 0x44, 0x00, 56, false, false, 0),
}; };
...@@ -318,11 +320,6 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, ...@@ -318,11 +320,6 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
return 0; return 0;
} }
static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset)
{
return EP93XX_GPIO_F_IRQ_BASE + offset;
}
static void ep93xx_init_irq_chip(struct device *dev, struct irq_chip *ic) static void ep93xx_init_irq_chip(struct device *dev, struct irq_chip *ic)
{ {
ic->irq_ack = ep93xx_gpio_irq_ack; ic->irq_ack = ep93xx_gpio_irq_ack;
...@@ -375,7 +372,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, ...@@ -375,7 +372,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
girq->parent_handler = ep93xx_gpio_ab_irq_handler; girq->parent_handler = ep93xx_gpio_ab_irq_handler;
girq->num_parents = 1; girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1, girq->parents = devm_kcalloc(dev, girq->num_parents,
sizeof(*girq->parents), sizeof(*girq->parents),
GFP_KERNEL); GFP_KERNEL);
if (!girq->parents) if (!girq->parents)
...@@ -393,20 +390,19 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, ...@@ -393,20 +390,19 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
/* /*
* FIXME: convert this to use hierarchical IRQ support! * FIXME: convert this to use hierarchical IRQ support!
* this requires fixing the root irqchip to be hierarchial. * this requires fixing the root irqchip to be hierarchical.
*/ */
girq->parent_handler = ep93xx_gpio_f_irq_handler; girq->parent_handler = ep93xx_gpio_f_irq_handler;
girq->num_parents = 8; girq->num_parents = 8;
girq->parents = devm_kcalloc(dev, 8, girq->parents = devm_kcalloc(dev, girq->num_parents,
sizeof(*girq->parents), sizeof(*girq->parents),
GFP_KERNEL); GFP_KERNEL);
if (!girq->parents) if (!girq->parents)
return -ENOMEM; return -ENOMEM;
/* Pick resources 1..8 for these IRQs */ /* Pick resources 1..8 for these IRQs */
for (i = 1; i <= 8; i++) for (i = 0; i < girq->num_parents; i++) {
girq->parents[i - 1] = platform_get_irq(pdev, i); girq->parents[i] = platform_get_irq(pdev, i + 1);
for (i = 0; i < 8; i++) { gpio_irq = bank->irq_base + i;
gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i;
irq_set_chip_data(gpio_irq, &epg->gc[5]); irq_set_chip_data(gpio_irq, &epg->gc[5]);
irq_set_chip_and_handler(gpio_irq, irq_set_chip_and_handler(gpio_irq,
girq->chip, girq->chip,
...@@ -415,7 +411,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc, ...@@ -415,7 +411,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
} }
girq->default_type = IRQ_TYPE_NONE; girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq; girq->handler = handle_level_irq;
gc->to_irq = ep93xx_gpio_f_to_irq; girq->first = bank->irq_base;
} }
return devm_gpiochip_add_data(dev, gc, epg); return devm_gpiochip_add_data(dev, gc, epg);
......
...@@ -325,7 +325,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) ...@@ -325,7 +325,7 @@ static int max77620_gpio_probe(struct platform_device *pdev)
girq->parents = NULL; girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE; girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_edge_irq; girq->handler = handle_edge_irq;
girq->init_hw = max77620_gpio_irq_init_hw, girq->init_hw = max77620_gpio_irq_init_hw;
girq->threaded = true; girq->threaded = true;
platform_set_drvdata(pdev, mgpio); platform_set_drvdata(pdev, mgpio);
......
...@@ -194,6 +194,11 @@ static int mrfld_gpio_set_config(struct gpio_chip *chip, unsigned int offset, ...@@ -194,6 +194,11 @@ static int mrfld_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
{ {
u32 debounce; u32 debounce;
if ((pinconf_to_config_param(config) == PIN_CONFIG_BIAS_DISABLE) ||
(pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) ||
(pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_DOWN))
return gpiochip_generic_config(chip, offset, config);
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP; return -ENOTSUPP;
......
...@@ -70,7 +70,12 @@ ...@@ -70,7 +70,12 @@
*/ */
#define PWM_BLINK_ON_DURATION_OFF 0x0 #define PWM_BLINK_ON_DURATION_OFF 0x0
#define PWM_BLINK_OFF_DURATION_OFF 0x4 #define PWM_BLINK_OFF_DURATION_OFF 0x4
#define PWM_BLINK_COUNTER_B_OFF 0x8
/* Armada 8k variant gpios register offsets */
#define AP80X_GPIO0_OFF_A8K 0x1040
#define CP11X_GPIO0_OFF_A8K 0x100
#define CP11X_GPIO1_OFF_A8K 0x140
/* The MV78200 has per-CPU registers for edge mask and level mask */ /* The MV78200 has per-CPU registers for edge mask and level mask */
#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18) #define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
...@@ -93,6 +98,7 @@ ...@@ -93,6 +98,7 @@
struct mvebu_pwm { struct mvebu_pwm {
struct regmap *regs; struct regmap *regs;
u32 offset;
unsigned long clk_rate; unsigned long clk_rate;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
struct pwm_chip chip; struct pwm_chip chip;
...@@ -283,12 +289,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) ...@@ -283,12 +289,12 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val)
*/ */
static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm)
{ {
return PWM_BLINK_ON_DURATION_OFF; return mvpwm->offset + PWM_BLINK_ON_DURATION_OFF;
} }
static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm)
{ {
return PWM_BLINK_OFF_DURATION_OFF; return mvpwm->offset + PWM_BLINK_OFF_DURATION_OFF;
} }
/* /*
...@@ -667,26 +673,21 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, ...@@ -667,26 +673,21 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
spin_lock_irqsave(&mvpwm->lock, flags); spin_lock_irqsave(&mvpwm->lock, flags);
regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u); regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u);
val = (unsigned long long) u * NSEC_PER_SEC; /* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */
do_div(val, mvpwm->clk_rate); if (u > 0)
if (val > UINT_MAX) val = u;
state->duty_cycle = UINT_MAX;
else if (val)
state->duty_cycle = val;
else else
state->duty_cycle = 1; val = UINT_MAX + 1ULL;
state->duty_cycle = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC,
mvpwm->clk_rate);
val = (unsigned long long) u; /* on duration */
regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u); regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u);
val += (unsigned long long) u; /* period = on + off duration */ /* period = on + off duration */
val *= NSEC_PER_SEC; if (u > 0)
do_div(val, mvpwm->clk_rate); val += u;
if (val > UINT_MAX)
state->period = UINT_MAX;
else if (val)
state->period = val;
else else
state->period = 1; val += UINT_MAX + 1ULL;
state->period = DIV_ROUND_UP_ULL(val * NSEC_PER_SEC, mvpwm->clk_rate);
regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u); regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
if (u) if (u)
...@@ -708,19 +709,27 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -708,19 +709,27 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle; val = (unsigned long long) mvpwm->clk_rate * state->duty_cycle;
do_div(val, NSEC_PER_SEC); do_div(val, NSEC_PER_SEC);
if (val > UINT_MAX) if (val > UINT_MAX + 1ULL)
return -EINVAL; return -EINVAL;
if (val) /*
* Zero on/off values don't work as expected. Experimentation shows
* that zero value is treated as 2^32. This behavior is not documented.
*/
if (val == UINT_MAX + 1ULL)
on = 0;
else if (val)
on = val; on = val;
else else
on = 1; on = 1;
val = (unsigned long long) mvpwm->clk_rate * val = (unsigned long long) mvpwm->clk_rate * state->period;
(state->period - state->duty_cycle);
do_div(val, NSEC_PER_SEC); do_div(val, NSEC_PER_SEC);
if (val > UINT_MAX) val -= on;
if (val > UINT_MAX + 1ULL)
return -EINVAL; return -EINVAL;
if (val) if (val == UINT_MAX + 1ULL)
off = 0;
else if (val)
off = val; off = val;
else else
off = 1; off = 1;
...@@ -778,12 +787,11 @@ static int mvebu_pwm_probe(struct platform_device *pdev, ...@@ -778,12 +787,11 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct mvebu_pwm *mvpwm; struct mvebu_pwm *mvpwm;
void __iomem *base; void __iomem *base;
u32 offset;
u32 set; u32 set;
if (!of_device_is_compatible(mvchip->chip.of_node, if (of_device_is_compatible(mvchip->chip.of_node,
"marvell,armada-370-gpio")) "marvell,armada-370-gpio")) {
return 0;
/* /*
* There are only two sets of PWM configuration registers for * There are only two sets of PWM configuration registers for
* all the GPIO lines on those SoCs which this driver reserves * all the GPIO lines on those SoCs which this driver reserves
...@@ -792,29 +800,44 @@ static int mvebu_pwm_probe(struct platform_device *pdev, ...@@ -792,29 +800,44 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
*/ */
if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm")) if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"))
return 0; return 0;
offset = 0;
} else if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
int ret = of_property_read_u32(dev->of_node,
"marvell,pwm-offset", &offset);
if (ret < 0)
return 0;
} else {
return 0;
}
if (IS_ERR(mvchip->clk)) if (IS_ERR(mvchip->clk))
return PTR_ERR(mvchip->clk); return PTR_ERR(mvchip->clk);
/*
* Use set A for lines of GPIO chip with id 0, B for GPIO chip
* with id 1. Don't allow further GPIO chips to be used for PWM.
*/
if (id == 0)
set = 0;
else if (id == 1)
set = U32_MAX;
else
return -EINVAL;
regmap_write(mvchip->regs,
GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL);
if (!mvpwm) if (!mvpwm)
return -ENOMEM; return -ENOMEM;
mvchip->mvpwm = mvpwm; mvchip->mvpwm = mvpwm;
mvpwm->mvchip = mvchip; mvpwm->mvchip = mvchip;
mvpwm->offset = offset;
if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
mvpwm->regs = mvchip->regs;
switch (mvchip->offset) {
case AP80X_GPIO0_OFF_A8K:
case CP11X_GPIO0_OFF_A8K:
/* Blink counter A */
set = 0;
break;
case CP11X_GPIO1_OFF_A8K:
/* Blink counter B */
set = U32_MAX;
mvpwm->offset += PWM_BLINK_COUNTER_B_OFF;
break;
default:
return -EINVAL;
}
} else {
base = devm_platform_ioremap_resource_byname(pdev, "pwm"); base = devm_platform_ioremap_resource_byname(pdev, "pwm");
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_ERR(base); return PTR_ERR(base);
...@@ -824,6 +847,21 @@ static int mvebu_pwm_probe(struct platform_device *pdev, ...@@ -824,6 +847,21 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
if (IS_ERR(mvpwm->regs)) if (IS_ERR(mvpwm->regs))
return PTR_ERR(mvpwm->regs); return PTR_ERR(mvpwm->regs);
/*
* Use set A for lines of GPIO chip with id 0, B for GPIO chip
* with id 1. Don't allow further GPIO chips to be used for PWM.
*/
if (id == 0)
set = 0;
else if (id == 1)
set = U32_MAX;
else
return -EINVAL;
}
regmap_write(mvchip->regs,
GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, set);
mvpwm->clk_rate = clk_get_rate(mvchip->clk); mvpwm->clk_rate = clk_get_rate(mvchip->clk);
if (!mvpwm->clk_rate) { if (!mvpwm->clk_rate) {
dev_err(dev, "failed to get clock rate\n"); dev_err(dev, "failed to get clock rate\n");
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
static const struct i2c_device_id pca953x_id[] = { static const struct i2c_device_id pca953x_id[] = {
{ "pca6416", 16 | PCA953X_TYPE | PCA_INT, }, { "pca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9505", 40 | PCA953X_TYPE | PCA_INT, }, { "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9506", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9534", 8 | PCA953X_TYPE | PCA_INT, }, { "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, }, { "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9536", 4 | PCA953X_TYPE, }, { "pca9536", 4 | PCA953X_TYPE, },
...@@ -1236,6 +1237,7 @@ static int pca953x_resume(struct device *dev) ...@@ -1236,6 +1237,7 @@ static int pca953x_resume(struct device *dev)
static const struct of_device_id pca953x_dt_ids[] = { static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), }, { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), },
{ .compatible = "nxp,pca9506", .data = OF_953X(40, PCA_INT), },
{ .compatible = "nxp,pca9534", .data = OF_953X( 8, PCA_INT), }, { .compatible = "nxp,pca9534", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), }, { .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), },
......
...@@ -332,7 +332,7 @@ static int pcf857x_probe(struct i2c_client *client, ...@@ -332,7 +332,7 @@ static int pcf857x_probe(struct i2c_client *client,
* reset state. Otherwise it flags pins to be driven low. * reset state. Otherwise it flags pins to be driven low.
*/ */
gpio->out = ~n_latch; gpio->out = ~n_latch;
gpio->status = gpio->out; gpio->status = gpio->read(gpio->client);
/* Enable irqchip if we have an interrupt */ /* Enable irqchip if we have an interrupt */
if (client->irq) { if (client->irq) {
......
...@@ -35,6 +35,8 @@ struct gpio_rcar_bank_info { ...@@ -35,6 +35,8 @@ struct gpio_rcar_bank_info {
struct gpio_rcar_info { struct gpio_rcar_info {
bool has_outdtsel; bool has_outdtsel;
bool has_both_edge_trigger; bool has_both_edge_trigger;
bool has_always_in;
bool has_inen;
}; };
struct gpio_rcar_priv { struct gpio_rcar_priv {
...@@ -62,6 +64,7 @@ struct gpio_rcar_priv { ...@@ -62,6 +64,7 @@ struct gpio_rcar_priv {
#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ #define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
#define OUTDTSEL 0x40 /* Output Data Select Register */ #define OUTDTSEL 0x40 /* Output Data Select Register */
#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ #define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
#define INEN 0x50 /* General Input Enable Register */
#define RCAR_MAX_GPIO_PER_BANK 32 #define RCAR_MAX_GPIO_PER_BANK 32
...@@ -302,9 +305,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) ...@@ -302,9 +305,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
struct gpio_rcar_priv *p = gpiochip_get_data(chip); struct gpio_rcar_priv *p = gpiochip_get_data(chip);
u32 bit = BIT(offset); u32 bit = BIT(offset);
/* testing on r8a7790 shows that INDT does not show correct pin state /*
* when configured as output, so use OUTDT in case of output pins */ * Before R-Car Gen3, INDT does not show correct pin state when
if (gpio_rcar_read(p, INOUTSEL) & bit) * configured as output, so use OUTDT in case of output pins
*/
if (!p->info.has_always_in && (gpio_rcar_read(p, INOUTSEL) & bit))
return !!(gpio_rcar_read(p, OUTDT) & bit); return !!(gpio_rcar_read(p, OUTDT) & bit);
else else
return !!(gpio_rcar_read(p, INDT) & bit); return !!(gpio_rcar_read(p, INDT) & bit);
...@@ -324,6 +329,11 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, ...@@ -324,6 +329,11 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask,
if (!bankmask) if (!bankmask)
return 0; return 0;
if (p->info.has_always_in) {
bits[0] = gpio_rcar_read(p, INDT) & bankmask;
return 0;
}
spin_lock_irqsave(&p->lock, flags); spin_lock_irqsave(&p->lock, flags);
outputs = gpio_rcar_read(p, INOUTSEL); outputs = gpio_rcar_read(p, INOUTSEL);
m = outputs & bankmask; m = outputs & bankmask;
...@@ -383,41 +393,35 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, ...@@ -383,41 +393,35 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset,
static const struct gpio_rcar_info gpio_rcar_info_gen1 = { static const struct gpio_rcar_info gpio_rcar_info_gen1 = {
.has_outdtsel = false, .has_outdtsel = false,
.has_both_edge_trigger = false, .has_both_edge_trigger = false,
.has_always_in = false,
.has_inen = false,
}; };
static const struct gpio_rcar_info gpio_rcar_info_gen2 = { static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
.has_outdtsel = true, .has_outdtsel = true,
.has_both_edge_trigger = true, .has_both_edge_trigger = true,
.has_always_in = false,
.has_inen = false,
};
static const struct gpio_rcar_info gpio_rcar_info_gen3 = {
.has_outdtsel = true,
.has_both_edge_trigger = true,
.has_always_in = true,
.has_inen = false,
};
static const struct gpio_rcar_info gpio_rcar_info_v3u = {
.has_outdtsel = true,
.has_both_edge_trigger = true,
.has_always_in = true,
.has_inen = true,
}; };
static const struct of_device_id gpio_rcar_of_table[] = { static const struct of_device_id gpio_rcar_of_table[] = {
{ {
.compatible = "renesas,gpio-r8a7743", .compatible = "renesas,gpio-r8a779a0",
/* RZ/G1 GPIO is identical to R-Car Gen2. */ .data = &gpio_rcar_info_v3u,
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7790",
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7791",
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7792",
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7793",
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7794",
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7795",
/* Gen3 GPIO is identical to Gen2. */
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7796",
/* Gen3 GPIO is identical to Gen2. */
.data = &gpio_rcar_info_gen2,
}, { }, {
.compatible = "renesas,rcar-gen1-gpio", .compatible = "renesas,rcar-gen1-gpio",
.data = &gpio_rcar_info_gen1, .data = &gpio_rcar_info_gen1,
...@@ -426,8 +430,7 @@ static const struct of_device_id gpio_rcar_of_table[] = { ...@@ -426,8 +430,7 @@ static const struct of_device_id gpio_rcar_of_table[] = {
.data = &gpio_rcar_info_gen2, .data = &gpio_rcar_info_gen2,
}, { }, {
.compatible = "renesas,rcar-gen3-gpio", .compatible = "renesas,rcar-gen3-gpio",
/* Gen3 GPIO is identical to Gen2. */ .data = &gpio_rcar_info_gen3,
.data = &gpio_rcar_info_gen2,
}, { }, {
.compatible = "renesas,gpio-rcar", .compatible = "renesas,gpio-rcar",
.data = &gpio_rcar_info_gen1, .data = &gpio_rcar_info_gen1,
...@@ -460,6 +463,17 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) ...@@ -460,6 +463,17 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
return 0; return 0;
} }
static void gpio_rcar_enable_inputs(struct gpio_rcar_priv *p)
{
u32 mask = GENMASK(p->gpio_chip.ngpio - 1, 0);
/* Select "Input Enable" in INEN */
if (p->gpio_chip.valid_mask)
mask &= p->gpio_chip.valid_mask[0];
if (mask)
gpio_rcar_write(p, INEN, gpio_rcar_read(p, INEN) | mask);
}
static int gpio_rcar_probe(struct platform_device *pdev) static int gpio_rcar_probe(struct platform_device *pdev)
{ {
struct gpio_rcar_priv *p; struct gpio_rcar_priv *p;
...@@ -549,6 +563,12 @@ static int gpio_rcar_probe(struct platform_device *pdev) ...@@ -549,6 +563,12 @@ static int gpio_rcar_probe(struct platform_device *pdev)
goto err1; goto err1;
} }
if (p->info.has_inen) {
pm_runtime_get_sync(p->dev);
gpio_rcar_enable_inputs(p);
pm_runtime_put(p->dev);
}
dev_info(dev, "driving %d GPIOs\n", npins); dev_info(dev, "driving %d GPIOs\n", npins);
return 0; return 0;
...@@ -624,6 +644,9 @@ static int gpio_rcar_resume(struct device *dev) ...@@ -624,6 +644,9 @@ static int gpio_rcar_resume(struct device *dev)
} }
} }
if (p->info.has_inen)
gpio_rcar_enable_inputs(p);
return 0; return 0;
} }
#endif /* CONFIG_PM_SLEEP*/ #endif /* CONFIG_PM_SLEEP*/
......
...@@ -65,13 +65,13 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev, ...@@ -65,13 +65,13 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
if (!irq_chip) if (!irq_chip)
return -ENOMEM; return -ENOMEM;
irq_chip->name = "sl28cpld-gpio-irq", irq_chip->name = "sl28cpld-gpio-irq";
irq_chip->irqs = sl28cpld_gpio_irqs; irq_chip->irqs = sl28cpld_gpio_irqs;
irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs); irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
irq_chip->num_regs = 1; irq_chip->num_regs = 1;
irq_chip->status_base = base + GPIO_REG_IP; irq_chip->status_base = base + GPIO_REG_IP;
irq_chip->mask_base = base + GPIO_REG_IE; irq_chip->mask_base = base + GPIO_REG_IE;
irq_chip->mask_invert = true, irq_chip->mask_invert = true;
irq_chip->ack_base = base + GPIO_REG_IP; irq_chip->ack_base = base + GPIO_REG_IP;
ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev), ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
......
...@@ -60,7 +60,6 @@ struct tegra_gpio_info; ...@@ -60,7 +60,6 @@ struct tegra_gpio_info;
struct tegra_gpio_bank { struct tegra_gpio_bank {
unsigned int bank; unsigned int bank;
unsigned int irq;
/* /*
* IRQ-core code uses raw locking, and thus, nested locking also * IRQ-core code uses raw locking, and thus, nested locking also
...@@ -81,7 +80,6 @@ struct tegra_gpio_bank { ...@@ -81,7 +80,6 @@ struct tegra_gpio_bank {
u32 dbc_enb[4]; u32 dbc_enb[4];
#endif #endif
u32 dbc_cnt[4]; u32 dbc_cnt[4];
struct tegra_gpio_info *tgi;
}; };
struct tegra_gpio_soc_config { struct tegra_gpio_soc_config {
...@@ -93,12 +91,12 @@ struct tegra_gpio_soc_config { ...@@ -93,12 +91,12 @@ struct tegra_gpio_soc_config {
struct tegra_gpio_info { struct tegra_gpio_info {
struct device *dev; struct device *dev;
void __iomem *regs; void __iomem *regs;
struct irq_domain *irq_domain;
struct tegra_gpio_bank *bank_info; struct tegra_gpio_bank *bank_info;
const struct tegra_gpio_soc_config *soc; const struct tegra_gpio_soc_config *soc;
struct gpio_chip gc; struct gpio_chip gc;
struct irq_chip ic; struct irq_chip ic;
u32 bank_count; u32 bank_count;
unsigned int *irqs;
}; };
static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi,
...@@ -274,17 +272,10 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, ...@@ -274,17 +272,10 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
return tegra_gpio_set_debounce(chip, offset, debounce); return tegra_gpio_set_debounce(chip, offset, debounce);
} }
static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
return irq_find_mapping(tgi->irq_domain, offset);
}
static void tegra_gpio_irq_ack(struct irq_data *d) static void tegra_gpio_irq_ack(struct irq_data *d)
{ {
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi; struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq; unsigned int gpio = d->hwirq;
tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio)); tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio));
...@@ -292,8 +283,8 @@ static void tegra_gpio_irq_ack(struct irq_data *d) ...@@ -292,8 +283,8 @@ static void tegra_gpio_irq_ack(struct irq_data *d)
static void tegra_gpio_irq_mask(struct irq_data *d) static void tegra_gpio_irq_mask(struct irq_data *d)
{ {
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi; struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq; unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0); tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0);
...@@ -301,8 +292,8 @@ static void tegra_gpio_irq_mask(struct irq_data *d) ...@@ -301,8 +292,8 @@ static void tegra_gpio_irq_mask(struct irq_data *d)
static void tegra_gpio_irq_unmask(struct irq_data *d) static void tegra_gpio_irq_unmask(struct irq_data *d)
{ {
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi; struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq; unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1); tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1);
...@@ -311,11 +302,14 @@ static void tegra_gpio_irq_unmask(struct irq_data *d) ...@@ -311,11 +302,14 @@ static void tegra_gpio_irq_unmask(struct irq_data *d)
static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{ {
unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type; unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type;
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi; struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
struct tegra_gpio_bank *bank;
unsigned long flags; unsigned long flags;
u32 val;
int ret; int ret;
u32 val;
bank = &tgi->bank_info[GPIO_BANK(d->hwirq)];
switch (type & IRQ_TYPE_SENSE_MASK) { switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_RISING:
...@@ -367,13 +361,16 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) ...@@ -367,13 +361,16 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
irq_set_handler_locked(d, handle_edge_irq); irq_set_handler_locked(d, handle_edge_irq);
return 0; if (d->parent_data)
ret = irq_chip_set_type_parent(d, type);
return ret;
} }
static void tegra_gpio_irq_shutdown(struct irq_data *d) static void tegra_gpio_irq_shutdown(struct irq_data *d)
{ {
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi; struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq; unsigned int gpio = d->hwirq;
tegra_gpio_irq_mask(d); tegra_gpio_irq_mask(d);
...@@ -382,13 +379,25 @@ static void tegra_gpio_irq_shutdown(struct irq_data *d) ...@@ -382,13 +379,25 @@ static void tegra_gpio_irq_shutdown(struct irq_data *d)
static void tegra_gpio_irq_handler(struct irq_desc *desc) static void tegra_gpio_irq_handler(struct irq_desc *desc)
{ {
unsigned int port, pin, gpio; struct tegra_gpio_info *tgi = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
struct irq_domain *domain = tgi->gc.irq.domain;
unsigned int irq = irq_desc_get_irq(desc);
struct tegra_gpio_bank *bank = NULL;
unsigned int port, pin, gpio, i;
bool unmasked = false; bool unmasked = false;
u32 lvl;
unsigned long sta; unsigned long sta;
struct irq_chip *chip = irq_desc_get_chip(desc); u32 lvl;
struct tegra_gpio_bank *bank = irq_desc_get_handler_data(desc);
struct tegra_gpio_info *tgi = bank->tgi; for (i = 0; i < tgi->bank_count; i++) {
if (tgi->irqs[i] == irq) {
bank = &tgi->bank_info[i];
break;
}
}
if (WARN_ON(bank == NULL))
return;
chained_irq_enter(chip, desc); chained_irq_enter(chip, desc);
...@@ -411,14 +420,47 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) ...@@ -411,14 +420,47 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc); chained_irq_exit(chip, desc);
} }
generic_handle_irq(irq_find_mapping(tgi->irq_domain, irq = irq_find_mapping(domain, gpio + pin);
gpio + pin)); if (WARN_ON(irq == 0))
continue;
generic_handle_irq(irq);
} }
} }
if (!unmasked) if (!unmasked)
chained_irq_exit(chip, desc); chained_irq_exit(chip, desc);
}
static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
unsigned int hwirq,
unsigned int type,
unsigned int *parent_hwirq,
unsigned int *parent_type)
{
*parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq);
*parent_type = type;
return 0;
}
static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
unsigned int parent_hwirq,
unsigned int parent_type)
{
struct irq_fwspec *fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3;
fwspec->param[0] = 0;
fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type;
return fwspec;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -497,19 +539,31 @@ static int tegra_gpio_suspend(struct device *dev) ...@@ -497,19 +539,31 @@ static int tegra_gpio_suspend(struct device *dev)
static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
{ {
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
struct tegra_gpio_bank *bank;
unsigned int gpio = d->hwirq; unsigned int gpio = d->hwirq;
u32 port, bit, mask; u32 port, bit, mask;
int err; int err;
err = irq_set_irq_wake(bank->irq, enable); bank = &tgi->bank_info[GPIO_BANK(d->hwirq)];
if (err)
return err;
port = GPIO_PORT(gpio); port = GPIO_PORT(gpio);
bit = GPIO_BIT(gpio); bit = GPIO_BIT(gpio);
mask = BIT(bit); mask = BIT(bit);
err = irq_set_irq_wake(tgi->irqs[bank->bank], enable);
if (err)
return err;
if (d->parent_data) {
err = irq_chip_set_wake_parent(d, enable);
if (err) {
irq_set_irq_wake(tgi->irqs[bank->bank], !enable);
return err;
}
}
if (enable) if (enable)
bank->wake_enb[port] |= mask; bank->wake_enb[port] |= mask;
else else
...@@ -519,6 +573,35 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) ...@@ -519,6 +573,35 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
} }
#endif #endif
static int tegra_gpio_irq_set_affinity(struct irq_data *data,
const struct cpumask *dest,
bool force)
{
if (data->parent_data)
return irq_chip_set_affinity_parent(data, dest, force);
return -EINVAL;
}
static int tegra_gpio_irq_request_resources(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
tegra_gpio_enable(tgi, d->hwirq);
return gpiochip_reqres_irq(chip, d->hwirq);
}
static void tegra_gpio_irq_release_resources(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
gpiochip_relres_irq(chip, d->hwirq);
tegra_gpio_enable(tgi, d->hwirq);
}
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h> #include <linux/debugfs.h>
...@@ -526,7 +609,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) ...@@ -526,7 +609,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
{ {
struct tegra_gpio_info *tgi = s->private; struct tegra_gpio_info *tgi = dev_get_drvdata(s->private);
unsigned int i, j; unsigned int i, j;
for (i = 0; i < tgi->bank_count; i++) { for (i = 0; i < tgi->bank_count; i++) {
...@@ -548,12 +631,10 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused) ...@@ -548,12 +631,10 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
return 0; return 0;
} }
DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio);
static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
{ {
debugfs_create_file("tegra_gpio", 0444, NULL, tgi, debugfs_create_devm_seqfile(tgi->dev, "tegra_gpio", NULL,
&tegra_dbg_gpio_fops); tegra_dbg_gpio_show);
} }
#else #else
...@@ -568,14 +649,18 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = { ...@@ -568,14 +649,18 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
}; };
static struct lock_class_key gpio_lock_class; static const struct of_device_id tegra_pmc_of_match[] = {
static struct lock_class_key gpio_request_class; { .compatible = "nvidia,tegra210-pmc", },
{ /* sentinel */ },
};
static int tegra_gpio_probe(struct platform_device *pdev) static int tegra_gpio_probe(struct platform_device *pdev)
{ {
struct tegra_gpio_info *tgi;
struct tegra_gpio_bank *bank; struct tegra_gpio_bank *bank;
unsigned int gpio, i, j; struct tegra_gpio_info *tgi;
struct gpio_irq_chip *irq;
struct device_node *np;
unsigned int i, j;
int ret; int ret;
tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL);
...@@ -604,7 +689,6 @@ static int tegra_gpio_probe(struct platform_device *pdev) ...@@ -604,7 +689,6 @@ static int tegra_gpio_probe(struct platform_device *pdev)
tgi->gc.direction_output = tegra_gpio_direction_output; tgi->gc.direction_output = tegra_gpio_direction_output;
tgi->gc.set = tegra_gpio_set; tgi->gc.set = tegra_gpio_set;
tgi->gc.get_direction = tegra_gpio_get_direction; tgi->gc.get_direction = tegra_gpio_get_direction;
tgi->gc.to_irq = tegra_gpio_to_irq;
tgi->gc.base = 0; tgi->gc.base = 0;
tgi->gc.ngpio = tgi->bank_count * 32; tgi->gc.ngpio = tgi->bank_count * 32;
tgi->gc.parent = &pdev->dev; tgi->gc.parent = &pdev->dev;
...@@ -619,6 +703,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) ...@@ -619,6 +703,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake; tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake;
#endif #endif
tgi->ic.irq_request_resources = tegra_gpio_irq_request_resources;
tgi->ic.irq_release_resources = tegra_gpio_irq_release_resources;
platform_set_drvdata(pdev, tgi); platform_set_drvdata(pdev, tgi);
...@@ -630,11 +716,10 @@ static int tegra_gpio_probe(struct platform_device *pdev) ...@@ -630,11 +716,10 @@ static int tegra_gpio_probe(struct platform_device *pdev)
if (!tgi->bank_info) if (!tgi->bank_info)
return -ENOMEM; return -ENOMEM;
tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node, tgi->irqs = devm_kcalloc(&pdev->dev, tgi->bank_count,
tgi->gc.ngpio, sizeof(*tgi->irqs), GFP_KERNEL);
&irq_domain_simple_ops, NULL); if (!tgi->irqs)
if (!tgi->irq_domain) return -ENOMEM;
return -ENODEV;
for (i = 0; i < tgi->bank_count; i++) { for (i = 0; i < tgi->bank_count; i++) {
ret = platform_get_irq(pdev, i); ret = platform_get_irq(pdev, i);
...@@ -643,8 +728,36 @@ static int tegra_gpio_probe(struct platform_device *pdev) ...@@ -643,8 +728,36 @@ static int tegra_gpio_probe(struct platform_device *pdev)
bank = &tgi->bank_info[i]; bank = &tgi->bank_info[i];
bank->bank = i; bank->bank = i;
bank->irq = ret;
bank->tgi = tgi; tgi->irqs[i] = ret;
for (j = 0; j < 4; j++) {
raw_spin_lock_init(&bank->lvl_lock[j]);
spin_lock_init(&bank->dbc_lock[j]);
}
}
irq = &tgi->gc.irq;
irq->chip = &tgi->ic;
irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq;
irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec;
irq->handler = handle_simple_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = tegra_gpio_irq_handler;
irq->parent_handler_data = tgi;
irq->num_parents = tgi->bank_count;
irq->parents = tgi->irqs;
np = of_find_matching_node(NULL, tegra_pmc_of_match);
if (np) {
irq->parent_domain = irq_find_host(np);
of_node_put(np);
if (!irq->parent_domain)
return -EPROBE_DEFER;
tgi->ic.irq_set_affinity = tegra_gpio_irq_set_affinity;
} }
tgi->regs = devm_platform_ioremap_resource(pdev, 0); tgi->regs = devm_platform_ioremap_resource(pdev, 0);
...@@ -660,33 +773,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) ...@@ -660,33 +773,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
} }
ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi); ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi);
if (ret < 0) { if (ret < 0)
irq_domain_remove(tgi->irq_domain);
return ret; return ret;
}
for (gpio = 0; gpio < tgi->gc.ngpio; gpio++) {
int irq = irq_create_mapping(tgi->irq_domain, gpio);
/* No validity check; all Tegra GPIOs are valid IRQs */
bank = &tgi->bank_info[GPIO_BANK(gpio)];
irq_set_chip_data(irq, bank);
irq_set_lockdep_class(irq, &gpio_lock_class, &gpio_request_class);
irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq);
}
for (i = 0; i < tgi->bank_count; i++) {
bank = &tgi->bank_info[i];
irq_set_chained_handler_and_data(bank->irq,
tegra_gpio_irq_handler, bank);
for (j = 0; j < 4; j++) {
raw_spin_lock_init(&bank->lvl_lock[j]);
spin_lock_init(&bank->dbc_lock[j]);
}
}
tegra_gpio_debuginit(tgi); tegra_gpio_debuginit(tgi);
...@@ -715,6 +803,7 @@ static const struct of_device_id tegra_gpio_of_match[] = { ...@@ -715,6 +803,7 @@ static const struct of_device_id tegra_gpio_of_match[] = {
{ .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, tegra_gpio_of_match);
static struct platform_driver tegra_gpio_driver = { static struct platform_driver tegra_gpio_driver = {
.driver = { .driver = {
...@@ -724,9 +813,11 @@ static struct platform_driver tegra_gpio_driver = { ...@@ -724,9 +813,11 @@ static struct platform_driver tegra_gpio_driver = {
}, },
.probe = tegra_gpio_probe, .probe = tegra_gpio_probe,
}; };
module_platform_driver(tegra_gpio_driver);
static int __init tegra_gpio_init(void)
{ MODULE_DESCRIPTION("NVIDIA Tegra GPIO controller driver");
return platform_driver_register(&tegra_gpio_driver); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
} MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
subsys_initcall(tegra_gpio_init); MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_AUTHOR("Erik Gilling <konkers@google.com>");
MODULE_LICENSE("GPL v2");
...@@ -657,7 +657,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) ...@@ -657,7 +657,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio->gpio.get_direction = tegra186_gpio_get_direction; gpio->gpio.get_direction = tegra186_gpio_get_direction;
gpio->gpio.direction_input = tegra186_gpio_direction_input; gpio->gpio.direction_input = tegra186_gpio_direction_input;
gpio->gpio.direction_output = tegra186_gpio_direction_output; gpio->gpio.direction_output = tegra186_gpio_direction_output;
gpio->gpio.get = tegra186_gpio_get, gpio->gpio.get = tegra186_gpio_get;
gpio->gpio.set = tegra186_gpio_set; gpio->gpio.set = tegra186_gpio_set;
gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.set_config = tegra186_gpio_set_config;
gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Toshiba Visconti GPIO Support
*
* (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation
* (C) Copyright 2020 TOSHIBA CORPORATION
*
* Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
*/
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
/* register offset */
#define GPIO_DIR 0x00
#define GPIO_IDATA 0x08
#define GPIO_ODATA 0x10
#define GPIO_OSET 0x18
#define GPIO_OCLR 0x20
#define GPIO_INTMODE 0x30
#define BASE_HW_IRQ 24
struct visconti_gpio {
void __iomem *base;
spinlock_t lock; /* protect gpio register */
struct gpio_chip gpio_chip;
struct irq_chip irq_chip;
};
static int visconti_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct visconti_gpio *priv = gpiochip_get_data(gc);
u32 offset = irqd_to_hwirq(d);
u32 bit = BIT(offset);
u32 intc_type = IRQ_TYPE_EDGE_RISING;
u32 intmode, odata;
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
odata = readl(priv->base + GPIO_ODATA);
intmode = readl(priv->base + GPIO_INTMODE);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
odata &= ~bit;
intmode &= ~bit;
break;
case IRQ_TYPE_EDGE_FALLING:
odata |= bit;
intmode &= ~bit;
break;
case IRQ_TYPE_EDGE_BOTH:
intmode |= bit;
break;
case IRQ_TYPE_LEVEL_HIGH:
intc_type = IRQ_TYPE_LEVEL_HIGH;
odata &= ~bit;
intmode &= ~bit;
break;
case IRQ_TYPE_LEVEL_LOW:
intc_type = IRQ_TYPE_LEVEL_HIGH;
odata |= bit;
intmode &= ~bit;
break;
default:
ret = -EINVAL;
goto err;
}
writel(odata, priv->base + GPIO_ODATA);
writel(intmode, priv->base + GPIO_INTMODE);
irq_set_irq_type(offset, intc_type);
ret = irq_chip_set_type_parent(d, type);
err:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
static int visconti_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
unsigned int child,
unsigned int child_type,
unsigned int *parent,
unsigned int *parent_type)
{
/* Interrupts 0..15 mapped to interrupts 24..39 on the GIC */
if (child < 16) {
/* All these interrupts are level high in the CPU */
*parent_type = IRQ_TYPE_LEVEL_HIGH;
*parent = child + BASE_HW_IRQ;
return 0;
}
return -EINVAL;
}
static void *visconti_gpio_populate_parent_fwspec(struct gpio_chip *chip,
unsigned int parent_hwirq,
unsigned int parent_type)
{
struct irq_fwspec *fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3;
fwspec->param[0] = 0;
fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type;
return fwspec;
}
static int visconti_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct visconti_gpio *priv;
struct irq_chip *irq_chip;
struct gpio_irq_chip *girq;
struct irq_domain *parent;
struct device_node *irq_parent;
struct fwnode_handle *fwnode;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->lock);
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
irq_parent = of_irq_find_parent(dev->of_node);
if (!irq_parent) {
dev_err(dev, "No IRQ parent node\n");
return -ENODEV;
}
parent = irq_find_host(irq_parent);
if (!parent) {
dev_err(dev, "No IRQ parent domain\n");
return -ENODEV;
}
fwnode = of_node_to_fwnode(irq_parent);
of_node_put(irq_parent);
ret = bgpio_init(&priv->gpio_chip, dev, 4,
priv->base + GPIO_IDATA,
priv->base + GPIO_OSET,
priv->base + GPIO_OCLR,
priv->base + GPIO_DIR,
NULL,
0);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
return ret;
}
irq_chip = &priv->irq_chip;
irq_chip->name = dev_name(dev);
irq_chip->irq_mask = irq_chip_mask_parent;
irq_chip->irq_unmask = irq_chip_unmask_parent;
irq_chip->irq_eoi = irq_chip_eoi_parent;
irq_chip->irq_set_type = visconti_gpio_irq_set_type;
irq_chip->flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
girq = &priv->gpio_chip.irq;
girq->chip = irq_chip;
girq->fwnode = fwnode;
girq->parent_domain = parent;
girq->child_to_parent_hwirq = visconti_gpio_child_to_parent_hwirq;
girq->populate_parent_alloc_arg = visconti_gpio_populate_parent_fwspec;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_level_irq;
ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
if (ret) {
dev_err(dev, "failed to add GPIO chip\n");
return ret;
}
platform_set_drvdata(pdev, priv);
return ret;
}
static const struct of_device_id visconti_gpio_of_match[] = {
{ .compatible = "toshiba,gpio-tmpv7708", },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, visconti_gpio_of_match);
static struct platform_driver visconti_gpio_driver = {
.probe = visconti_gpio_probe,
.driver = {
.name = "visconti_gpio",
.of_match_table = of_match_ptr(visconti_gpio_of_match),
}
};
module_platform_driver(visconti_gpio_driver);
MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
MODULE_DESCRIPTION("Toshiba Visconti GPIO Driver");
MODULE_LICENSE("GPL v2");
...@@ -216,7 +216,7 @@ static void vx855gpio_gpio_setup(struct vx855_gpio *vg) ...@@ -216,7 +216,7 @@ static void vx855gpio_gpio_setup(struct vx855_gpio *vg)
c->direction_output = vx855gpio_direction_output; c->direction_output = vx855gpio_direction_output;
c->get = vx855gpio_get; c->get = vx855gpio_get;
c->set = vx855gpio_set; c->set = vx855gpio_set;
c->set_config = vx855gpio_set_config, c->set_config = vx855gpio_set_config;
c->dbg_show = NULL; c->dbg_show = NULL;
c->base = 0; c->base = 0;
c->ngpio = NR_VX855_GP; c->ngpio = NR_VX855_GP;
......
...@@ -73,6 +73,8 @@ ...@@ -73,6 +73,8 @@
enum ctrl_register { enum ctrl_register {
CTRL_IN, CTRL_IN,
CTRL_OUT, CTRL_OUT,
IRQ_STATUS,
IRQ_MASK,
}; };
/* /*
...@@ -112,22 +114,29 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type) ...@@ -112,22 +114,29 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type)
return reg; return reg;
} }
static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) static inline int to_ireg(int gpio, enum ctrl_register type, unsigned int *mask)
{ {
unsigned int reg, mask; unsigned int reg = type == IRQ_STATUS ? IRQ_STATUS_BASE : IRQ_MASK_BASE;
if (gpio < GROUP0_NR_IRQS) { if (gpio < GROUP0_NR_IRQS) {
reg = IRQ_MASK_BASE; reg += 0;
mask = BIT(gpio % GROUP0_NR_IRQS); *mask = BIT(gpio);
} else { } else {
reg = IRQ_MASK_BASE + 1; reg += 1;
mask = BIT((gpio - GROUP0_NR_IRQS) % GROUP1_NR_IRQS); *mask = BIT(gpio - GROUP0_NR_IRQS);
} }
return reg;
}
static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio)
{
unsigned int mask, reg = to_ireg(gpio, IRQ_MASK, &mask);
if (wg->set_irq_mask) if (wg->set_irq_mask)
regmap_update_bits(wg->regmap, reg, mask, mask); regmap_set_bits(wg->regmap, reg, mask);
else else
regmap_update_bits(wg->regmap, reg, mask, 0); regmap_clear_bits(wg->regmap, reg, mask);
} }
static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio)
...@@ -207,9 +216,9 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) ...@@ -207,9 +216,9 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value)
return; return;
if (value) if (value)
regmap_update_bits(wg->regmap, reg, 1, 1); regmap_set_bits(wg->regmap, reg, 1);
else else
regmap_update_bits(wg->regmap, reg, 1, 0); regmap_clear_bits(wg->regmap, reg, 1);
} }
static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio,
...@@ -324,7 +333,8 @@ static struct irq_chip wcove_irqchip = { ...@@ -324,7 +333,8 @@ static struct irq_chip wcove_irqchip = {
static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) static irqreturn_t wcove_gpio_irq_handler(int irq, void *data)
{ {
struct wcove_gpio *wg = (struct wcove_gpio *)data; struct wcove_gpio *wg = (struct wcove_gpio *)data;
unsigned int pending, virq, gpio, mask, offset; unsigned int virq, gpio;
unsigned long pending;
u8 p[2]; u8 p[2];
if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) { if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) {
...@@ -339,15 +349,12 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) ...@@ -339,15 +349,12 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data)
/* Iterate until no interrupt is pending */ /* Iterate until no interrupt is pending */
while (pending) { while (pending) {
/* One iteration is for all pending bits */ /* One iteration is for all pending bits */
for_each_set_bit(gpio, (const unsigned long *)&pending, for_each_set_bit(gpio, &pending, WCOVE_GPIO_NUM) {
WCOVE_GPIO_NUM) { unsigned int mask, reg = to_ireg(gpio, IRQ_STATUS, &mask);
offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0;
mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) :
BIT(gpio);
virq = irq_find_mapping(wg->chip.irq.domain, gpio); virq = irq_find_mapping(wg->chip.irq.domain, gpio);
handle_nested_irq(virq); handle_nested_irq(virq);
regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset, regmap_set_bits(wg->regmap, reg, mask);
mask, mask);
} }
/* Next iteration */ /* Next iteration */
...@@ -367,30 +374,26 @@ static void wcove_gpio_dbg_show(struct seq_file *s, ...@@ -367,30 +374,26 @@ static void wcove_gpio_dbg_show(struct seq_file *s,
{ {
unsigned int ctlo, ctli, irq_mask, irq_status; unsigned int ctlo, ctli, irq_mask, irq_status;
struct wcove_gpio *wg = gpiochip_get_data(chip); struct wcove_gpio *wg = gpiochip_get_data(chip);
int gpio, offset, group, ret = 0; int gpio, mask, ret = 0;
for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) { for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) {
group = gpio < GROUP0_NR_IRQS ? 0 : 1;
ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo); ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo);
ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli); ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli);
ret += regmap_read(wg->regmap, IRQ_MASK_BASE + group, ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask);
&irq_mask); ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status);
ret += regmap_read(wg->regmap, IRQ_STATUS_BASE + group,
&irq_status);
if (ret) { if (ret) {
pr_err("Failed to read registers: ctrl out/in or irq status/mask\n"); pr_err("Failed to read registers: ctrl out/in or irq status/mask\n");
break; break;
} }
offset = gpio % 8;
seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n", seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n",
gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ", gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ",
ctli & 0x1 ? "hi" : "lo", ctli & 0x1 ? "hi" : "lo",
ctli & CTLI_INTCNT_NE ? "fall" : " ", ctli & CTLI_INTCNT_NE ? "fall" : " ",
ctli & CTLI_INTCNT_PE ? "rise" : " ", ctli & CTLI_INTCNT_PE ? "rise" : " ",
ctlo, ctlo,
irq_mask & BIT(offset) ? "mask " : "unmask", irq_mask & mask ? "mask " : "unmask",
irq_status & BIT(offset) ? "pending" : " "); irq_status & mask ? "pending" : " ");
} }
} }
...@@ -434,7 +437,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) ...@@ -434,7 +437,7 @@ static int wcove_gpio_probe(struct platform_device *pdev)
wg->chip.get_direction = wcove_gpio_get_direction; wg->chip.get_direction = wcove_gpio_get_direction;
wg->chip.get = wcove_gpio_get; wg->chip.get = wcove_gpio_get;
wg->chip.set = wcove_gpio_set; wg->chip.set = wcove_gpio_set;
wg->chip.set_config = wcove_gpio_set_config, wg->chip.set_config = wcove_gpio_set_config;
wg->chip.base = -1; wg->chip.base = -1;
wg->chip.ngpio = WCOVE_VGPIO_NUM; wg->chip.ngpio = WCOVE_VGPIO_NUM;
wg->chip.can_sleep = true; wg->chip.can_sleep = true;
...@@ -473,14 +476,12 @@ static int wcove_gpio_probe(struct platform_device *pdev) ...@@ -473,14 +476,12 @@ static int wcove_gpio_probe(struct platform_device *pdev)
} }
/* Enable GPIO0 interrupts */ /* Enable GPIO0 interrupts */
ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK, ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 0, GPIO_IRQ0_MASK);
0x00);
if (ret) if (ret)
return ret; return ret;
/* Enable GPIO1 interrupts */ /* Enable GPIO1 interrupts */
ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK, ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK);
0x00);
if (ret) if (ret)
return ret; return ret;
......
...@@ -10,10 +10,13 @@ ...@@ -10,10 +10,13 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
/* Register Offset Definitions */ /* Register Offset Definitions */
...@@ -22,6 +25,11 @@ ...@@ -22,6 +25,11 @@
#define XGPIO_CHANNEL_OFFSET 0x8 #define XGPIO_CHANNEL_OFFSET 0x8
#define XGPIO_GIER_OFFSET 0x11c /* Global Interrupt Enable */
#define XGPIO_GIER_IE BIT(31)
#define XGPIO_IPISR_OFFSET 0x120 /* IP Interrupt Status */
#define XGPIO_IPIER_OFFSET 0x128 /* IP Interrupt Enable */
/* Read/Write access to the GPIO registers */ /* Read/Write access to the GPIO registers */
#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86) #if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86)
# define xgpio_readreg(offset) readl(offset) # define xgpio_readreg(offset) readl(offset)
...@@ -36,9 +44,15 @@ ...@@ -36,9 +44,15 @@
* @gc: GPIO chip * @gc: GPIO chip
* @regs: register block * @regs: register block
* @gpio_width: GPIO width for every channel * @gpio_width: GPIO width for every channel
* @gpio_state: GPIO state shadow register * @gpio_state: GPIO write state shadow register
* @gpio_last_irq_read: GPIO read state register from last interrupt
* @gpio_dir: GPIO direction shadow register * @gpio_dir: GPIO direction shadow register
* @gpio_lock: Lock used for synchronization * @gpio_lock: Lock used for synchronization
* @irq: IRQ used by GPIO device
* @irqchip: IRQ chip
* @irq_enable: GPIO IRQ enable/disable bitfield
* @irq_rising_edge: GPIO IRQ rising edge enable/disable bitfield
* @irq_falling_edge: GPIO IRQ falling edge enable/disable bitfield
* @clk: clock resource for this driver * @clk: clock resource for this driver
*/ */
struct xgpio_instance { struct xgpio_instance {
...@@ -46,8 +60,14 @@ struct xgpio_instance { ...@@ -46,8 +60,14 @@ struct xgpio_instance {
void __iomem *regs; void __iomem *regs;
unsigned int gpio_width[2]; unsigned int gpio_width[2];
u32 gpio_state[2]; u32 gpio_state[2];
u32 gpio_last_irq_read[2];
u32 gpio_dir[2]; u32 gpio_dir[2];
spinlock_t gpio_lock[2]; spinlock_t gpio_lock; /* For serializing operations */
int irq;
struct irq_chip irqchip;
u32 irq_enable[2];
u32 irq_rising_edge[2];
u32 irq_falling_edge[2];
struct clk *clk; struct clk *clk;
}; };
...@@ -113,7 +133,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) ...@@ -113,7 +133,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
int index = xgpio_index(chip, gpio); int index = xgpio_index(chip, gpio);
int offset = xgpio_offset(chip, gpio); int offset = xgpio_offset(chip, gpio);
spin_lock_irqsave(&chip->gpio_lock[index], flags); spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write to GPIO signal and set its direction to output */ /* Write to GPIO signal and set its direction to output */
if (val) if (val)
...@@ -124,7 +144,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) ...@@ -124,7 +144,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
xgpio_regoffset(chip, gpio), chip->gpio_state[index]); xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags); spin_unlock_irqrestore(&chip->gpio_lock, flags);
} }
/** /**
...@@ -144,7 +164,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, ...@@ -144,7 +164,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
int index = xgpio_index(chip, 0); int index = xgpio_index(chip, 0);
int offset, i; int offset, i;
spin_lock_irqsave(&chip->gpio_lock[index], flags); spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write to GPIO signals */ /* Write to GPIO signals */
for (i = 0; i < gc->ngpio; i++) { for (i = 0; i < gc->ngpio; i++) {
...@@ -155,9 +175,9 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, ...@@ -155,9 +175,9 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET, index * XGPIO_CHANNEL_OFFSET,
chip->gpio_state[index]); chip->gpio_state[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags); spin_unlock_irqrestore(&chip->gpio_lock, flags);
index = xgpio_index(chip, i); index = xgpio_index(chip, i);
spin_lock_irqsave(&chip->gpio_lock[index], flags); spin_lock_irqsave(&chip->gpio_lock, flags);
} }
if (__test_and_clear_bit(i, mask)) { if (__test_and_clear_bit(i, mask)) {
offset = xgpio_offset(chip, i); offset = xgpio_offset(chip, i);
...@@ -171,7 +191,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, ...@@ -171,7 +191,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET + xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET, chip->gpio_state[index]); index * XGPIO_CHANNEL_OFFSET, chip->gpio_state[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags); spin_unlock_irqrestore(&chip->gpio_lock, flags);
} }
/** /**
...@@ -190,14 +210,14 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) ...@@ -190,14 +210,14 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
int index = xgpio_index(chip, gpio); int index = xgpio_index(chip, gpio);
int offset = xgpio_offset(chip, gpio); int offset = xgpio_offset(chip, gpio);
spin_lock_irqsave(&chip->gpio_lock[index], flags); spin_lock_irqsave(&chip->gpio_lock, flags);
/* Set the GPIO bit in shadow register and set direction as input */ /* Set the GPIO bit in shadow register and set direction as input */
chip->gpio_dir[index] |= BIT(offset); chip->gpio_dir[index] |= BIT(offset);
xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET + xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET +
xgpio_regoffset(chip, gpio), chip->gpio_dir[index]); xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags); spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0; return 0;
} }
...@@ -221,7 +241,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) ...@@ -221,7 +241,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
int index = xgpio_index(chip, gpio); int index = xgpio_index(chip, gpio);
int offset = xgpio_offset(chip, gpio); int offset = xgpio_offset(chip, gpio);
spin_lock_irqsave(&chip->gpio_lock[index], flags); spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write state of GPIO signal */ /* Write state of GPIO signal */
if (val) if (val)
...@@ -236,7 +256,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) ...@@ -236,7 +256,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET + xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET +
xgpio_regoffset(chip, gpio), chip->gpio_dir[index]); xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags); spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0; return 0;
} }
...@@ -259,6 +279,39 @@ static void xgpio_save_regs(struct xgpio_instance *chip) ...@@ -259,6 +279,39 @@ static void xgpio_save_regs(struct xgpio_instance *chip)
chip->gpio_dir[1]); chip->gpio_dir[1]);
} }
static int xgpio_request(struct gpio_chip *chip, unsigned int offset)
{
int ret;
ret = pm_runtime_get_sync(chip->parent);
/*
* If the device is already active pm_runtime_get() will return 1 on
* success, but gpio_request still needs to return 0.
*/
return ret < 0 ? ret : 0;
}
static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
{
pm_runtime_put(chip->parent);
}
static int __maybe_unused xgpio_suspend(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
if (!data) {
dev_err(dev, "irq_get_irq_data() failed\n");
return -EINVAL;
}
if (!irqd_is_wakeup_set(data))
return pm_runtime_force_suspend(dev);
return 0;
}
/** /**
* xgpio_remove - Remove method for the GPIO device. * xgpio_remove - Remove method for the GPIO device.
* @pdev: pointer to the platform device * @pdev: pointer to the platform device
...@@ -271,11 +324,223 @@ static int xgpio_remove(struct platform_device *pdev) ...@@ -271,11 +324,223 @@ static int xgpio_remove(struct platform_device *pdev)
{ {
struct xgpio_instance *gpio = platform_get_drvdata(pdev); struct xgpio_instance *gpio = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(gpio->clk); clk_disable_unprepare(gpio->clk);
return 0; return 0;
} }
/**
* xgpio_irq_ack - Acknowledge a child GPIO interrupt.
* @irq_data: per IRQ and chip data passed down to chip functions
* This currently does nothing, but irq_ack is unconditionally called by
* handle_edge_irq and therefore must be defined.
*/
static void xgpio_irq_ack(struct irq_data *irq_data)
{
}
static int __maybe_unused xgpio_resume(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
if (!data) {
dev_err(dev, "irq_get_irq_data() failed\n");
return -EINVAL;
}
if (!irqd_is_wakeup_set(data))
return pm_runtime_force_resume(dev);
return 0;
}
static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
clk_disable(gpio->clk);
return 0;
}
static int __maybe_unused xgpio_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
return clk_enable(gpio->clk);
}
static const struct dev_pm_ops xgpio_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
SET_RUNTIME_PM_OPS(xgpio_runtime_suspend,
xgpio_runtime_resume, NULL)
};
/**
* xgpio_irq_mask - Write the specified signal of the GPIO device.
* @irq_data: per IRQ and chip data passed down to chip functions
*/
static void xgpio_irq_mask(struct irq_data *irq_data)
{
unsigned long flags;
struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
int irq_offset = irqd_to_hwirq(irq_data);
int index = xgpio_index(chip, irq_offset);
int offset = xgpio_offset(chip, irq_offset);
spin_lock_irqsave(&chip->gpio_lock, flags);
chip->irq_enable[index] &= ~BIT(offset);
if (!chip->irq_enable[index]) {
/* Disable per channel interrupt */
u32 temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
temp &= ~BIT(index);
xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp);
}
spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
* xgpio_irq_unmask - Write the specified signal of the GPIO device.
* @irq_data: per IRQ and chip data passed down to chip functions
*/
static void xgpio_irq_unmask(struct irq_data *irq_data)
{
unsigned long flags;
struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
int irq_offset = irqd_to_hwirq(irq_data);
int index = xgpio_index(chip, irq_offset);
int offset = xgpio_offset(chip, irq_offset);
u32 old_enable = chip->irq_enable[index];
spin_lock_irqsave(&chip->gpio_lock, flags);
chip->irq_enable[index] |= BIT(offset);
if (!old_enable) {
/* Clear any existing per-channel interrupts */
u32 val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET) &
BIT(index);
if (val)
xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val);
/* Update GPIO IRQ read data before enabling interrupt*/
val = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET);
chip->gpio_last_irq_read[index] = val;
/* Enable per channel interrupt */
val = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
val |= BIT(index);
xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val);
}
spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
* xgpio_set_irq_type - Write the specified signal of the GPIO device.
* @irq_data: Per IRQ and chip data passed down to chip functions
* @type: Interrupt type that is to be set for the gpio pin
*
* Return:
* 0 if interrupt type is supported otherwise -EINVAL
*/
static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
{
struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
int irq_offset = irqd_to_hwirq(irq_data);
int index = xgpio_index(chip, irq_offset);
int offset = xgpio_offset(chip, irq_offset);
/*
* The Xilinx GPIO hardware provides a single interrupt status
* indication for any state change in a given GPIO channel (bank).
* Therefore, only rising edge or falling edge triggers are
* supported.
*/
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
chip->irq_rising_edge[index] |= BIT(offset);
chip->irq_falling_edge[index] |= BIT(offset);
break;
case IRQ_TYPE_EDGE_RISING:
chip->irq_rising_edge[index] |= BIT(offset);
chip->irq_falling_edge[index] &= ~BIT(offset);
break;
case IRQ_TYPE_EDGE_FALLING:
chip->irq_rising_edge[index] &= ~BIT(offset);
chip->irq_falling_edge[index] |= BIT(offset);
break;
default:
return -EINVAL;
}
irq_set_handler_locked(irq_data, handle_edge_irq);
return 0;
}
/**
* xgpio_irqhandler - Gpio interrupt service routine
* @desc: Pointer to interrupt description
*/
static void xgpio_irqhandler(struct irq_desc *desc)
{
struct xgpio_instance *chip = irq_desc_get_handler_data(desc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
u32 num_channels = chip->gpio_width[1] ? 2 : 1;
u32 offset = 0, index;
u32 status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status);
chained_irq_enter(irqchip, desc);
for (index = 0; index < num_channels; index++) {
if ((status & BIT(index))) {
unsigned long rising_events, falling_events, all_events;
unsigned long flags;
u32 data, bit;
unsigned int irq;
spin_lock_irqsave(&chip->gpio_lock, flags);
data = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET);
rising_events = data &
~chip->gpio_last_irq_read[index] &
chip->irq_enable[index] &
chip->irq_rising_edge[index];
falling_events = ~data &
chip->gpio_last_irq_read[index] &
chip->irq_enable[index] &
chip->irq_falling_edge[index];
dev_dbg(chip->gc.parent,
"IRQ chan %u rising 0x%lx falling 0x%lx\n",
index, rising_events, falling_events);
all_events = rising_events | falling_events;
chip->gpio_last_irq_read[index] = data;
spin_unlock_irqrestore(&chip->gpio_lock, flags);
for_each_set_bit(bit, &all_events, 32) {
irq = irq_find_mapping(chip->gc.irq.domain,
offset + bit);
generic_handle_irq(irq);
}
}
offset += chip->gpio_width[index];
}
chained_irq_exit(irqchip, desc);
}
/** /**
* xgpio_of_probe - Probe method for the GPIO device. * xgpio_of_probe - Probe method for the GPIO device.
* @pdev: pointer to the platform device * @pdev: pointer to the platform device
...@@ -289,7 +554,10 @@ static int xgpio_probe(struct platform_device *pdev) ...@@ -289,7 +554,10 @@ static int xgpio_probe(struct platform_device *pdev)
struct xgpio_instance *chip; struct xgpio_instance *chip;
int status = 0; int status = 0;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
u32 is_dual; u32 is_dual = 0;
u32 cells = 2;
struct gpio_irq_chip *girq;
u32 temp;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) if (!chip)
...@@ -305,6 +573,15 @@ static int xgpio_probe(struct platform_device *pdev) ...@@ -305,6 +573,15 @@ static int xgpio_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0])) if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0]))
chip->gpio_dir[0] = 0xFFFFFFFF; chip->gpio_dir[0] = 0xFFFFFFFF;
/* Update cells with gpio-cells value */
if (of_property_read_u32(np, "#gpio-cells", &cells))
dev_dbg(&pdev->dev, "Missing gpio-cells property\n");
if (cells != 2) {
dev_err(&pdev->dev, "#gpio-cells mismatch\n");
return -EINVAL;
}
/* /*
* Check device node and parent device node for device width * Check device node and parent device node for device width
* and assume default width of 32 * and assume default width of 32
...@@ -312,7 +589,10 @@ static int xgpio_probe(struct platform_device *pdev) ...@@ -312,7 +589,10 @@ static int xgpio_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0])) if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0]))
chip->gpio_width[0] = 32; chip->gpio_width[0] = 32;
spin_lock_init(&chip->gpio_lock[0]); if (chip->gpio_width[0] > 32)
return -EINVAL;
spin_lock_init(&chip->gpio_lock);
if (of_property_read_u32(np, "xlnx,is-dual", &is_dual)) if (of_property_read_u32(np, "xlnx,is-dual", &is_dual))
is_dual = 0; is_dual = 0;
...@@ -336,7 +616,8 @@ static int xgpio_probe(struct platform_device *pdev) ...@@ -336,7 +616,8 @@ static int xgpio_probe(struct platform_device *pdev)
&chip->gpio_width[1])) &chip->gpio_width[1]))
chip->gpio_width[1] = 32; chip->gpio_width[1] = 32;
spin_lock_init(&chip->gpio_lock[1]); if (chip->gpio_width[1] > 32)
return -EINVAL;
} }
chip->gc.base = -1; chip->gc.base = -1;
...@@ -344,8 +625,11 @@ static int xgpio_probe(struct platform_device *pdev) ...@@ -344,8 +625,11 @@ static int xgpio_probe(struct platform_device *pdev)
chip->gc.parent = &pdev->dev; chip->gc.parent = &pdev->dev;
chip->gc.direction_input = xgpio_dir_in; chip->gc.direction_input = xgpio_dir_in;
chip->gc.direction_output = xgpio_dir_out; chip->gc.direction_output = xgpio_dir_out;
chip->gc.of_gpio_n_cells = cells;
chip->gc.get = xgpio_get; chip->gc.get = xgpio_get;
chip->gc.set = xgpio_set; chip->gc.set = xgpio_set;
chip->gc.request = xgpio_request;
chip->gc.free = xgpio_free;
chip->gc.set_multiple = xgpio_set_multiple; chip->gc.set_multiple = xgpio_set_multiple;
chip->gc.label = dev_name(&pdev->dev); chip->gc.label = dev_name(&pdev->dev);
...@@ -357,28 +641,68 @@ static int xgpio_probe(struct platform_device *pdev) ...@@ -357,28 +641,68 @@ static int xgpio_probe(struct platform_device *pdev)
} }
chip->clk = devm_clk_get_optional(&pdev->dev, NULL); chip->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(chip->clk)) { if (IS_ERR(chip->clk))
if (PTR_ERR(chip->clk) != -EPROBE_DEFER) return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk), "input clock not found.\n");
dev_dbg(&pdev->dev, "Input clock not found\n");
return PTR_ERR(chip->clk);
}
status = clk_prepare_enable(chip->clk); status = clk_prepare_enable(chip->clk);
if (status < 0) { if (status < 0) {
dev_err(&pdev->dev, "Failed to prepare clk\n"); dev_err(&pdev->dev, "Failed to prepare clk\n");
return status; return status;
} }
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
xgpio_save_regs(chip); xgpio_save_regs(chip);
chip->irq = platform_get_irq_optional(pdev, 0);
if (chip->irq <= 0)
goto skip_irq;
chip->irqchip.name = "gpio-xilinx";
chip->irqchip.irq_ack = xgpio_irq_ack;
chip->irqchip.irq_mask = xgpio_irq_mask;
chip->irqchip.irq_unmask = xgpio_irq_unmask;
chip->irqchip.irq_set_type = xgpio_set_irq_type;
/* Disable per-channel interrupts */
xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, 0);
/* Clear any existing per-channel interrupts */
temp = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, temp);
/* Enable global interrupts */
xgpio_writereg(chip->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE);
girq = &chip->gc.irq;
girq->chip = &chip->irqchip;
girq->parent_handler = xgpio_irqhandler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(&pdev->dev, 1,
sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents) {
status = -ENOMEM;
goto err_pm_put;
}
girq->parents[0] = chip->irq;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
skip_irq:
status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip);
if (status) { if (status) {
dev_err(&pdev->dev, "failed to add GPIO chip\n"); dev_err(&pdev->dev, "failed to add GPIO chip\n");
clk_disable_unprepare(chip->clk); goto err_pm_put;
return status;
} }
pm_runtime_put(&pdev->dev);
return 0; return 0;
err_pm_put:
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable_unprepare(chip->clk);
return status;
} }
static const struct of_device_id xgpio_of_match[] = { static const struct of_device_id xgpio_of_match[] = {
...@@ -394,6 +718,7 @@ static struct platform_driver xgpio_plat_driver = { ...@@ -394,6 +718,7 @@ static struct platform_driver xgpio_plat_driver = {
.driver = { .driver = {
.name = "gpio-xilinx", .name = "gpio-xilinx",
.of_match_table = xgpio_of_match, .of_match_table = xgpio_of_match,
.pm = &xgpio_dev_pm_ops,
}, },
}; };
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* ZTE ZX296702 GPIO driver
*
* Author: Jun Nie <jun.nie@linaro.org>
*
* Copyright (C) 2015 Linaro Ltd.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#define ZX_GPIO_DIR 0x00
#define ZX_GPIO_IVE 0x04
#define ZX_GPIO_IV 0x08
#define ZX_GPIO_IEP 0x0C
#define ZX_GPIO_IEN 0x10
#define ZX_GPIO_DI 0x14
#define ZX_GPIO_DO1 0x18
#define ZX_GPIO_DO0 0x1C
#define ZX_GPIO_DO 0x20
#define ZX_GPIO_IM 0x28
#define ZX_GPIO_IE 0x2C
#define ZX_GPIO_MIS 0x30
#define ZX_GPIO_IC 0x34
#define ZX_GPIO_NR 16
struct zx_gpio {
raw_spinlock_t lock;
void __iomem *base;
struct gpio_chip gc;
};
static int zx_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct zx_gpio *chip = gpiochip_get_data(gc);
unsigned long flags;
u16 gpiodir;
if (offset >= gc->ngpio)
return -EINVAL;
raw_spin_lock_irqsave(&chip->lock, flags);
gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR);
gpiodir &= ~BIT(offset);
writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static int zx_direction_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct zx_gpio *chip = gpiochip_get_data(gc);
unsigned long flags;
u16 gpiodir;
if (offset >= gc->ngpio)
return -EINVAL;
raw_spin_lock_irqsave(&chip->lock, flags);
gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR);
gpiodir |= BIT(offset);
writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR);
if (value)
writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1);
else
writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static int zx_get_value(struct gpio_chip *gc, unsigned offset)
{
struct zx_gpio *chip = gpiochip_get_data(gc);
return !!(readw_relaxed(chip->base + ZX_GPIO_DI) & BIT(offset));
}
static void zx_set_value(struct gpio_chip *gc, unsigned offset, int value)
{
struct zx_gpio *chip = gpiochip_get_data(gc);
if (value)
writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1);
else
writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0);
}
static int zx_irq_type(struct irq_data *d, unsigned trigger)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct zx_gpio *chip = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(d);
unsigned long flags;
u16 gpiois, gpioi_epos, gpioi_eneg, gpioiev;
u16 bit = BIT(offset);
if (offset < 0 || offset >= ZX_GPIO_NR)
return -EINVAL;
raw_spin_lock_irqsave(&chip->lock, flags);
gpioiev = readw_relaxed(chip->base + ZX_GPIO_IV);
gpiois = readw_relaxed(chip->base + ZX_GPIO_IVE);
gpioi_epos = readw_relaxed(chip->base + ZX_GPIO_IEP);
gpioi_eneg = readw_relaxed(chip->base + ZX_GPIO_IEN);
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
gpiois |= bit;
if (trigger & IRQ_TYPE_LEVEL_HIGH)
gpioiev |= bit;
else
gpioiev &= ~bit;
} else
gpiois &= ~bit;
if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
gpioi_epos |= bit;
gpioi_eneg |= bit;
} else {
if (trigger & IRQ_TYPE_EDGE_RISING) {
gpioi_epos |= bit;
gpioi_eneg &= ~bit;
} else if (trigger & IRQ_TYPE_EDGE_FALLING) {
gpioi_eneg |= bit;
gpioi_epos &= ~bit;
}
}
writew_relaxed(gpiois, chip->base + ZX_GPIO_IVE);
writew_relaxed(gpioi_epos, chip->base + ZX_GPIO_IEP);
writew_relaxed(gpioi_eneg, chip->base + ZX_GPIO_IEN);
writew_relaxed(gpioiev, chip->base + ZX_GPIO_IV);
raw_spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static void zx_irq_handler(struct irq_desc *desc)
{
unsigned long pending;
int offset;
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct zx_gpio *chip = gpiochip_get_data(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
chained_irq_enter(irqchip, desc);
pending = readw_relaxed(chip->base + ZX_GPIO_MIS);
writew_relaxed(pending, chip->base + ZX_GPIO_IC);
if (pending) {
for_each_set_bit(offset, &pending, ZX_GPIO_NR)
generic_handle_irq(irq_find_mapping(gc->irq.domain,
offset));
}
chained_irq_exit(irqchip, desc);
}
static void zx_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct zx_gpio *chip = gpiochip_get_data(gc);
u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR);
u16 gpioie;
raw_spin_lock(&chip->lock);
gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) | mask;
writew_relaxed(gpioie, chip->base + ZX_GPIO_IM);
gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) & ~mask;
writew_relaxed(gpioie, chip->base + ZX_GPIO_IE);
raw_spin_unlock(&chip->lock);
}
static void zx_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct zx_gpio *chip = gpiochip_get_data(gc);
u16 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR);
u16 gpioie;
raw_spin_lock(&chip->lock);
gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) & ~mask;
writew_relaxed(gpioie, chip->base + ZX_GPIO_IM);
gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) | mask;
writew_relaxed(gpioie, chip->base + ZX_GPIO_IE);
raw_spin_unlock(&chip->lock);
}
static struct irq_chip zx_irqchip = {
.name = "zx-gpio",
.irq_mask = zx_irq_mask,
.irq_unmask = zx_irq_unmask,
.irq_set_type = zx_irq_type,
};
static int zx_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct zx_gpio *chip;
struct gpio_irq_chip *girq;
int irq, id, ret;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
id = of_alias_get_id(dev->of_node, "gpio");
raw_spin_lock_init(&chip->lock);
chip->gc.request = gpiochip_generic_request;
chip->gc.free = gpiochip_generic_free;
chip->gc.direction_input = zx_direction_input;
chip->gc.direction_output = zx_direction_output;
chip->gc.get = zx_get_value;
chip->gc.set = zx_set_value;
chip->gc.base = ZX_GPIO_NR * id;
chip->gc.ngpio = ZX_GPIO_NR;
chip->gc.label = dev_name(dev);
chip->gc.parent = dev;
chip->gc.owner = THIS_MODULE;
/*
* irq_chip support
*/
writew_relaxed(0xffff, chip->base + ZX_GPIO_IM);
writew_relaxed(0, chip->base + ZX_GPIO_IE);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
girq = &chip->gc.irq;
girq->chip = &zx_irqchip;
girq->parent_handler = zx_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(&pdev->dev, 1,
sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->parents[0] = irq;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_simple_irq;
ret = gpiochip_add_data(&chip->gc, chip);
if (ret)
return ret;
platform_set_drvdata(pdev, chip);
dev_info(dev, "ZX GPIO chip registered\n");
return 0;
}
static const struct of_device_id zx_gpio_match[] = {
{
.compatible = "zte,zx296702-gpio",
},
{ },
};
static struct platform_driver zx_gpio_driver = {
.probe = zx_gpio_probe,
.driver = {
.name = "zx_gpio",
.of_match_table = of_match_ptr(zx_gpio_match),
},
};
builtin_platform_driver(zx_gpio_driver)
...@@ -245,11 +245,34 @@ static int visconti_set_mux(struct pinctrl_dev *pctldev, ...@@ -245,11 +245,34 @@ static int visconti_set_mux(struct pinctrl_dev *pctldev,
return 0; return 0;
} }
static int visconti_gpio_request_enable(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned int pin)
{
struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev);
const struct visconti_mux *gpio_mux = &priv->devdata->gpio_mux[pin];
unsigned long flags;
unsigned int val;
dev_dbg(priv->dev, "%s: pin = %d\n", __func__, pin);
/* update mux */
spin_lock_irqsave(&priv->lock, flags);
val = readl(priv->base + gpio_mux->offset);
val &= ~gpio_mux->mask;
val |= gpio_mux->val;
writel(val, priv->base + gpio_mux->offset);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static const struct pinmux_ops visconti_pinmux_ops = { static const struct pinmux_ops visconti_pinmux_ops = {
.get_functions_count = visconti_get_functions_count, .get_functions_count = visconti_get_functions_count,
.get_function_name = visconti_get_function_name, .get_function_name = visconti_get_function_name,
.get_function_groups = visconti_get_function_groups, .get_function_groups = visconti_get_function_groups,
.set_mux = visconti_set_mux, .set_mux = visconti_set_mux,
.gpio_request_enable = visconti_gpio_request_enable,
.strict = true, .strict = true,
}; };
......
...@@ -75,7 +75,7 @@ struct gpiod_hog { ...@@ -75,7 +75,7 @@ struct gpiod_hog {
* gpiod_get_index() * gpiod_get_index()
*/ */
#define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \ #define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \
{ \ (struct gpiod_lookup) { \
.key = _key, \ .key = _key, \
.chip_hwnum = _chip_hwnum, \ .chip_hwnum = _chip_hwnum, \
.con_id = _con_id, \ .con_id = _con_id, \
...@@ -87,7 +87,7 @@ struct gpiod_hog { ...@@ -87,7 +87,7 @@ struct gpiod_hog {
* Simple definition of a single GPIO hog in an array. * Simple definition of a single GPIO hog in an array.
*/ */
#define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \ #define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \
{ \ (struct gpiod_hog) { \
.chip_label = _chip_label, \ .chip_label = _chip_label, \
.chip_hwnum = _chip_hwnum, \ .chip_hwnum = _chip_hwnum, \
.line_name = _line_name, \ .line_name = _line_name, \
......
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/* /*
* <linux/gpio.h> - userspace ABI for the GPIO character devices * <linux/gpio.h> - userspace ABI for the GPIO character devices
* *
...@@ -212,7 +212,7 @@ struct gpio_v2_line_request { ...@@ -212,7 +212,7 @@ struct gpio_v2_line_request {
* @offset: the local offset on this GPIO chip, fill this in when * @offset: the local offset on this GPIO chip, fill this in when
* requesting the line information from the kernel * requesting the line information from the kernel
* @num_attrs: the number of attributes in @attrs * @num_attrs: the number of attributes in @attrs
* @flags: flags for the GPIO lines, with values from &enum * @flags: flags for this GPIO line, with values from &enum
* gpio_v2_line_flag, such as %GPIO_V2_LINE_FLAG_ACTIVE_LOW, * gpio_v2_line_flag, such as %GPIO_V2_LINE_FLAG_ACTIVE_LOW,
* %GPIO_V2_LINE_FLAG_OUTPUT etc, added together. * %GPIO_V2_LINE_FLAG_OUTPUT etc, added together.
* @attrs: the configuration attributes associated with the line * @attrs: the configuration attributes associated with the line
......
...@@ -83,7 +83,7 @@ EXPORT_SYMBOL(get_option); ...@@ -83,7 +83,7 @@ EXPORT_SYMBOL(get_option);
* get_options - Parse a string into a list of integers * get_options - Parse a string into a list of integers
* @str: String to be parsed * @str: String to be parsed
* @nints: size of integer array * @nints: size of integer array
* @ints: integer array * @ints: integer array (must have room for at least one element)
* *
* This function parses a string containing a comma-separated * This function parses a string containing a comma-separated
* list of integers, a hyphen-separated range of _positive_ integers, * list of integers, a hyphen-separated range of _positive_ integers,
...@@ -91,6 +91,14 @@ EXPORT_SYMBOL(get_option); ...@@ -91,6 +91,14 @@ EXPORT_SYMBOL(get_option);
* full, or when no more numbers can be retrieved from the * full, or when no more numbers can be retrieved from the
* string. * string.
* *
* When @nints is 0, the function just validates the given @str and
* returns the amount of parseable integers as described below.
*
* Returns:
*
* The first element is filled by the number of collected integers
* in the range. The rest is what was parsed from the @str.
*
* Return value is the character in the string which caused * Return value is the character in the string which caused
* the parse to end (typically a null terminator, if @str is * the parse to end (typically a null terminator, if @str is
* completely parseable). * completely parseable).
...@@ -98,15 +106,20 @@ EXPORT_SYMBOL(get_option); ...@@ -98,15 +106,20 @@ EXPORT_SYMBOL(get_option);
char *get_options(const char *str, int nints, int *ints) char *get_options(const char *str, int nints, int *ints)
{ {
bool validate = (nints == 0);
int res, i = 1; int res, i = 1;
while (i < nints) { while (i < nints || validate) {
res = get_option((char **)&str, ints + i); int *pint = validate ? ints : ints + i;
res = get_option((char **)&str, pint);
if (res == 0) if (res == 0)
break; break;
if (res == 3) { if (res == 3) {
int n = validate ? 0 : nints - i;
int range_nums; int range_nums;
range_nums = get_range((char **)&str, ints + i, nints - i);
range_nums = get_range((char **)&str, pint, n);
if (range_nums < 0) if (range_nums < 0)
break; break;
/* /*
......
...@@ -18,6 +18,26 @@ static const int cmdline_test_values[] = { ...@@ -18,6 +18,26 @@ static const int cmdline_test_values[] = {
1, 3, 2, 1, 1, 1, 3, 1, 1, 3, 2, 1, 1, 1, 3, 1,
}; };
static_assert(ARRAY_SIZE(cmdline_test_strings) == ARRAY_SIZE(cmdline_test_values));
static const char *cmdline_test_range_strings[] = {
"-7" , "--7" , "-1-2" , "7--9",
"7-" , "-7--9", "7-9," , "9-7" ,
"5-a", "a-5" , "5-8" , ",8-5",
"+,1", "-,4" , "-3,0-1,6", "4,-" ,
" +2", " -9" , "0-1,-3,6", "- 9" ,
};
static const int cmdline_test_range_values[][16] = {
{ 1, -7, }, { 0, -0, }, { 4, -1, 0, +1, 2, }, { 0, 7, },
{ 0, +7, }, { 0, -7, }, { 3, +7, 8, +9, 0, }, { 0, 9, },
{ 0, +5, }, { 0, -0, }, { 4, +5, 6, +7, 8, }, { 0, 0, },
{ 0, +0, }, { 0, -0, }, { 4, -3, 0, +1, 6, }, { 1, 4, },
{ 0, +0, }, { 0, -0, }, { 4, +0, 1, -3, 6, }, { 0, 0, },
};
static_assert(ARRAY_SIZE(cmdline_test_range_strings) == ARRAY_SIZE(cmdline_test_range_values));
static void cmdline_do_one_test(struct kunit *test, const char *in, int rc, int offset) static void cmdline_do_one_test(struct kunit *test, const char *in, int rc, int offset)
{ {
const char *fmt = "Pattern: %s"; const char *fmt = "Pattern: %s";
...@@ -84,10 +104,46 @@ static void cmdline_test_tail_int(struct kunit *test) ...@@ -84,10 +104,46 @@ static void cmdline_test_tail_int(struct kunit *test)
} while (++i < ARRAY_SIZE(cmdline_test_strings)); } while (++i < ARRAY_SIZE(cmdline_test_strings));
} }
static void cmdline_do_one_range_test(struct kunit *test, const char *in,
unsigned int n, const int *e)
{
unsigned int i;
int r[16];
int *p;
memset(r, 0, sizeof(r));
get_options(in, ARRAY_SIZE(r), r);
KUNIT_EXPECT_EQ_MSG(test, r[0], e[0], "in test %u (parsed) expected %d numbers, got %d",
n, e[0], r[0]);
for (i = 1; i < ARRAY_SIZE(r); i++)
KUNIT_EXPECT_EQ_MSG(test, r[i], e[i], "in test %u at %u", n, i);
memset(r, 0, sizeof(r));
get_options(in, 0, r);
KUNIT_EXPECT_EQ_MSG(test, r[0], e[0], "in test %u (validated) expected %d numbers, got %d",
n, e[0], r[0]);
p = memchr_inv(&r[1], 0, sizeof(r) - sizeof(r[0]));
KUNIT_EXPECT_PTR_EQ_MSG(test, p, (int *)0, "in test %u at %u out of bound", n, p - r);
}
static void cmdline_test_range(struct kunit *test)
{
unsigned int i = 0;
do {
const char *str = cmdline_test_range_strings[i];
const int *e = cmdline_test_range_values[i];
cmdline_do_one_range_test(test, str, i, e);
} while (++i < ARRAY_SIZE(cmdline_test_range_strings));
}
static struct kunit_case cmdline_test_cases[] = { static struct kunit_case cmdline_test_cases[] = {
KUNIT_CASE(cmdline_test_noint), KUNIT_CASE(cmdline_test_noint),
KUNIT_CASE(cmdline_test_lead_int), KUNIT_CASE(cmdline_test_lead_int),
KUNIT_CASE(cmdline_test_tail_int), KUNIT_CASE(cmdline_test_tail_int),
KUNIT_CASE(cmdline_test_range),
{} {}
}; };
......
...@@ -32,74 +32,6 @@ ...@@ -32,74 +32,6 @@
* following api will request gpio lines, do the operation and then * following api will request gpio lines, do the operation and then
* release these lines. * release these lines.
*/ */
/**
* gpiotools_request_linehandle() - request gpio lines in a gpiochip
* @device_name: The name of gpiochip without prefix "/dev/",
* such as "gpiochip0"
* @lines: An array desired lines, specified by offset
* index for the associated GPIO device.
* @num_lines: The number of lines to request.
* @flag: The new flag for requsted gpio. Reference
* "linux/gpio.h" for the meaning of flag.
* @data: Default value will be set to gpio when flag is
* GPIOHANDLE_REQUEST_OUTPUT.
* @consumer_label: The name of consumer, such as "sysfs",
* "powerkey". This is useful for other users to
* know who is using.
*
* Request gpio lines through the ioctl provided by chardev. User
* could call gpiotools_set_values() and gpiotools_get_values() to
* read and write respectively through the returned fd. Call
* gpiotools_release_linehandle() to release these lines after that.
*
* Return: On success return the fd;
* On failure return the errno.
*/
int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
unsigned int num_lines, unsigned int flag,
struct gpiohandle_data *data,
const char *consumer_label)
{
struct gpiohandle_request req;
char *chrdev_name;
int fd;
int i;
int ret;
ret = asprintf(&chrdev_name, "/dev/%s", device_name);
if (ret < 0)
return -ENOMEM;
fd = open(chrdev_name, 0);
if (fd == -1) {
ret = -errno;
fprintf(stderr, "Failed to open %s, %s\n",
chrdev_name, strerror(errno));
goto exit_free_name;
}
for (i = 0; i < num_lines; i++)
req.lineoffsets[i] = lines[i];
req.flags = flag;
strcpy(req.consumer_label, consumer_label);
req.lines = num_lines;
if (flag & GPIOHANDLE_REQUEST_OUTPUT)
memcpy(req.default_values, data, sizeof(req.default_values));
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (ret == -1) {
ret = -errno;
fprintf(stderr, "Failed to issue %s (%d), %s\n",
"GPIO_GET_LINEHANDLE_IOCTL", ret, strerror(errno));
}
if (close(fd) == -1)
perror("Failed to close GPIO character device file");
exit_free_name:
free(chrdev_name);
return ret < 0 ? ret : req.fd;
}
/** /**
* gpiotools_request_line() - request gpio lines in a gpiochip * gpiotools_request_line() - request gpio lines in a gpiochip
...@@ -215,27 +147,6 @@ int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values) ...@@ -215,27 +147,6 @@ int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)
return ret; return ret;
} }
/**
* gpiotools_release_linehandle(): Release the line(s) of gpiochip
* @fd: The fd returned by
* gpiotools_request_linehandle().
*
* Return: On success return 0;
* On failure return the errno.
*/
int gpiotools_release_linehandle(const int fd)
{
int ret;
ret = close(fd);
if (ret == -1) {
perror("Failed to close GPIO LINEHANDLE device file");
ret = -errno;
}
return ret;
}
/** /**
* gpiotools_release_line(): Release the line(s) of gpiochip * gpiotools_release_line(): Release the line(s) of gpiochip
* @fd: The fd returned by * @fd: The fd returned by
......
...@@ -24,12 +24,6 @@ static inline int check_prefix(const char *str, const char *prefix) ...@@ -24,12 +24,6 @@ static inline int check_prefix(const char *str, const char *prefix)
strncmp(str, prefix, strlen(prefix)) == 0; strncmp(str, prefix, strlen(prefix)) == 0;
} }
int gpiotools_request_linehandle(const char *device_name, unsigned int *lines,
unsigned int num_lines, unsigned int flag,
struct gpiohandle_data *data,
const char *consumer_label);
int gpiotools_release_linehandle(const int fd);
int gpiotools_request_line(const char *device_name, int gpiotools_request_line(const char *device_name,
unsigned int *lines, unsigned int *lines,
unsigned int num_lines, unsigned int num_lines,
......
...@@ -126,15 +126,6 @@ ARCH ?= $(SUBARCH) ...@@ -126,15 +126,6 @@ ARCH ?= $(SUBARCH)
export KSFT_KHDR_INSTALL_DONE := 1 export KSFT_KHDR_INSTALL_DONE := 1
export BUILD export BUILD
# build and run gpio when output directory is the src dir.
# gpio has dependency on tools/gpio and builds tools/gpio
# objects in the src directory in all cases making the src
# repo dirty even when objects are relocated.
ifneq (1,$(DEFAULT_INSTALL_HDR_PATH))
TMP := $(filter-out gpio, $(TARGETS))
TARGETS := $(TMP)
endif
# set default goal to all, so make without a target runs all, even when # set default goal to all, so make without a target runs all, even when
# all isn't the first target in the file. # all isn't the first target in the file.
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
VAR_CFLAGS := $(shell pkg-config --cflags mount 2>/dev/null)
VAR_LDLIBS := $(shell pkg-config --libs mount 2>/dev/null)
ifeq ($(VAR_LDLIBS),)
VAR_LDLIBS := -lmount -I/usr/include/libmount
endif
CFLAGS += -O2 -g -std=gnu99 -Wall -I../../../../usr/include/ $(VAR_CFLAGS)
LDLIBS += $(VAR_LDLIBS)
TEST_PROGS := gpio-mockup.sh TEST_PROGS := gpio-mockup.sh
TEST_FILES := gpio-mockup-sysfs.sh TEST_FILES := gpio-mockup-sysfs.sh
TEST_GEN_PROGS_EXTENDED := gpio-mockup-chardev TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev
KSFT_KHDR_INSTALL := 1
include ../lib.mk include ../lib.mk
GPIODIR := $(realpath ../../../gpio)
GPIOOUT := $(OUTPUT)/tools-gpio/
GPIOOBJ := $(GPIOOUT)/gpio-utils.o
CLEAN += ; $(RM) -rf $(GPIOOUT)
$(TEST_GEN_PROGS_EXTENDED): $(GPIOOBJ)
$(GPIOOUT):
mkdir -p $@
$(GPIOOBJ): $(GPIOOUT)
$(MAKE) OUTPUT=$(GPIOOUT) -C $(GPIODIR)
CONFIG_GPIOLIB=y CONFIG_GPIOLIB=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_MOCKUP=m CONFIG_GPIO_MOCKUP=m
// SPDX-License-Identifier: GPL-2.0
/*
* GPIO mockup cdev test helper
*
* Copyright (C) 2020 Kent Gibson
*/
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>
#define CONSUMER "gpio-mockup-cdev"
static int request_line_v2(int cfd, unsigned int offset,
uint64_t flags, unsigned int val)
{
struct gpio_v2_line_request req;
int ret;
memset(&req, 0, sizeof(req));
req.num_lines = 1;
req.offsets[0] = offset;
req.config.flags = flags;
strcpy(req.consumer, CONSUMER);
if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
req.config.num_attrs = 1;
req.config.attrs[0].mask = 1;
req.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
if (val)
req.config.attrs[0].attr.values = 1;
}
ret = ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req);
if (ret == -1)
return -errno;
return req.fd;
}
static int get_value_v2(int lfd)
{
struct gpio_v2_line_values vals;
int ret;
memset(&vals, 0, sizeof(vals));
vals.mask = 1;
ret = ioctl(lfd, GPIO_V2_LINE_GET_VALUES_IOCTL, &vals);
if (ret == -1)
return -errno;
return vals.bits & 0x1;
}
static int request_line_v1(int cfd, unsigned int offset,
uint32_t flags, unsigned int val)
{
struct gpiohandle_request req;
int ret;
memset(&req, 0, sizeof(req));
req.lines = 1;
req.lineoffsets[0] = offset;
req.flags = flags;
strcpy(req.consumer_label, CONSUMER);
if (flags & GPIOHANDLE_REQUEST_OUTPUT)
req.default_values[0] = val;
ret = ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (ret == -1)
return -errno;
return req.fd;
}
static int get_value_v1(int lfd)
{
struct gpiohandle_data vals;
int ret;
memset(&vals, 0, sizeof(vals));
ret = ioctl(lfd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &vals);
if (ret == -1)
return -errno;
return vals.values[0];
}
static void usage(char *prog)
{
printf("Usage: %s [-l] [-b <bias>] [-s <value>] [-u <uAPI>] <gpiochip> <offset>\n", prog);
printf(" -b: set line bias to one of pull-down, pull-up, disabled\n");
printf(" (default is to leave bias unchanged):\n");
printf(" -l: set line active low (default is active high)\n");
printf(" -s: set line value (default is to get line value)\n");
printf(" -u: uAPI version to use (default is 2)\n");
exit(-1);
}
static int wait_signal(void)
{
int sig;
sigset_t wset;
sigemptyset(&wset);
sigaddset(&wset, SIGHUP);
sigaddset(&wset, SIGINT);
sigaddset(&wset, SIGTERM);
sigwait(&wset, &sig);
return sig;
}
int main(int argc, char *argv[])
{
char *chip;
int opt, ret, cfd, lfd;
unsigned int offset, val, abiv;
uint32_t flags_v1;
uint64_t flags_v2;
abiv = 2;
ret = 0;
flags_v1 = GPIOHANDLE_REQUEST_INPUT;
flags_v2 = GPIO_V2_LINE_FLAG_INPUT;
while ((opt = getopt(argc, argv, "lb:s:u:")) != -1) {
switch (opt) {
case 'l':
flags_v1 |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
flags_v2 |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
break;
case 'b':
if (strcmp("pull-up", optarg) == 0) {
flags_v1 |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
} else if (strcmp("pull-down", optarg) == 0) {
flags_v1 |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;
flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
} else if (strcmp("disabled", optarg) == 0) {
flags_v1 |= GPIOHANDLE_REQUEST_BIAS_DISABLE;
flags_v2 |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
}
break;
case 's':
val = atoi(optarg);
flags_v1 &= ~GPIOHANDLE_REQUEST_INPUT;
flags_v1 |= GPIOHANDLE_REQUEST_OUTPUT;
flags_v2 &= ~GPIO_V2_LINE_FLAG_INPUT;
flags_v2 |= GPIO_V2_LINE_FLAG_OUTPUT;
break;
case 'u':
abiv = atoi(optarg);
break;
default:
usage(argv[0]);
}
}
if (argc < optind + 2)
usage(argv[0]);
chip = argv[optind];
offset = atoi(argv[optind + 1]);
cfd = open(chip, 0);
if (cfd == -1) {
fprintf(stderr, "Failed to open %s: %s\n", chip, strerror(errno));
return -errno;
}
if (abiv == 1)
lfd = request_line_v1(cfd, offset, flags_v1, val);
else
lfd = request_line_v2(cfd, offset, flags_v2, val);
close(cfd);
if (lfd < 0) {
fprintf(stderr, "Failed to request %s:%d: %s\n", chip, offset, strerror(-lfd));
return lfd;
}
if (flags_v2 & GPIO_V2_LINE_FLAG_OUTPUT) {
wait_signal();
} else {
if (abiv == 1)
ret = get_value_v1(lfd);
else
ret = get_value_v2(lfd);
}
close(lfd);
return ret;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* GPIO chardev test helper
*
* Copyright (C) 2016 Bamvor Jian Zhang
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <libmount.h>
#include <err.h>
#include <dirent.h>
#include <linux/gpio.h>
#include "../../../gpio/gpio-utils.h"
#define CONSUMER "gpio-selftest"
#define GC_NUM 10
enum direction {
OUT,
IN
};
static int get_debugfs(char **path)
{
struct libmnt_context *cxt;
struct libmnt_table *tb;
struct libmnt_iter *itr = NULL;
struct libmnt_fs *fs;
int found = 0, ret;
cxt = mnt_new_context();
if (!cxt)
err(EXIT_FAILURE, "libmount context allocation failed");
itr = mnt_new_iter(MNT_ITER_FORWARD);
if (!itr)
err(EXIT_FAILURE, "failed to initialize libmount iterator");
if (mnt_context_get_mtab(cxt, &tb))
err(EXIT_FAILURE, "failed to read mtab");
while (mnt_table_next_fs(tb, itr, &fs) == 0) {
const char *type = mnt_fs_get_fstype(fs);
if (!strcmp(type, "debugfs")) {
found = 1;
break;
}
}
if (found) {
ret = asprintf(path, "%s/gpio", mnt_fs_get_target(fs));
if (ret < 0)
err(EXIT_FAILURE, "failed to format string");
}
mnt_free_iter(itr);
mnt_free_context(cxt);
if (!found)
return -1;
return 0;
}
static int gpio_debugfs_get(const char *consumer, int *dir, int *value)
{
char *debugfs;
FILE *f;
char *line = NULL;
size_t len = 0;
char *cur;
int found = 0;
if (get_debugfs(&debugfs) != 0)
err(EXIT_FAILURE, "debugfs is not mounted");
f = fopen(debugfs, "r");
if (!f)
err(EXIT_FAILURE, "read from gpio debugfs failed");
/*
* gpio-2 ( |gpio-selftest ) in lo
*/
while (getline(&line, &len, f) != -1) {
cur = strstr(line, consumer);
if (cur == NULL)
continue;
cur = strchr(line, ')');
if (!cur)
continue;
cur += 2;
if (!strncmp(cur, "out", 3)) {
*dir = OUT;
cur += 4;
} else if (!strncmp(cur, "in", 2)) {
*dir = IN;
cur += 4;
}
if (!strncmp(cur, "hi", 2))
*value = 1;
else if (!strncmp(cur, "lo", 2))
*value = 0;
found = 1;
break;
}
free(debugfs);
fclose(f);
free(line);
if (!found)
return -1;
return 0;
}
static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret)
{
struct gpiochip_info *cinfo;
struct gpiochip_info *current;
const struct dirent *ent;
DIR *dp;
char *chrdev_name;
int fd;
int i = 0;
cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1);
if (!cinfo)
err(EXIT_FAILURE, "gpiochip_info allocation failed");
current = cinfo;
dp = opendir("/dev");
if (!dp) {
*ret = -errno;
goto error_out;
} else {
*ret = 0;
}
while (ent = readdir(dp), ent) {
if (check_prefix(ent->d_name, "gpiochip")) {
*ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name);
if (*ret < 0)
goto error_out;
fd = open(chrdev_name, 0);
if (fd == -1) {
*ret = -errno;
fprintf(stderr, "Failed to open %s\n",
chrdev_name);
goto error_close_dir;
}
*ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current);
if (*ret == -1) {
perror("Failed to issue CHIPINFO IOCTL\n");
goto error_close_dir;
}
close(fd);
if (strcmp(current->label, gpiochip_name) == 0
|| check_prefix(current->label, gpiochip_name)) {
*ret = 0;
current++;
i++;
}
}
}
if ((!*ret && i == 0) || *ret < 0) {
free(cinfo);
cinfo = NULL;
}
if (!*ret && i > 0) {
cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i);
*ret = i;
}
error_close_dir:
closedir(dp);
error_out:
if (*ret < 0)
err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret));
return cinfo;
}
int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value)
{
struct gpiohandle_data data;
unsigned int lines[] = {line};
int fd;
int debugfs_dir = IN;
int debugfs_value = 0;
int ret;
data.values[0] = value;
ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data,
CONSUMER);
if (ret < 0)
goto fail_out;
else
fd = ret;
ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value);
if (ret) {
ret = -EINVAL;
goto fail_out;
}
if (flag & GPIOHANDLE_REQUEST_INPUT) {
if (debugfs_dir != IN) {
errno = -EINVAL;
ret = -errno;
}
} else if (flag & GPIOHANDLE_REQUEST_OUTPUT) {
if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW)
debugfs_value = !debugfs_value;
if (!(debugfs_dir == OUT && value == debugfs_value)) {
errno = -EINVAL;
ret = -errno;
}
}
gpiotools_release_linehandle(fd);
fail_out:
if (ret)
err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>",
cinfo->name, line, flag, value);
return ret;
}
void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line)
{
printf("line<%d>", line);
gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0);
printf(".");
gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1);
printf(".");
gpio_pin_test(cinfo, line,
GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
0);
printf(".");
gpio_pin_test(cinfo, line,
GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
1);
printf(".");
gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0);
printf(".");
}
/*
* ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip
* Return 0 if successful or exit with EXIT_FAILURE if test failed.
* gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g.
* gpio-mockup
* is_valid_gpio_chip: Whether the gpio_chip is valid. 1 means valid,
* 0 means invalid which could not be found by
* list_gpiochip.
*/
int main(int argc, char *argv[])
{
char *prefix;
int valid;
struct gpiochip_info *cinfo;
struct gpiochip_info *current;
int i;
int ret;
if (argc < 3) {
printf("Usage: %s prefix is_valid", argv[0]);
exit(EXIT_FAILURE);
}
prefix = argv[1];
valid = strcmp(argv[2], "true") == 0 ? 1 : 0;
printf("Test gpiochip %s: ", prefix);
cinfo = list_gpiochip(prefix, &ret);
if (!cinfo) {
if (!valid && ret == 0) {
printf("Invalid test successful\n");
ret = 0;
goto out;
} else {
ret = -EINVAL;
goto out;
}
} else if (cinfo && !valid) {
ret = -EINVAL;
goto out;
}
current = cinfo;
for (i = 0; i < ret; i++) {
gpio_pin_tests(current, 0);
gpio_pin_tests(current, current->lines - 1);
gpio_pin_tests(current, random() % current->lines);
current++;
}
ret = 0;
printf("successful\n");
out:
if (ret)
fprintf(stderr, "gpio<%s> test failed\n", prefix);
if (cinfo)
free(cinfo);
if (ret)
exit(EXIT_FAILURE);
return ret;
}
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
is_consistent()
{
val=
active_low_sysfs=`cat $GPIO_SYSFS/gpio$nr/active_low` # Overrides functions in gpio-mockup.sh to test using the GPIO SYSFS uAPI
val_sysfs=`cat $GPIO_SYSFS/gpio$nr/value`
dir_sysfs=`cat $GPIO_SYSFS/gpio$nr/direction`
gpio_this_debugfs=`cat $GPIO_DEBUGFS |grep "gpio-$nr" | sed "s/(.*)//g"` SYSFS=`grep -w sysfs /proc/mounts | cut -f2 -d' '`
dir_debugfs=`echo $gpio_this_debugfs | awk '{print $2}'` [ -d "$SYSFS" ] || skip "sysfs is not mounted"
val_debugfs=`echo $gpio_this_debugfs | awk '{print $3}'`
if [ $val_debugfs = "lo" ]; then
val=0
elif [ $val_debugfs = "hi" ]; then
val=1
fi
if [ $active_low_sysfs = "1" ]; then GPIO_SYSFS="${SYSFS}/class/gpio"
if [ $val = "0" ]; then [ -d "$GPIO_SYSFS" ] || skip "CONFIG_GPIO_SYSFS is not selected"
val="1"
else
val="0"
fi
fi
if [ $val_sysfs = $val ] && [ $dir_sysfs = $dir_debugfs ]; then PLATFORM_SYSFS=$SYSFS/devices/platform
echo -n "."
else
echo "test fail, exit"
die
fi
}
test_pin_logic() sysfs_nr=
{ sysfs_ldir=
nr=$1
direction=$2
active_low=$3
value=$4
echo $direction > $GPIO_SYSFS/gpio$nr/direction
echo $active_low > $GPIO_SYSFS/gpio$nr/active_low
if [ $direction = "out" ]; then
echo $value > $GPIO_SYSFS/gpio$nr/value
fi
is_consistent $nr
}
test_one_pin() # determine the sysfs GPIO number given the $chip and $offset
# e.g. gpiochip1:32
find_sysfs_nr()
{ {
nr=$1 # e.g. /sys/devices/platform/gpio-mockup.1/gpiochip1
local platform=$(find $PLATFORM_SYSFS -mindepth 2 -maxdepth 2 -type d -name $chip)
echo -n "test pin<$nr>" [ "$platform" ] || fail "can't find platform of $chip"
# e.g. /sys/devices/platform/gpio-mockup.1/gpio/gpiochip508/base
echo $nr > $GPIO_SYSFS/export 2>/dev/null local base=$(find ${platform%/*}/gpio/ -mindepth 2 -maxdepth 2 -type f -name base)
[ "$base" ] || fail "can't find base of $chip"
if [ X$? != X0 ]; then sysfs_nr=$(($(< "$base") + $offset))
echo "test GPIO pin $nr failed" sysfs_ldir="$GPIO_SYSFS/gpio$sysfs_nr"
die
fi
#"Checking if the sysfs is consistent with debugfs: "
is_consistent $nr
#"Checking the logic of active_low: "
test_pin_logic $nr out 1 1
test_pin_logic $nr out 1 0
test_pin_logic $nr out 0 1
test_pin_logic $nr out 0 0
#"Checking the logic of direction: "
test_pin_logic $nr in 1 1
test_pin_logic $nr out 1 0
test_pin_logic $nr low 0 1
test_pin_logic $nr high 0 0
echo $nr > $GPIO_SYSFS/unexport
echo "successful"
} }
test_one_pin_fail() acquire_line()
{ {
nr=$1 [ "$sysfs_nr" ] && return
find_sysfs_nr
echo $nr > $GPIO_SYSFS/export 2>/dev/null echo "$sysfs_nr" > "$GPIO_SYSFS/export"
if [ X$? != X0 ]; then
echo "test invalid pin $nr successful"
else
echo "test invalid pin $nr failed"
echo $nr > $GPIO_SYSFS/unexport 2>/dev/null
die
fi
} }
list_chip() # The helpers being overridden...
get_line()
{ {
echo `ls -d $GPIO_DRV_SYSFS/gpiochip* 2>/dev/null` [ -e "$sysfs_ldir/value" ] && echo $(< "$sysfs_ldir/value")
} }
test_chip() set_line()
{ {
chip=$1 acquire_line
name=`basename $chip`
base=`cat $chip/base` for option in $*; do
ngpio=`cat $chip/ngpio` case $option in
printf "%-10s %-5s %-5s\n" $name $base $ngpio active-high)
if [ $ngpio = "0" ]; then echo 0 > "$sysfs_ldir/active_low"
echo "number of gpio is zero is not allowed". ;;
fi active-low)
test_one_pin $base echo 1 > "$sysfs_ldir/active_low"
test_one_pin $(($base + $ngpio - 1)) ;;
test_one_pin $((( RANDOM % $ngpio ) + $base )) input)
echo "in" > "$sysfs_ldir/direction"
;;
0)
echo "out" > "$sysfs_ldir/direction"
echo 0 > "$sysfs_ldir/value"
;;
1)
echo "out" > "$sysfs_ldir/direction"
echo 1 > "$sysfs_ldir/value"
;;
esac
done
} }
test_chips_sysfs() release_line()
{ {
gpiochip=`list_chip $module` [ "$sysfs_nr" ] || return 0
if [ X"$gpiochip" = X ]; then echo "$sysfs_nr" > "$GPIO_SYSFS/unexport"
if [ X"$valid" = Xfalse ]; then sysfs_nr=
echo "successful" sysfs_ldir=
else
echo "fail"
die
fi
else
for chip in $gpiochip; do
test_chip $chip
done
fi
} }
#!/bin/bash #!/bin/bash -efu
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
#exit status #exit status
#1: Internal error #0: success
#2: sysfs/debugfs not mount #1: fail
#3: insert module fail when gpio-mockup is a module. #4: skip test - including run as non-root user
#4: Skip test including run as non-root user.
#5: other reason. BASE=${0%/*}
SYSFS=
GPIO_SYSFS=
GPIO_DRV_SYSFS=
DEBUGFS= DEBUGFS=
GPIO_DEBUGFS= GPIO_DEBUGFS=
dev_type= dev_type="cdev"
module= module="gpio-mockup"
verbose=
full_test=
random=
uapi_opt=
active_opt=
bias_opt=
line_set_pid=
# Kselftest framework requirement - SKIP code is 4. # Kselftest return codes
ksft_fail=1
ksft_skip=4 ksft_skip=4
usage() usage()
{ {
echo "Usage:" echo "Usage:"
echo "$0 [-f] [-m name] [-t type]" echo "$0 [-frv] [-t type]"
echo "-f: full test. It maybe conflict with existence gpio device." echo "-f: full test (minimal set run by default)"
echo "-m: module name, default name is gpio-mockup. It could also test" echo "-r: test random lines as well as fence posts"
echo " other gpio device." echo "-t: interface type:"
echo "-t: interface type: chardev(char device) and sysfs(being" echo " cdev (character device ABI) - default"
echo " deprecated). The first one is default" echo " cdev_v1 (deprecated character device ABI)"
echo "" echo " sysfs (deprecated SYSFS ABI)"
echo "$0 -h" echo "-v: verbose progress reporting"
echo "This usage" exit $ksft_fail
} }
prerequisite() skip()
{ {
msg="skip all tests:" echo "$*" >&2
if [ $UID != 0 ]; then echo "GPIO $module test SKIP"
echo $msg must be run as root >&2
exit $ksft_skip exit $ksft_skip
fi
SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
if [ ! -d "$SYSFS" ]; then
echo $msg sysfs is not mounted >&2
exit 2
fi
GPIO_SYSFS=`echo $SYSFS/class/gpio`
GPIO_DRV_SYSFS=`echo $SYSFS/devices/platform/$module/gpio`
DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'`
if [ ! -d "$DEBUGFS" ]; then
echo $msg debugfs is not mounted >&2
exit 2
fi
GPIO_DEBUGFS=`echo $DEBUGFS/gpio`
source gpio-mockup-sysfs.sh
} }
try_insert_module() prerequisite()
{ {
if [ -d "$GPIO_DRV_SYSFS" ]; then [ $(id -u) -eq 0 ] || skip "must be run as root"
echo "$GPIO_DRV_SYSFS exist. Skip insert module"
else DEBUGFS=$(grep -w debugfs /proc/mounts | cut -f2 -d' ')
modprobe -q $module $1 [ -d "$DEBUGFS" ] || skip "debugfs is not mounted"
if [ X$? != X0 ]; then
echo $msg insmod $module failed >&2 GPIO_DEBUGFS=$DEBUGFS/$module
exit 3
fi
fi
} }
remove_module() remove_module()
...@@ -74,133 +59,345 @@ remove_module() ...@@ -74,133 +59,345 @@ remove_module()
modprobe -r -q $module modprobe -r -q $module
} }
die() cleanup()
{ {
set +e
release_line
remove_module remove_module
exit 5 jobs -p | xargs -r kill > /dev/null 2>&1
} }
test_chips() fail()
{ {
if [ X$dev_type = Xsysfs ]; then echo "test failed: $*" >&2
echo "WARNING: sysfs ABI of gpio is going to deprecated." echo "GPIO $module test FAIL"
test_chips_sysfs $* exit $ksft_fail
else }
$BASE/gpio-mockup-chardev $*
fi try_insert_module()
{
modprobe -q $module "$1" || fail "insert $module failed with error $?"
}
log()
{
[ -z "$verbose" ] || echo "$*"
}
# The following line helpers, release_Line, get_line and set_line, all
# make use of the global $chip and $offset variables.
#
# This implementation drives the GPIO character device (cdev) uAPI.
# Other implementations may override these to test different uAPIs.
# Release any resources related to the line
release_line()
{
[ "$line_set_pid" ] && kill $line_set_pid && wait $line_set_pid || true
line_set_pid=
}
# Read the current value of the line
get_line()
{
release_line
local cdev_opts=${uapi_opt}${active_opt}
$BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset
echo $?
} }
gpio_test() # Set the state of the line
#
# Changes to line configuration are provided as parameters.
# The line is assumed to be an output if the line value 0 or 1 is
# specified, else an input.
set_line()
{ {
param=$1 local val=
valid=$2
release_line
# parse config options...
for option in $*; do
case $option in
active-low)
active_opt="-l "
;;
active-high)
active_opt=
;;
bias-none)
bias_opt=
;;
pull-down)
bias_opt="-bpull-down "
;;
pull-up)
bias_opt="-bpull-up "
;;
0)
val=0
;;
1)
val=1
;;
esac
done
if [ X"$param" = X ]; then local cdev_opts=${uapi_opt}${active_opt}
die if [ "$val" ]; then
$BASE/gpio-mockup-cdev $cdev_opts -s$val /dev/$chip $offset &
# failure to set is detected by reading mockup and toggling values
line_set_pid=$!
# allow for gpio-mockup-cdev to launch and request line
# (there is limited value in checking if line has been requested)
sleep 0.01
elif [ "$bias_opt" ]; then
cdev_opts=${cdev_opts}${bias_opt}
$BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset || true
fi fi
try_insert_module "gpio_mockup_ranges=$param"
echo -n "GPIO $module test with ranges: <"
echo "$param>: "
printf "%-10s %s\n" $param
test_chips $module $valid
remove_module
} }
BASE=`dirname $0` assert_line()
{
local val
# don't need any retry here as set_mock allows for propagation
val=$(get_line)
[ "$val" = "$1" ] || fail "line value is ${val:-empty} when $1 was expected"
}
# The following mockup helpers all make use of the $mock_line
assert_mock()
{
local backoff_wait=10
local retry=0
local val
# retry allows for set propagation from uAPI to mockup
while true; do
val=$(< $mock_line)
[ "$val" = "$1" ] && break
retry=$((retry + 1))
[ $retry -lt 5 ] || fail "mockup $mock_line value ${val:-empty} when $1 expected"
sleep $(printf "%0.2f" $((backoff_wait))e-3)
backoff_wait=$((backoff_wait * 2))
done
}
set_mock()
{
echo "$1" > $mock_line
# allow for set propagation - so we won't be in a race with set_line
assert_mock "$1"
}
# test the functionality of a line
#
# The line is set from the mockup side and is read from the userspace side
# (input), and is set from the userspace side and is read from the mockup side
# (output).
#
# Setting the mockup pull using the userspace interface bias settings is
# tested where supported by the userspace interface (cdev).
test_line()
{
chip=$1
offset=$2
log "test_line $chip $offset"
mock_line=$GPIO_DEBUGFS/$chip/$offset
[ -e "$mock_line" ] || fail "missing line $chip:$offset"
dev_type= # test input active-high
TEMP=`getopt -o fhm:t: -n '$0' -- "$@"` set_mock 1
set_line input active-high
assert_line 1
set_mock 0
assert_line 0
set_mock 1
assert_line 1
if [ "$?" != "0" ]; then if [ "$full_test" ]; then
echo "Parameter process failed, Terminating..." >&2 if [ "$dev_type" != "sysfs" ]; then
exit 1 # test pulls
fi set_mock 0
set_line input pull-up
assert_line 1
set_mock 0
assert_line 0
set_mock 1
set_line input pull-down
assert_line 0
set_mock 1
assert_line 1
set_line bias-none
fi
# test input active-low
set_mock 0
set_line active-low
assert_line 1
set_mock 1
assert_line 0
set_mock 0
assert_line 1
# test output active-high
set_mock 1
set_line active-high 0
assert_mock 0
set_line 1
assert_mock 1
set_line 0
assert_mock 0
fi
# test output active-low
set_mock 0
set_line active-low 0
assert_mock 1
set_line 1
assert_mock 0
set_line 0
assert_mock 1
release_line
}
test_no_line()
{
log test_no_line "$*"
[ ! -e "$GPIO_DEBUGFS/$1/$2" ] || fail "unexpected line $1:$2"
}
# Load the module and check that the expected number of gpiochips, with the
# expected number of lines, are created and are functional.
#
# $1 is the gpio_mockup_ranges parameter for the module
# The remaining parameters are the number of lines, n, expected for each of
# the gpiochips expected to be created.
#
# For each gpiochip the fence post lines, 0 and n-1, are tested, and the
# line on the far side of the fence post, n, is tested to not exist.
#
# If the $random flag is set then a random line in the middle of the
# gpiochip is tested as well.
insmod_test()
{
local ranges=
local gc=
local width=
# Note the quotes around `$TEMP': they are essential! [ "${1:-}" ] || fail "missing ranges"
eval set -- "$TEMP" ranges=$1 ; shift
try_insert_module "gpio_mockup_ranges=$ranges"
log "GPIO $module test with ranges: <$ranges>:"
# e.g. /sys/kernel/debug/gpio-mockup/gpiochip1
gpiochip=$(find "$DEBUGFS/$module/" -name gpiochip* -type d | sort)
for chip in $gpiochip; do
gc=${chip##*/}
[ "${1:-}" ] || fail "unexpected chip - $gc"
width=$1 ; shift
test_line $gc 0
if [ "$random" -a $width -gt 2 ]; then
test_line $gc $((RANDOM % ($width - 2) + 1))
fi
test_line $gc $(($width - 1))
test_no_line $gc $width
done
[ "${1:-}" ] && fail "missing expected chip of width $1"
remove_module || fail "failed to remove module with error $?"
}
while true; do while getopts ":frvt:" opt; do
case $1 in case $opt in
-f) f)
full_test=true full_test=true
shift
;; ;;
-h) r)
usage random=true
exit
;;
-m)
module=$2
shift 2
;; ;;
-t) t)
dev_type=$2 dev_type=$OPTARG
shift 2
;; ;;
--) v)
shift verbose=true
break
;; ;;
*) *)
echo "Internal error!" usage
exit 1
;; ;;
esac esac
done done
shift $((OPTIND - 1))
if [ X"$module" = X ]; then [ "${1:-}" ] && fail "unknown argument '$1'"
module="gpio-mockup"
fi
if [ X$dev_type != Xsysfs ]; then
dev_type="chardev"
fi
prerequisite prerequisite
echo "1. Test dynamic allocation of gpio successful means insert gpiochip and" trap 'exit $ksft_fail' SIGTERM SIGINT
echo " manipulate gpio pin successful" trap cleanup EXIT
gpio_test "-1,32" true
gpio_test "-1,32,-1,32" true case "$dev_type" in
gpio_test "-1,32,-1,32,-1,32" true sysfs)
if [ X$full_test = Xtrue ]; then source $BASE/gpio-mockup-sysfs.sh
gpio_test "-1,32,32,64" true echo "WARNING: gpio sysfs ABI is deprecated."
gpio_test "-1,32,40,64,-1,5" true ;;
gpio_test "-1,32,32,64,-1,32" true cdev_v1)
gpio_test "0,32,32,64,-1,32,-1,32" true echo "WARNING: gpio cdev ABI v1 is deprecated."
gpio_test "-1,32,-1,32,0,32,32,64" true uapi_opt="-u1 "
echo "2. Do basic test: successful means insert gpiochip and" ;;
echo " manipulate gpio pin successful" cdev)
gpio_test "0,32" true ;;
gpio_test "0,32,32,64" true *)
gpio_test "0,32,40,64,64,96" true fail "unknown interface type: $dev_type"
;;
esac
remove_module || fail "can't remove existing $module module"
# manual gpio allocation tests fail if a physical chip already exists
[ "$full_test" -a -e "/dev/gpiochip0" ] && skip "full tests conflict with gpiochip0"
echo "1. Module load tests"
echo "1.1. dynamic allocation of gpio"
insmod_test "-1,32" 32
insmod_test "-1,23,-1,32" 23 32
insmod_test "-1,23,-1,26,-1,32" 23 26 32
if [ "$full_test" ]; then
echo "1.2. manual allocation of gpio"
insmod_test "0,32" 32
insmod_test "0,32,32,60" 32 28
insmod_test "0,32,40,64,64,96" 32 24 32
echo "1.3. dynamic and manual allocation of gpio"
insmod_test "-1,32,32,62" 32 30
insmod_test "-1,22,-1,23,0,24,32,64" 22 23 24 32
insmod_test "-1,32,32,60,-1,29" 32 28 29
insmod_test "-1,32,40,64,-1,5" 32 24 5
insmod_test "0,32,32,44,-1,22,-1,31" 32 12 22 31
fi fi
echo "3. Error test: successful means insert gpiochip failed" echo "2. Module load error tests"
echo "3.1 Test number of gpio overflow" echo "2.1 gpio overflow"
#Currently: The max number of gpio(1024) is defined in arm architecture. # Currently: The max number of gpio(1024) is defined in arm architecture.
gpio_test "-1,32,-1,1024" false insmod_test "-1,1024"
if [ X$full_test = Xtrue ]; then if [ "$full_test" ]; then
echo "3.2 Test zero line of gpio" echo "2.2 no lines defined"
gpio_test "0,0" false insmod_test "0,0"
echo "3.3 Test range overlap" echo "2.3 ignore range overlap"
echo "3.3.1 Test corner case" insmod_test "0,32,0,1" 32
gpio_test "0,32,0,1" false insmod_test "0,32,1,5" 32
gpio_test "0,32,32,64,32,40" false insmod_test "0,32,30,35" 32
gpio_test "0,32,35,64,35,45" false insmod_test "0,32,31,32" 32
gpio_test "0,32,31,32" false insmod_test "10,32,30,35" 22
gpio_test "0,32,32,64,36,37" false insmod_test "10,32,9,14" 22
gpio_test "0,32,35,64,34,36" false insmod_test "0,32,20,21,40,56" 32 16
echo "3.3.2 Test inserting invalid second gpiochip" insmod_test "0,32,32,64,32,40" 32 32
gpio_test "0,32,30,35" false insmod_test "0,32,32,64,36,37" 32 32
gpio_test "0,32,1,5" false insmod_test "0,32,35,64,34,36" 32 29
gpio_test "10,32,9,14" false insmod_test "0,30,35,64,35,45" 30 29
gpio_test "10,32,30,35" false insmod_test "0,32,40,56,30,33" 32 16
echo "3.3.3 Test others" insmod_test "0,32,40,56,30,41" 32 16
gpio_test "0,32,40,56,39,45" false insmod_test "0,32,40,56,39,45" 32 16
gpio_test "0,32,40,56,30,33" false
gpio_test "0,32,40,56,30,41" false
gpio_test "0,32,40,56,20,21" false
fi fi
echo GPIO test PASS echo "GPIO $module test PASS"
...@@ -129,13 +129,11 @@ l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ ...@@ -129,13 +129,11 @@ l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \
grep -v "VAR_LDLIBS" | awk -F: '{print $1}') grep -v "VAR_LDLIBS" | awk -F: '{print $1}')
# Level 3 # Level 3
# gpio, memfd and others use pkg-config to find mount and fuse libs # memfd and others use pkg-config to find mount and fuse libs
# respectively and save it in VAR_LDLIBS. If pkg-config doesn't find # respectively and save it in VAR_LDLIBS. If pkg-config doesn't find
# any, VAR_LDLIBS set to default. # any, VAR_LDLIBS set to default.
# Use the default value and filter out pkg-config for dependency check. # Use the default value and filter out pkg-config for dependency check.
# e.g: # e.g:
# gpio/Makefile
# VAR_LDLIBS := $(shell pkg-config --libs mount) 2>/dev/null)
# memfd/Makefile # memfd/Makefile
# VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null) # VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment