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),
......
This diff is collapsed.
...@@ -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;
......
This diff is collapsed.
// 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
} }
This diff is collapsed.
...@@ -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