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