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