gpio updates for v5.12

- 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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmAzzkgACgkQEacuoBRx
 13K9xA//ZjxuUdX+q3tcfcljMrSAYWVK0PwxiHtjMRCcMX5Mkg/VP0Aqig0/CAWv
 b/98JNBLX8GxWhy4WlIVgkK2NZTrPAs3c1sYxufb7AgqmhOaqA6d1xZ0oYdhPykc
 H5yy55AEhiNMZCwEFWJXZSisWKsZ+Nnz+XZl5wWEsTWcvQgOGveK/+nxk//UW2Kj
 ymtO4LnivC7vABd/gBSZshJwjE45/os8QBS2HTKw3bcHa7p9Sd72e0zYthXzIS9J
 nECOSBdHMnfcIthFrQWXOXZz72xf3KjpoouztDEKqTOVFUplxQ0BxgwfrjjNd3qJ
 sHXtIzb3ZsGXc8yDoSmSE7ahspsP/x/uYfTxDr7dn9m9obSTWwy5vXK9xJaGGk0r
 TLnPZTESXqu/Eoek22ll7345RNSFlz8g0bCbO1avdtWYTxg/oaWxr1SEvznEiDZ5
 I36K8XGPye1P2dZ3v08cjIUW6QGx9HEZmN3Djzh/pnMvhF72BDy/fipR4fnIJGOc
 ptmrHK0BWvU0fyj58TqeOXkafAulrqoKIu5sOTEkVUQlRqVF6E7pp5/KcxJv8xJs
 zbcmu/26MYkJYeo/AGpshhTD6EDnKnlab1VHrHgSkA5zKdVghRqqd4EGyRKUyQ5e
 WxEpPPG67RUIAZrmqA3eYMWncUhvD1LRTILN8ZLPlES1/9YUlIE=
 =OZtR
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2021-02-22 10:00:46 -08:00
commit 882d6edfc4
44 changed files with 1817 additions and 1403 deletions

View File

@ -80,6 +80,11 @@ Required properties:
- offset: offset address inside the syscon block
Optional properties:
- marvell,pwm-offset: offset address of PWM duration control registers inside
the syscon block
Example:
ap_syscon: system-controller@6f4000 {
compatible = "syscon", "simple-mfd";
@ -101,6 +106,9 @@ ap_syscon: system-controller@6f4000 {
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&ap_pinctrl 0 0 19>;
marvell,pwm-offset = <0x10c0>;
#pwm-cells = <2>;
clocks = <&ap_clk 3>;
};
};

View File

@ -7,6 +7,7 @@ Required Properties:
"ti,k2g-gpio", "ti,keystone-gpio": for 66AK2G
"ti,am654-gpio", "ti,keystone-gpio": for TI K3 AM654
"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
registers.

View File

@ -32,6 +32,7 @@ properties:
- maxim,max7327
- nxp,pca6416
- nxp,pca9505
- nxp,pca9506
- nxp,pca9534
- nxp,pca9535
- nxp,pca9536
@ -70,7 +71,7 @@ properties:
gpio-line-names:
minItems: 1
maxItems: 32
maxItems: 40
interrupts:
maxItems: 1

View File

@ -48,6 +48,9 @@ properties:
- renesas,gpio-r8a77995 # R-Car D3
- const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2
- items:
- const: renesas,gpio-r8a779a0 # R-Car V3U
reg:
maxItems: 1

View File

@ -0,0 +1,70 @@
# 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>;
};
};
...

View File

@ -1,24 +0,0 @@
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>;
};

View File

@ -361,12 +361,13 @@ corresponding chip driver. In that case a significantly improved performance
can be expected. If simultaneous access is not possible the GPIOs will be
accessed sequentially.
The functions take three arguments:
The functions take four arguments:
* array_size - the number of array elements
* desc_array - an array of GPIO descriptors
* array_info - optional information obtained from gpiod_get_array()
* value_bitmap - a bitmap to store the GPIOs' values (get) or
a bitmap of values to assign to the GPIOs (set)
a bitmap of values to assign to the GPIOs (set)
The descriptor array can be obtained using the gpiod_get_array() function
or one of its variants. If the group of descriptors returned by that function

View File

@ -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
that can be used as either an input or an output:
LOW: gpiod_direction_output(gpio, 0) ... this drives the signal and overrides
the pullup.
**LOW**: ``gpiod_direction_output(gpio, 0)`` ... this drives the signal and
overrides the pullup.
HIGH: gpiod_direction_input(gpio) ... this turns off the output, so the pullup
(or some other device) controls the signal.
**HIGH**: ``gpiod_direction_input(gpio)`` ... this turns off the output, so
the pullup (or some other device) controls the signal.
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

View File

@ -2609,10 +2609,12 @@ S: Supported
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/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/watchdog/toshiba,visconti-wdt.yaml
F: arch/arm64/boot/dts/toshiba/
F: drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
F: drivers/gpio/gpio-visconti.c
F: drivers/pinctrl/visconti/
F: drivers/watchdog/visconti_wdt.c
N: visconti

View File

@ -487,11 +487,11 @@ config GPIO_PXA
Say yes here to support the PXA GPIO device
config GPIO_RCAR
tristate "Renesas R-Car GPIO"
tristate "Renesas R-Car and RZ/G GPIO support"
depends on ARCH_RENESAS || COMPILE_TEST
select GPIOLIB_IRQCHIP
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
bool "RDA Micro GPIO controller support"
@ -595,7 +595,7 @@ config GPIO_TB10X
select OF_GPIO
config GPIO_TEGRA
bool "NVIDIA Tegra GPIO support"
tristate "NVIDIA Tegra GPIO support"
default ARCH_TEGRA
depends on ARCH_TEGRA || COMPILE_TEST
depends on OF_GPIO
@ -648,6 +648,16 @@ config GPIO_VF610
help
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
tristate "NEC VR4100 series General-purpose I/O Uint support"
depends on CPU_VR41XX
@ -670,7 +680,7 @@ config GPIO_WCD934X
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 gpio controller driver"
depends on MFD_WCD934X && OF_GPIO
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.
config GPIO_XGENE
@ -694,6 +704,8 @@ config GPIO_XGENE_SB
config GPIO_XILINX
tristate "Xilinx GPIO support"
select GPIOLIB_IRQCHIP
depends on OF_GPIO
help
Say yes here to support the Xilinx FPGA GPIO device
@ -731,13 +743,6 @@ config GPIO_ZYNQ
help
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
tristate "Loongson1 GPIO support"
depends on MACH_LOONGSON32
@ -1623,8 +1628,7 @@ config GPIO_MOCKUP
select IRQ_SIM
help
This enables GPIO Testing driver, which provides a way to test GPIO
subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS
must be selected for this test.
subsystem through sysfs (or char device) and debugfs.
User could use it through the script in
tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in
it.

View File

@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.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_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
@ -163,6 +162,7 @@ obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.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_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
@ -179,5 +179,4 @@ obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o
obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o
obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o

View File

@ -62,34 +62,6 @@ static char *get_arg(char **args)
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,
int hwnum, unsigned int *n)
{
@ -100,8 +72,7 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
if (!lookups)
return -ENOMEM;
lookups->table[*n] =
(struct gpiod_lookup)GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0);
(*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,
static int aggr_parse(struct gpio_aggregator *aggr)
{
char *name, *offsets, *p;
char *args = aggr->args;
unsigned long *bitmap;
unsigned int i, n = 0;
char *name, *offsets;
int error = 0;
bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL);
@ -130,7 +101,8 @@ static int aggr_parse(struct gpio_aggregator *aggr)
goto free_bitmap;
}
if (!isrange(offsets)) {
p = get_options(offsets, 0, &error);
if (error == 0 || *p) {
/* Named GPIO line */
error = aggr_add_gpio(aggr, name, U16_MAX, &n);
if (error)
@ -271,7 +243,7 @@ static DRIVER_ATTR_WO(delete_device);
static struct attribute *gpio_aggregator_attrs[] = {
&driver_attr_new_device.attr,
&driver_attr_delete_device.attr,
NULL,
NULL
};
ATTRIBUTE_GROUPS(gpio_aggregator);
@ -545,7 +517,7 @@ static const struct of_device_id gpio_aggregator_dt_ids[] = {
* Add GPIO-operated devices controlled from userspace below,
* or use "driver_override" in sysfs
*/
{},
{}
};
MODULE_DEVICE_TABLE(of, gpio_aggregator_dt_ids);
#endif

View File

@ -12,7 +12,8 @@
#define GPIO_OUT_REG(offset) (BD70528_REG_GPIO1_OUT + (offset) * 2)
struct bd70528_gpio {
struct rohm_regmap_dev chip;
struct regmap *regmap;
struct device *dev;
struct gpio_chip gpio;
};
@ -35,11 +36,11 @@ static int bd70528_set_debounce(struct bd70528_gpio *bdgpio,
val = BD70528_DEBOUNCE_50MS;
break;
default:
dev_err(bdgpio->chip.dev,
dev_err(bdgpio->dev,
"Invalid debounce value %u\n", debounce);
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);
}
@ -49,9 +50,9 @@ static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset)
int val, ret;
/* 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) {
dev_err(bdgpio->chip.dev, "Could not read gpio direction\n");
dev_err(bdgpio->dev, "Could not read gpio direction\n");
return ret;
}
if (val & BD70528_GPIO_OUT_EN_MASK)
@ -67,13 +68,13 @@ static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
return regmap_update_bits(bdgpio->chip.regmap,
return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset),
BD70528_GPIO_DRIVE_MASK,
BD70528_GPIO_OPEN_DRAIN);
break;
case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(bdgpio->chip.regmap,
return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset),
BD70528_GPIO_DRIVE_MASK,
BD70528_GPIO_PUSH_PULL);
@ -93,7 +94,7 @@ static int bd70528_direction_input(struct gpio_chip *chip, unsigned int offset)
struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
/* 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_DISABLE);
}
@ -105,10 +106,10 @@ static void bd70528_gpio_set(struct gpio_chip *chip, unsigned int offset,
struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
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);
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,
@ -117,7 +118,7 @@ static int bd70528_direction_output(struct gpio_chip *chip, unsigned int offset,
struct bd70528_gpio *bdgpio = gpiochip_get_data(chip);
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_ENABLE);
}
@ -129,11 +130,11 @@ static int bd70528_gpio_get_o(struct bd70528_gpio *bdgpio, unsigned int offset)
int ret;
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)
ret = !!(val & BD70528_GPIO_OUT_MASK);
else
dev_err(bdgpio->chip.dev, "GPIO (out) state read failed\n");
dev_err(bdgpio->dev, "GPIO (out) state read failed\n");
return ret;
}
@ -143,12 +144,12 @@ static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset)
unsigned int val;
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)
ret = !(val & GPIO_IN_STATE_MASK(offset));
else
dev_err(bdgpio->chip.dev, "GPIO (in) state read failed\n");
dev_err(bdgpio->dev, "GPIO (in) state read failed\n");
return ret;
}
@ -173,29 +174,22 @@ static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset)
else if (ret == GPIO_LINE_DIRECTION_IN)
ret = bd70528_gpio_get_i(bdgpio, offset);
else
dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n");
dev_err(bdgpio->dev, "failed to read GPIO direction\n");
return ret;
}
static int bd70528_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bd70528_gpio *bdgpio;
struct rohm_regmap_dev *bd70528;
int ret;
bd70528 = dev_get_drvdata(pdev->dev.parent);
if (!bd70528) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
GFP_KERNEL);
bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL);
if (!bdgpio)
return -ENOMEM;
bdgpio->chip.dev = &pdev->dev;
bdgpio->gpio.parent = pdev->dev.parent;
bdgpio->dev = dev;
bdgpio->gpio.parent = dev->parent;
bdgpio->gpio.label = "bd70528-gpio";
bdgpio->gpio.owner = THIS_MODULE;
bdgpio->gpio.get_direction = bd70528_get_direction;
@ -208,14 +202,15 @@ static int bd70528_probe(struct platform_device *pdev)
bdgpio->gpio.ngpio = 4;
bdgpio->gpio.base = -1;
#ifdef CONFIG_OF_GPIO
bdgpio->gpio.of_node = pdev->dev.parent->of_node;
bdgpio->gpio.of_node = dev->parent->of_node;
#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,
bdgpio);
ret = devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio);
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;
}

View File

@ -11,7 +11,8 @@
#define HALL_GPIO_OFFSET 3
struct bd71828_gpio {
struct rohm_regmap_dev chip;
struct regmap *regmap;
struct device *dev;
struct gpio_chip gpio;
};
@ -29,10 +30,10 @@ static void bd71828_gpio_set(struct gpio_chip *chip, unsigned int offset,
if (offset == HALL_GPIO_OFFSET)
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);
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)
@ -42,10 +43,10 @@ static int bd71828_gpio_get(struct gpio_chip *chip, unsigned int offset)
struct bd71828_gpio *bdgpio = gpiochip_get_data(chip);
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);
else
ret = regmap_read(bdgpio->chip.regmap, GPIO_OUT_REG(offset),
ret = regmap_read(bdgpio->regmap, GPIO_OUT_REG(offset),
&val);
if (!ret)
ret = (val & BD71828_GPIO_OUT_MASK);
@ -63,12 +64,12 @@ static int bd71828_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
return regmap_update_bits(bdgpio->chip.regmap,
return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset),
BD71828_GPIO_DRIVE_MASK,
BD71828_GPIO_OPEN_DRAIN);
case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(bdgpio->chip.regmap,
return regmap_update_bits(bdgpio->regmap,
GPIO_OUT_REG(offset),
BD71828_GPIO_DRIVE_MASK,
BD71828_GPIO_PUSH_PULL);
@ -96,22 +97,15 @@ static int bd71828_get_direction(struct gpio_chip *chip, unsigned int offset)
static int bd71828_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bd71828_gpio *bdgpio;
struct rohm_regmap_dev *bd71828;
bd71828 = dev_get_drvdata(pdev->dev.parent);
if (!bd71828) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
bdgpio = devm_kzalloc(&pdev->dev, sizeof(*bdgpio),
GFP_KERNEL);
bdgpio = devm_kzalloc(dev, sizeof(*bdgpio), GFP_KERNEL);
if (!bdgpio)
return -ENOMEM;
bdgpio->chip.dev = &pdev->dev;
bdgpio->gpio.parent = pdev->dev.parent;
bdgpio->dev = dev;
bdgpio->gpio.parent = dev->parent;
bdgpio->gpio.label = "bd71828-gpio";
bdgpio->gpio.owner = THIS_MODULE;
bdgpio->gpio.get_direction = bd71828_get_direction;
@ -127,11 +121,12 @@ static int bd71828_probe(struct platform_device *pdev)
* "gpio-reserved-ranges" and exclude them from control
*/
bdgpio->gpio.ngpio = 4;
bdgpio->gpio.of_node = pdev->dev.parent->of_node;
bdgpio->chip.regmap = bd71828->regmap;
bdgpio->gpio.of_node = dev->parent->of_node;
bdgpio->regmap = dev_get_regmap(dev->parent, NULL);
if (!bdgpio->regmap)
return -ENODEV;
return devm_gpiochip_add_data(&pdev->dev, &bdgpio->gpio,
bdgpio);
return devm_gpiochip_add_data(dev, &bdgpio->gpio, bdgpio);
}
static struct platform_driver bd71828_gpio = {

View File

@ -31,6 +31,8 @@
/* Maximum value for irq capable line identifiers */
#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:
* F0..F7 (16..24) to irq 80..87.
@ -292,14 +294,14 @@ struct ep93xx_gpio_bank {
static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
/* 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 */
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("D", 0x0c, 0x1c, 0x00, 24, false, false, 0),
EP93XX_GPIO_BANK("E", 0x20, 0x24, 0x00, 32, false, false, 0),
/* 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("H", 0x40, 0x44, 0x00, 56, false, false, 0),
};
@ -318,11 +320,6 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
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)
{
ic->irq_ack = ep93xx_gpio_irq_ack;
@ -375,7 +372,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
girq->parent_handler = ep93xx_gpio_ab_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1,
girq->parents = devm_kcalloc(dev, girq->num_parents,
sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
@ -393,20 +390,19 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
/*
* 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->num_parents = 8;
girq->parents = devm_kcalloc(dev, 8,
girq->parents = devm_kcalloc(dev, girq->num_parents,
sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
/* Pick resources 1..8 for these IRQs */
for (i = 1; i <= 8; i++)
girq->parents[i - 1] = platform_get_irq(pdev, i);
for (i = 0; i < 8; i++) {
gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i;
for (i = 0; i < girq->num_parents; i++) {
girq->parents[i] = platform_get_irq(pdev, i + 1);
gpio_irq = bank->irq_base + i;
irq_set_chip_data(gpio_irq, &epg->gc[5]);
irq_set_chip_and_handler(gpio_irq,
girq->chip,
@ -415,7 +411,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
}
girq->default_type = IRQ_TYPE_NONE;
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);

View File

@ -325,7 +325,7 @@ static int max77620_gpio_probe(struct platform_device *pdev)
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_edge_irq;
girq->init_hw = max77620_gpio_irq_init_hw,
girq->init_hw = max77620_gpio_irq_init_hw;
girq->threaded = true;
platform_set_drvdata(pdev, mgpio);

View File

@ -194,6 +194,11 @@ static int mrfld_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
{
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)
return -ENOTSUPP;

View File

@ -70,7 +70,12 @@
*/
#define PWM_BLINK_ON_DURATION_OFF 0x0
#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 */
#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
@ -93,6 +98,7 @@
struct mvebu_pwm {
struct regmap *regs;
u32 offset;
unsigned long clk_rate;
struct gpio_desc *gpiod;
struct pwm_chip chip;
@ -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)
{
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)
{
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,
spin_lock_irqsave(&mvpwm->lock, flags);
regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u);
val = (unsigned long long) u * NSEC_PER_SEC;
do_div(val, mvpwm->clk_rate);
if (val > UINT_MAX)
state->duty_cycle = UINT_MAX;
else if (val)
state->duty_cycle = val;
/* Hardware treats zero as 2^32. See mvebu_pwm_apply(). */
if (u > 0)
val = u;
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);
val += (unsigned long long) u; /* period = on + off duration */
val *= NSEC_PER_SEC;
do_div(val, mvpwm->clk_rate);
if (val > UINT_MAX)
state->period = UINT_MAX;
else if (val)
state->period = val;
/* period = on + off duration */
if (u > 0)
val += u;
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);
if (u)
@ -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;
do_div(val, NSEC_PER_SEC);
if (val > UINT_MAX)
if (val > UINT_MAX + 1ULL)
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;
else
on = 1;
val = (unsigned long long) mvpwm->clk_rate *
(state->period - state->duty_cycle);
val = (unsigned long long) mvpwm->clk_rate * state->period;
do_div(val, NSEC_PER_SEC);
if (val > UINT_MAX)
val -= on;
if (val > UINT_MAX + 1ULL)
return -EINVAL;
if (val)
if (val == UINT_MAX + 1ULL)
off = 0;
else if (val)
off = val;
else
off = 1;
@ -778,51 +787,80 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct mvebu_pwm *mvpwm;
void __iomem *base;
u32 offset;
u32 set;
if (!of_device_is_compatible(mvchip->chip.of_node,
"marvell,armada-370-gpio"))
return 0;
/*
* There are only two sets of PWM configuration registers for
* all the GPIO lines on those SoCs which this driver reserves
* for the first two GPIO chips. So if the resource is missing
* we can't treat it as an error.
*/
if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"))
if (of_device_is_compatible(mvchip->chip.of_node,
"marvell,armada-370-gpio")) {
/*
* There are only two sets of PWM configuration registers for
* all the GPIO lines on those SoCs which this driver reserves
* for the first two GPIO chips. So if the resource is missing
* we can't treat it as an error.
*/
if (!platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"))
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))
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);
if (!mvpwm)
return -ENOMEM;
mvchip->mvpwm = mvpwm;
mvpwm->mvchip = mvchip;
mvpwm->offset = offset;
base = devm_platform_ioremap_resource_byname(pdev, "pwm");
if (IS_ERR(base))
return PTR_ERR(base);
if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) {
mvpwm->regs = mvchip->regs;
mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base,
&mvebu_gpio_regmap_config);
if (IS_ERR(mvpwm->regs))
return PTR_ERR(mvpwm->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");
if (IS_ERR(base))
return PTR_ERR(base);
mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base,
&mvebu_gpio_regmap_config);
if (IS_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);
if (!mvpwm->clk_rate) {

View File

@ -73,6 +73,7 @@
static const struct i2c_device_id pca953x_id[] = {
{ "pca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9506", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9536", 4 | PCA953X_TYPE, },
@ -1236,6 +1237,7 @@ static int pca953x_resume(struct device *dev)
static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "nxp,pca6416", .data = OF_953X(16, 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,pca9535", .data = OF_953X(16, PCA_INT), },
{ .compatible = "nxp,pca9536", .data = OF_953X( 4, 0), },

View File

@ -332,7 +332,7 @@ static int pcf857x_probe(struct i2c_client *client,
* reset state. Otherwise it flags pins to be driven low.
*/
gpio->out = ~n_latch;
gpio->status = gpio->out;
gpio->status = gpio->read(gpio->client);
/* Enable irqchip if we have an interrupt */
if (client->irq) {

View File

@ -35,6 +35,8 @@ struct gpio_rcar_bank_info {
struct gpio_rcar_info {
bool has_outdtsel;
bool has_both_edge_trigger;
bool has_always_in;
bool has_inen;
};
struct gpio_rcar_priv {
@ -62,6 +64,7 @@ struct gpio_rcar_priv {
#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
#define OUTDTSEL 0x40 /* Output Data 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
@ -302,9 +305,11 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset)
struct gpio_rcar_priv *p = gpiochip_get_data(chip);
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 */
if (gpio_rcar_read(p, INOUTSEL) & bit)
/*
* Before R-Car Gen3, INDT does not show correct pin state when
* 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);
else
return !!(gpio_rcar_read(p, INDT) & bit);
@ -324,6 +329,11 @@ static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask,
if (!bankmask)
return 0;
if (p->info.has_always_in) {
bits[0] = gpio_rcar_read(p, INDT) & bankmask;
return 0;
}
spin_lock_irqsave(&p->lock, flags);
outputs = gpio_rcar_read(p, INOUTSEL);
m = outputs & bankmask;
@ -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 = {
.has_outdtsel = false,
.has_both_edge_trigger = false,
.has_always_in = false,
.has_inen = false,
};
static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
.has_outdtsel = 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[] = {
{
.compatible = "renesas,gpio-r8a7743",
/* RZ/G1 GPIO is identical to R-Car Gen2. */
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,gpio-r8a7790",
.data = &gpio_rcar_info_gen2,
}, {
.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,gpio-r8a779a0",
.data = &gpio_rcar_info_v3u,
}, {
.compatible = "renesas,rcar-gen1-gpio",
.data = &gpio_rcar_info_gen1,
@ -426,8 +430,7 @@ static const struct of_device_id gpio_rcar_of_table[] = {
.data = &gpio_rcar_info_gen2,
}, {
.compatible = "renesas,rcar-gen3-gpio",
/* Gen3 GPIO is identical to Gen2. */
.data = &gpio_rcar_info_gen2,
.data = &gpio_rcar_info_gen3,
}, {
.compatible = "renesas,gpio-rcar",
.data = &gpio_rcar_info_gen1,
@ -460,6 +463,17 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins)
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)
{
struct gpio_rcar_priv *p;
@ -549,6 +563,12 @@ static int gpio_rcar_probe(struct platform_device *pdev)
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);
return 0;
@ -624,6 +644,9 @@ static int gpio_rcar_resume(struct device *dev)
}
}
if (p->info.has_inen)
gpio_rcar_enable_inputs(p);
return 0;
}
#endif /* CONFIG_PM_SLEEP*/

View File

@ -65,13 +65,13 @@ static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
if (!irq_chip)
return -ENOMEM;
irq_chip->name = "sl28cpld-gpio-irq",
irq_chip->name = "sl28cpld-gpio-irq";
irq_chip->irqs = sl28cpld_gpio_irqs;
irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
irq_chip->num_regs = 1;
irq_chip->status_base = base + GPIO_REG_IP;
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;
ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),

View File

@ -60,7 +60,6 @@ struct tegra_gpio_info;
struct tegra_gpio_bank {
unsigned int bank;
unsigned int irq;
/*
* IRQ-core code uses raw locking, and thus, nested locking also
@ -81,7 +80,6 @@ struct tegra_gpio_bank {
u32 dbc_enb[4];
#endif
u32 dbc_cnt[4];
struct tegra_gpio_info *tgi;
};
struct tegra_gpio_soc_config {
@ -93,12 +91,12 @@ struct tegra_gpio_soc_config {
struct tegra_gpio_info {
struct device *dev;
void __iomem *regs;
struct irq_domain *irq_domain;
struct tegra_gpio_bank *bank_info;
const struct tegra_gpio_soc_config *soc;
struct gpio_chip gc;
struct irq_chip ic;
u32 bank_count;
unsigned int *irqs;
};
static inline void tegra_gpio_writel(struct tegra_gpio_info *tgi,
@ -274,17 +272,10 @@ static int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
return tegra_gpio_set_debounce(chip, offset, debounce);
}
static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
return irq_find_mapping(tgi->irq_domain, offset);
}
static void tegra_gpio_irq_ack(struct irq_data *d)
{
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi;
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio));
@ -292,8 +283,8 @@ static void tegra_gpio_irq_ack(struct irq_data *d)
static void tegra_gpio_irq_mask(struct irq_data *d)
{
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi;
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0);
@ -301,8 +292,8 @@ static void tegra_gpio_irq_mask(struct irq_data *d)
static void tegra_gpio_irq_unmask(struct irq_data *d)
{
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi;
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1);
@ -311,11 +302,14 @@ static void tegra_gpio_irq_unmask(struct irq_data *d)
static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type;
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi;
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
struct tegra_gpio_bank *bank;
unsigned long flags;
u32 val;
int ret;
u32 val;
bank = &tgi->bank_info[GPIO_BANK(d->hwirq)];
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_RISING:
@ -367,13 +361,16 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
irq_set_handler_locked(d, handle_edge_irq);
return 0;
if (d->parent_data)
ret = irq_chip_set_type_parent(d, type);
return ret;
}
static void tegra_gpio_irq_shutdown(struct irq_data *d)
{
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = bank->tgi;
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
unsigned int gpio = d->hwirq;
tegra_gpio_irq_mask(d);
@ -382,13 +379,25 @@ static void tegra_gpio_irq_shutdown(struct irq_data *d)
static void tegra_gpio_irq_handler(struct irq_desc *desc)
{
unsigned int port, pin, gpio;
bool unmasked = false;
u32 lvl;
unsigned long sta;
struct tegra_gpio_info *tgi = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
struct tegra_gpio_bank *bank = irq_desc_get_handler_data(desc);
struct tegra_gpio_info *tgi = bank->tgi;
struct irq_domain *domain = tgi->gc.irq.domain;
unsigned int irq = irq_desc_get_irq(desc);
struct tegra_gpio_bank *bank = NULL;
unsigned int port, pin, gpio, i;
bool unmasked = false;
unsigned long sta;
u32 lvl;
for (i = 0; i < tgi->bank_count; i++) {
if (tgi->irqs[i] == irq) {
bank = &tgi->bank_info[i];
break;
}
}
if (WARN_ON(bank == NULL))
return;
chained_irq_enter(chip, desc);
@ -411,14 +420,47 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
generic_handle_irq(irq_find_mapping(tgi->irq_domain,
gpio + pin));
irq = irq_find_mapping(domain, gpio + pin);
if (WARN_ON(irq == 0))
continue;
generic_handle_irq(irq);
}
}
if (!unmasked)
chained_irq_exit(chip, desc);
}
static int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
unsigned int hwirq,
unsigned int type,
unsigned int *parent_hwirq,
unsigned int *parent_type)
{
*parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq);
*parent_type = type;
return 0;
}
static void *tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip,
unsigned int parent_hwirq,
unsigned int parent_type)
{
struct irq_fwspec *fwspec;
fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
if (!fwspec)
return NULL;
fwspec->fwnode = chip->irq.parent_domain->fwnode;
fwspec->param_count = 3;
fwspec->param[0] = 0;
fwspec->param[1] = parent_hwirq;
fwspec->param[2] = parent_type;
return fwspec;
}
#ifdef CONFIG_PM_SLEEP
@ -497,19 +539,31 @@ static int tegra_gpio_suspend(struct device *dev)
static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
{
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
struct tegra_gpio_bank *bank;
unsigned int gpio = d->hwirq;
u32 port, bit, mask;
int err;
err = irq_set_irq_wake(bank->irq, enable);
if (err)
return err;
bank = &tgi->bank_info[GPIO_BANK(d->hwirq)];
port = GPIO_PORT(gpio);
bit = GPIO_BIT(gpio);
mask = BIT(bit);
err = irq_set_irq_wake(tgi->irqs[bank->bank], enable);
if (err)
return err;
if (d->parent_data) {
err = irq_chip_set_wake_parent(d, enable);
if (err) {
irq_set_irq_wake(tgi->irqs[bank->bank], !enable);
return err;
}
}
if (enable)
bank->wake_enb[port] |= mask;
else
@ -519,6 +573,35 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
}
#endif
static int tegra_gpio_irq_set_affinity(struct irq_data *data,
const struct cpumask *dest,
bool force)
{
if (data->parent_data)
return irq_chip_set_affinity_parent(data, dest, force);
return -EINVAL;
}
static int tegra_gpio_irq_request_resources(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
tegra_gpio_enable(tgi, d->hwirq);
return gpiochip_reqres_irq(chip, d->hwirq);
}
static void tegra_gpio_irq_release_resources(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
gpiochip_relres_irq(chip, d->hwirq);
tegra_gpio_enable(tgi, d->hwirq);
}
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
@ -526,7 +609,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
{
struct tegra_gpio_info *tgi = s->private;
struct tegra_gpio_info *tgi = dev_get_drvdata(s->private);
unsigned int i, j;
for (i = 0; i < tgi->bank_count; i++) {
@ -548,12 +631,10 @@ static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
return 0;
}
DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio);
static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
{
debugfs_create_file("tegra_gpio", 0444, NULL, tgi,
&tegra_dbg_gpio_fops);
debugfs_create_devm_seqfile(tgi->dev, "tegra_gpio", NULL,
tegra_dbg_gpio_show);
}
#else
@ -568,14 +649,18 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
};
static struct lock_class_key gpio_lock_class;
static struct lock_class_key gpio_request_class;
static const struct of_device_id tegra_pmc_of_match[] = {
{ .compatible = "nvidia,tegra210-pmc", },
{ /* sentinel */ },
};
static int tegra_gpio_probe(struct platform_device *pdev)
{
struct tegra_gpio_info *tgi;
struct tegra_gpio_bank *bank;
unsigned int gpio, i, j;
struct tegra_gpio_info *tgi;
struct gpio_irq_chip *irq;
struct device_node *np;
unsigned int i, j;
int ret;
tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL);
@ -604,7 +689,6 @@ static int tegra_gpio_probe(struct platform_device *pdev)
tgi->gc.direction_output = tegra_gpio_direction_output;
tgi->gc.set = tegra_gpio_set;
tgi->gc.get_direction = tegra_gpio_get_direction;
tgi->gc.to_irq = tegra_gpio_to_irq;
tgi->gc.base = 0;
tgi->gc.ngpio = tgi->bank_count * 32;
tgi->gc.parent = &pdev->dev;
@ -619,6 +703,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake;
#endif
tgi->ic.irq_request_resources = tegra_gpio_irq_request_resources;
tgi->ic.irq_release_resources = tegra_gpio_irq_release_resources;
platform_set_drvdata(pdev, tgi);
@ -630,11 +716,10 @@ static int tegra_gpio_probe(struct platform_device *pdev)
if (!tgi->bank_info)
return -ENOMEM;
tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
tgi->gc.ngpio,
&irq_domain_simple_ops, NULL);
if (!tgi->irq_domain)
return -ENODEV;
tgi->irqs = devm_kcalloc(&pdev->dev, tgi->bank_count,
sizeof(*tgi->irqs), GFP_KERNEL);
if (!tgi->irqs)
return -ENOMEM;
for (i = 0; i < tgi->bank_count; i++) {
ret = platform_get_irq(pdev, i);
@ -643,8 +728,36 @@ static int tegra_gpio_probe(struct platform_device *pdev)
bank = &tgi->bank_info[i];
bank->bank = i;
bank->irq = ret;
bank->tgi = tgi;
tgi->irqs[i] = ret;
for (j = 0; j < 4; j++) {
raw_spin_lock_init(&bank->lvl_lock[j]);
spin_lock_init(&bank->dbc_lock[j]);
}
}
irq = &tgi->gc.irq;
irq->chip = &tgi->ic;
irq->fwnode = of_node_to_fwnode(pdev->dev.of_node);
irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq;
irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec;
irq->handler = handle_simple_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = tegra_gpio_irq_handler;
irq->parent_handler_data = tgi;
irq->num_parents = tgi->bank_count;
irq->parents = tgi->irqs;
np = of_find_matching_node(NULL, tegra_pmc_of_match);
if (np) {
irq->parent_domain = irq_find_host(np);
of_node_put(np);
if (!irq->parent_domain)
return -EPROBE_DEFER;
tgi->ic.irq_set_affinity = tegra_gpio_irq_set_affinity;
}
tgi->regs = devm_platform_ioremap_resource(pdev, 0);
@ -660,33 +773,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
}
ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi);
if (ret < 0) {
irq_domain_remove(tgi->irq_domain);
if (ret < 0)
return ret;
}
for (gpio = 0; gpio < tgi->gc.ngpio; gpio++) {
int irq = irq_create_mapping(tgi->irq_domain, gpio);
/* No validity check; all Tegra GPIOs are valid IRQs */
bank = &tgi->bank_info[GPIO_BANK(gpio)];
irq_set_chip_data(irq, bank);
irq_set_lockdep_class(irq, &gpio_lock_class, &gpio_request_class);
irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq);
}
for (i = 0; i < tgi->bank_count; i++) {
bank = &tgi->bank_info[i];
irq_set_chained_handler_and_data(bank->irq,
tegra_gpio_irq_handler, bank);
for (j = 0; j < 4; j++) {
raw_spin_lock_init(&bank->lvl_lock[j]);
spin_lock_init(&bank->dbc_lock[j]);
}
}
tegra_gpio_debuginit(tgi);
@ -715,18 +803,21 @@ static const struct of_device_id tegra_gpio_of_match[] = {
{ .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_gpio_of_match);
static struct platform_driver tegra_gpio_driver = {
.driver = {
.name = "tegra-gpio",
.pm = &tegra_gpio_pm_ops,
.driver = {
.name = "tegra-gpio",
.pm = &tegra_gpio_pm_ops,
.of_match_table = tegra_gpio_of_match,
},
.probe = tegra_gpio_probe,
.probe = tegra_gpio_probe,
};
module_platform_driver(tegra_gpio_driver);
static int __init tegra_gpio_init(void)
{
return platform_driver_register(&tegra_gpio_driver);
}
subsys_initcall(tegra_gpio_init);
MODULE_DESCRIPTION("NVIDIA Tegra GPIO controller driver");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_AUTHOR("Erik Gilling <konkers@google.com>");
MODULE_LICENSE("GPL v2");

View File

@ -657,7 +657,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio->gpio.get_direction = tegra186_gpio_get_direction;
gpio->gpio.direction_input = tegra186_gpio_direction_input;
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_config = tegra186_gpio_set_config;
gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges;

View File

@ -0,0 +1,218 @@
// 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");

View File

@ -216,7 +216,7 @@ static void vx855gpio_gpio_setup(struct vx855_gpio *vg)
c->direction_output = vx855gpio_direction_output;
c->get = vx855gpio_get;
c->set = vx855gpio_set;
c->set_config = vx855gpio_set_config,
c->set_config = vx855gpio_set_config;
c->dbg_show = NULL;
c->base = 0;
c->ngpio = NR_VX855_GP;

View File

@ -73,6 +73,8 @@
enum ctrl_register {
CTRL_IN,
CTRL_OUT,
IRQ_STATUS,
IRQ_MASK,
};
/*
@ -112,22 +114,29 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type)
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) {
reg = IRQ_MASK_BASE;
mask = BIT(gpio % GROUP0_NR_IRQS);
reg += 0;
*mask = BIT(gpio);
} else {
reg = IRQ_MASK_BASE + 1;
mask = BIT((gpio - GROUP0_NR_IRQS) % GROUP1_NR_IRQS);
reg += 1;
*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)
regmap_update_bits(wg->regmap, reg, mask, mask);
regmap_set_bits(wg->regmap, reg, mask);
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)
@ -207,9 +216,9 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value)
return;
if (value)
regmap_update_bits(wg->regmap, reg, 1, 1);
regmap_set_bits(wg->regmap, reg, 1);
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,
@ -324,7 +333,8 @@ static struct irq_chip wcove_irqchip = {
static irqreturn_t wcove_gpio_irq_handler(int irq, void *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];
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)
/* Iterate until no interrupt is pending */
while (pending) {
/* One iteration is for all pending bits */
for_each_set_bit(gpio, (const unsigned long *)&pending,
WCOVE_GPIO_NUM) {
offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0;
mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) :
BIT(gpio);
for_each_set_bit(gpio, &pending, WCOVE_GPIO_NUM) {
unsigned int mask, reg = to_ireg(gpio, IRQ_STATUS, &mask);
virq = irq_find_mapping(wg->chip.irq.domain, gpio);
handle_nested_irq(virq);
regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset,
mask, mask);
regmap_set_bits(wg->regmap, reg, mask);
}
/* Next iteration */
@ -367,30 +374,26 @@ static void wcove_gpio_dbg_show(struct seq_file *s,
{
unsigned int ctlo, ctli, irq_mask, irq_status;
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++) {
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_IN), &ctli);
ret += regmap_read(wg->regmap, IRQ_MASK_BASE + group,
&irq_mask);
ret += regmap_read(wg->regmap, IRQ_STATUS_BASE + group,
&irq_status);
ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_MASK, &mask), &irq_mask);
ret += regmap_read(wg->regmap, to_ireg(gpio, IRQ_STATUS, &mask), &irq_status);
if (ret) {
pr_err("Failed to read registers: ctrl out/in or irq status/mask\n");
break;
}
offset = gpio % 8;
seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n",
gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ",
ctli & 0x1 ? "hi" : "lo",
ctli & CTLI_INTCNT_NE ? "fall" : " ",
ctli & CTLI_INTCNT_PE ? "rise" : " ",
ctlo,
irq_mask & BIT(offset) ? "mask " : "unmask",
irq_status & BIT(offset) ? "pending" : " ");
irq_mask & mask ? "mask " : "unmask",
irq_status & mask ? "pending" : " ");
}
}
@ -434,7 +437,7 @@ static int wcove_gpio_probe(struct platform_device *pdev)
wg->chip.get_direction = wcove_gpio_get_direction;
wg->chip.get = wcove_gpio_get;
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.ngpio = WCOVE_VGPIO_NUM;
wg->chip.can_sleep = true;
@ -473,14 +476,12 @@ static int wcove_gpio_probe(struct platform_device *pdev)
}
/* Enable GPIO0 interrupts */
ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK,
0x00);
ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 0, GPIO_IRQ0_MASK);
if (ret)
return ret;
/* Enable GPIO1 interrupts */
ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK,
0x00);
ret = regmap_clear_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK);
if (ret)
return ret;

View File

@ -10,10 +10,13 @@
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
/* Register Offset Definitions */
@ -22,6 +25,11 @@
#define XGPIO_CHANNEL_OFFSET 0x8
#define XGPIO_GIER_OFFSET 0x11c /* Global Interrupt Enable */
#define XGPIO_GIER_IE BIT(31)
#define XGPIO_IPISR_OFFSET 0x120 /* IP Interrupt Status */
#define XGPIO_IPIER_OFFSET 0x128 /* IP Interrupt Enable */
/* Read/Write access to the GPIO registers */
#if defined(CONFIG_ARCH_ZYNQ) || defined(CONFIG_X86)
# define xgpio_readreg(offset) readl(offset)
@ -36,9 +44,15 @@
* @gc: GPIO chip
* @regs: register block
* @gpio_width: GPIO width for every channel
* @gpio_state: GPIO state shadow register
* @gpio_state: GPIO write state shadow register
* @gpio_last_irq_read: GPIO read state register from last interrupt
* @gpio_dir: GPIO direction shadow register
* @gpio_lock: Lock used for synchronization
* @irq: IRQ used by GPIO device
* @irqchip: IRQ chip
* @irq_enable: GPIO IRQ enable/disable bitfield
* @irq_rising_edge: GPIO IRQ rising edge enable/disable bitfield
* @irq_falling_edge: GPIO IRQ falling edge enable/disable bitfield
* @clk: clock resource for this driver
*/
struct xgpio_instance {
@ -46,8 +60,14 @@ struct xgpio_instance {
void __iomem *regs;
unsigned int gpio_width[2];
u32 gpio_state[2];
u32 gpio_last_irq_read[2];
u32 gpio_dir[2];
spinlock_t gpio_lock[2];
spinlock_t gpio_lock; /* For serializing operations */
int irq;
struct irq_chip irqchip;
u32 irq_enable[2];
u32 irq_rising_edge[2];
u32 irq_falling_edge[2];
struct clk *clk;
};
@ -113,7 +133,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
int index = xgpio_index(chip, gpio);
int offset = xgpio_offset(chip, gpio);
spin_lock_irqsave(&chip->gpio_lock[index], flags);
spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write to GPIO signal and set its direction to output */
if (val)
@ -124,7 +144,7 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
xgpio_regoffset(chip, gpio), chip->gpio_state[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
@ -144,7 +164,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
int index = xgpio_index(chip, 0);
int offset, i;
spin_lock_irqsave(&chip->gpio_lock[index], flags);
spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write to GPIO signals */
for (i = 0; i < gc->ngpio; i++) {
@ -155,9 +175,9 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET,
chip->gpio_state[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
spin_unlock_irqrestore(&chip->gpio_lock, flags);
index = xgpio_index(chip, i);
spin_lock_irqsave(&chip->gpio_lock[index], flags);
spin_lock_irqsave(&chip->gpio_lock, flags);
}
if (__test_and_clear_bit(i, mask)) {
offset = xgpio_offset(chip, i);
@ -171,7 +191,7 @@ static void xgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
xgpio_writereg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET, chip->gpio_state[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
@ -190,14 +210,14 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
int index = xgpio_index(chip, gpio);
int offset = xgpio_offset(chip, gpio);
spin_lock_irqsave(&chip->gpio_lock[index], flags);
spin_lock_irqsave(&chip->gpio_lock, flags);
/* Set the GPIO bit in shadow register and set direction as input */
chip->gpio_dir[index] |= BIT(offset);
xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET +
xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0;
}
@ -221,7 +241,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
int index = xgpio_index(chip, gpio);
int offset = xgpio_offset(chip, gpio);
spin_lock_irqsave(&chip->gpio_lock[index], flags);
spin_lock_irqsave(&chip->gpio_lock, flags);
/* Write state of GPIO signal */
if (val)
@ -236,7 +256,7 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
xgpio_writereg(chip->regs + XGPIO_TRI_OFFSET +
xgpio_regoffset(chip, gpio), chip->gpio_dir[index]);
spin_unlock_irqrestore(&chip->gpio_lock[index], flags);
spin_unlock_irqrestore(&chip->gpio_lock, flags);
return 0;
}
@ -259,6 +279,39 @@ static void xgpio_save_regs(struct xgpio_instance *chip)
chip->gpio_dir[1]);
}
static int xgpio_request(struct gpio_chip *chip, unsigned int offset)
{
int ret;
ret = pm_runtime_get_sync(chip->parent);
/*
* If the device is already active pm_runtime_get() will return 1 on
* success, but gpio_request still needs to return 0.
*/
return ret < 0 ? ret : 0;
}
static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
{
pm_runtime_put(chip->parent);
}
static int __maybe_unused xgpio_suspend(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
if (!data) {
dev_err(dev, "irq_get_irq_data() failed\n");
return -EINVAL;
}
if (!irqd_is_wakeup_set(data))
return pm_runtime_force_suspend(dev);
return 0;
}
/**
* xgpio_remove - Remove method for the GPIO device.
* @pdev: pointer to the platform device
@ -271,11 +324,223 @@ static int xgpio_remove(struct platform_device *pdev)
{
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(gpio->clk);
return 0;
}
/**
* xgpio_irq_ack - Acknowledge a child GPIO interrupt.
* @irq_data: per IRQ and chip data passed down to chip functions
* This currently does nothing, but irq_ack is unconditionally called by
* handle_edge_irq and therefore must be defined.
*/
static void xgpio_irq_ack(struct irq_data *irq_data)
{
}
static int __maybe_unused xgpio_resume(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
if (!data) {
dev_err(dev, "irq_get_irq_data() failed\n");
return -EINVAL;
}
if (!irqd_is_wakeup_set(data))
return pm_runtime_force_resume(dev);
return 0;
}
static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
clk_disable(gpio->clk);
return 0;
}
static int __maybe_unused xgpio_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct xgpio_instance *gpio = platform_get_drvdata(pdev);
return clk_enable(gpio->clk);
}
static const struct dev_pm_ops xgpio_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
SET_RUNTIME_PM_OPS(xgpio_runtime_suspend,
xgpio_runtime_resume, NULL)
};
/**
* xgpio_irq_mask - Write the specified signal of the GPIO device.
* @irq_data: per IRQ and chip data passed down to chip functions
*/
static void xgpio_irq_mask(struct irq_data *irq_data)
{
unsigned long flags;
struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
int irq_offset = irqd_to_hwirq(irq_data);
int index = xgpio_index(chip, irq_offset);
int offset = xgpio_offset(chip, irq_offset);
spin_lock_irqsave(&chip->gpio_lock, flags);
chip->irq_enable[index] &= ~BIT(offset);
if (!chip->irq_enable[index]) {
/* Disable per channel interrupt */
u32 temp = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
temp &= ~BIT(index);
xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, temp);
}
spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
* xgpio_irq_unmask - Write the specified signal of the GPIO device.
* @irq_data: per IRQ and chip data passed down to chip functions
*/
static void xgpio_irq_unmask(struct irq_data *irq_data)
{
unsigned long flags;
struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
int irq_offset = irqd_to_hwirq(irq_data);
int index = xgpio_index(chip, irq_offset);
int offset = xgpio_offset(chip, irq_offset);
u32 old_enable = chip->irq_enable[index];
spin_lock_irqsave(&chip->gpio_lock, flags);
chip->irq_enable[index] |= BIT(offset);
if (!old_enable) {
/* Clear any existing per-channel interrupts */
u32 val = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET) &
BIT(index);
if (val)
xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, val);
/* Update GPIO IRQ read data before enabling interrupt*/
val = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET);
chip->gpio_last_irq_read[index] = val;
/* Enable per channel interrupt */
val = xgpio_readreg(chip->regs + XGPIO_IPIER_OFFSET);
val |= BIT(index);
xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, val);
}
spin_unlock_irqrestore(&chip->gpio_lock, flags);
}
/**
* xgpio_set_irq_type - Write the specified signal of the GPIO device.
* @irq_data: Per IRQ and chip data passed down to chip functions
* @type: Interrupt type that is to be set for the gpio pin
*
* Return:
* 0 if interrupt type is supported otherwise -EINVAL
*/
static int xgpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
{
struct xgpio_instance *chip = irq_data_get_irq_chip_data(irq_data);
int irq_offset = irqd_to_hwirq(irq_data);
int index = xgpio_index(chip, irq_offset);
int offset = xgpio_offset(chip, irq_offset);
/*
* The Xilinx GPIO hardware provides a single interrupt status
* indication for any state change in a given GPIO channel (bank).
* Therefore, only rising edge or falling edge triggers are
* supported.
*/
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_BOTH:
chip->irq_rising_edge[index] |= BIT(offset);
chip->irq_falling_edge[index] |= BIT(offset);
break;
case IRQ_TYPE_EDGE_RISING:
chip->irq_rising_edge[index] |= BIT(offset);
chip->irq_falling_edge[index] &= ~BIT(offset);
break;
case IRQ_TYPE_EDGE_FALLING:
chip->irq_rising_edge[index] &= ~BIT(offset);
chip->irq_falling_edge[index] |= BIT(offset);
break;
default:
return -EINVAL;
}
irq_set_handler_locked(irq_data, handle_edge_irq);
return 0;
}
/**
* xgpio_irqhandler - Gpio interrupt service routine
* @desc: Pointer to interrupt description
*/
static void xgpio_irqhandler(struct irq_desc *desc)
{
struct xgpio_instance *chip = irq_desc_get_handler_data(desc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
u32 num_channels = chip->gpio_width[1] ? 2 : 1;
u32 offset = 0, index;
u32 status = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, status);
chained_irq_enter(irqchip, desc);
for (index = 0; index < num_channels; index++) {
if ((status & BIT(index))) {
unsigned long rising_events, falling_events, all_events;
unsigned long flags;
u32 data, bit;
unsigned int irq;
spin_lock_irqsave(&chip->gpio_lock, flags);
data = xgpio_readreg(chip->regs + XGPIO_DATA_OFFSET +
index * XGPIO_CHANNEL_OFFSET);
rising_events = data &
~chip->gpio_last_irq_read[index] &
chip->irq_enable[index] &
chip->irq_rising_edge[index];
falling_events = ~data &
chip->gpio_last_irq_read[index] &
chip->irq_enable[index] &
chip->irq_falling_edge[index];
dev_dbg(chip->gc.parent,
"IRQ chan %u rising 0x%lx falling 0x%lx\n",
index, rising_events, falling_events);
all_events = rising_events | falling_events;
chip->gpio_last_irq_read[index] = data;
spin_unlock_irqrestore(&chip->gpio_lock, flags);
for_each_set_bit(bit, &all_events, 32) {
irq = irq_find_mapping(chip->gc.irq.domain,
offset + bit);
generic_handle_irq(irq);
}
}
offset += chip->gpio_width[index];
}
chained_irq_exit(irqchip, desc);
}
/**
* xgpio_of_probe - Probe method for the GPIO device.
* @pdev: pointer to the platform device
@ -289,7 +554,10 @@ static int xgpio_probe(struct platform_device *pdev)
struct xgpio_instance *chip;
int status = 0;
struct device_node *np = pdev->dev.of_node;
u32 is_dual;
u32 is_dual = 0;
u32 cells = 2;
struct gpio_irq_chip *girq;
u32 temp;
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@ -305,6 +573,15 @@ static int xgpio_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0]))
chip->gpio_dir[0] = 0xFFFFFFFF;
/* Update cells with gpio-cells value */
if (of_property_read_u32(np, "#gpio-cells", &cells))
dev_dbg(&pdev->dev, "Missing gpio-cells property\n");
if (cells != 2) {
dev_err(&pdev->dev, "#gpio-cells mismatch\n");
return -EINVAL;
}
/*
* Check device node and parent device node for device width
* and assume default width of 32
@ -312,7 +589,10 @@ static int xgpio_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "xlnx,gpio-width", &chip->gpio_width[0]))
chip->gpio_width[0] = 32;
spin_lock_init(&chip->gpio_lock[0]);
if (chip->gpio_width[0] > 32)
return -EINVAL;
spin_lock_init(&chip->gpio_lock);
if (of_property_read_u32(np, "xlnx,is-dual", &is_dual))
is_dual = 0;
@ -336,7 +616,8 @@ static int xgpio_probe(struct platform_device *pdev)
&chip->gpio_width[1]))
chip->gpio_width[1] = 32;
spin_lock_init(&chip->gpio_lock[1]);
if (chip->gpio_width[1] > 32)
return -EINVAL;
}
chip->gc.base = -1;
@ -344,8 +625,11 @@ static int xgpio_probe(struct platform_device *pdev)
chip->gc.parent = &pdev->dev;
chip->gc.direction_input = xgpio_dir_in;
chip->gc.direction_output = xgpio_dir_out;
chip->gc.of_gpio_n_cells = cells;
chip->gc.get = xgpio_get;
chip->gc.set = xgpio_set;
chip->gc.request = xgpio_request;
chip->gc.free = xgpio_free;
chip->gc.set_multiple = xgpio_set_multiple;
chip->gc.label = dev_name(&pdev->dev);
@ -357,28 +641,68 @@ static int xgpio_probe(struct platform_device *pdev)
}
chip->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(chip->clk)) {
if (PTR_ERR(chip->clk) != -EPROBE_DEFER)
dev_dbg(&pdev->dev, "Input clock not found\n");
return PTR_ERR(chip->clk);
}
if (IS_ERR(chip->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(chip->clk), "input clock not found.\n");
status = clk_prepare_enable(chip->clk);
if (status < 0) {
dev_err(&pdev->dev, "Failed to prepare clk\n");
return status;
}
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
xgpio_save_regs(chip);
chip->irq = platform_get_irq_optional(pdev, 0);
if (chip->irq <= 0)
goto skip_irq;
chip->irqchip.name = "gpio-xilinx";
chip->irqchip.irq_ack = xgpio_irq_ack;
chip->irqchip.irq_mask = xgpio_irq_mask;
chip->irqchip.irq_unmask = xgpio_irq_unmask;
chip->irqchip.irq_set_type = xgpio_set_irq_type;
/* Disable per-channel interrupts */
xgpio_writereg(chip->regs + XGPIO_IPIER_OFFSET, 0);
/* Clear any existing per-channel interrupts */
temp = xgpio_readreg(chip->regs + XGPIO_IPISR_OFFSET);
xgpio_writereg(chip->regs + XGPIO_IPISR_OFFSET, temp);
/* Enable global interrupts */
xgpio_writereg(chip->regs + XGPIO_GIER_OFFSET, XGPIO_GIER_IE);
girq = &chip->gc.irq;
girq->chip = &chip->irqchip;
girq->parent_handler = xgpio_irqhandler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(&pdev->dev, 1,
sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents) {
status = -ENOMEM;
goto err_pm_put;
}
girq->parents[0] = chip->irq;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
skip_irq:
status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip);
if (status) {
dev_err(&pdev->dev, "failed to add GPIO chip\n");
clk_disable_unprepare(chip->clk);
return status;
goto err_pm_put;
}
pm_runtime_put(&pdev->dev);
return 0;
err_pm_put:
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable_unprepare(chip->clk);
return status;
}
static const struct of_device_id xgpio_of_match[] = {
@ -394,6 +718,7 @@ static struct platform_driver xgpio_plat_driver = {
.driver = {
.name = "gpio-xilinx",
.of_match_table = xgpio_of_match,
.pm = &xgpio_dev_pm_ops,
},
};

View File

@ -1,289 +0,0 @@
// 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)

View File

@ -245,11 +245,34 @@ static int visconti_set_mux(struct pinctrl_dev *pctldev,
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 = {
.get_functions_count = visconti_get_functions_count,
.get_function_name = visconti_get_function_name,
.get_function_groups = visconti_get_function_groups,
.set_mux = visconti_set_mux,
.gpio_request_enable = visconti_gpio_request_enable,
.strict = true,
};

View File

@ -75,7 +75,7 @@ struct gpiod_hog {
* gpiod_get_index()
*/
#define GPIO_LOOKUP_IDX(_key, _chip_hwnum, _con_id, _idx, _flags) \
{ \
(struct gpiod_lookup) { \
.key = _key, \
.chip_hwnum = _chip_hwnum, \
.con_id = _con_id, \
@ -87,7 +87,7 @@ struct gpiod_hog {
* Simple definition of a single GPIO hog in an array.
*/
#define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \
{ \
(struct gpiod_hog) { \
.chip_label = _chip_label, \
.chip_hwnum = _chip_hwnum, \
.line_name = _line_name, \

View File

@ -1,4 +1,4 @@
/* 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
*
@ -212,7 +212,7 @@ struct gpio_v2_line_request {
* @offset: the local offset on this GPIO chip, fill this in when
* requesting the line information from the kernel
* @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_OUTPUT etc, added together.
* @attrs: the configuration attributes associated with the line

View File

@ -83,7 +83,7 @@ EXPORT_SYMBOL(get_option);
* get_options - Parse a string into a list of integers
* @str: String to be parsed
* @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
* list of integers, a hyphen-separated range of _positive_ integers,
@ -91,6 +91,14 @@ EXPORT_SYMBOL(get_option);
* full, or when no more numbers can be retrieved from the
* 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
* the parse to end (typically a null terminator, if @str is
* completely parseable).
@ -98,15 +106,20 @@ EXPORT_SYMBOL(get_option);
char *get_options(const char *str, int nints, int *ints)
{
bool validate = (nints == 0);
int res, i = 1;
while (i < nints) {
res = get_option((char **)&str, ints + i);
while (i < nints || validate) {
int *pint = validate ? ints : ints + i;
res = get_option((char **)&str, pint);
if (res == 0)
break;
if (res == 3) {
int n = validate ? 0 : nints - i;
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)
break;
/*

View File

@ -18,6 +18,26 @@ static const int cmdline_test_values[] = {
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)
{
const char *fmt = "Pattern: %s";
@ -84,10 +104,46 @@ static void cmdline_test_tail_int(struct kunit *test)
} 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[] = {
KUNIT_CASE(cmdline_test_noint),
KUNIT_CASE(cmdline_test_lead_int),
KUNIT_CASE(cmdline_test_tail_int),
KUNIT_CASE(cmdline_test_range),
{}
};

View File

@ -32,74 +32,6 @@
* following api will request gpio lines, do the operation and then
* 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
@ -215,27 +147,6 @@ int gpiotools_get_values(const int fd, struct gpio_v2_line_values *values)
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
* @fd: The fd returned by

View File

@ -24,12 +24,6 @@ static inline int check_prefix(const char *str, const char *prefix)
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,
unsigned int *lines,
unsigned int num_lines,

View File

@ -126,15 +126,6 @@ ARCH ?= $(SUBARCH)
export KSFT_KHDR_INSTALL_DONE := 1
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
# all isn't the first target in the file.
.DEFAULT_GOAL := all

View File

@ -1,31 +1,7 @@
# 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_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
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)

View File

@ -1,2 +1,3 @@
CONFIG_GPIOLIB=y
CONFIG_GPIO_CDEV=y
CONFIG_GPIO_MOCKUP=m

View File

@ -0,0 +1,198 @@
// 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;
}

View File

@ -1,323 +0,0 @@
// 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;
}

View File

@ -1,135 +1,77 @@
# SPDX-License-Identifier: GPL-2.0
is_consistent()
# Overrides functions in gpio-mockup.sh to test using the GPIO SYSFS uAPI
SYSFS=`grep -w sysfs /proc/mounts | cut -f2 -d' '`
[ -d "$SYSFS" ] || skip "sysfs is not mounted"
GPIO_SYSFS="${SYSFS}/class/gpio"
[ -d "$GPIO_SYSFS" ] || skip "CONFIG_GPIO_SYSFS is not selected"
PLATFORM_SYSFS=$SYSFS/devices/platform
sysfs_nr=
sysfs_ldir=
# determine the sysfs GPIO number given the $chip and $offset
# e.g. gpiochip1:32
find_sysfs_nr()
{
val=
active_low_sysfs=`cat $GPIO_SYSFS/gpio$nr/active_low`
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"`
dir_debugfs=`echo $gpio_this_debugfs | awk '{print $2}'`
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
if [ $val = "0" ]; then
val="1"
else
val="0"
fi
fi
if [ $val_sysfs = $val ] && [ $dir_sysfs = $dir_debugfs ]; then
echo -n "."
else
echo "test fail, exit"
die
fi
# e.g. /sys/devices/platform/gpio-mockup.1/gpiochip1
local platform=$(find $PLATFORM_SYSFS -mindepth 2 -maxdepth 2 -type d -name $chip)
[ "$platform" ] || fail "can't find platform of $chip"
# e.g. /sys/devices/platform/gpio-mockup.1/gpio/gpiochip508/base
local base=$(find ${platform%/*}/gpio/ -mindepth 2 -maxdepth 2 -type f -name base)
[ "$base" ] || fail "can't find base of $chip"
sysfs_nr=$(($(< "$base") + $offset))
sysfs_ldir="$GPIO_SYSFS/gpio$sysfs_nr"
}
test_pin_logic()
acquire_line()
{
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
[ "$sysfs_nr" ] && return
find_sysfs_nr
echo "$sysfs_nr" > "$GPIO_SYSFS/export"
}
test_one_pin()
# The helpers being overridden...
get_line()
{
nr=$1
echo -n "test pin<$nr>"
echo $nr > $GPIO_SYSFS/export 2>/dev/null
if [ X$? != X0 ]; then
echo "test GPIO pin $nr failed"
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"
[ -e "$sysfs_ldir/value" ] && echo $(< "$sysfs_ldir/value")
}
test_one_pin_fail()
set_line()
{
nr=$1
acquire_line
echo $nr > $GPIO_SYSFS/export 2>/dev/null
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
for option in $*; do
case $option in
active-high)
echo 0 > "$sysfs_ldir/active_low"
;;
active-low)
echo 1 > "$sysfs_ldir/active_low"
;;
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
}
list_chip()
release_line()
{
echo `ls -d $GPIO_DRV_SYSFS/gpiochip* 2>/dev/null`
[ "$sysfs_nr" ] || return 0
echo "$sysfs_nr" > "$GPIO_SYSFS/unexport"
sysfs_nr=
sysfs_ldir=
}
test_chip()
{
chip=$1
name=`basename $chip`
base=`cat $chip/base`
ngpio=`cat $chip/ngpio`
printf "%-10s %-5s %-5s\n" $name $base $ngpio
if [ $ngpio = "0" ]; then
echo "number of gpio is zero is not allowed".
fi
test_one_pin $base
test_one_pin $(($base + $ngpio - 1))
test_one_pin $((( RANDOM % $ngpio ) + $base ))
}
test_chips_sysfs()
{
gpiochip=`list_chip $module`
if [ X"$gpiochip" = X ]; then
if [ X"$valid" = Xfalse ]; then
echo "successful"
else
echo "fail"
die
fi
else
for chip in $gpiochip; do
test_chip $chip
done
fi
}

View File

@ -1,72 +1,57 @@
#!/bin/bash
#!/bin/bash -efu
# SPDX-License-Identifier: GPL-2.0
#exit status
#1: Internal error
#2: sysfs/debugfs not mount
#3: insert module fail when gpio-mockup is a module.
#4: Skip test including run as non-root user.
#5: other reason.
#0: success
#1: fail
#4: skip test - including run as non-root user
SYSFS=
GPIO_SYSFS=
GPIO_DRV_SYSFS=
BASE=${0%/*}
DEBUGFS=
GPIO_DEBUGFS=
dev_type=
module=
dev_type="cdev"
module="gpio-mockup"
verbose=
full_test=
random=
uapi_opt=
active_opt=
bias_opt=
line_set_pid=
# Kselftest framework requirement - SKIP code is 4.
# Kselftest return codes
ksft_fail=1
ksft_skip=4
usage()
{
echo "Usage:"
echo "$0 [-f] [-m name] [-t type]"
echo "-f: full test. It maybe conflict with existence gpio device."
echo "-m: module name, default name is gpio-mockup. It could also test"
echo " other gpio device."
echo "-t: interface type: chardev(char device) and sysfs(being"
echo " deprecated). The first one is default"
echo ""
echo "$0 -h"
echo "This usage"
echo "$0 [-frv] [-t type]"
echo "-f: full test (minimal set run by default)"
echo "-r: test random lines as well as fence posts"
echo "-t: interface type:"
echo " cdev (character device ABI) - default"
echo " cdev_v1 (deprecated character device ABI)"
echo " sysfs (deprecated SYSFS ABI)"
echo "-v: verbose progress reporting"
exit $ksft_fail
}
skip()
{
echo "$*" >&2
echo "GPIO $module test SKIP"
exit $ksft_skip
}
prerequisite()
{
msg="skip all tests:"
if [ $UID != 0 ]; then
echo $msg must be run as root >&2
exit $ksft_skip
fi
SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
if [ ! -d "$SYSFS" ]; then
echo $msg sysfs is not mounted >&2
exit 2
fi
GPIO_SYSFS=`echo $SYSFS/class/gpio`
GPIO_DRV_SYSFS=`echo $SYSFS/devices/platform/$module/gpio`
DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'`
if [ ! -d "$DEBUGFS" ]; then
echo $msg debugfs is not mounted >&2
exit 2
fi
GPIO_DEBUGFS=`echo $DEBUGFS/gpio`
source gpio-mockup-sysfs.sh
}
[ $(id -u) -eq 0 ] || skip "must be run as root"
try_insert_module()
{
if [ -d "$GPIO_DRV_SYSFS" ]; then
echo "$GPIO_DRV_SYSFS exist. Skip insert module"
else
modprobe -q $module $1
if [ X$? != X0 ]; then
echo $msg insmod $module failed >&2
exit 3
fi
fi
DEBUGFS=$(grep -w debugfs /proc/mounts | cut -f2 -d' ')
[ -d "$DEBUGFS" ] || skip "debugfs is not mounted"
GPIO_DEBUGFS=$DEBUGFS/$module
}
remove_module()
@ -74,133 +59,345 @@ remove_module()
modprobe -r -q $module
}
die()
cleanup()
{
set +e
release_line
remove_module
exit 5
jobs -p | xargs -r kill > /dev/null 2>&1
}
test_chips()
fail()
{
if [ X$dev_type = Xsysfs ]; then
echo "WARNING: sysfs ABI of gpio is going to deprecated."
test_chips_sysfs $*
else
$BASE/gpio-mockup-chardev $*
echo "test failed: $*" >&2
echo "GPIO $module test FAIL"
exit $ksft_fail
}
try_insert_module()
{
modprobe -q $module "$1" || fail "insert $module failed with error $?"
}
log()
{
[ -z "$verbose" ] || echo "$*"
}
# The following line helpers, release_Line, get_line and set_line, all
# make use of the global $chip and $offset variables.
#
# This implementation drives the GPIO character device (cdev) uAPI.
# Other implementations may override these to test different uAPIs.
# Release any resources related to the line
release_line()
{
[ "$line_set_pid" ] && kill $line_set_pid && wait $line_set_pid || true
line_set_pid=
}
# Read the current value of the line
get_line()
{
release_line
local cdev_opts=${uapi_opt}${active_opt}
$BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset
echo $?
}
# Set the state of the line
#
# Changes to line configuration are provided as parameters.
# The line is assumed to be an output if the line value 0 or 1 is
# specified, else an input.
set_line()
{
local val=
release_line
# parse config options...
for option in $*; do
case $option in
active-low)
active_opt="-l "
;;
active-high)
active_opt=
;;
bias-none)
bias_opt=
;;
pull-down)
bias_opt="-bpull-down "
;;
pull-up)
bias_opt="-bpull-up "
;;
0)
val=0
;;
1)
val=1
;;
esac
done
local cdev_opts=${uapi_opt}${active_opt}
if [ "$val" ]; then
$BASE/gpio-mockup-cdev $cdev_opts -s$val /dev/$chip $offset &
# failure to set is detected by reading mockup and toggling values
line_set_pid=$!
# allow for gpio-mockup-cdev to launch and request line
# (there is limited value in checking if line has been requested)
sleep 0.01
elif [ "$bias_opt" ]; then
cdev_opts=${cdev_opts}${bias_opt}
$BASE/gpio-mockup-cdev $cdev_opts /dev/$chip $offset || true
fi
}
gpio_test()
assert_line()
{
param=$1
valid=$2
if [ X"$param" = X ]; then
die
fi
try_insert_module "gpio_mockup_ranges=$param"
echo -n "GPIO $module test with ranges: <"
echo "$param>: "
printf "%-10s %s\n" $param
test_chips $module $valid
remove_module
local val
# don't need any retry here as set_mock allows for propagation
val=$(get_line)
[ "$val" = "$1" ] || fail "line value is ${val:-empty} when $1 was expected"
}
BASE=`dirname $0`
# The following mockup helpers all make use of the $mock_line
assert_mock()
{
local backoff_wait=10
local retry=0
local val
# retry allows for set propagation from uAPI to mockup
while true; do
val=$(< $mock_line)
[ "$val" = "$1" ] && break
retry=$((retry + 1))
[ $retry -lt 5 ] || fail "mockup $mock_line value ${val:-empty} when $1 expected"
sleep $(printf "%0.2f" $((backoff_wait))e-3)
backoff_wait=$((backoff_wait * 2))
done
}
dev_type=
TEMP=`getopt -o fhm:t: -n '$0' -- "$@"`
set_mock()
{
echo "$1" > $mock_line
# allow for set propagation - so we won't be in a race with set_line
assert_mock "$1"
}
if [ "$?" != "0" ]; then
echo "Parameter process failed, Terminating..." >&2
exit 1
fi
# test the functionality of a line
#
# The line is set from the mockup side and is read from the userspace side
# (input), and is set from the userspace side and is read from the mockup side
# (output).
#
# Setting the mockup pull using the userspace interface bias settings is
# tested where supported by the userspace interface (cdev).
test_line()
{
chip=$1
offset=$2
log "test_line $chip $offset"
mock_line=$GPIO_DEBUGFS/$chip/$offset
[ -e "$mock_line" ] || fail "missing line $chip:$offset"
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
# test input active-high
set_mock 1
set_line input active-high
assert_line 1
set_mock 0
assert_line 0
set_mock 1
assert_line 1
while true; do
case $1 in
-f)
if [ "$full_test" ]; then
if [ "$dev_type" != "sysfs" ]; then
# test pulls
set_mock 0
set_line input pull-up
assert_line 1
set_mock 0
assert_line 0
set_mock 1
set_line input pull-down
assert_line 0
set_mock 1
assert_line 1
set_line bias-none
fi
# test input active-low
set_mock 0
set_line active-low
assert_line 1
set_mock 1
assert_line 0
set_mock 0
assert_line 1
# test output active-high
set_mock 1
set_line active-high 0
assert_mock 0
set_line 1
assert_mock 1
set_line 0
assert_mock 0
fi
# test output active-low
set_mock 0
set_line active-low 0
assert_mock 1
set_line 1
assert_mock 0
set_line 0
assert_mock 1
release_line
}
test_no_line()
{
log test_no_line "$*"
[ ! -e "$GPIO_DEBUGFS/$1/$2" ] || fail "unexpected line $1:$2"
}
# Load the module and check that the expected number of gpiochips, with the
# expected number of lines, are created and are functional.
#
# $1 is the gpio_mockup_ranges parameter for the module
# The remaining parameters are the number of lines, n, expected for each of
# the gpiochips expected to be created.
#
# For each gpiochip the fence post lines, 0 and n-1, are tested, and the
# line on the far side of the fence post, n, is tested to not exist.
#
# If the $random flag is set then a random line in the middle of the
# gpiochip is tested as well.
insmod_test()
{
local ranges=
local gc=
local width=
[ "${1:-}" ] || fail "missing ranges"
ranges=$1 ; shift
try_insert_module "gpio_mockup_ranges=$ranges"
log "GPIO $module test with ranges: <$ranges>:"
# e.g. /sys/kernel/debug/gpio-mockup/gpiochip1
gpiochip=$(find "$DEBUGFS/$module/" -name gpiochip* -type d | sort)
for chip in $gpiochip; do
gc=${chip##*/}
[ "${1:-}" ] || fail "unexpected chip - $gc"
width=$1 ; shift
test_line $gc 0
if [ "$random" -a $width -gt 2 ]; then
test_line $gc $((RANDOM % ($width - 2) + 1))
fi
test_line $gc $(($width - 1))
test_no_line $gc $width
done
[ "${1:-}" ] && fail "missing expected chip of width $1"
remove_module || fail "failed to remove module with error $?"
}
while getopts ":frvt:" opt; do
case $opt in
f)
full_test=true
shift
;;
-h)
usage
exit
r)
random=true
;;
-m)
module=$2
shift 2
t)
dev_type=$OPTARG
;;
-t)
dev_type=$2
shift 2
;;
--)
shift
break
v)
verbose=true
;;
*)
echo "Internal error!"
exit 1
usage
;;
esac
done
shift $((OPTIND - 1))
if [ X"$module" = X ]; then
module="gpio-mockup"
fi
if [ X$dev_type != Xsysfs ]; then
dev_type="chardev"
fi
[ "${1:-}" ] && fail "unknown argument '$1'"
prerequisite
echo "1. Test dynamic allocation of gpio successful means insert gpiochip and"
echo " manipulate gpio pin successful"
gpio_test "-1,32" true
gpio_test "-1,32,-1,32" true
gpio_test "-1,32,-1,32,-1,32" true
if [ X$full_test = Xtrue ]; then
gpio_test "-1,32,32,64" true
gpio_test "-1,32,40,64,-1,5" true
gpio_test "-1,32,32,64,-1,32" true
gpio_test "0,32,32,64,-1,32,-1,32" true
gpio_test "-1,32,-1,32,0,32,32,64" true
echo "2. Do basic test: successful means insert gpiochip and"
echo " manipulate gpio pin successful"
gpio_test "0,32" true
gpio_test "0,32,32,64" true
gpio_test "0,32,40,64,64,96" true
trap 'exit $ksft_fail' SIGTERM SIGINT
trap cleanup EXIT
case "$dev_type" in
sysfs)
source $BASE/gpio-mockup-sysfs.sh
echo "WARNING: gpio sysfs ABI is deprecated."
;;
cdev_v1)
echo "WARNING: gpio cdev ABI v1 is deprecated."
uapi_opt="-u1 "
;;
cdev)
;;
*)
fail "unknown interface type: $dev_type"
;;
esac
remove_module || fail "can't remove existing $module module"
# manual gpio allocation tests fail if a physical chip already exists
[ "$full_test" -a -e "/dev/gpiochip0" ] && skip "full tests conflict with gpiochip0"
echo "1. Module load tests"
echo "1.1. dynamic allocation of gpio"
insmod_test "-1,32" 32
insmod_test "-1,23,-1,32" 23 32
insmod_test "-1,23,-1,26,-1,32" 23 26 32
if [ "$full_test" ]; then
echo "1.2. manual allocation of gpio"
insmod_test "0,32" 32
insmod_test "0,32,32,60" 32 28
insmod_test "0,32,40,64,64,96" 32 24 32
echo "1.3. dynamic and manual allocation of gpio"
insmod_test "-1,32,32,62" 32 30
insmod_test "-1,22,-1,23,0,24,32,64" 22 23 24 32
insmod_test "-1,32,32,60,-1,29" 32 28 29
insmod_test "-1,32,40,64,-1,5" 32 24 5
insmod_test "0,32,32,44,-1,22,-1,31" 32 12 22 31
fi
echo "3. Error test: successful means insert gpiochip failed"
echo "3.1 Test number of gpio overflow"
#Currently: The max number of gpio(1024) is defined in arm architecture.
gpio_test "-1,32,-1,1024" false
if [ X$full_test = Xtrue ]; then
echo "3.2 Test zero line of gpio"
gpio_test "0,0" false
echo "3.3 Test range overlap"
echo "3.3.1 Test corner case"
gpio_test "0,32,0,1" false
gpio_test "0,32,32,64,32,40" false
gpio_test "0,32,35,64,35,45" false
gpio_test "0,32,31,32" false
gpio_test "0,32,32,64,36,37" false
gpio_test "0,32,35,64,34,36" false
echo "3.3.2 Test inserting invalid second gpiochip"
gpio_test "0,32,30,35" false
gpio_test "0,32,1,5" false
gpio_test "10,32,9,14" false
gpio_test "10,32,30,35" false
echo "3.3.3 Test others"
gpio_test "0,32,40,56,39,45" false
gpio_test "0,32,40,56,30,33" false
gpio_test "0,32,40,56,30,41" false
gpio_test "0,32,40,56,20,21" false
echo "2. Module load error tests"
echo "2.1 gpio overflow"
# Currently: The max number of gpio(1024) is defined in arm architecture.
insmod_test "-1,1024"
if [ "$full_test" ]; then
echo "2.2 no lines defined"
insmod_test "0,0"
echo "2.3 ignore range overlap"
insmod_test "0,32,0,1" 32
insmod_test "0,32,1,5" 32
insmod_test "0,32,30,35" 32
insmod_test "0,32,31,32" 32
insmod_test "10,32,30,35" 22
insmod_test "10,32,9,14" 22
insmod_test "0,32,20,21,40,56" 32 16
insmod_test "0,32,32,64,32,40" 32 32
insmod_test "0,32,32,64,36,37" 32 32
insmod_test "0,32,35,64,34,36" 32 29
insmod_test "0,30,35,64,35,45" 30 29
insmod_test "0,32,40,56,30,33" 32 16
insmod_test "0,32,40,56,30,41" 32 16
insmod_test "0,32,40,56,39,45" 32 16
fi
echo GPIO test PASS
echo "GPIO $module test PASS"

View File

@ -129,13 +129,11 @@ l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \
grep -v "VAR_LDLIBS" | awk -F: '{print $1}')
# 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
# any, VAR_LDLIBS set to default.
# Use the default value and filter out pkg-config for dependency check.
# e.g:
# gpio/Makefile
# VAR_LDLIBS := $(shell pkg-config --libs mount) 2>/dev/null)
# memfd/Makefile
# VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null)