mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 10:33:41 +02:00
gpio updates for v6.8
Core GPIOLIB:
- protect the global list of GPIO devices with a read-write semaphore as
it is rarely modified but can be traversed by multiple readers at once
- remove GPIO devices from the global list when they are *unregistered*
and not when they are *released* (which only happens when the last
reference is dropped) as this may lead to a successful lookup of an
unregistered device
- remove the unnecessary "extra_checks" switch
- rename functions that are called with a lock taken
- remove duplicate includes
Character device handling:
- use locking guards to reduce the code size
- allocate the big linereq structure using the more suitable kvzalloc()
- redulce the size of critical sections
- improve documentation
- move the debounce_period_us field out of struct gpio_desc
New drivers:
- Nuvoton NPCM SGPIO driver for BMC NPCM7xx/NPCM8xx
- Realtek DHC (Digital Home Center) SoC GPIO driver
Driver improvements:
- replace gpiochip_is_requested() with a safer alternative in the form of
gpiochip_dup_line_label() as the former returns a pointer to a string that
can be deleted
- implement the dbg_show() callback in gpio-sim
- improve the coding style for local variables by removing unnecessary tabs
- use generic device properties instead of OF variants in gpio-mmio
- use the preferred coding style for __free() in gpio-mockup
- reuse PM ops from the gpio-tangier in gpio-elkhartlake
- rework PM and use cleanup helpers in gpio-tangier
- fix the EIC configuration in gpio-pmic-eic-sprd
- remove the unneeded call to platform_set_drvdata() in gpio-sifive
- use generic GPIO helpers for driver callbacks in gpio-dwapb
- add clock support on certain pins of gpio-ixp4xx
- don't use the core-specific DEBUG_GPIO switch in drivers
- kerneldoc improvements
DT bindings:
- add bindings for the new Realtek and Nuvoton devices
- allow gpio-ranges in gpio-dwapb
- support GPIO hogs in gpio-rockchip
- describe the label property in gpio-zynqmp-modepin
Other:
- header cleanups
- forward declarations cleanups
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmWdlcgACgkQEacuoBRx
13LUwQ//QG1RU5oEOe+mMw6qtsRl5CC4ufiQIbDFtDspitKvlJqx9wK3ZZpK8EOT
xRQxitZn7wQMkCUmxsAY4g+EMf6k1hpeX9bb1aZHbDcJrznQy3Md4pkqvybk7TrH
f6bIAdV97T+vgeXwrpCnwf41xpTuX6TJqXR7xFhw05r6n495WuCZUjm+DcjgROZu
mignIGeHzDpBKThspw6EPaz4iMuKFjBJbdrIsoNwqUF6W0Oy+era9ATfyVl8LfxQ
QDHLIa875Q5LMmBZoGZdMzpyIcNaplrt6POqi1/O7e7xSFSZeb2j85Z64ehPT32c
VITkj2ZU+XAGylN5lII5LbJbm8Gmz7GCaIw300Ng1LqT/VP52EUG4jdpLh/x0Qff
TlDS1095P4/1ATrJKzx1FJskVvfEmfGZ4/gOOjrrQp6NSG5jTPVjZ4TtC9LHWH0l
qqEG3933ayM36ypxCiiEJYGztWdRI0GUwDyaCwYGDdBQbinKKsfdYEcFqwLm98C4
9vmRKzJlvWjKBRhnU/hGn04wHoK6XeyvdHct53WAs5+o+ncxiF/zmURZganFnhhV
KckqChmgIJ0LWu7ygg3Xcy/iSQjTpnG8sNJapjwSN0crF4cQFSC3yfOUC9BKu8k2
sGyHoOWUfoj1/dRorPARc4xQaPQYR2LscswNrlxlKTavMuX+wqU=
=bcXn
-----END PGP SIGNATURE-----
Merge tag 'gpio-updates-for-v6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"We have two new drivers, an assortment of updates and cleanups to many
others, and first part of the big rework of the core GPIOLIB that's
currently underway.
Add to that some code shrink in the character device module and
updates to DT bindings and that's pretty much it.
Core GPIOLIB:
- protect the global list of GPIO devices with a read-write semaphore
as it is rarely modified but can be traversed by multiple readers
at once
- remove GPIO devices from the global list when they are
*unregistered* and not when they are *released* (which only happens
when the last reference is dropped) as this may lead to a
successful lookup of an unregistered device
- remove the unnecessary "extra_checks" switch
- rename functions that are called with a lock taken
- remove duplicate includes
Character device handling:
- use locking guards to reduce the code size
- allocate the big linereq structure using the more suitable
kvzalloc()
- redulce the size of critical sections
- improve documentation
- move the debounce_period_us field out of struct gpio_desc
New drivers:
- Nuvoton NPCM SGPIO driver for BMC NPCM7xx/NPCM8xx
- Realtek DHC (Digital Home Center) SoC GPIO driver
Driver improvements:
- replace gpiochip_is_requested() with a safer alternative in the
form of gpiochip_dup_line_label() as the former returns a pointer
to a string that can be deleted
- implement the dbg_show() callback in gpio-sim
- improve the coding style for local variables by removing
unnecessary tabs
- use generic device properties instead of OF variants in gpio-mmio
- use the preferred coding style for __free() in gpio-mockup
- reuse PM ops from the gpio-tangier in gpio-elkhartlake
- rework PM and use cleanup helpers in gpio-tangier
- fix the EIC configuration in gpio-pmic-eic-sprd
- remove the unneeded call to platform_set_drvdata() in gpio-sifive
- use generic GPIO helpers for driver callbacks in gpio-dwapb
- add clock support on certain pins of gpio-ixp4xx
- don't use the core-specific DEBUG_GPIO switch in drivers
- kerneldoc improvements
DT bindings:
- add bindings for the new Realtek and Nuvoton devices
- allow gpio-ranges in gpio-dwapb
- support GPIO hogs in gpio-rockchip
- describe the label property in gpio-zynqmp-modepin
Other:
- header cleanups
- forward declarations cleanups"
* tag 'gpio-updates-for-v6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (55 commits)
gpiolib: replace the GPIO device mutex with a read-write semaphore
gpiolib: remove the GPIO device from the list when it's unregistered
gpio: nuvoton: Add Nuvoton NPCM sgpio driver
dt-bindings: gpio: add NPCM sgpio driver bindings
gpio: rtd: Add support for Realtek DHC(Digital Home Center) RTD SoCs
dt-bindings: gpio: realtek: Add realtek,rtd-gpio
gpio: pmic-eic-sprd: Configure the bit corresponding to the EIC through offset
gpio: dwapb: Use generic request, free and set_config
gpio: sysfs: drop tabs from local variable declarations
gpiolib: drop tabs from local variable declarations
gpiolib: remove extra_checks
gpio: tps65219: don't use CONFIG_DEBUG_GPIO
gpiolib: cdev: replace locking wrappers for gpio_device with guards
gpiolib: cdev: replace locking wrappers for config_mutex with guards
gpiolib: cdev: allocate linereq using kvzalloc()
gpiolib: cdev: include overflow.h
gpiolib: cdev: reduce locking in gpio_desc_to_lineinfo()
gpiolib: cdev: improve documentation of get/set values
gpiolib: cdev: fully adopt guard() and scoped_guard()
gpiolib: remove debounce_period_us from struct gpio_desc
...
This commit is contained in:
commit
576db73424
|
|
@ -72,7 +72,7 @@ required:
|
|||
- reg
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
- "brcm,gpio-bank-widths"
|
||||
- brcm,gpio-bank-widths
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
|
|||
87
Documentation/devicetree/bindings/gpio/nuvoton,sgpio.yaml
Normal file
87
Documentation/devicetree/bindings/gpio/nuvoton,sgpio.yaml
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/nuvoton,sgpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Nuvoton SGPIO controller
|
||||
|
||||
maintainers:
|
||||
- Jim LIU <JJLIU0@nuvoton.com>
|
||||
|
||||
description: |
|
||||
This SGPIO controller is for NUVOTON NPCM7xx and NPCM8xx SoC and detailed
|
||||
information is in the NPCM7XX/8XX SERIAL I/O EXPANSION INTERFACE section.
|
||||
Nuvoton NPCM7xx SGPIO module is combines a serial to parallel IC (HC595)
|
||||
and a parallel to serial IC (HC165).
|
||||
Clock is a division of the APB3 clock.
|
||||
This interface has 4 pins (D_out , D_in, S_CLK, LDSH).
|
||||
NPCM7xx/NPCM8xx have two sgpio modules. Each module can support up
|
||||
to 64 output pins, and up to 64 input pins, the pin is only for GPI or GPO.
|
||||
GPIO pins can be programmed to support the following options
|
||||
- Support interrupt option for each input port and various interrupt
|
||||
sensitivity options (level-high, level-low, edge-high, edge-low)
|
||||
- ngpios is number of nuvoton,input-ngpios GPIO lines and nuvoton,output-ngpios GPIO lines.
|
||||
nuvoton,input-ngpios GPIO lines is only for GPI.
|
||||
nuvoton,output-ngpios GPIO lines is only for GPO.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nuvoton,npcm750-sgpio
|
||||
- nuvoton,npcm845-sgpio
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
nuvoton,input-ngpios:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The numbers of GPIO's exposed. GPIO lines are only for GPI.
|
||||
minimum: 0
|
||||
maximum: 64
|
||||
|
||||
nuvoton,output-ngpios:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The numbers of GPIO's exposed. GPIO lines are only for GPO.
|
||||
minimum: 0
|
||||
maximum: 64
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
- interrupts
|
||||
- nuvoton,input-ngpios
|
||||
- nuvoton,output-ngpios
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
gpio8: gpio@101000 {
|
||||
compatible = "nuvoton,npcm750-sgpio";
|
||||
reg = <0x101000 0x200>;
|
||||
clocks = <&clk NPCM7XX_CLK_APB3>;
|
||||
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
nuvoton,input-ngpios = <64>;
|
||||
nuvoton,output-ngpios = <64>;
|
||||
};
|
||||
69
Documentation/devicetree/bindings/gpio/realtek,rtd-gpio.yaml
Normal file
69
Documentation/devicetree/bindings/gpio/realtek,rtd-gpio.yaml
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2023 Realtek Semiconductor Corporation
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/realtek,rtd-gpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek DHC GPIO controller
|
||||
|
||||
maintainers:
|
||||
- Tzuyi Chang <tychang@realtek.com>
|
||||
|
||||
description:
|
||||
The GPIO controller is designed for the Realtek DHC (Digital Home Center)
|
||||
RTD series SoC family, which are high-definition media processor SoCs.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- realtek,rtd1295-misc-gpio
|
||||
- realtek,rtd1295-iso-gpio
|
||||
- realtek,rtd1315e-iso-gpio
|
||||
- realtek,rtd1319-iso-gpio
|
||||
- realtek,rtd1319d-iso-gpio
|
||||
- realtek,rtd1395-iso-gpio
|
||||
- realtek,rtd1619-iso-gpio
|
||||
- realtek,rtd1619b-iso-gpio
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: GPIO controller registers
|
||||
- description: GPIO interrupt registers
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Interrupt number of the assert GPIO interrupt, which is
|
||||
triggered when there is a rising edge.
|
||||
- description: Interrupt number of the deassert GPIO interrupt, which is
|
||||
triggered when there is a falling edge.
|
||||
|
||||
gpio-ranges: true
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- gpio-ranges
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
gpio@100 {
|
||||
compatible = "realtek,rtd1319d-iso-gpio";
|
||||
reg = <0x100 0x100>,
|
||||
<0x0 0xb0>;
|
||||
interrupt-parent = <&iso_irq_mux>;
|
||||
interrupts = <19>, <20>;
|
||||
gpio-ranges = <&pinctrl 0 0 82>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
|
@ -41,6 +41,13 @@ properties:
|
|||
"#interrupt-cells":
|
||||
const: 2
|
||||
|
||||
patternProperties:
|
||||
"^.+-hog(-[0-9]+)?$":
|
||||
type: object
|
||||
|
||||
required:
|
||||
- gpio-hog
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ patternProperties:
|
|||
minItems: 1
|
||||
maxItems: 32
|
||||
|
||||
gpio-ranges: true
|
||||
|
||||
ngpios:
|
||||
default: 32
|
||||
minimum: 1
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ properties:
|
|||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
label: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- gpio-controller
|
||||
|
|
@ -37,6 +39,7 @@ examples:
|
|||
compatible = "xlnx,zynqmp-gpio-modepin";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
label = "modepin";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -478,6 +478,13 @@ config GPIO_MXS
|
|||
select GPIO_GENERIC
|
||||
select GENERIC_IRQ_CHIP
|
||||
|
||||
config GPIO_NPCM_SGPIO
|
||||
bool "Nuvoton SGPIO support"
|
||||
depends on ARCH_NPCM || COMPILE_TEST
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to support Nuvoton NPCM7XX/NPCM8XX SGPIO functionality.
|
||||
|
||||
config GPIO_OCTEON
|
||||
tristate "Cavium OCTEON GPIO"
|
||||
depends on CAVIUM_OCTEON_SOC
|
||||
|
|
@ -553,6 +560,19 @@ config GPIO_ROCKCHIP
|
|||
help
|
||||
Say yes here to support GPIO on Rockchip SoCs.
|
||||
|
||||
config GPIO_RTD
|
||||
tristate "Realtek DHC GPIO support"
|
||||
depends on ARCH_REALTEK
|
||||
default y
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
This option enables support for GPIOs found on Realtek DHC(Digital
|
||||
Home Center) SoCs family, including RTD1295, RTD1315E, RTD1319,
|
||||
RTD1319D, RTD1395, RTD1619 and RTD1619B.
|
||||
|
||||
Say yes here to support GPIO functionality and GPIO interrupt on
|
||||
Realtek DHC SoCs.
|
||||
|
||||
config GPIO_SAMA5D2_PIOBU
|
||||
tristate "SAMA5D2 PIOBU GPIO support"
|
||||
depends on MFD_SYSCON
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
|
|||
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
|
||||
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
|
||||
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
|
||||
obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o
|
||||
obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
|
||||
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
|
||||
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
|
||||
|
|
@ -137,6 +138,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
|
|||
obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
|
||||
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
|
||||
obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
|
||||
obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o
|
||||
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
||||
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
|
||||
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
||||
|
|
|
|||
|
|
@ -416,11 +416,12 @@ static int dwapb_gpio_set_config(struct gpio_chip *gc, unsigned offset,
|
|||
{
|
||||
u32 debounce;
|
||||
|
||||
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
|
||||
return -ENOTSUPP;
|
||||
if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) {
|
||||
debounce = pinconf_to_config_argument(config);
|
||||
return dwapb_gpio_set_debounce(gc, offset, debounce);
|
||||
}
|
||||
|
||||
debounce = pinconf_to_config_argument(config);
|
||||
return dwapb_gpio_set_debounce(gc, offset, debounce);
|
||||
return gpiochip_generic_config(gc, offset, config);
|
||||
}
|
||||
|
||||
static int dwapb_convert_irqs(struct dwapb_gpio_port_irqchip *pirq,
|
||||
|
|
@ -530,10 +531,14 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
|
|||
port->gc.fwnode = pp->fwnode;
|
||||
port->gc.ngpio = pp->ngpio;
|
||||
port->gc.base = pp->gpio_base;
|
||||
port->gc.request = gpiochip_generic_request;
|
||||
port->gc.free = gpiochip_generic_free;
|
||||
|
||||
/* Only port A support debounce */
|
||||
if (pp->idx == 0)
|
||||
port->gc.set_config = dwapb_gpio_set_config;
|
||||
else
|
||||
port->gc.set_config = gpiochip_generic_config;
|
||||
|
||||
/* Only port A can provide interrupts in all configurations of the IP */
|
||||
if (pp->idx == 0)
|
||||
|
|
|
|||
|
|
@ -55,18 +55,6 @@ static int ehl_gpio_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ehl_gpio_suspend(struct device *dev)
|
||||
{
|
||||
return tng_gpio_suspend(dev);
|
||||
}
|
||||
|
||||
static int ehl_gpio_resume(struct device *dev)
|
||||
{
|
||||
return tng_gpio_resume(dev);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(ehl_gpio_pm_ops, ehl_gpio_suspend, ehl_gpio_resume);
|
||||
|
||||
static const struct platform_device_id ehl_gpio_ids[] = {
|
||||
{ "gpio-elkhartlake" },
|
||||
{ }
|
||||
|
|
@ -76,7 +64,7 @@ MODULE_DEVICE_TABLE(platform, ehl_gpio_ids);
|
|||
static struct platform_driver ehl_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-elkhartlake",
|
||||
.pm = pm_sleep_ptr(&ehl_gpio_pm_ops),
|
||||
.pm = pm_sleep_ptr(&tng_gpio_pm_ops),
|
||||
},
|
||||
.probe = ehl_gpio_probe,
|
||||
.id_table = ehl_gpio_ids,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,18 @@
|
|||
#define IXP4XX_GPIO_STYLE_MASK GENMASK(2, 0)
|
||||
#define IXP4XX_GPIO_STYLE_SIZE 3
|
||||
|
||||
/*
|
||||
* Clock output control register defines.
|
||||
*/
|
||||
#define IXP4XX_GPCLK_CLK0DC_SHIFT 0
|
||||
#define IXP4XX_GPCLK_CLK0TC_SHIFT 4
|
||||
#define IXP4XX_GPCLK_CLK0_MASK GENMASK(7, 0)
|
||||
#define IXP4XX_GPCLK_MUX14 BIT(8)
|
||||
#define IXP4XX_GPCLK_CLK1DC_SHIFT 16
|
||||
#define IXP4XX_GPCLK_CLK1TC_SHIFT 20
|
||||
#define IXP4XX_GPCLK_CLK1_MASK GENMASK(23, 16)
|
||||
#define IXP4XX_GPCLK_MUX15 BIT(24)
|
||||
|
||||
/**
|
||||
* struct ixp4xx_gpio - IXP4 GPIO state container
|
||||
* @dev: containing device for this instance
|
||||
|
|
@ -202,6 +214,8 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
|
|||
struct ixp4xx_gpio *g;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct device_node *irq_parent;
|
||||
bool clk_14, clk_15;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
|
||||
|
|
@ -225,13 +239,48 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
g->fwnode = of_node_to_fwnode(np);
|
||||
|
||||
/*
|
||||
* If either clock output is enabled explicitly in the device tree
|
||||
* we take full control of the clock by masking off all bits for
|
||||
* the clock control and selectively enabling them. Otherwise
|
||||
* we leave the hardware default settings.
|
||||
*
|
||||
* Enable clock outputs with default timings of requested clock.
|
||||
* If you need control over TC and DC, add these to the device
|
||||
* tree bindings and use them here.
|
||||
*/
|
||||
clk_14 = of_property_read_bool(np, "intel,ixp4xx-gpio14-clkout");
|
||||
clk_15 = of_property_read_bool(np, "intel,ixp4xx-gpio15-clkout");
|
||||
|
||||
/*
|
||||
* Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on
|
||||
* specific machines.
|
||||
*/
|
||||
if (of_machine_is_compatible("dlink,dsm-g600-a") ||
|
||||
of_machine_is_compatible("iom,nas-100d"))
|
||||
__raw_writel(0x0, g->base + IXP4XX_REG_GPCLK);
|
||||
val = 0;
|
||||
else {
|
||||
val = __raw_readl(g->base + IXP4XX_REG_GPCLK);
|
||||
|
||||
if (clk_14 || clk_15) {
|
||||
val &= ~(IXP4XX_GPCLK_MUX14 | IXP4XX_GPCLK_MUX15);
|
||||
val &= ~IXP4XX_GPCLK_CLK0_MASK;
|
||||
val &= ~IXP4XX_GPCLK_CLK1_MASK;
|
||||
if (clk_14) {
|
||||
/* IXP4XX_GPCLK_CLK0DC implicit low */
|
||||
val |= (1 << IXP4XX_GPCLK_CLK0TC_SHIFT);
|
||||
val |= IXP4XX_GPCLK_MUX14;
|
||||
}
|
||||
|
||||
if (clk_15) {
|
||||
/* IXP4XX_GPCLK_CLK1DC implicit low */
|
||||
val |= (1 << IXP4XX_GPCLK_CLK1TC_SHIFT);
|
||||
val |= IXP4XX_GPCLK_MUX15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__raw_writel(val, g->base + IXP4XX_REG_GPCLK);
|
||||
|
||||
/*
|
||||
* This is a very special big-endian ARM issue: when the IXP4xx is
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/**
|
||||
/*
|
||||
* Copyright (C) 2006 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
|
||||
* Copyright (C) 2009 Wolfram Sang, Pengutronix
|
||||
|
|
|
|||
|
|
@ -40,25 +40,22 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
|
|||
* `.......````.```
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
|
|
@ -688,7 +685,6 @@ static void __iomem *bgpio_map(struct platform_device *pdev,
|
|||
return devm_ioremap_resource(&pdev->dev, r);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bgpio_of_match[] = {
|
||||
{ .compatible = "brcm,bcm6345-gpio" },
|
||||
{ .compatible = "wd,mbl-gpio" },
|
||||
|
|
@ -697,36 +693,27 @@ static const struct of_device_id bgpio_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, bgpio_of_match);
|
||||
|
||||
static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev,
|
||||
unsigned long *flags)
|
||||
static struct bgpio_pdata *bgpio_parse_fw(struct device *dev, unsigned long *flags)
|
||||
{
|
||||
struct bgpio_pdata *pdata;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
if (!dev_fwnode(dev))
|
||||
return NULL;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(struct bgpio_pdata),
|
||||
GFP_KERNEL);
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->base = -1;
|
||||
|
||||
if (of_device_is_big_endian(pdev->dev.of_node))
|
||||
if (device_is_big_endian(dev))
|
||||
*flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER;
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "no-output"))
|
||||
if (device_property_read_bool(dev, "no-output"))
|
||||
*flags |= BGPIOF_NO_OUTPUT;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int bgpio_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
|
@ -743,7 +730,7 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
|
|||
struct gpio_chip *gc;
|
||||
struct bgpio_pdata *pdata;
|
||||
|
||||
pdata = bgpio_parse_dt(pdev, &flags);
|
||||
pdata = bgpio_parse_fw(dev, &flags);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
|
|
@ -814,7 +801,7 @@ MODULE_DEVICE_TABLE(platform, bgpio_id_table);
|
|||
static struct platform_driver bgpio_driver = {
|
||||
.driver = {
|
||||
.name = "basic-mmio-gpio",
|
||||
.of_match_table = of_match_ptr(bgpio_of_match),
|
||||
.of_match_table = bgpio_of_match,
|
||||
},
|
||||
.id_table = bgpio_id_table,
|
||||
.probe = bgpio_pdev_probe,
|
||||
|
|
|
|||
|
|
@ -354,7 +354,6 @@ static const struct file_operations gpio_mockup_debugfs_ops = {
|
|||
static void gpio_mockup_debugfs_setup(struct device *dev,
|
||||
struct gpio_mockup_chip *chip)
|
||||
{
|
||||
struct device *child __free(put_device) = NULL;
|
||||
struct gpio_mockup_dbgfs_private *priv;
|
||||
struct gpio_chip *gc;
|
||||
const char *devname;
|
||||
|
|
@ -367,7 +366,7 @@ static void gpio_mockup_debugfs_setup(struct device *dev,
|
|||
* There can only be a single GPIO device per platform device in
|
||||
* gpio-mockup so using device_find_any_child() is OK.
|
||||
*/
|
||||
child = device_find_any_child(dev);
|
||||
struct device *child __free(put_device) = device_find_any_child(dev);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
|
|
|
|||
619
drivers/gpio/gpio-npcm-sgpio.c
Normal file
619
drivers/gpio/gpio-npcm-sgpio.c
Normal file
|
|
@ -0,0 +1,619 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Nuvoton NPCM Serial GPIO Driver
|
||||
*
|
||||
* Copyright (C) 2021 Nuvoton Technologies
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#define MAX_NR_HW_SGPIO 64
|
||||
|
||||
#define NPCM_IOXCFG1 0x2A
|
||||
#define NPCM_IOXCFG1_SFT_CLK GENMASK(3, 0)
|
||||
#define NPCM_IOXCFG1_SCLK_POL BIT(4)
|
||||
#define NPCM_IOXCFG1_LDSH_POL BIT(5)
|
||||
|
||||
#define NPCM_IOXCTS 0x28
|
||||
#define NPCM_IOXCTS_IOXIF_EN BIT(7)
|
||||
#define NPCM_IOXCTS_RD_MODE GENMASK(2, 1)
|
||||
#define NPCM_IOXCTS_RD_MODE_PERIODIC BIT(2)
|
||||
|
||||
#define NPCM_IOXCFG2 0x2B
|
||||
#define NPCM_IOXCFG2_PORT GENMASK(3, 0)
|
||||
|
||||
#define NPCM_IXOEVCFG_MASK GENMASK(1, 0)
|
||||
#define NPCM_IXOEVCFG_FALLING BIT(1)
|
||||
#define NPCM_IXOEVCFG_RISING BIT(0)
|
||||
#define NPCM_IXOEVCFG_BOTH (NPCM_IXOEVCFG_FALLING | NPCM_IXOEVCFG_RISING)
|
||||
|
||||
#define NPCM_CLK_MHZ (8 * HZ_PER_MHZ)
|
||||
#define NPCM_750_OPT 6
|
||||
#define NPCM_845_OPT 5
|
||||
|
||||
#define GPIO_BANK(x) ((x) / 8)
|
||||
#define GPIO_BIT(x) ((x) % 8)
|
||||
|
||||
/*
|
||||
* Select the frequency of shift clock.
|
||||
* The shift clock is a division of the APB clock.
|
||||
*/
|
||||
struct npcm_clk_cfg {
|
||||
unsigned int *sft_clk;
|
||||
unsigned int *clk_sel;
|
||||
unsigned int cfg_opt;
|
||||
};
|
||||
|
||||
struct npcm_sgpio {
|
||||
struct gpio_chip chip;
|
||||
struct clk *pclk;
|
||||
struct irq_chip intc;
|
||||
raw_spinlock_t lock;
|
||||
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
u8 nin_sgpio;
|
||||
u8 nout_sgpio;
|
||||
u8 in_port;
|
||||
u8 out_port;
|
||||
u8 int_type[MAX_NR_HW_SGPIO];
|
||||
};
|
||||
|
||||
struct npcm_sgpio_bank {
|
||||
u8 rdata_reg;
|
||||
u8 wdata_reg;
|
||||
u8 event_config;
|
||||
u8 event_status;
|
||||
};
|
||||
|
||||
enum npcm_sgpio_reg {
|
||||
READ_DATA,
|
||||
WRITE_DATA,
|
||||
EVENT_CFG,
|
||||
EVENT_STS,
|
||||
};
|
||||
|
||||
static const struct npcm_sgpio_bank npcm_sgpio_banks[] = {
|
||||
{
|
||||
.wdata_reg = 0x00,
|
||||
.rdata_reg = 0x08,
|
||||
.event_config = 0x10,
|
||||
.event_status = 0x20,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x01,
|
||||
.rdata_reg = 0x09,
|
||||
.event_config = 0x12,
|
||||
.event_status = 0x21,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x02,
|
||||
.rdata_reg = 0x0a,
|
||||
.event_config = 0x14,
|
||||
.event_status = 0x22,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x03,
|
||||
.rdata_reg = 0x0b,
|
||||
.event_config = 0x16,
|
||||
.event_status = 0x23,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x04,
|
||||
.rdata_reg = 0x0c,
|
||||
.event_config = 0x18,
|
||||
.event_status = 0x24,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x05,
|
||||
.rdata_reg = 0x0d,
|
||||
.event_config = 0x1a,
|
||||
.event_status = 0x25,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x06,
|
||||
.rdata_reg = 0x0e,
|
||||
.event_config = 0x1c,
|
||||
.event_status = 0x26,
|
||||
},
|
||||
{
|
||||
.wdata_reg = 0x07,
|
||||
.rdata_reg = 0x0f,
|
||||
.event_config = 0x1e,
|
||||
.event_status = 0x27,
|
||||
},
|
||||
};
|
||||
|
||||
static void __iomem *bank_reg(struct npcm_sgpio *gpio,
|
||||
const struct npcm_sgpio_bank *bank,
|
||||
const enum npcm_sgpio_reg reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case READ_DATA:
|
||||
return gpio->base + bank->rdata_reg;
|
||||
case WRITE_DATA:
|
||||
return gpio->base + bank->wdata_reg;
|
||||
case EVENT_CFG:
|
||||
return gpio->base + bank->event_config;
|
||||
case EVENT_STS:
|
||||
return gpio->base + bank->event_status;
|
||||
default:
|
||||
/* actually if code runs to here, it's an error case */
|
||||
dev_WARN(gpio->chip.parent, "Getting here is an error condition");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct npcm_sgpio_bank *offset_to_bank(unsigned int offset)
|
||||
{
|
||||
unsigned int bank = GPIO_BANK(offset);
|
||||
|
||||
return &npcm_sgpio_banks[bank];
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irqd_to_data(struct irq_data *d,
|
||||
struct npcm_sgpio **gpio,
|
||||
const struct npcm_sgpio_bank **bank,
|
||||
u8 *bit, unsigned int *offset)
|
||||
{
|
||||
struct npcm_sgpio *internal;
|
||||
|
||||
*offset = irqd_to_hwirq(d);
|
||||
internal = irq_data_get_irq_chip_data(d);
|
||||
|
||||
*gpio = internal;
|
||||
*offset -= internal->nout_sgpio;
|
||||
*bank = offset_to_bank(*offset);
|
||||
*bit = GPIO_BIT(*offset);
|
||||
}
|
||||
|
||||
static int npcm_sgpio_init_port(struct npcm_sgpio *gpio)
|
||||
{
|
||||
u8 in_port, out_port, set_port, reg;
|
||||
|
||||
in_port = GPIO_BANK(gpio->nin_sgpio);
|
||||
if (GPIO_BIT(gpio->nin_sgpio) > 0)
|
||||
in_port += 1;
|
||||
|
||||
out_port = GPIO_BANK(gpio->nout_sgpio);
|
||||
if (GPIO_BIT(gpio->nout_sgpio) > 0)
|
||||
out_port += 1;
|
||||
|
||||
gpio->in_port = in_port;
|
||||
gpio->out_port = out_port;
|
||||
set_port = (out_port & NPCM_IOXCFG2_PORT) << 4 |
|
||||
(in_port & NPCM_IOXCFG2_PORT);
|
||||
iowrite8(set_port, gpio->base + NPCM_IOXCFG2);
|
||||
|
||||
reg = ioread8(gpio->base + NPCM_IOXCFG2);
|
||||
|
||||
return reg == set_port ? 0 : -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
static int npcm_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct npcm_sgpio *gpio = gpiochip_get_data(gc);
|
||||
|
||||
return offset < gpio->nout_sgpio ? -EINVAL : 0;
|
||||
|
||||
}
|
||||
|
||||
static int npcm_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
|
||||
{
|
||||
gc->set(gc, offset, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int npcm_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct npcm_sgpio *gpio = gpiochip_get_data(gc);
|
||||
|
||||
if (offset < gpio->nout_sgpio)
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
}
|
||||
|
||||
static void npcm_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
|
||||
{
|
||||
struct npcm_sgpio *gpio = gpiochip_get_data(gc);
|
||||
const struct npcm_sgpio_bank *bank = offset_to_bank(offset);
|
||||
void __iomem *addr;
|
||||
u8 reg = 0;
|
||||
|
||||
addr = bank_reg(gpio, bank, WRITE_DATA);
|
||||
reg = ioread8(addr);
|
||||
|
||||
if (val)
|
||||
reg |= BIT(GPIO_BIT(offset));
|
||||
else
|
||||
reg &= ~BIT(GPIO_BIT(offset));
|
||||
|
||||
iowrite8(reg, addr);
|
||||
}
|
||||
|
||||
static int npcm_sgpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct npcm_sgpio *gpio = gpiochip_get_data(gc);
|
||||
const struct npcm_sgpio_bank *bank;
|
||||
void __iomem *addr;
|
||||
u8 reg;
|
||||
|
||||
if (offset < gpio->nout_sgpio) {
|
||||
bank = offset_to_bank(offset);
|
||||
addr = bank_reg(gpio, bank, WRITE_DATA);
|
||||
} else {
|
||||
offset -= gpio->nout_sgpio;
|
||||
bank = offset_to_bank(offset);
|
||||
addr = bank_reg(gpio, bank, READ_DATA);
|
||||
}
|
||||
|
||||
reg = ioread8(addr);
|
||||
|
||||
return !!(reg & BIT(GPIO_BIT(offset)));
|
||||
}
|
||||
|
||||
static void npcm_sgpio_setup_enable(struct npcm_sgpio *gpio, bool enable)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = ioread8(gpio->base + NPCM_IOXCTS);
|
||||
reg = (reg & ~NPCM_IOXCTS_RD_MODE) | NPCM_IOXCTS_RD_MODE_PERIODIC;
|
||||
|
||||
if (enable)
|
||||
reg |= NPCM_IOXCTS_IOXIF_EN;
|
||||
else
|
||||
reg &= ~NPCM_IOXCTS_IOXIF_EN;
|
||||
|
||||
iowrite8(reg, gpio->base + NPCM_IOXCTS);
|
||||
}
|
||||
|
||||
static int npcm_sgpio_setup_clk(struct npcm_sgpio *gpio,
|
||||
const struct npcm_clk_cfg *clk_cfg)
|
||||
{
|
||||
unsigned long apb_freq;
|
||||
u32 val;
|
||||
u8 tmp;
|
||||
int i;
|
||||
|
||||
apb_freq = clk_get_rate(gpio->pclk);
|
||||
tmp = ioread8(gpio->base + NPCM_IOXCFG1) & ~NPCM_IOXCFG1_SFT_CLK;
|
||||
|
||||
for (i = clk_cfg->cfg_opt-1; i > 0; i--) {
|
||||
val = apb_freq / clk_cfg->sft_clk[i];
|
||||
if (NPCM_CLK_MHZ > val) {
|
||||
iowrite8(clk_cfg->clk_sel[i] | tmp,
|
||||
gpio->base + NPCM_IOXCFG1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irq_init_valid_mask(struct gpio_chip *gc,
|
||||
unsigned long *valid_mask,
|
||||
unsigned int ngpios)
|
||||
{
|
||||
struct npcm_sgpio *gpio = gpiochip_get_data(gc);
|
||||
|
||||
/* input GPIOs in the high range */
|
||||
bitmap_set(valid_mask, gpio->nout_sgpio, gpio->nin_sgpio);
|
||||
bitmap_clear(valid_mask, 0, gpio->nout_sgpio);
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irq_set_mask(struct irq_data *d, bool set)
|
||||
{
|
||||
const struct npcm_sgpio_bank *bank;
|
||||
struct npcm_sgpio *gpio;
|
||||
unsigned long flags;
|
||||
void __iomem *addr;
|
||||
unsigned int offset;
|
||||
u16 reg, type;
|
||||
u8 bit;
|
||||
|
||||
npcm_sgpio_irqd_to_data(d, &gpio, &bank, &bit, &offset);
|
||||
addr = bank_reg(gpio, bank, EVENT_CFG);
|
||||
|
||||
reg = ioread16(addr);
|
||||
if (set) {
|
||||
reg &= ~(NPCM_IXOEVCFG_MASK << (bit * 2));
|
||||
} else {
|
||||
type = gpio->int_type[offset];
|
||||
reg |= (type << (bit * 2));
|
||||
}
|
||||
|
||||
raw_spin_lock_irqsave(&gpio->lock, flags);
|
||||
|
||||
npcm_sgpio_setup_enable(gpio, false);
|
||||
|
||||
iowrite16(reg, addr);
|
||||
|
||||
npcm_sgpio_setup_enable(gpio, true);
|
||||
|
||||
addr = bank_reg(gpio, bank, EVENT_STS);
|
||||
reg = ioread8(addr);
|
||||
reg |= BIT(bit);
|
||||
iowrite8(reg, addr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irq_ack(struct irq_data *d)
|
||||
{
|
||||
const struct npcm_sgpio_bank *bank;
|
||||
struct npcm_sgpio *gpio;
|
||||
unsigned long flags;
|
||||
void __iomem *status_addr;
|
||||
unsigned int offset;
|
||||
u8 bit;
|
||||
|
||||
npcm_sgpio_irqd_to_data(d, &gpio, &bank, &bit, &offset);
|
||||
status_addr = bank_reg(gpio, bank, EVENT_STS);
|
||||
raw_spin_lock_irqsave(&gpio->lock, flags);
|
||||
iowrite8(BIT(bit), status_addr);
|
||||
raw_spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
npcm_sgpio_irq_set_mask(d, true);
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
npcm_sgpio_irq_set_mask(d, false);
|
||||
}
|
||||
|
||||
static int npcm_sgpio_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
const struct npcm_sgpio_bank *bank;
|
||||
irq_flow_handler_t handler;
|
||||
struct npcm_sgpio *gpio;
|
||||
unsigned long flags;
|
||||
void __iomem *addr;
|
||||
unsigned int offset;
|
||||
u16 reg, val;
|
||||
u8 bit;
|
||||
|
||||
npcm_sgpio_irqd_to_data(d, &gpio, &bank, &bit, &offset);
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
val = NPCM_IXOEVCFG_BOTH;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
val = NPCM_IXOEVCFG_RISING;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
val = NPCM_IXOEVCFG_FALLING;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (type & IRQ_TYPE_LEVEL_MASK)
|
||||
handler = handle_level_irq;
|
||||
else
|
||||
handler = handle_edge_irq;
|
||||
|
||||
gpio->int_type[offset] = val;
|
||||
|
||||
raw_spin_lock_irqsave(&gpio->lock, flags);
|
||||
npcm_sgpio_setup_enable(gpio, false);
|
||||
addr = bank_reg(gpio, bank, EVENT_CFG);
|
||||
reg = ioread16(addr);
|
||||
|
||||
reg |= (val << (bit * 2));
|
||||
|
||||
iowrite16(reg, addr);
|
||||
npcm_sgpio_setup_enable(gpio, true);
|
||||
raw_spin_unlock_irqrestore(&gpio->lock, flags);
|
||||
|
||||
irq_set_handler_locked(d, handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void npcm_sgpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *ic = irq_desc_get_chip(desc);
|
||||
struct npcm_sgpio *gpio = gpiochip_get_data(gc);
|
||||
unsigned int i, j, girq;
|
||||
unsigned long reg;
|
||||
|
||||
chained_irq_enter(ic, desc);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(npcm_sgpio_banks); i++) {
|
||||
const struct npcm_sgpio_bank *bank = &npcm_sgpio_banks[i];
|
||||
|
||||
reg = ioread8(bank_reg(gpio, bank, EVENT_STS));
|
||||
for_each_set_bit(j, ®, 8) {
|
||||
girq = irq_find_mapping(gc->irq.domain,
|
||||
i * 8 + gpio->nout_sgpio + j);
|
||||
generic_handle_domain_irq(gc->irq.domain, girq);
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(ic, desc);
|
||||
}
|
||||
|
||||
static const struct irq_chip sgpio_irq_chip = {
|
||||
.name = "sgpio-irq",
|
||||
.irq_ack = npcm_sgpio_irq_ack,
|
||||
.irq_mask = npcm_sgpio_irq_mask,
|
||||
.irq_unmask = npcm_sgpio_irq_unmask,
|
||||
.irq_set_type = npcm_sgpio_set_type,
|
||||
.flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static int npcm_sgpio_setup_irqs(struct npcm_sgpio *gpio,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int rc, i;
|
||||
struct gpio_irq_chip *irq;
|
||||
|
||||
rc = platform_get_irq(pdev, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
gpio->irq = rc;
|
||||
|
||||
npcm_sgpio_setup_enable(gpio, false);
|
||||
|
||||
/* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */
|
||||
for (i = 0; i < ARRAY_SIZE(npcm_sgpio_banks); i++) {
|
||||
const struct npcm_sgpio_bank *bank = &npcm_sgpio_banks[i];
|
||||
|
||||
iowrite16(0, bank_reg(gpio, bank, EVENT_CFG));
|
||||
iowrite8(0xff, bank_reg(gpio, bank, EVENT_STS));
|
||||
}
|
||||
|
||||
irq = &gpio->chip.irq;
|
||||
gpio_irq_chip_set_chip(irq, &sgpio_irq_chip);
|
||||
irq->init_valid_mask = npcm_sgpio_irq_init_valid_mask;
|
||||
irq->handler = handle_bad_irq;
|
||||
irq->default_type = IRQ_TYPE_NONE;
|
||||
irq->parent_handler = npcm_sgpio_irq_handler;
|
||||
irq->parent_handler_data = gpio;
|
||||
irq->parents = &gpio->irq;
|
||||
irq->num_parents = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int npcm_sgpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct npcm_sgpio *gpio;
|
||||
const struct npcm_clk_cfg *clk_cfg;
|
||||
int rc;
|
||||
u32 nin_gpios, nout_gpios;
|
||||
|
||||
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||
if (!gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(gpio->base))
|
||||
return PTR_ERR(gpio->base);
|
||||
|
||||
clk_cfg = device_get_match_data(&pdev->dev);
|
||||
if (!clk_cfg)
|
||||
return -EINVAL;
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "nuvoton,input-ngpios",
|
||||
&nin_gpios);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(&pdev->dev, rc, "Could not read ngpios property\n");
|
||||
|
||||
rc = device_property_read_u32(&pdev->dev, "nuvoton,output-ngpios",
|
||||
&nout_gpios);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(&pdev->dev, rc, "Could not read ngpios property\n");
|
||||
|
||||
gpio->nin_sgpio = nin_gpios;
|
||||
gpio->nout_sgpio = nout_gpios;
|
||||
if (gpio->nin_sgpio > MAX_NR_HW_SGPIO ||
|
||||
gpio->nout_sgpio > MAX_NR_HW_SGPIO)
|
||||
return dev_err_probe(&pdev->dev, -EINVAL, "Number of GPIOs exceeds the maximum of %d: input: %d output: %d\n", MAX_NR_HW_SGPIO, nin_gpios, nout_gpios);
|
||||
|
||||
gpio->pclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(gpio->pclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpio->pclk), "Could not get pclk\n");
|
||||
|
||||
rc = npcm_sgpio_setup_clk(gpio, clk_cfg);
|
||||
if (rc < 0)
|
||||
return dev_err_probe(&pdev->dev, rc, "Failed to setup clock\n");
|
||||
|
||||
raw_spin_lock_init(&gpio->lock);
|
||||
gpio->chip.parent = &pdev->dev;
|
||||
gpio->chip.ngpio = gpio->nin_sgpio + gpio->nout_sgpio;
|
||||
gpio->chip.direction_input = npcm_sgpio_dir_in;
|
||||
gpio->chip.direction_output = npcm_sgpio_dir_out;
|
||||
gpio->chip.get_direction = npcm_sgpio_get_direction;
|
||||
gpio->chip.get = npcm_sgpio_get;
|
||||
gpio->chip.set = npcm_sgpio_set;
|
||||
gpio->chip.label = dev_name(&pdev->dev);
|
||||
gpio->chip.base = -1;
|
||||
|
||||
rc = npcm_sgpio_init_port(gpio);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = npcm_sgpio_setup_irqs(gpio, pdev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
|
||||
if (rc)
|
||||
return dev_err_probe(&pdev->dev, rc, "GPIO registering failed\n");
|
||||
|
||||
npcm_sgpio_setup_enable(gpio, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int npcm750_SFT_CLK[NPCM_750_OPT] = {
|
||||
1024, 32, 8, 4, 3, 2,
|
||||
};
|
||||
|
||||
static unsigned int npcm750_CLK_SEL[NPCM_750_OPT] = {
|
||||
0x00, 0x05, 0x07, 0x0C, 0x0D, 0x0E,
|
||||
};
|
||||
|
||||
static unsigned int npcm845_SFT_CLK[NPCM_845_OPT] = {
|
||||
1024, 32, 16, 8, 4,
|
||||
};
|
||||
|
||||
static unsigned int npcm845_CLK_SEL[NPCM_845_OPT] = {
|
||||
0x00, 0x05, 0x06, 0x07, 0x0C,
|
||||
};
|
||||
|
||||
static struct npcm_clk_cfg npcm750_sgpio_pdata = {
|
||||
.sft_clk = npcm750_SFT_CLK,
|
||||
.clk_sel = npcm750_CLK_SEL,
|
||||
.cfg_opt = NPCM_750_OPT,
|
||||
};
|
||||
|
||||
static const struct npcm_clk_cfg npcm845_sgpio_pdata = {
|
||||
.sft_clk = npcm845_SFT_CLK,
|
||||
.clk_sel = npcm845_CLK_SEL,
|
||||
.cfg_opt = NPCM_845_OPT,
|
||||
};
|
||||
|
||||
static const struct of_device_id npcm_sgpio_of_table[] = {
|
||||
{ .compatible = "nuvoton,npcm750-sgpio", .data = &npcm750_sgpio_pdata, },
|
||||
{ .compatible = "nuvoton,npcm845-sgpio", .data = &npcm845_sgpio_pdata, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, npcm_sgpio_of_table);
|
||||
|
||||
static struct platform_driver npcm_sgpio_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = npcm_sgpio_of_table,
|
||||
},
|
||||
.probe = npcm_sgpio_probe,
|
||||
};
|
||||
module_platform_driver(npcm_sgpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Jim Liu <jjliu0@nuvoton.com>");
|
||||
MODULE_AUTHOR("Joseph Liu <kwliu@nuvoton.com>");
|
||||
MODULE_DESCRIPTION("Nuvoton NPCM Serial GPIO Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -151,8 +151,8 @@ static void sprd_pmic_eic_irq_mask(struct irq_data *data)
|
|||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
pmic_eic->reg[REG_IE] = 0;
|
||||
pmic_eic->reg[REG_TRIG] = 0;
|
||||
pmic_eic->reg[REG_IE] &= ~BIT(offset);
|
||||
pmic_eic->reg[REG_TRIG] &= ~BIT(offset);
|
||||
|
||||
gpiochip_disable_irq(chip, offset);
|
||||
}
|
||||
|
|
@ -165,8 +165,8 @@ static void sprd_pmic_eic_irq_unmask(struct irq_data *data)
|
|||
|
||||
gpiochip_enable_irq(chip, offset);
|
||||
|
||||
pmic_eic->reg[REG_IE] = 1;
|
||||
pmic_eic->reg[REG_TRIG] = 1;
|
||||
pmic_eic->reg[REG_IE] |= BIT(offset);
|
||||
pmic_eic->reg[REG_TRIG] |= BIT(offset);
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_irq_set_type(struct irq_data *data,
|
||||
|
|
@ -174,13 +174,14 @@ static int sprd_pmic_eic_irq_set_type(struct irq_data *data,
|
|||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
pmic_eic->reg[REG_IEV] = 1;
|
||||
pmic_eic->reg[REG_IEV] |= BIT(offset);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
pmic_eic->reg[REG_IEV] = 0;
|
||||
pmic_eic->reg[REG_IEV] &= ~BIT(offset);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
|
|
@ -222,15 +223,15 @@ static void sprd_pmic_eic_bus_sync_unlock(struct irq_data *data)
|
|||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV, 1);
|
||||
} else {
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV,
|
||||
pmic_eic->reg[REG_IEV]);
|
||||
!!(pmic_eic->reg[REG_IEV] & BIT(offset)));
|
||||
}
|
||||
|
||||
/* Set irq unmask */
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IE,
|
||||
pmic_eic->reg[REG_IE]);
|
||||
!!(pmic_eic->reg[REG_IE] & BIT(offset)));
|
||||
/* Generate trigger start pulse for debounce EIC */
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_TRIG,
|
||||
pmic_eic->reg[REG_TRIG]);
|
||||
!!(pmic_eic->reg[REG_TRIG] & BIT(offset)));
|
||||
|
||||
mutex_unlock(&pmic_eic->buslock);
|
||||
}
|
||||
|
|
|
|||
604
drivers/gpio/gpio-rtd.c
Normal file
604
drivers/gpio/gpio-rtd.c
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Realtek DHC gpio driver
|
||||
*
|
||||
* Copyright (c) 2023 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define RTD_GPIO_DEBOUNCE_1US 0
|
||||
#define RTD_GPIO_DEBOUNCE_10US 1
|
||||
#define RTD_GPIO_DEBOUNCE_100US 2
|
||||
#define RTD_GPIO_DEBOUNCE_1MS 3
|
||||
#define RTD_GPIO_DEBOUNCE_10MS 4
|
||||
#define RTD_GPIO_DEBOUNCE_20MS 5
|
||||
#define RTD_GPIO_DEBOUNCE_30MS 6
|
||||
|
||||
/**
|
||||
* struct rtd_gpio_info - Specific GPIO register information
|
||||
* @name: GPIO device name
|
||||
* @gpio_base: GPIO base number
|
||||
* @num_gpios: The number of GPIOs
|
||||
* @dir_offset: Offset for GPIO direction registers
|
||||
* @dato_offset: Offset for GPIO data output registers
|
||||
* @dati_offset: Offset for GPIO data input registers
|
||||
* @ie_offset: Offset for GPIO interrupt enable registers
|
||||
* @dp_offset: Offset for GPIO detection polarity registers
|
||||
* @gpa_offset: Offset for GPIO assert interrupt status registers
|
||||
* @gpda_offset: Offset for GPIO deassert interrupt status registers
|
||||
* @deb_offset: Offset for GPIO debounce registers
|
||||
* @deb_val: Register values representing the GPIO debounce time
|
||||
* @get_deb_setval: Used to get the corresponding value for setting the debounce register
|
||||
*/
|
||||
struct rtd_gpio_info {
|
||||
const char *name;
|
||||
unsigned int gpio_base;
|
||||
unsigned int num_gpios;
|
||||
u8 *dir_offset;
|
||||
u8 *dato_offset;
|
||||
u8 *dati_offset;
|
||||
u8 *ie_offset;
|
||||
u8 *dp_offset;
|
||||
u8 *gpa_offset;
|
||||
u8 *gpda_offset;
|
||||
u8 *deb_offset;
|
||||
u8 *deb_val;
|
||||
u8 (*get_deb_setval)(const struct rtd_gpio_info *info,
|
||||
unsigned int offset, u8 deb_index,
|
||||
u8 *reg_offset, u8 *shift);
|
||||
};
|
||||
|
||||
struct rtd_gpio {
|
||||
struct gpio_chip gpio_chip;
|
||||
const struct rtd_gpio_info *info;
|
||||
void __iomem *base;
|
||||
void __iomem *irq_base;
|
||||
unsigned int irqs[2];
|
||||
raw_spinlock_t lock;
|
||||
};
|
||||
|
||||
static u8 rtd_gpio_get_deb_setval(const struct rtd_gpio_info *info, unsigned int offset,
|
||||
u8 deb_index, u8 *reg_offset, u8 *shift)
|
||||
{
|
||||
*reg_offset = info->deb_offset[offset / 8];
|
||||
*shift = (offset % 8) * 4;
|
||||
return info->deb_val[deb_index];
|
||||
}
|
||||
|
||||
static u8 rtd1295_misc_gpio_get_deb_setval(const struct rtd_gpio_info *info, unsigned int offset,
|
||||
u8 deb_index, u8 *reg_offset, u8 *shift)
|
||||
{
|
||||
*reg_offset = info->deb_offset[0];
|
||||
*shift = (offset % 8) * 4;
|
||||
return info->deb_val[deb_index];
|
||||
}
|
||||
|
||||
static u8 rtd1295_iso_gpio_get_deb_setval(const struct rtd_gpio_info *info, unsigned int offset,
|
||||
u8 deb_index, u8 *reg_offset, u8 *shift)
|
||||
{
|
||||
*reg_offset = info->deb_offset[0];
|
||||
*shift = 0;
|
||||
return info->deb_val[deb_index];
|
||||
}
|
||||
|
||||
static const struct rtd_gpio_info rtd_iso_gpio_info = {
|
||||
.name = "rtd_iso_gpio",
|
||||
.gpio_base = 0,
|
||||
.num_gpios = 82,
|
||||
.dir_offset = (u8 []){ 0x0, 0x18, 0x2c },
|
||||
.dato_offset = (u8 []){ 0x4, 0x1c, 0x30 },
|
||||
.dati_offset = (u8 []){ 0x8, 0x20, 0x34 },
|
||||
.ie_offset = (u8 []){ 0xc, 0x24, 0x38 },
|
||||
.dp_offset = (u8 []){ 0x10, 0x28, 0x3c },
|
||||
.gpa_offset = (u8 []){ 0x8, 0xe0, 0x90 },
|
||||
.gpda_offset = (u8 []){ 0xc, 0xe4, 0x94 },
|
||||
.deb_offset = (u8 []){ 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c,
|
||||
0x60, 0x64, 0x68, 0x6c },
|
||||
.deb_val = (u8 []){ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 },
|
||||
.get_deb_setval = rtd_gpio_get_deb_setval,
|
||||
};
|
||||
|
||||
static const struct rtd_gpio_info rtd1619_iso_gpio_info = {
|
||||
.name = "rtd1619_iso_gpio",
|
||||
.gpio_base = 0,
|
||||
.num_gpios = 86,
|
||||
.dir_offset = (u8 []){ 0x0, 0x18, 0x2c },
|
||||
.dato_offset = (u8 []){ 0x4, 0x1c, 0x30 },
|
||||
.dati_offset = (u8 []){ 0x8, 0x20, 0x34 },
|
||||
.ie_offset = (u8 []){ 0xc, 0x24, 0x38 },
|
||||
.dp_offset = (u8 []){ 0x10, 0x28, 0x3c },
|
||||
.gpa_offset = (u8 []){ 0x8, 0xe0, 0x90 },
|
||||
.gpda_offset = (u8 []){ 0xc, 0xe4, 0x94 },
|
||||
.deb_offset = (u8 []){ 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c,
|
||||
0x60, 0x64, 0x68, 0x6c },
|
||||
.deb_val = (u8 []){ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 },
|
||||
.get_deb_setval = rtd_gpio_get_deb_setval,
|
||||
};
|
||||
|
||||
static const struct rtd_gpio_info rtd1395_iso_gpio_info = {
|
||||
.name = "rtd1395_iso_gpio",
|
||||
.gpio_base = 0,
|
||||
.num_gpios = 57,
|
||||
.dir_offset = (u8 []){ 0x0, 0x18 },
|
||||
.dato_offset = (u8 []){ 0x4, 0x1c },
|
||||
.dati_offset = (u8 []){ 0x8, 0x20 },
|
||||
.ie_offset = (u8 []){ 0xc, 0x24 },
|
||||
.dp_offset = (u8 []){ 0x10, 0x28 },
|
||||
.gpa_offset = (u8 []){ 0x8, 0xe0 },
|
||||
.gpda_offset = (u8 []){ 0xc, 0xe4 },
|
||||
.deb_offset = (u8 []){ 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c },
|
||||
.deb_val = (u8 []){ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 },
|
||||
.get_deb_setval = rtd_gpio_get_deb_setval,
|
||||
};
|
||||
|
||||
static const struct rtd_gpio_info rtd1295_misc_gpio_info = {
|
||||
.name = "rtd1295_misc_gpio",
|
||||
.gpio_base = 0,
|
||||
.num_gpios = 101,
|
||||
.dir_offset = (u8 []){ 0x0, 0x4, 0x8, 0xc },
|
||||
.dato_offset = (u8 []){ 0x10, 0x14, 0x18, 0x1c },
|
||||
.dati_offset = (u8 []){ 0x20, 0x24, 0x28, 0x2c },
|
||||
.ie_offset = (u8 []){ 0x30, 0x34, 0x38, 0x3c },
|
||||
.dp_offset = (u8 []){ 0x40, 0x44, 0x48, 0x4c },
|
||||
.gpa_offset = (u8 []){ 0x40, 0x44, 0xa4, 0xb8 },
|
||||
.gpda_offset = (u8 []){ 0x54, 0x58, 0xa8, 0xbc},
|
||||
.deb_offset = (u8 []){ 0x50 },
|
||||
.deb_val = (u8 []){ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 },
|
||||
.get_deb_setval = rtd1295_misc_gpio_get_deb_setval,
|
||||
};
|
||||
|
||||
static const struct rtd_gpio_info rtd1295_iso_gpio_info = {
|
||||
.name = "rtd1295_iso_gpio",
|
||||
.gpio_base = 101,
|
||||
.num_gpios = 35,
|
||||
.dir_offset = (u8 []){ 0x0, 0x18 },
|
||||
.dato_offset = (u8 []){ 0x4, 0x1c },
|
||||
.dati_offset = (u8 []){ 0x8, 0x20 },
|
||||
.ie_offset = (u8 []){ 0xc, 0x24 },
|
||||
.dp_offset = (u8 []){ 0x10, 0x28 },
|
||||
.gpa_offset = (u8 []){ 0x8, 0xe0 },
|
||||
.gpda_offset = (u8 []){ 0xc, 0xe4 },
|
||||
.deb_offset = (u8 []){ 0x14 },
|
||||
.deb_val = (u8 []){ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 },
|
||||
.get_deb_setval = rtd1295_iso_gpio_get_deb_setval,
|
||||
};
|
||||
|
||||
static int rtd_gpio_dir_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
return data->info->dir_offset[offset / 32];
|
||||
}
|
||||
|
||||
static int rtd_gpio_dato_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
return data->info->dato_offset[offset / 32];
|
||||
}
|
||||
|
||||
static int rtd_gpio_dati_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
return data->info->dati_offset[offset / 32];
|
||||
}
|
||||
|
||||
static int rtd_gpio_ie_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
return data->info->ie_offset[offset / 32];
|
||||
}
|
||||
|
||||
static int rtd_gpio_dp_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
return data->info->dp_offset[offset / 32];
|
||||
}
|
||||
|
||||
|
||||
static int rtd_gpio_gpa_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
/* Each GPIO assert interrupt status register contains 31 GPIOs. */
|
||||
return data->info->gpa_offset[offset / 31];
|
||||
}
|
||||
|
||||
static int rtd_gpio_gpda_offset(struct rtd_gpio *data, unsigned int offset)
|
||||
{
|
||||
/* Each GPIO deassert interrupt status register contains 31 GPIOs. */
|
||||
return data->info->gpda_offset[offset / 31];
|
||||
}
|
||||
|
||||
static int rtd_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct rtd_gpio *data = gpiochip_get_data(chip);
|
||||
u8 deb_val, deb_index, reg_offset, shift;
|
||||
unsigned int write_en;
|
||||
u32 val;
|
||||
|
||||
switch (debounce) {
|
||||
case 1:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_1US;
|
||||
break;
|
||||
case 10:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_10US;
|
||||
break;
|
||||
case 100:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_100US;
|
||||
break;
|
||||
case 1000:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_1MS;
|
||||
break;
|
||||
case 10000:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_10MS;
|
||||
break;
|
||||
case 20000:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_20MS;
|
||||
break;
|
||||
case 30000:
|
||||
deb_index = RTD_GPIO_DEBOUNCE_30MS;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
deb_val = data->info->get_deb_setval(data->info, offset, deb_index, ®_offset, &shift);
|
||||
write_en = BIT(shift + 3);
|
||||
val = (deb_val << shift) | write_en;
|
||||
|
||||
guard(raw_spinlock_irqsave)(&data->lock);
|
||||
writel_relaxed(val, data->base + reg_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtd_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
|
||||
unsigned long config)
|
||||
{
|
||||
int debounce;
|
||||
|
||||
switch (pinconf_to_config_param(config)) {
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
return gpiochip_generic_config(chip, offset, config);
|
||||
case PIN_CONFIG_INPUT_DEBOUNCE:
|
||||
debounce = pinconf_to_config_argument(config);
|
||||
return rtd_gpio_set_debounce(chip, offset, debounce);
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static void rtd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||
{
|
||||
struct rtd_gpio *data = gpiochip_get_data(chip);
|
||||
u32 mask = BIT(offset % 32);
|
||||
int dato_reg_offset;
|
||||
u32 val;
|
||||
|
||||
dato_reg_offset = rtd_gpio_dato_offset(data, offset);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&data->lock);
|
||||
|
||||
val = readl_relaxed(data->base + dato_reg_offset);
|
||||
if (value)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
writel_relaxed(val, data->base + dato_reg_offset);
|
||||
}
|
||||
|
||||
static int rtd_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct rtd_gpio *data = gpiochip_get_data(chip);
|
||||
int dato_reg_offset = rtd_gpio_dato_offset(data, offset);
|
||||
int dati_reg_offset = rtd_gpio_dati_offset(data, offset);
|
||||
int dir_reg_offset = rtd_gpio_dir_offset(data, offset);
|
||||
int dat_reg_offset;
|
||||
u32 val;
|
||||
|
||||
guard(raw_spinlock_irqsave)(&data->lock);
|
||||
|
||||
val = readl_relaxed(data->base + dir_reg_offset);
|
||||
dat_reg_offset = (val & BIT(offset % 32)) ? dato_reg_offset : dati_reg_offset;
|
||||
val = readl_relaxed(data->base + dat_reg_offset);
|
||||
|
||||
return !!(val & BIT(offset % 32));
|
||||
}
|
||||
|
||||
static int rtd_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct rtd_gpio *data = gpiochip_get_data(chip);
|
||||
int reg_offset;
|
||||
u32 val;
|
||||
|
||||
reg_offset = rtd_gpio_dir_offset(data, offset);
|
||||
val = readl_relaxed(data->base + reg_offset);
|
||||
if (val & BIT(offset % 32))
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
}
|
||||
|
||||
static int rtd_gpio_set_direction(struct gpio_chip *chip, unsigned int offset, bool out)
|
||||
{
|
||||
struct rtd_gpio *data = gpiochip_get_data(chip);
|
||||
u32 mask = BIT(offset % 32);
|
||||
int reg_offset;
|
||||
u32 val;
|
||||
|
||||
reg_offset = rtd_gpio_dir_offset(data, offset);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&data->lock);
|
||||
|
||||
val = readl_relaxed(data->base + reg_offset);
|
||||
if (out)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
writel_relaxed(val, data->base + reg_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtd_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return rtd_gpio_set_direction(chip, offset, false);
|
||||
}
|
||||
|
||||
static int rtd_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value)
|
||||
{
|
||||
rtd_gpio_set(chip, offset, value);
|
||||
|
||||
return rtd_gpio_set_direction(chip, offset, true);
|
||||
}
|
||||
|
||||
static bool rtd_gpio_check_ie(struct rtd_gpio *data, int irq)
|
||||
{
|
||||
int mask = BIT(irq % 32);
|
||||
int ie_reg_offset;
|
||||
u32 enable;
|
||||
|
||||
ie_reg_offset = rtd_gpio_ie_offset(data, irq);
|
||||
enable = readl_relaxed(data->base + ie_reg_offset);
|
||||
|
||||
return enable & mask;
|
||||
}
|
||||
|
||||
static void rtd_gpio_irq_handle(struct irq_desc *desc)
|
||||
{
|
||||
int (*get_reg_offset)(struct rtd_gpio *gpio, unsigned int offset);
|
||||
struct rtd_gpio *data = irq_desc_get_handler_data(desc);
|
||||
struct irq_domain *domain = data->gpio_chip.irq.domain;
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
unsigned int irq = irq_desc_get_irq(desc);
|
||||
unsigned long status;
|
||||
int reg_offset, i, j;
|
||||
unsigned int hwirq;
|
||||
|
||||
if (irq == data->irqs[0])
|
||||
get_reg_offset = &rtd_gpio_gpa_offset;
|
||||
else if (irq == data->irqs[1])
|
||||
get_reg_offset = &rtd_gpio_gpda_offset;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
/* Each GPIO interrupt status register contains 31 GPIOs. */
|
||||
for (i = 0; i < data->info->num_gpios; i += 31) {
|
||||
reg_offset = get_reg_offset(data, i);
|
||||
|
||||
/*
|
||||
* Bit 0 is the write_en bit, bit 0 to 31 corresponds to 31 GPIOs.
|
||||
* When bit 0 is set to 0, write 1 to the other bits to clear the status.
|
||||
* When bit 0 is set to 1, write 1 to the other bits to set the status.
|
||||
*/
|
||||
status = readl_relaxed(data->irq_base + reg_offset);
|
||||
status &= ~BIT(0);
|
||||
writel_relaxed(status, data->irq_base + reg_offset);
|
||||
|
||||
for_each_set_bit(j, &status, 32) {
|
||||
hwirq = i + j - 1;
|
||||
if (rtd_gpio_check_ie(data, hwirq)) {
|
||||
int girq = irq_find_mapping(domain, hwirq);
|
||||
u32 irq_type = irq_get_trigger_type(girq);
|
||||
|
||||
if ((irq == data->irqs[1]) && (irq_type != IRQ_TYPE_EDGE_BOTH))
|
||||
break;
|
||||
generic_handle_domain_irq(domain, hwirq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void rtd_gpio_enable_irq(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct rtd_gpio *data = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
/* Bit 0 is write_en and bit 1 to 31 is correspond to 31 GPIOs. */
|
||||
u32 clr_mask = BIT(hwirq % 31) << 1;
|
||||
|
||||
u32 ie_mask = BIT(hwirq % 32);
|
||||
int gpda_reg_offset;
|
||||
int gpa_reg_offset;
|
||||
int ie_reg_offset;
|
||||
u32 val;
|
||||
|
||||
ie_reg_offset = rtd_gpio_ie_offset(data, hwirq);
|
||||
gpa_reg_offset = rtd_gpio_gpa_offset(data, hwirq);
|
||||
gpda_reg_offset = rtd_gpio_gpda_offset(data, hwirq);
|
||||
|
||||
gpiochip_enable_irq(gc, hwirq);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&data->lock);
|
||||
|
||||
writel_relaxed(clr_mask, data->irq_base + gpa_reg_offset);
|
||||
writel_relaxed(clr_mask, data->irq_base + gpda_reg_offset);
|
||||
|
||||
val = readl_relaxed(data->base + ie_reg_offset);
|
||||
val |= ie_mask;
|
||||
writel_relaxed(val, data->base + ie_reg_offset);
|
||||
}
|
||||
|
||||
static void rtd_gpio_disable_irq(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct rtd_gpio *data = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
u32 ie_mask = BIT(hwirq % 32);
|
||||
int ie_reg_offset;
|
||||
u32 val;
|
||||
|
||||
ie_reg_offset = rtd_gpio_ie_offset(data, hwirq);
|
||||
|
||||
scoped_guard(raw_spinlock_irqsave, &data->lock) {
|
||||
val = readl_relaxed(data->base + ie_reg_offset);
|
||||
val &= ~ie_mask;
|
||||
writel_relaxed(val, data->base + ie_reg_offset);
|
||||
}
|
||||
|
||||
gpiochip_disable_irq(gc, hwirq);
|
||||
}
|
||||
|
||||
static int rtd_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct rtd_gpio *data = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
u32 mask = BIT(hwirq % 32);
|
||||
int dp_reg_offset;
|
||||
bool polarity;
|
||||
u32 val;
|
||||
|
||||
dp_reg_offset = rtd_gpio_dp_offset(data, hwirq);
|
||||
|
||||
switch (type & IRQ_TYPE_SENSE_MASK) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
polarity = 1;
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
polarity = 0;
|
||||
break;
|
||||
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
polarity = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scoped_guard(raw_spinlock_irqsave, &data->lock) {
|
||||
val = readl_relaxed(data->base + dp_reg_offset);
|
||||
if (polarity)
|
||||
val |= mask;
|
||||
else
|
||||
val &= ~mask;
|
||||
writel_relaxed(val, data->base + dp_reg_offset);
|
||||
}
|
||||
|
||||
irq_set_handler_locked(d, handle_simple_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_chip rtd_gpio_irq_chip = {
|
||||
.name = "rtd-gpio",
|
||||
.irq_enable = rtd_gpio_enable_irq,
|
||||
.irq_disable = rtd_gpio_disable_irq,
|
||||
.irq_set_type = rtd_gpio_irq_set_type,
|
||||
.flags = IRQCHIP_IMMUTABLE,
|
||||
};
|
||||
|
||||
static int rtd_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct gpio_irq_chip *irq_chip;
|
||||
struct rtd_gpio *data;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->irqs[0] = platform_get_irq(pdev, 0);
|
||||
if (data->irqs[0] < 0)
|
||||
return data->irqs[0];
|
||||
|
||||
data->irqs[1] = platform_get_irq(pdev, 1);
|
||||
if (data->irqs[1] < 0)
|
||||
return data->irqs[1];
|
||||
|
||||
data->info = device_get_match_data(dev);
|
||||
if (!data->info)
|
||||
return -EINVAL;
|
||||
|
||||
raw_spin_lock_init(&data->lock);
|
||||
|
||||
data->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(data->base))
|
||||
return PTR_ERR(data->base);
|
||||
|
||||
data->irq_base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(data->irq_base))
|
||||
return PTR_ERR(data->irq_base);
|
||||
|
||||
data->gpio_chip.label = dev_name(dev);
|
||||
data->gpio_chip.base = -1;
|
||||
data->gpio_chip.ngpio = data->info->num_gpios;
|
||||
data->gpio_chip.request = gpiochip_generic_request;
|
||||
data->gpio_chip.free = gpiochip_generic_free;
|
||||
data->gpio_chip.get_direction = rtd_gpio_get_direction;
|
||||
data->gpio_chip.direction_input = rtd_gpio_direction_input;
|
||||
data->gpio_chip.direction_output = rtd_gpio_direction_output;
|
||||
data->gpio_chip.set = rtd_gpio_set;
|
||||
data->gpio_chip.get = rtd_gpio_get;
|
||||
data->gpio_chip.set_config = rtd_gpio_set_config;
|
||||
data->gpio_chip.parent = dev;
|
||||
|
||||
irq_chip = &data->gpio_chip.irq;
|
||||
irq_chip->handler = handle_bad_irq;
|
||||
irq_chip->default_type = IRQ_TYPE_NONE;
|
||||
irq_chip->parent_handler = rtd_gpio_irq_handle;
|
||||
irq_chip->parent_handler_data = data;
|
||||
irq_chip->num_parents = 2;
|
||||
irq_chip->parents = data->irqs;
|
||||
|
||||
gpio_irq_chip_set_chip(irq_chip, &rtd_gpio_irq_chip);
|
||||
|
||||
return devm_gpiochip_add_data(dev, &data->gpio_chip, data);
|
||||
}
|
||||
|
||||
static const struct of_device_id rtd_gpio_of_matches[] = {
|
||||
{ .compatible = "realtek,rtd1295-misc-gpio", .data = &rtd1295_misc_gpio_info },
|
||||
{ .compatible = "realtek,rtd1295-iso-gpio", .data = &rtd1295_iso_gpio_info },
|
||||
{ .compatible = "realtek,rtd1395-iso-gpio", .data = &rtd1395_iso_gpio_info },
|
||||
{ .compatible = "realtek,rtd1619-iso-gpio", .data = &rtd1619_iso_gpio_info },
|
||||
{ .compatible = "realtek,rtd1319-iso-gpio", .data = &rtd_iso_gpio_info },
|
||||
{ .compatible = "realtek,rtd1619b-iso-gpio", .data = &rtd_iso_gpio_info },
|
||||
{ .compatible = "realtek,rtd1319d-iso-gpio", .data = &rtd_iso_gpio_info },
|
||||
{ .compatible = "realtek,rtd1315e-iso-gpio", .data = &rtd_iso_gpio_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rtd_gpio_of_matches);
|
||||
|
||||
static struct platform_driver rtd_gpio_platform_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-rtd",
|
||||
.of_match_table = rtd_gpio_of_matches,
|
||||
},
|
||||
.probe = rtd_gpio_probe,
|
||||
};
|
||||
module_platform_driver(rtd_gpio_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Realtek DHC SoC gpio driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -250,7 +250,6 @@ static int sifive_gpio_probe(struct platform_device *pdev)
|
|||
girq->handler = handle_bad_irq;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
return gpiochip_add_data(&chip->gc, chip);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irq_sim.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
|
@ -224,6 +226,25 @@ static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset)
|
|||
}
|
||||
}
|
||||
|
||||
static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc)
|
||||
{
|
||||
struct gpio_sim_chip *chip = gpiochip_get_data(gc);
|
||||
const char *label;
|
||||
int i;
|
||||
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
for_each_requested_gpio(gc, i, label)
|
||||
seq_printf(seq, " gpio-%-3d (%s) %s,%s\n",
|
||||
gc->base + i,
|
||||
label,
|
||||
test_bit(i, chip->direction_map) ? "input" :
|
||||
test_bit(i, chip->value_map) ? "output-high" :
|
||||
"output-low",
|
||||
test_bit(i, chip->pull_map) ? "pull-up" :
|
||||
"pull-down");
|
||||
}
|
||||
|
||||
static ssize_t gpio_sim_sysfs_val_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
|
@ -460,6 +481,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
|
|||
gc->to_irq = gpio_sim_to_irq;
|
||||
gc->request = gpio_sim_request;
|
||||
gc->free = gpio_sim_free;
|
||||
gc->dbg_show = PTR_IF(IS_ENABLED(CONFIG_DEBUG_FS), gpio_sim_dbg_show);
|
||||
gc->can_sleep = true;
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, gc, chip);
|
||||
|
|
@ -1546,6 +1568,6 @@ static void __exit gpio_sim_exit(void)
|
|||
}
|
||||
module_exit(gpio_sim_exit);
|
||||
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl");
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
|
||||
MODULE_DESCRIPTION("GPIO Simulator Module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -255,7 +256,6 @@ static void stmpe_dbg_show_one(struct seq_file *s,
|
|||
{
|
||||
struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc);
|
||||
struct stmpe *stmpe = stmpe_gpio->stmpe;
|
||||
const char *label = gpiochip_is_requested(gc, offset);
|
||||
bool val = !!stmpe_gpio_get(gc, offset);
|
||||
u8 bank = offset / 8;
|
||||
u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB + bank];
|
||||
|
|
@ -263,6 +263,10 @@ static void stmpe_dbg_show_one(struct seq_file *s,
|
|||
int ret;
|
||||
u8 dir;
|
||||
|
||||
char *label __free(kfree) = gpiochip_dup_line_label(gc, offset);
|
||||
if (IS_ERR(label))
|
||||
return;
|
||||
|
||||
ret = stmpe_reg_read(stmpe, dir_reg);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
|
|
@ -19,6 +20,7 @@
|
|||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/types.h>
|
||||
|
|
@ -91,37 +93,31 @@ static int tng_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
|||
static void tng_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||
{
|
||||
struct tng_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
void __iomem *reg;
|
||||
u8 shift;
|
||||
|
||||
reg = gpio_reg_and_bit(chip, offset, value ? GPSR : GPCR, &shift);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
writel(BIT(shift), reg);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int tng_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tng_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
void __iomem *gpdr;
|
||||
u32 value;
|
||||
u8 shift;
|
||||
|
||||
gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
value = readl(gpdr);
|
||||
value &= ~BIT(shift);
|
||||
writel(value, gpdr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -129,21 +125,18 @@ static int tng_gpio_direction_output(struct gpio_chip *chip, unsigned int offset
|
|||
int value)
|
||||
{
|
||||
struct tng_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
void __iomem *gpdr;
|
||||
u8 shift;
|
||||
|
||||
gpdr = gpio_reg_and_bit(chip, offset, GPDR, &shift);
|
||||
tng_gpio_set(chip, offset, value);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
value = readl(gpdr);
|
||||
value |= BIT(shift);
|
||||
writel(value, gpdr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -164,14 +157,13 @@ static int tng_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
|
|||
unsigned int debounce)
|
||||
{
|
||||
struct tng_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned long flags;
|
||||
void __iomem *gfbr;
|
||||
u32 value;
|
||||
u8 shift;
|
||||
|
||||
gfbr = gpio_reg_and_bit(chip, offset, GFBR, &shift);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
value = readl(gfbr);
|
||||
if (debounce)
|
||||
|
|
@ -180,8 +172,6 @@ static int tng_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset,
|
|||
value |= BIT(shift);
|
||||
writel(value, gfbr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -207,27 +197,25 @@ static void tng_irq_ack(struct irq_data *d)
|
|||
{
|
||||
struct tng_gpio *priv = irq_data_get_irq_chip_data(d);
|
||||
irq_hw_number_t gpio = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
void __iomem *gisr;
|
||||
u8 shift;
|
||||
|
||||
gisr = gpio_reg_and_bit(&priv->chip, gpio, GISR, &shift);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
writel(BIT(shift), gisr);
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask)
|
||||
{
|
||||
unsigned long flags;
|
||||
void __iomem *gimr;
|
||||
u32 value;
|
||||
u8 shift;
|
||||
|
||||
gimr = gpio_reg_and_bit(&priv->chip, gpio, GIMR, &shift);
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
value = readl(gimr);
|
||||
if (unmask)
|
||||
|
|
@ -235,8 +223,6 @@ static void tng_irq_unmask_mask(struct tng_gpio *priv, u32 gpio, bool unmask)
|
|||
else
|
||||
value &= ~BIT(shift);
|
||||
writel(value, gimr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static void tng_irq_mask(struct irq_data *d)
|
||||
|
|
@ -267,10 +253,9 @@ static int tng_irq_set_type(struct irq_data *d, unsigned int type)
|
|||
void __iomem *gitr = gpio_reg(&priv->chip, gpio, GITR);
|
||||
void __iomem *glpr = gpio_reg(&priv->chip, gpio, GLPR);
|
||||
u8 shift = gpio % 32;
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
value = readl(grer);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
|
|
@ -311,8 +296,6 @@ static int tng_irq_set_type(struct irq_data *d, unsigned int type)
|
|||
irq_set_handler_locked(d, handle_edge_irq);
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -324,10 +307,11 @@ static int tng_irq_set_wake(struct irq_data *d, unsigned int on)
|
|||
void __iomem *gwmr = gpio_reg(&priv->chip, gpio, priv->wake_regs.gwmr);
|
||||
void __iomem *gwsr = gpio_reg(&priv->chip, gpio, priv->wake_regs.gwsr);
|
||||
u8 shift = gpio % 32;
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
dev_dbg(priv->dev, "%s wake for gpio %lu\n", str_enable_disable(on), gpio);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
/* Clear the existing wake status */
|
||||
writel(BIT(shift), gwsr);
|
||||
|
|
@ -339,9 +323,6 @@ static int tng_irq_set_wake(struct irq_data *d, unsigned int on)
|
|||
value &= ~BIT(shift);
|
||||
writel(value, gwmr);
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
dev_dbg(priv->dev, "%s wake for gpio %lu\n", str_enable_disable(on), gpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -477,14 +458,13 @@ int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_tng_gpio_probe, GPIO_TANGIER);
|
||||
|
||||
int tng_gpio_suspend(struct device *dev)
|
||||
static int tng_gpio_suspend(struct device *dev)
|
||||
{
|
||||
struct tng_gpio *priv = dev_get_drvdata(dev);
|
||||
struct tng_gpio_context *ctx = priv->ctx;
|
||||
unsigned long flags;
|
||||
unsigned int base;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32, ctx++) {
|
||||
/* GPLR is RO, values read will be restored using GPSR */
|
||||
|
|
@ -498,20 +478,16 @@ int tng_gpio_suspend(struct device *dev)
|
|||
ctx->gwmr = readl(gpio_reg(&priv->chip, base, priv->wake_regs.gwmr));
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tng_gpio_suspend, GPIO_TANGIER);
|
||||
|
||||
int tng_gpio_resume(struct device *dev)
|
||||
static int tng_gpio_resume(struct device *dev)
|
||||
{
|
||||
struct tng_gpio *priv = dev_get_drvdata(dev);
|
||||
struct tng_gpio_context *ctx = priv->ctx;
|
||||
unsigned long flags;
|
||||
unsigned int base;
|
||||
|
||||
raw_spin_lock_irqsave(&priv->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32, ctx++) {
|
||||
/* GPLR is RO, values read will be restored using GPSR */
|
||||
|
|
@ -525,11 +501,10 @@ int tng_gpio_resume(struct device *dev)
|
|||
writel(ctx->gwmr, gpio_reg(&priv->chip, base, priv->wake_regs.gwmr));
|
||||
}
|
||||
|
||||
raw_spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(tng_gpio_resume, GPIO_TANGIER);
|
||||
|
||||
EXPORT_NS_GPL_SIMPLE_DEV_PM_OPS(tng_gpio_pm_ops, tng_gpio_suspend, tng_gpio_resume, GPIO_TANGIER);
|
||||
|
||||
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
|
||||
MODULE_AUTHOR("Pandith N <pandith.n@intel.com>");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#define _GPIO_TANGIER_H_
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
|
@ -111,7 +112,6 @@ struct tng_gpio {
|
|||
|
||||
int devm_tng_gpio_probe(struct device *dev, struct tng_gpio *gpio);
|
||||
|
||||
int tng_gpio_suspend(struct device *dev);
|
||||
int tng_gpio_resume(struct device *dev);
|
||||
extern const struct dev_pm_ops tng_gpio_pm_ops;
|
||||
|
||||
#endif /* _GPIO_TANGIER_H_ */
|
||||
|
|
|
|||
|
|
@ -96,16 +96,16 @@ static int tps65219_gpio_change_direction(struct gpio_chip *gc, unsigned int off
|
|||
* Below can be used for test purpose only.
|
||||
*/
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_GPIO)) {
|
||||
int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG,
|
||||
TPS65219_GPIO0_DIR_MASK, direction);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n",
|
||||
direction, offset);
|
||||
return ret;
|
||||
}
|
||||
#if 0
|
||||
int ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_MFP_1_CONFIG,
|
||||
TPS65219_GPIO0_DIR_MASK, direction);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"GPIO DEBUG enabled: Fail to change direction to %u for GPIO%d.\n",
|
||||
direction, offset);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev_err(dev,
|
||||
"GPIO%d direction set by NVM, change to %u failed, not allowed by specification\n",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -160,18 +161,21 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
for (i = 0; i < chip->ngpio; i++) {
|
||||
int gpio = i + chip->base;
|
||||
int reg;
|
||||
const char *label, *pull, *powerdomain;
|
||||
const char *pull, *powerdomain;
|
||||
|
||||
/* We report the GPIO even if it's not requested since
|
||||
* we're also reporting things like alternate
|
||||
* functions which apply even when the GPIO is not in
|
||||
* use as a GPIO.
|
||||
*/
|
||||
label = gpiochip_is_requested(chip, i);
|
||||
if (!label)
|
||||
label = "Unrequested";
|
||||
char *label __free(kfree) = gpiochip_dup_line_label(chip, i);
|
||||
if (IS_ERR(label)) {
|
||||
dev_err(wm831x->dev, "Failed to duplicate label\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
|
||||
seq_printf(s, " gpio-%-3d (%-20.20s) ",
|
||||
gpio, label ?: "Unrequested");
|
||||
|
||||
reg = wm831x_reg_read(wm831x, WM831X_GPIO1_CONTROL + i);
|
||||
if (reg < 0) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -193,18 +194,20 @@ static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
for (i = 0; i < chip->ngpio; i++) {
|
||||
int gpio = i + chip->base;
|
||||
int reg;
|
||||
const char *label;
|
||||
|
||||
/* We report the GPIO even if it's not requested since
|
||||
* we're also reporting things like alternate
|
||||
* functions which apply even when the GPIO is not in
|
||||
* use as a GPIO.
|
||||
*/
|
||||
label = gpiochip_is_requested(chip, i);
|
||||
if (!label)
|
||||
label = "Unrequested";
|
||||
char *label __free(kfree) = gpiochip_dup_line_label(chip, i);
|
||||
if (IS_ERR(label)) {
|
||||
dev_err(wm8994->dev, "Failed to duplicate label\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio, label);
|
||||
seq_printf(s, " gpio-%-3d (%-20.20s) ", gpio,
|
||||
label ?: "Unrequested");
|
||||
|
||||
reg = wm8994_reg_read(wm8994, WM8994_GPIO_1 + i);
|
||||
if (reg < 0) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@
|
|||
* @dir: GPIO direction shadow register
|
||||
* @gpio_lock: Lock used for synchronization
|
||||
* @irq: IRQ used by GPIO device
|
||||
* @irqchip: IRQ chip
|
||||
* @enable: GPIO IRQ enable/disable bitfield
|
||||
* @rising_edge: GPIO IRQ rising edge enable/disable bitfield
|
||||
* @falling_edge: GPIO IRQ falling edge enable/disable bitfield
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -165,10 +165,10 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
|
|||
/* Caller holds gpiod-data mutex. */
|
||||
static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
|
||||
{
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
struct gpio_desc *desc = data->desc;
|
||||
unsigned long irq_flags;
|
||||
int ret;
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
struct gpio_desc *desc = data->desc;
|
||||
unsigned long irq_flags;
|
||||
int ret;
|
||||
|
||||
data->irq = gpiod_to_irq(desc);
|
||||
if (data->irq < 0)
|
||||
|
|
@ -259,7 +259,7 @@ static ssize_t edge_store(struct device *dev,
|
|||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
ssize_t status = size;
|
||||
ssize_t status = size;
|
||||
int flags;
|
||||
|
||||
flags = sysfs_match_string(trigger_names, buf);
|
||||
|
|
@ -292,10 +292,11 @@ static DEVICE_ATTR_RW(edge);
|
|||
/* Caller holds gpiod-data mutex. */
|
||||
static int gpio_sysfs_set_active_low(struct device *dev, int value)
|
||||
{
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
struct gpio_desc *desc = data->desc;
|
||||
int status = 0;
|
||||
unsigned int flags = data->irq_flags;
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
unsigned int flags = data->irq_flags;
|
||||
struct gpio_desc *desc = data->desc;
|
||||
int status = 0;
|
||||
|
||||
|
||||
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
|
||||
return 0;
|
||||
|
|
@ -331,9 +332,9 @@ static ssize_t active_low_show(struct device *dev,
|
|||
static ssize_t active_low_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
ssize_t status;
|
||||
long value;
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
ssize_t status;
|
||||
long value;
|
||||
|
||||
status = kstrtol(buf, 0, &value);
|
||||
if (status)
|
||||
|
|
@ -399,7 +400,7 @@ static const struct attribute_group *gpio_groups[] = {
|
|||
static ssize_t base_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct gpio_chip *chip = dev_get_drvdata(dev);
|
||||
const struct gpio_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", chip->base);
|
||||
}
|
||||
|
|
@ -408,7 +409,7 @@ static DEVICE_ATTR_RO(base);
|
|||
static ssize_t label_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct gpio_chip *chip = dev_get_drvdata(dev);
|
||||
const struct gpio_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%s\n", chip->label ?: "");
|
||||
}
|
||||
|
|
@ -417,7 +418,7 @@ static DEVICE_ATTR_RO(label);
|
|||
static ssize_t ngpio_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct gpio_chip *chip = dev_get_drvdata(dev);
|
||||
const struct gpio_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", chip->ngpio);
|
||||
}
|
||||
|
|
@ -441,11 +442,10 @@ static ssize_t export_store(const struct class *class,
|
|||
const struct class_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
long gpio;
|
||||
struct gpio_desc *desc;
|
||||
int status;
|
||||
struct gpio_chip *gc;
|
||||
int offset;
|
||||
struct gpio_desc *desc;
|
||||
struct gpio_chip *gc;
|
||||
int status, offset;
|
||||
long gpio;
|
||||
|
||||
status = kstrtol(buf, 0, &gpio);
|
||||
if (status < 0)
|
||||
|
|
@ -496,9 +496,9 @@ static ssize_t unexport_store(const struct class *class,
|
|||
const struct class_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
long gpio;
|
||||
struct gpio_desc *desc;
|
||||
int status;
|
||||
struct gpio_desc *desc;
|
||||
int status;
|
||||
long gpio;
|
||||
|
||||
status = kstrtol(buf, 0, &gpio);
|
||||
if (status < 0)
|
||||
|
|
@ -559,14 +559,13 @@ static struct class gpio_class = {
|
|||
*/
|
||||
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_device *gdev;
|
||||
struct gpiod_data *data;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
const char *ioname = NULL;
|
||||
struct device *dev;
|
||||
int offset;
|
||||
const char *ioname = NULL;
|
||||
struct gpio_device *gdev;
|
||||
struct gpiod_data *data;
|
||||
struct gpio_chip *chip;
|
||||
unsigned long flags;
|
||||
struct device *dev;
|
||||
int status, offset;
|
||||
|
||||
/* can't export until sysfs is available ... */
|
||||
if (!class_is_registered(&gpio_class)) {
|
||||
|
|
@ -733,9 +732,9 @@ EXPORT_SYMBOL_GPL(gpiod_unexport);
|
|||
|
||||
int gpiochip_sysfs_register(struct gpio_device *gdev)
|
||||
{
|
||||
struct device *dev;
|
||||
struct device *parent;
|
||||
struct gpio_chip *chip = gdev->chip;
|
||||
struct device *parent;
|
||||
struct device *dev;
|
||||
|
||||
/*
|
||||
* Many systems add gpio chips for SOC support very early,
|
||||
|
|
@ -769,6 +768,25 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int gpiochip_sysfs_register_all(void)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
int ret;
|
||||
|
||||
guard(rwsem_read)(&gpio_devices_sem);
|
||||
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
if (gdev->mockdev)
|
||||
continue;
|
||||
|
||||
ret = gpiochip_sysfs_register(gdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
|
|
@ -793,9 +811,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
|||
|
||||
static int __init gpiolib_sysfs_init(void)
|
||||
{
|
||||
int status;
|
||||
unsigned long flags;
|
||||
struct gpio_device *gdev;
|
||||
int status;
|
||||
|
||||
status = class_register(&gpio_class);
|
||||
if (status < 0)
|
||||
|
|
@ -807,26 +823,6 @@ static int __init gpiolib_sysfs_init(void)
|
|||
* We run before arch_initcall() so chip->dev nodes can have
|
||||
* registered, and so arch_initcall() can always gpiod_export().
|
||||
*/
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
if (gdev->mockdev)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* TODO we yield gpio_lock here because
|
||||
* gpiochip_sysfs_register() acquires a mutex. This is unsafe
|
||||
* and needs to be fixed.
|
||||
*
|
||||
* Also it would be nice to use gpio_device_find() here so we
|
||||
* can keep gpio_chips local to gpiolib.c, but the yield of
|
||||
* gpio_lock prevents us from doing this.
|
||||
*/
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
status = gpiochip_sysfs_register(gdev);
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
return status;
|
||||
return gpiochip_sysfs_register_all();
|
||||
}
|
||||
postcore_initcall(gpiolib_sysfs_init);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
#ifndef GPIOLIB_SYSFS_H
|
||||
#define GPIOLIB_SYSFS_H
|
||||
|
||||
#ifdef CONFIG_GPIO_SYSFS
|
||||
|
||||
struct gpio_device;
|
||||
|
||||
#ifdef CONFIG_GPIO_SYSFS
|
||||
|
||||
int gpiochip_sysfs_register(struct gpio_device *gdev);
|
||||
int gpiochip_sysfs_register_all(void);
|
||||
void gpiochip_sysfs_unregister(struct gpio_device *gdev);
|
||||
|
||||
#else
|
||||
|
|
@ -17,6 +18,11 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int gpiochip_sysfs_register_all(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
|
|
@ -15,6 +16,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
@ -45,19 +47,6 @@
|
|||
* GPIOs can sometimes cost only an instruction or two per bit.
|
||||
*/
|
||||
|
||||
|
||||
/* When debugging, extend minimal trust to callers and platform code.
|
||||
* Also emit diagnostic messages that may help initial bringup, when
|
||||
* board setup or driver bugs are most common.
|
||||
*
|
||||
* Otherwise, minimize overhead in what may be bitbanging codepaths.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
#define extra_checks 1
|
||||
#else
|
||||
#define extra_checks 0
|
||||
#endif
|
||||
|
||||
/* Device and char device-related information */
|
||||
static DEFINE_IDA(gpio_ida);
|
||||
static dev_t gpio_devt;
|
||||
|
|
@ -94,7 +83,9 @@ DEFINE_SPINLOCK(gpio_lock);
|
|||
|
||||
static DEFINE_MUTEX(gpio_lookup_lock);
|
||||
static LIST_HEAD(gpio_lookup_list);
|
||||
|
||||
LIST_HEAD(gpio_devices);
|
||||
DECLARE_RWSEM(gpio_devices_sem);
|
||||
|
||||
static DEFINE_MUTEX(gpio_machine_hogs_mutex);
|
||||
static LIST_HEAD(gpio_machine_hogs);
|
||||
|
|
@ -126,20 +117,15 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
|
|||
struct gpio_desc *gpio_to_desc(unsigned gpio)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
if (gdev->base <= gpio &&
|
||||
gdev->base + gdev->ngpio > gpio) {
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
return &gdev->descs[gpio - gdev->base];
|
||||
scoped_guard(rwsem_read, &gpio_devices_sem) {
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
if (gdev->base <= gpio &&
|
||||
gdev->base + gdev->ngpio > gpio)
|
||||
return &gdev->descs[gpio - gdev->base];
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
pr_warn("invalid GPIO %d\n", gpio);
|
||||
|
||||
|
|
@ -254,6 +240,20 @@ int gpio_device_get_base(struct gpio_device *gdev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_device_get_base);
|
||||
|
||||
/**
|
||||
* gpio_device_get_label() - Get the label of this GPIO device
|
||||
* @gdev: GPIO device
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to the string containing the GPIO device label. The string's
|
||||
* lifetime is tied to that of the underlying GPIO device.
|
||||
*/
|
||||
const char *gpio_device_get_label(struct gpio_device *gdev)
|
||||
{
|
||||
return gdev->label;
|
||||
}
|
||||
EXPORT_SYMBOL(gpio_device_get_label);
|
||||
|
||||
/**
|
||||
* gpio_device_get_chip() - Get the gpio_chip implementation of this GPIO device
|
||||
* @gdev: GPIO device
|
||||
|
|
@ -276,7 +276,7 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
|
|||
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
|
||||
|
||||
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
|
||||
static int gpiochip_find_base(int ngpio)
|
||||
static int gpiochip_find_base_unlocked(int ngpio)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
int base = GPIO_DYNAMIC_BASE;
|
||||
|
|
@ -349,7 +349,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
|
|||
* Return -EBUSY if the new chip overlaps with some other chip's integer
|
||||
* space.
|
||||
*/
|
||||
static int gpiodev_add_to_list(struct gpio_device *gdev)
|
||||
static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
|
||||
{
|
||||
struct gpio_device *prev, *next;
|
||||
|
||||
|
|
@ -398,26 +398,21 @@ static int gpiodev_add_to_list(struct gpio_device *gdev)
|
|||
static struct gpio_desc *gpio_name_to_desc(const char * const name)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
unsigned long flags;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
guard(rwsem_read)(&gpio_devices_sem);
|
||||
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
struct gpio_desc *desc;
|
||||
|
||||
for_each_gpio_desc(gdev->chip, desc) {
|
||||
if (desc->name && !strcmp(desc->name, name)) {
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
if (desc->name && !strcmp(desc->name, name))
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -655,11 +650,6 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
|
|||
static void gpiodev_release(struct device *dev)
|
||||
{
|
||||
struct gpio_device *gdev = to_gpio_device(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
list_del(&gdev->list);
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
ida_free(&gpio_ida, gdev->id);
|
||||
kfree_const(gdev->label);
|
||||
|
|
@ -817,7 +807,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
struct lock_class_key *request_key)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
int base = 0;
|
||||
int ret = 0;
|
||||
|
|
@ -882,48 +871,45 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
|
||||
gdev->ngpio = gc->ngpio;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
scoped_guard(rwsem_write, &gpio_devices_sem) {
|
||||
/*
|
||||
* TODO: this allocates a Linux GPIO number base in the global
|
||||
* GPIO numberspace for this chip. In the long run we want to
|
||||
* get *rid* of this numberspace and use only descriptors, but
|
||||
* it may be a pipe dream. It will not happen before we get rid
|
||||
* of the sysfs interface anyways.
|
||||
*/
|
||||
base = gc->base;
|
||||
|
||||
/*
|
||||
* TODO: this allocates a Linux GPIO number base in the global
|
||||
* GPIO numberspace for this chip. In the long run we want to
|
||||
* get *rid* of this numberspace and use only descriptors, but
|
||||
* it may be a pipe dream. It will not happen before we get rid
|
||||
* of the sysfs interface anyways.
|
||||
*/
|
||||
base = gc->base;
|
||||
if (base < 0) {
|
||||
base = gpiochip_find_base(gc->ngpio);
|
||||
if (base < 0) {
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
ret = base;
|
||||
base = 0;
|
||||
base = gpiochip_find_base_unlocked(gc->ngpio);
|
||||
if (base < 0) {
|
||||
ret = base;
|
||||
base = 0;
|
||||
goto err_free_label;
|
||||
}
|
||||
/*
|
||||
* TODO: it should not be necessary to reflect the assigned
|
||||
* base outside of the GPIO subsystem. Go over drivers and
|
||||
* see if anyone makes use of this, else drop this and assign
|
||||
* a poison instead.
|
||||
*/
|
||||
gc->base = base;
|
||||
} else {
|
||||
dev_warn(&gdev->dev,
|
||||
"Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
|
||||
}
|
||||
gdev->base = base;
|
||||
|
||||
ret = gpiodev_add_to_list_unlocked(gdev);
|
||||
if (ret) {
|
||||
chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
|
||||
goto err_free_label;
|
||||
}
|
||||
/*
|
||||
* TODO: it should not be necessary to reflect the assigned
|
||||
* base outside of the GPIO subsystem. Go over drivers and
|
||||
* see if anyone makes use of this, else drop this and assign
|
||||
* a poison instead.
|
||||
*/
|
||||
gc->base = base;
|
||||
} else {
|
||||
dev_warn(&gdev->dev,
|
||||
"Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
|
||||
|
||||
for (i = 0; i < gc->ngpio; i++)
|
||||
gdev->descs[i].gdev = gdev;
|
||||
}
|
||||
gdev->base = base;
|
||||
|
||||
ret = gpiodev_add_to_list(gdev);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
|
||||
goto err_free_label;
|
||||
}
|
||||
|
||||
for (i = 0; i < gc->ngpio; i++)
|
||||
gdev->descs[i].gdev = gdev;
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
|
||||
|
|
@ -1015,9 +1001,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
|||
goto err_print_message;
|
||||
}
|
||||
err_remove_from_list:
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
list_del(&gdev->list);
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
scoped_guard(rwsem_write, &gpio_devices_sem)
|
||||
list_del(&gdev->list);
|
||||
err_free_label:
|
||||
kfree_const(gdev->label);
|
||||
err_free_descs:
|
||||
|
|
@ -1048,8 +1033,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);
|
|||
void gpiochip_remove(struct gpio_chip *gc)
|
||||
{
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
|
||||
down_write(&gdev->sem);
|
||||
|
||||
|
|
@ -1071,7 +1056,7 @@ void gpiochip_remove(struct gpio_chip *gc)
|
|||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
for (i = 0; i < gdev->ngpio; i++) {
|
||||
if (gpiochip_is_requested(gc, i))
|
||||
if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags))
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
|
@ -1080,6 +1065,9 @@ void gpiochip_remove(struct gpio_chip *gc)
|
|||
dev_crit(&gdev->dev,
|
||||
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
|
||||
|
||||
scoped_guard(rwsem_write, &gpio_devices_sem)
|
||||
list_del(&gdev->list);
|
||||
|
||||
/*
|
||||
* The gpiochip side puts its use of the device to rest here:
|
||||
* if there are no userspace clients, the chardev and device will
|
||||
|
|
@ -1126,7 +1114,7 @@ struct gpio_device *gpio_device_find(void *data,
|
|||
*/
|
||||
might_sleep();
|
||||
|
||||
guard(spinlock_irqsave)(&gpio_lock);
|
||||
guard(rwsem_read)(&gpio_devices_sem);
|
||||
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
if (gdev->chip && match(gdev->chip, data))
|
||||
|
|
@ -2185,10 +2173,10 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
|
|||
*/
|
||||
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
|
||||
{
|
||||
struct gpio_chip *gc = desc->gdev->chip;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
unsigned offset;
|
||||
struct gpio_chip *gc = desc->gdev->chip;
|
||||
unsigned long flags;
|
||||
unsigned int offset;
|
||||
int ret;
|
||||
|
||||
if (label) {
|
||||
label = kstrdup_const(label, GFP_KERNEL);
|
||||
|
|
@ -2300,9 +2288,9 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
|
|||
|
||||
static bool gpiod_free_commit(struct gpio_desc *desc)
|
||||
{
|
||||
bool ret = false;
|
||||
unsigned long flags;
|
||||
struct gpio_chip *gc;
|
||||
struct gpio_chip *gc;
|
||||
unsigned long flags;
|
||||
bool ret = false;
|
||||
|
||||
might_sleep();
|
||||
|
||||
|
|
@ -2330,9 +2318,6 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
|
|||
clear_bit(FLAG_IS_HOGGED, &desc->flags);
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
desc->hog = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_GPIO_CDEV
|
||||
WRITE_ONCE(desc->debounce_period_us, 0);
|
||||
#endif
|
||||
ret = true;
|
||||
}
|
||||
|
|
@ -2353,38 +2338,53 @@ void gpiod_free(struct gpio_desc *desc)
|
|||
return;
|
||||
|
||||
if (!gpiod_free_commit(desc))
|
||||
WARN_ON(extra_checks);
|
||||
WARN_ON(1);
|
||||
|
||||
module_put(desc->gdev->owner);
|
||||
gpio_device_put(desc->gdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiochip_is_requested - return string iff signal was requested
|
||||
* @gc: controller managing the signal
|
||||
* @offset: of signal within controller's 0..(ngpio - 1) range
|
||||
* gpiochip_dup_line_label - Get a copy of the consumer label.
|
||||
* @gc: GPIO chip controlling this line.
|
||||
* @offset: Hardware offset of the line.
|
||||
*
|
||||
* Returns NULL if the GPIO is not currently requested, else a string.
|
||||
* The string returned is the label passed to gpio_request(); if none has been
|
||||
* passed it is a meaningless, non-NULL constant.
|
||||
* Returns:
|
||||
* Pointer to a copy of the consumer label if the line is requested or NULL
|
||||
* if it's not. If a valid pointer was returned, it must be freed using
|
||||
* kfree(). In case of a memory allocation error, the function returns %ENOMEM.
|
||||
*
|
||||
* This function is for use by GPIO controller drivers. The label can
|
||||
* help with diagnostics, and knowing that the signal is used as a GPIO
|
||||
* can help avoid accidentally multiplexing it to another controller.
|
||||
* Must not be called from atomic context.
|
||||
*/
|
||||
const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset)
|
||||
char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
char *label;
|
||||
|
||||
desc = gpiochip_get_desc(gc, offset);
|
||||
if (IS_ERR(desc))
|
||||
return NULL;
|
||||
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
|
||||
guard(spinlock_irqsave)(&gpio_lock);
|
||||
|
||||
if (!test_bit(FLAG_REQUESTED, &desc->flags))
|
||||
return NULL;
|
||||
return desc->label;
|
||||
|
||||
/*
|
||||
* FIXME: Once we mark gpiod_direction_input/output() and
|
||||
* gpiod_get_direction() with might_sleep(), we'll be able to protect
|
||||
* the GPIO descriptors with mutex (while value setting operations will
|
||||
* become lockless).
|
||||
*
|
||||
* Until this happens, this allocation needs to be atomic.
|
||||
*/
|
||||
label = kstrdup(desc->label, GFP_ATOMIC);
|
||||
if (!label)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return label;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_is_requested);
|
||||
EXPORT_SYMBOL_GPL(gpiochip_dup_line_label);
|
||||
|
||||
/**
|
||||
* gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor
|
||||
|
|
@ -2564,8 +2564,8 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
|
|||
*/
|
||||
int gpiod_direction_input(struct gpio_desc *desc)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
int ret = 0;
|
||||
struct gpio_chip *gc;
|
||||
int ret = 0;
|
||||
|
||||
VALIDATE_DESC(desc);
|
||||
gc = desc->gdev->chip;
|
||||
|
|
@ -2914,7 +2914,7 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des
|
|||
|
||||
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
struct gpio_chip *gc;
|
||||
int value;
|
||||
|
||||
gc = desc->gdev->chip;
|
||||
|
|
@ -3209,7 +3209,7 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value
|
|||
|
||||
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
struct gpio_chip *gc;
|
||||
|
||||
gc = desc->gdev->chip;
|
||||
trace_gpio_value(desc_to_gpio(desc), 0, value);
|
||||
|
|
@ -3716,7 +3716,7 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
|
|||
*/
|
||||
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
VALIDATE_DESC(desc);
|
||||
return gpiod_get_raw_value_commit(desc);
|
||||
}
|
||||
|
|
@ -3735,7 +3735,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc)
|
|||
{
|
||||
int value;
|
||||
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
VALIDATE_DESC(desc);
|
||||
value = gpiod_get_raw_value_commit(desc);
|
||||
if (value < 0)
|
||||
|
|
@ -3766,7 +3766,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
|
|||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
if (!desc_array)
|
||||
return -EINVAL;
|
||||
return gpiod_get_array_value_complex(true, true, array_size,
|
||||
|
|
@ -3792,7 +3792,7 @@ int gpiod_get_array_value_cansleep(unsigned int array_size,
|
|||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
if (!desc_array)
|
||||
return -EINVAL;
|
||||
return gpiod_get_array_value_complex(false, true, array_size,
|
||||
|
|
@ -3813,7 +3813,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);
|
|||
*/
|
||||
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
VALIDATE_DESC_VOID(desc);
|
||||
gpiod_set_raw_value_commit(desc, value);
|
||||
}
|
||||
|
|
@ -3831,7 +3831,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
|
|||
*/
|
||||
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
VALIDATE_DESC_VOID(desc);
|
||||
gpiod_set_value_nocheck(desc, value);
|
||||
}
|
||||
|
|
@ -3854,7 +3854,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
|||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
if (!desc_array)
|
||||
return -EINVAL;
|
||||
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
|
||||
|
|
@ -3896,7 +3896,7 @@ int gpiod_set_array_value_cansleep(unsigned int array_size,
|
|||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
might_sleep();
|
||||
if (!desc_array)
|
||||
return -EINVAL;
|
||||
return gpiod_set_array_value_complex(false, true, array_size,
|
||||
|
|
@ -4696,13 +4696,11 @@ core_initcall(gpiolib_dev_init);
|
|||
|
||||
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
|
||||
{
|
||||
struct gpio_chip *gc = gdev->chip;
|
||||
struct gpio_desc *desc;
|
||||
unsigned gpio = gdev->base;
|
||||
int value;
|
||||
bool is_out;
|
||||
bool is_irq;
|
||||
bool active_low;
|
||||
struct gpio_chip *gc = gdev->chip;
|
||||
bool active_low, is_irq, is_out;
|
||||
unsigned int gpio = gdev->base;
|
||||
struct gpio_desc *desc;
|
||||
int value;
|
||||
|
||||
for_each_gpio_desc(gc, desc) {
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags)) {
|
||||
|
|
@ -4727,35 +4725,33 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
|
|||
|
||||
static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct gpio_device *gdev = NULL;
|
||||
loff_t index = *pos;
|
||||
|
||||
s->private = "";
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
list_for_each_entry(gdev, &gpio_devices, list)
|
||||
if (index-- == 0) {
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
guard(rwsem_read)(&gpio_devices_sem);
|
||||
|
||||
list_for_each_entry(gdev, &gpio_devices, list) {
|
||||
if (index-- == 0)
|
||||
return gdev;
|
||||
}
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct gpio_device *gdev = v;
|
||||
void *ret = NULL;
|
||||
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
if (list_is_last(&gdev->list, &gpio_devices))
|
||||
ret = NULL;
|
||||
else
|
||||
ret = list_first_entry(&gdev->list, struct gpio_device, list);
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
scoped_guard(rwsem_read, &gpio_devices_sem) {
|
||||
if (list_is_last(&gdev->list, &gpio_devices))
|
||||
ret = NULL;
|
||||
else
|
||||
ret = list_first_entry(&gdev->list, struct gpio_device,
|
||||
list);
|
||||
}
|
||||
|
||||
s->private = "\n";
|
||||
++*pos;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
|
|
@ -136,6 +137,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
|
|||
|
||||
extern spinlock_t gpio_lock;
|
||||
extern struct list_head gpio_devices;
|
||||
extern struct rw_semaphore gpio_devices_sem;
|
||||
|
||||
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
|
||||
|
||||
|
|
@ -147,7 +149,6 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
|
|||
* @label: Name of the consumer
|
||||
* @name: Line name
|
||||
* @hog: Pointer to the device node that hogs this line (if any)
|
||||
* @debounce_period_us: Debounce period in microseconds
|
||||
*
|
||||
* These are obtained using gpiod_get() and are preferable to the old
|
||||
* integer-based handles.
|
||||
|
|
@ -185,10 +186,6 @@ struct gpio_desc {
|
|||
#ifdef CONFIG_OF_DYNAMIC
|
||||
struct device_node *hog;
|
||||
#endif
|
||||
#ifdef CONFIG_GPIO_CDEV
|
||||
/* debounce period in microseconds */
|
||||
unsigned int debounce_period_us;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
|
@ -1173,7 +1174,6 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
const char *pull_str = NULL;
|
||||
const char *pull = NULL;
|
||||
unsigned long flags;
|
||||
const char *label;
|
||||
unsigned int pin;
|
||||
|
||||
pin = vg->soc->pins[i].number;
|
||||
|
|
@ -1200,9 +1200,10 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
seq_printf(s, "Pin %i: can't retrieve community\n", pin);
|
||||
continue;
|
||||
}
|
||||
label = gpiochip_is_requested(chip, i);
|
||||
if (!label)
|
||||
label = "Unrequested";
|
||||
|
||||
char *label __free(kfree) = gpiochip_dup_line_label(chip, i);
|
||||
if (IS_ERR(label))
|
||||
continue;
|
||||
|
||||
switch (conf0 & BYT_PULL_ASSIGN_MASK) {
|
||||
case BYT_PULL_ASSIGN_UP:
|
||||
|
|
@ -1231,7 +1232,7 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
seq_printf(s,
|
||||
" gpio-%-3d (%-20.20s) %s %s %s pad-%-3d offset:0x%03x mux:%d %s%s%s",
|
||||
pin,
|
||||
label,
|
||||
label ?: "Unrequested",
|
||||
val & BYT_INPUT_EN ? " " : "in",
|
||||
val & BYT_OUTPUT_EN ? " " : "out",
|
||||
str_hi_lo(val & BYT_LEVEL),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
*
|
||||
* Driver allows to use AxB5xx unused pins to be used as GPIO
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
|
|
@ -453,12 +455,11 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s,
|
|||
unsigned offset, unsigned gpio)
|
||||
{
|
||||
struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev);
|
||||
const char *label = gpiochip_is_requested(chip, offset - 1);
|
||||
u8 gpio_offset = offset - 1;
|
||||
int mode = -1;
|
||||
bool is_out;
|
||||
bool pd;
|
||||
int ret;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
const char *modes[] = {
|
||||
[ABX500_DEFAULT] = "default",
|
||||
|
|
@ -474,6 +475,10 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s,
|
|||
[ABX500_GPIO_PULL_UP] = "pull up",
|
||||
};
|
||||
|
||||
char *label __free(kfree) = gpiochip_dup_line_label(chip, offset - 1);
|
||||
if (IS_ERR(label))
|
||||
goto out;
|
||||
|
||||
ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG,
|
||||
gpio_offset, &is_out);
|
||||
if (ret < 0)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
* Copyright (C) 2011-2013 Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
|
|
@ -917,7 +918,6 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s,
|
|||
struct pinctrl_dev *pctldev, struct gpio_chip *chip,
|
||||
unsigned offset, unsigned gpio)
|
||||
{
|
||||
const char *label = gpiochip_is_requested(chip, offset);
|
||||
struct nmk_gpio_chip *nmk_chip = gpiochip_get_data(chip);
|
||||
int mode;
|
||||
bool is_out;
|
||||
|
|
@ -934,6 +934,10 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s,
|
|||
[NMK_GPIO_ALT_C+4] = "altC4",
|
||||
};
|
||||
|
||||
char *label = gpiochip_dup_line_label(chip, offset);
|
||||
if (IS_ERR(label))
|
||||
return;
|
||||
|
||||
clk_enable(nmk_chip->clk);
|
||||
is_out = !!(readl(nmk_chip->addr + NMK_GPIO_DIR) & BIT(offset));
|
||||
pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & BIT(offset));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) Sunplus Tech / Tibbo Tech.
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
|
|
@ -500,16 +501,15 @@ static int sppctl_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
|
|||
|
||||
static void sppctl_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||
{
|
||||
const char *label;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chip->ngpio; i++) {
|
||||
label = gpiochip_is_requested(chip, i);
|
||||
if (!label)
|
||||
label = "";
|
||||
char *label __free(kfree) = gpiochip_dup_line_label(chip, i);
|
||||
if (IS_ERR(label))
|
||||
continue;
|
||||
|
||||
seq_printf(s, " gpio-%03d (%-16.16s | %-16.16s)", i + chip->base,
|
||||
chip->names[i], label);
|
||||
chip->names[i], label ?: "");
|
||||
seq_printf(s, " %c", sppctl_gpio_get_direction(chip, i) ? 'I' : 'O');
|
||||
seq_printf(s, ":%d", sppctl_gpio_get(chip, i));
|
||||
seq_printf(s, " %s", sppctl_first_get(chip, i) ? "gpi" : "mux");
|
||||
|
|
|
|||
|
|
@ -531,19 +531,40 @@ struct gpio_chip {
|
|||
#endif /* CONFIG_OF_GPIO */
|
||||
};
|
||||
|
||||
const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset);
|
||||
char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset);
|
||||
|
||||
|
||||
struct _gpiochip_for_each_data {
|
||||
const char **label;
|
||||
unsigned int *i;
|
||||
};
|
||||
|
||||
DEFINE_CLASS(_gpiochip_for_each_data,
|
||||
struct _gpiochip_for_each_data,
|
||||
if (*_T.label) kfree(*_T.label),
|
||||
({
|
||||
struct _gpiochip_for_each_data _data = { label, i };
|
||||
*_data.i = 0;
|
||||
_data;
|
||||
}),
|
||||
const char **label, int *i)
|
||||
|
||||
/**
|
||||
* for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range
|
||||
* @chip: the chip to query
|
||||
* @i: loop variable
|
||||
* @base: first GPIO in the range
|
||||
* @size: amount of GPIOs to check starting from @base
|
||||
* @label: label of current GPIO
|
||||
* @_chip: the chip to query
|
||||
* @_i: loop variable
|
||||
* @_base: first GPIO in the range
|
||||
* @_size: amount of GPIOs to check starting from @base
|
||||
* @_label: label of current GPIO
|
||||
*/
|
||||
#define for_each_requested_gpio_in_range(chip, i, base, size, label) \
|
||||
for (i = 0; i < size; i++) \
|
||||
if ((label = gpiochip_is_requested(chip, base + i)) == NULL) {} else
|
||||
#define for_each_requested_gpio_in_range(_chip, _i, _base, _size, _label) \
|
||||
for (CLASS(_gpiochip_for_each_data, _data)(&_label, &_i); \
|
||||
*_data.i < _size; \
|
||||
(*_data.i)++, kfree(*(_data.label)), *_data.label = NULL) \
|
||||
if ((*_data.label = \
|
||||
gpiochip_dup_line_label(_chip, _base + *_data.i)) == NULL) {} \
|
||||
else if (IS_ERR(*_data.label)) {} \
|
||||
else
|
||||
|
||||
/* Iterates over all requested GPIO of the given @chip */
|
||||
#define for_each_requested_gpio(chip, i, label) \
|
||||
|
|
@ -701,7 +722,6 @@ int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
|
|||
#else
|
||||
|
||||
#include <asm/bug.h>
|
||||
#include <asm/errno.h>
|
||||
|
||||
static inline int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
|
||||
struct irq_domain *domain)
|
||||
|
|
@ -786,11 +806,10 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc);
|
|||
|
||||
/* struct gpio_device getters */
|
||||
int gpio_device_get_base(struct gpio_device *gdev);
|
||||
const char *gpio_device_get_label(struct gpio_device *gdev);
|
||||
|
||||
#else /* CONFIG_GPIOLIB */
|
||||
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <asm/bug.h>
|
||||
|
||||
static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
|
||||
|
|
|
|||
|
|
@ -80,12 +80,38 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
|
|||
|
||||
bool fwnode_device_is_available(const struct fwnode_handle *fwnode);
|
||||
|
||||
static inline bool fwnode_device_is_big_endian(const struct fwnode_handle *fwnode)
|
||||
{
|
||||
if (fwnode_property_present(fwnode, "big-endian"))
|
||||
return true;
|
||||
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
|
||||
fwnode_property_present(fwnode, "native-endian"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline
|
||||
bool fwnode_device_is_compatible(const struct fwnode_handle *fwnode, const char *compat)
|
||||
{
|
||||
return fwnode_property_match_string(fwnode, "compatible", compat) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_is_big_endian - check if a device has BE registers
|
||||
* @dev: Pointer to the struct device
|
||||
*
|
||||
* Returns: true if the device has a "big-endian" property, or if the kernel
|
||||
* was compiled for BE *and* the device has a "native-endian" property.
|
||||
* Returns false otherwise.
|
||||
*
|
||||
* Callers would nominally use ioread32be/iowrite32be if
|
||||
* device_is_big_endian() == true, or readl/writel otherwise.
|
||||
*/
|
||||
static inline bool device_is_big_endian(const struct device *dev)
|
||||
{
|
||||
return fwnode_device_is_big_endian(dev_fwnode(dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* device_is_compatible - match 'compatible' property of the device with a given string
|
||||
* @dev: Pointer to the struct device
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user