Merge tag 'gpio-updates-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio updates from Bartosz Golaszewski:
 "We have a lot of code refactoring using common helpers and ended up
  removing more lines then we're adding this release cycle.

  Nothing really stands out, just small updates all over the place.

  Core GPIOLIB updates:
   - wake-up poll() in user-space on device unbind
   - improve fwnode usage
   - interrupt domain handling improvements
   - correctly handle the ngpios property in gpio-mmio

  Driver cleanups:
   - remove unneeded calls to platform_set_drvdata() all around the
     place
   - remove unneeded of_match_ptr() expansions whenever a driver depends
     on CONFIG_OF
   - remove redundant calls to dev_err_probe() from gpio-omap and
     gpio-davinci

  Driver improvements:
   - use autopointers and guards from cleanup.h in gpio-sim
   - shrink code in gpio-sim using some common helpers
   - convert the idio family of drivers to using gpio-regmap
   - convert gpio-ws16c48 to using gpio-regmap
   - use devres to simplify code in gpio-pisosr and gpio-mxc
   - update gpio-sifive: support IRQ wake, improve interrupt handling,
     allow building as module
   - make gpio-ge and gpio-bcm-kona OF-independent (plus some minor
     tweaks)
   - add support for new models in gpio-pca953x and gpio-ds4520
   - add runtime PM support to gpio-mxc
   - fix a build warning in gpio-mxs
   - add support for adding pin ranges to gpio-mlxbf3
   - add counter/timer support to gpio-104-dio-48e
   - switch to dynamic GPIO base allocation in gpio-vf610
   - minor oneliners here and there

  Device-tree bindings updates:
   - enable the gpio-line-names property in snps,dw-apb and STMPE GPIO
   - document new models in fsl-imx-gpio, ds4520 and pca95xx
   - convert the bindings for brcm,kona-gpio to YAML"

* tag 'gpio-updates-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (94 commits)
  gpio: pca953x: add support for TCA9538
  dt-bindings: gpio: pca95xx: document new tca9538 chip
  gpio: pca953x: Use i2c_get_match_data()
  gpio: mlxbf3: use capital "OR" for multiple licenses in SPDX
  gpio: pcf857x: Extend match data support for OF tables
  gpio: vf610: switch to dynamic allocat GPIO base
  gpiolib: provide and use gpiod_line_state_notify()
  gpio: cdev: wake up lineevent poll() on device unbind
  gpio: cdev: wake up linereq poll() on device unbind
  gpio: cdev: wake up chardev poll() on device unbind
  gpiolib: add a second blocking notifier to struct gpio_device
  gpio: cdev: open-code to_gpio_chardev_data()
  gpiolib: rename the gpio_device notifier
  gpio: mlxbf3: Support add_pin_ranges()
  gpio: mxc: Use helper function devm_clk_get_optional_enabled()
  gpio: pca9570: fix kerneldoc
  gpio: sim: simplify code with cleanup helpers
  gpio: sim: replace memmove() + strstrip() with skip_spaces() + strim()
  gpio: sim: simplify gpio_sim_device_config_live_store()
  gpio: mxc: release the parent IRQ in runtime suspend
  ...
This commit is contained in:
Linus Torvalds
2023-08-29 10:21:56 -07:00
88 changed files with 1668 additions and 2068 deletions

View File

@@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/adi,ds4520-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: DS4520 I2C GPIO expander
maintainers:
- Okan Sahin <okan.sahin@analog.com>
properties:
compatible:
enum:
- adi,ds4520-gpio
reg:
maxItems: 1
gpio-controller: true
"#gpio-cells":
const: 2
ngpios:
minimum: 1
maximum: 9
required:
- compatible
- reg
- gpio-controller
- "#gpio-cells"
- ngpios
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
gpio@50 {
compatible = "adi,ds4520-gpio";
reg = <0x50>;
ngpios = <9>;
gpio-controller;
#gpio-cells = <2>;
};
};

View File

@@ -1,52 +0,0 @@
Broadcom Kona Family GPIO
=========================
This GPIO driver is used in the following Broadcom SoCs:
BCM11130, BCM11140, BCM11351, BCM28145, BCM28155
The Broadcom GPIO Controller IP can be configured prior to synthesis to
support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The
GPIO controller only supports edge, not level, triggering of interrupts.
Required properties
-------------------
- compatible: "brcm,bcm11351-gpio", "brcm,kona-gpio"
- reg: Physical base address and length of the controller's registers.
- interrupts: The interrupt outputs from the controller. There is one GPIO
interrupt per GPIO bank. The number of interrupts listed depends on the
number of GPIO banks on the SoC. The interrupts must be ordered by bank,
starting with bank 0. There is always a 1:1 mapping between banks and
IRQs.
- #gpio-cells: Should be <2>. The first cell is the pin number, the second
cell is used to specify optional parameters:
- bit 0 specifies polarity (0 for normal, 1 for inverted)
See also "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt.
- #interrupt-cells: Should be <2>. The first cell is the GPIO number. The
second cell is used to specify flags. The following subset of flags is
supported:
- trigger type (bits[1:0]):
1 = low-to-high edge triggered.
2 = high-to-low edge triggered.
3 = low-to-high or high-to-low edge triggered
Valid values are 1, 2, 3
See also .../devicetree/bindings/interrupt-controller/interrupts.txt.
- gpio-controller: Marks the device node as a GPIO controller.
- interrupt-controller: Marks the device node as an interrupt controller.
Example:
gpio: gpio@35003000 {
compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio";
reg = <0x35003000 0x800>;
interrupts =
<GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH
GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH
GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH
GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH
GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH
GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
#interrupt-cells = <2>;
gpio-controller;
interrupt-controller;
};

View File

@@ -0,0 +1,100 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/brcm,kona-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Kona family GPIO controller
description:
The Broadcom GPIO Controller IP can be configured prior to synthesis to
support up to 8 banks of 32 GPIOs where each bank has its own IRQ. The
GPIO controller only supports edge, not level, triggering of interrupts.
maintainers:
- Ray Jui <rjui@broadcom.com>
properties:
compatible:
items:
- enum:
- brcm,bcm11351-gpio
- brcm,bcm21664-gpio
- brcm,bcm23550-gpio
- const: brcm,kona-gpio
reg:
maxItems: 1
interrupts:
minItems: 4
maxItems: 6
description:
The interrupt outputs from the controller. There is one GPIO interrupt
per GPIO bank. The number of interrupts listed depends on the number of
GPIO banks on the SoC. The interrupts must be ordered by bank, starting
with bank 0. There is always a 1:1 mapping between banks and IRQs.
'#gpio-cells':
const: 2
'#interrupt-cells':
const: 2
gpio-controller: true
interrupt-controller: true
required:
- compatible
- reg
- interrupts
- '#gpio-cells'
- '#interrupt-cells'
- gpio-controller
- interrupt-controller
allOf:
- if:
properties:
compatible:
contains:
const: brcm,bcm11351-gpio
then:
properties:
interrupts:
minItems: 6
- if:
properties:
compatible:
contains:
enum:
- brcm,bcm21664-gpio
- brcm,bcm23550-gpio
then:
properties:
interrupts:
maxItems: 4
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
gpio@35003000 {
compatible = "brcm,bcm11351-gpio", "brcm,kona-gpio";
reg = <0x35003000 0x800>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
#interrupt-cells = <2>;
gpio-controller;
interrupt-controller;
};
...

View File

@@ -32,10 +32,12 @@ properties:
- fsl,imx6sx-gpio - fsl,imx6sx-gpio
- fsl,imx6ul-gpio - fsl,imx6ul-gpio
- fsl,imx7d-gpio - fsl,imx7d-gpio
- fsl,imx8dxl-gpio
- fsl,imx8mm-gpio - fsl,imx8mm-gpio
- fsl,imx8mn-gpio - fsl,imx8mn-gpio
- fsl,imx8mp-gpio - fsl,imx8mp-gpio
- fsl,imx8mq-gpio - fsl,imx8mq-gpio
- fsl,imx8qm-gpio
- fsl,imx8qxp-gpio - fsl,imx8qxp-gpio
- fsl,imxrt1050-gpio - fsl,imxrt1050-gpio
- fsl,imxrt1170-gpio - fsl,imxrt1170-gpio

View File

@@ -66,6 +66,7 @@ properties:
- ti,tca6408 - ti,tca6408
- ti,tca6416 - ti,tca6416
- ti,tca6424 - ti,tca6424
- ti,tca9538
- ti,tca9539 - ti,tca9539
- ti,tca9554 - ti,tca9554

View File

@@ -61,6 +61,10 @@ patternProperties:
'#gpio-cells': '#gpio-cells':
const: 2 const: 2
gpio-line-names:
minItems: 1
maxItems: 32
ngpios: ngpios:
default: 32 default: 32
minimum: 1 minimum: 1

View File

@@ -28,6 +28,10 @@ properties:
gpio-controller: true gpio-controller: true
gpio-line-names:
minItems: 1
maxItems: 24
interrupt-controller: true interrupt-controller: true
st,norequest-mask: st,norequest-mask:

View File

@@ -4195,7 +4195,7 @@ BROADCOM KONA GPIO DRIVER
M: Ray Jui <rjui@broadcom.com> M: Ray Jui <rjui@broadcom.com>
R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
S: Supported S: Supported
F: Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt F: Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml
F: drivers/gpio/gpio-bcm-kona.c F: drivers/gpio/gpio-bcm-kona.c
BROADCOM MPI3 STORAGE CONTROLLER DRIVER BROADCOM MPI3 STORAGE CONTROLLER DRIVER

View File

@@ -111,6 +111,9 @@ config GPIO_MAX730X
config GPIO_IDIO_16 config GPIO_IDIO_16
tristate tristate
select REGMAP_IRQ
select GPIOLIB_IRQCHIP
select GPIO_REGMAP
help help
Enables support for the idio-16 library functions. The idio-16 library Enables support for the idio-16 library functions. The idio-16 library
provides functions to facilitate communication with devices within the provides functions to facilitate communication with devices within the
@@ -191,7 +194,7 @@ config GPIO_RASPBERRYPI_EXP
config GPIO_BCM_KONA config GPIO_BCM_KONA
bool "Broadcom Kona GPIO" bool "Broadcom Kona GPIO"
depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST) depends on ARCH_BCM_MOBILE || COMPILE_TEST
help help
Turn on GPIO support for Broadcom "Kona" chips. Turn on GPIO support for Broadcom "Kona" chips.
@@ -283,7 +286,7 @@ config GPIO_EXAR
config GPIO_GE_FPGA config GPIO_GE_FPGA
bool "GE FPGA based GPIO" bool "GE FPGA based GPIO"
depends on GE_FPGA depends on GE_FPGA || COMPILE_TEST
select GPIO_GENERIC select GPIO_GENERIC
help help
Support for common GPIO functionality provided on some GE Single Board Support for common GPIO functionality provided on some GE Single Board
@@ -564,7 +567,7 @@ config GPIO_SAMA5D2_PIOBU
maintain their value during backup/self-refresh. maintain their value during backup/self-refresh.
config GPIO_SIFIVE config GPIO_SIFIVE
bool "SiFive GPIO support" tristate "SiFive GPIO support"
depends on OF_GPIO depends on OF_GPIO
select IRQ_DOMAIN_HIERARCHY select IRQ_DOMAIN_HIERARCHY
select GPIO_GENERIC select GPIO_GENERIC
@@ -858,6 +861,7 @@ config GPIO_104_DIO_48E
select REGMAP_IRQ select REGMAP_IRQ
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
select GPIO_I8255 select GPIO_I8255
select I8254
help help
Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E, Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E,
104-DIO-24E). The base port addresses for the devices may be 104-DIO-24E). The base port addresses for the devices may be
@@ -868,7 +872,7 @@ config GPIO_104_IDIO_16
tristate "ACCES 104-IDIO-16 GPIO support" tristate "ACCES 104-IDIO-16 GPIO support"
depends on PC104 depends on PC104
select ISA_BUS_API select ISA_BUS_API
select GPIOLIB_IRQCHIP select REGMAP_MMIO
select GPIO_IDIO_16 select GPIO_IDIO_16
help help
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16, Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
@@ -994,7 +998,10 @@ config GPIO_WINBOND
config GPIO_WS16C48 config GPIO_WS16C48
tristate "WinSystems WS16C48 GPIO support" tristate "WinSystems WS16C48 GPIO support"
select ISA_BUS_API select ISA_BUS_API
select REGMAP_IRQ
select REGMAP_MMIO
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
select GPIO_REGMAP
help help
Enables GPIO support for the WinSystems WS16C48. The base port Enables GPIO support for the WinSystems WS16C48. The base port
addresses for the devices may be configured via the base module addresses for the devices may be configured via the base module
@@ -1028,6 +1035,17 @@ config GPIO_FXL6408
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called gpio-fxl6408. be called gpio-fxl6408.
config GPIO_DS4520
tristate "DS4520 I2C GPIO expander"
select REGMAP_I2C
select GPIO_REGMAP
help
GPIO driver for ADI DS4520 I2C-based GPIO expander.
Say yes here to enable the GPIO driver for the ADI DS4520 chip.
To compile this driver as a module, choose M here: the module will
be called gpio-ds4520.
config GPIO_GW_PLD config GPIO_GW_PLD
tristate "Gateworks PLD GPIO Expander" tristate "Gateworks PLD GPIO Expander"
depends on OF_GPIO depends on OF_GPIO
@@ -1640,7 +1658,7 @@ config GPIO_PCH
config GPIO_PCI_IDIO_16 config GPIO_PCI_IDIO_16
tristate "ACCES PCI-IDIO-16 GPIO support" tristate "ACCES PCI-IDIO-16 GPIO support"
select GPIOLIB_IRQCHIP select REGMAP_MMIO
select GPIO_IDIO_16 select GPIO_IDIO_16
help help
Enables GPIO support for the ACCES PCI-IDIO-16. An interrupt is Enables GPIO support for the ACCES PCI-IDIO-16. An interrupt is
@@ -1650,7 +1668,10 @@ config GPIO_PCI_IDIO_16
config GPIO_PCIE_IDIO_24 config GPIO_PCIE_IDIO_24
tristate "ACCES PCIe-IDIO-24 GPIO support" tristate "ACCES PCIe-IDIO-24 GPIO support"
select REGMAP_IRQ
select REGMAP_MMIO
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
select GPIO_REGMAP
help help
Enables GPIO support for the ACCES PCIe-IDIO-24 family (PCIe-IDIO-24, Enables GPIO support for the ACCES PCIe-IDIO-24 family (PCIe-IDIO-24,
PCIe-IDI-24, PCIe-IDO-24, PCIe-IDIO-12). An interrupt is generated PCIe-IDI-24, PCIe-IDO-24, PCIe-IDIO-12). An interrupt is generated

View File

@@ -52,6 +52,7 @@ obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
obj-$(CONFIG_GPIO_DS4520) += gpio-ds4520.o
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o
obj-$(CONFIG_GPIO_ELKHARTLAKE) += gpio-elkhartlake.o obj-$(CONFIG_GPIO_ELKHARTLAKE) += gpio-elkhartlake.o

View File

@@ -9,6 +9,7 @@
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i8254.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/isa.h> #include <linux/isa.h>
@@ -16,6 +17,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
#include "gpio-i8255.h" #include "gpio-i8255.h"
@@ -37,6 +39,8 @@ MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers");
#define DIO48E_ENABLE_INTERRUPT 0xB #define DIO48E_ENABLE_INTERRUPT 0xB
#define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT #define DIO48E_DISABLE_INTERRUPT DIO48E_ENABLE_INTERRUPT
#define DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING 0xD
#define DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING
#define DIO48E_CLEAR_INTERRUPT 0xF #define DIO48E_CLEAR_INTERRUPT 0xF
#define DIO48E_NUM_PPI 2 #define DIO48E_NUM_PPI 2
@@ -75,18 +79,20 @@ static const struct regmap_access_table dio48e_precious_table = {
.yes_ranges = dio48e_precious_ranges, .yes_ranges = dio48e_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges), .n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges),
}; };
static const struct regmap_config dio48e_regmap_config = {
.reg_bits = 8, static const struct regmap_range pit_wr_ranges[] = {
.reg_stride = 1, regmap_reg_range(0x0, 0x3),
.val_bits = 8, };
.io_port = true, static const struct regmap_range pit_rd_ranges[] = {
.max_register = 0xF, regmap_reg_range(0x0, 0x2),
.wr_table = &dio48e_wr_table, };
.rd_table = &dio48e_rd_table, static const struct regmap_access_table pit_wr_table = {
.volatile_table = &dio48e_volatile_table, .yes_ranges = pit_wr_ranges,
.precious_table = &dio48e_precious_table, .n_yes_ranges = ARRAY_SIZE(pit_wr_ranges),
.cache_type = REGCACHE_FLAT, };
.use_raw_spinlock = true, static const struct regmap_access_table pit_rd_table = {
.yes_ranges = pit_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(pit_rd_ranges),
}; };
/* only bit 3 on each respective Port C supports interrupts */ /* only bit 3 on each respective Port C supports interrupts */
@@ -102,14 +108,56 @@ static const struct regmap_irq dio48e_regmap_irqs[] = {
/** /**
* struct dio48e_gpio - GPIO device private data structure * struct dio48e_gpio - GPIO device private data structure
* @lock: synchronization lock to prevent I/O race conditions
* @map: Regmap for the device * @map: Regmap for the device
* @regs: virtual mapping for device registers
* @flags: IRQ flags saved during locking
* @irq_mask: Current IRQ mask state on the device * @irq_mask: Current IRQ mask state on the device
*/ */
struct dio48e_gpio { struct dio48e_gpio {
raw_spinlock_t lock;
struct regmap *map; struct regmap *map;
void __iomem *regs;
unsigned long flags;
unsigned int irq_mask; unsigned int irq_mask;
}; };
static void dio48e_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock)
{
struct dio48e_gpio *const dio48egpio = lock_arg;
unsigned long flags;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
dio48egpio->flags = flags;
}
static void dio48e_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock)
{
struct dio48e_gpio *const dio48egpio = lock_arg;
raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags);
}
static void pit_regmap_lock(void *lock_arg) __acquires(&dio48egpio->lock)
{
struct dio48e_gpio *const dio48egpio = lock_arg;
unsigned long flags;
raw_spin_lock_irqsave(&dio48egpio->lock, flags);
dio48egpio->flags = flags;
iowrite8(0x00, dio48egpio->regs + DIO48E_ENABLE_COUNTER_TIMER_ADDRESSING);
}
static void pit_regmap_unlock(void *lock_arg) __releases(&dio48egpio->lock)
{
struct dio48e_gpio *const dio48egpio = lock_arg;
ioread8(dio48egpio->regs + DIO48E_DISABLE_COUNTER_TIMER_ADDRESSING);
raw_spin_unlock_irqrestore(&dio48egpio->lock, dio48egpio->flags);
}
static int dio48e_handle_mask_sync(const int index, static int dio48e_handle_mask_sync(const int index,
const unsigned int mask_buf_def, const unsigned int mask_buf_def,
const unsigned int mask_buf, const unsigned int mask_buf,
@@ -176,6 +224,9 @@ static int dio48e_probe(struct device *dev, unsigned int id)
struct i8255_regmap_config config = {}; struct i8255_regmap_config config = {};
void __iomem *regs; void __iomem *regs;
struct regmap *map; struct regmap *map;
struct regmap_config dio48e_regmap_config;
struct regmap_config pit_regmap_config;
struct i8254_regmap_config pit_config;
int err; int err;
struct regmap_irq_chip *chip; struct regmap_irq_chip *chip;
struct dio48e_gpio *dio48egpio; struct dio48e_gpio *dio48egpio;
@@ -187,21 +238,58 @@ static int dio48e_probe(struct device *dev, unsigned int id)
return -EBUSY; return -EBUSY;
} }
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
if (!dio48egpio)
return -ENOMEM;
regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT); regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT);
if (!regs) if (!regs)
return -ENOMEM; return -ENOMEM;
dio48egpio->regs = regs;
raw_spin_lock_init(&dio48egpio->lock);
dio48e_regmap_config = (struct regmap_config) {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.lock = dio48e_regmap_lock,
.unlock = dio48e_regmap_unlock,
.lock_arg = dio48egpio,
.io_port = true,
.wr_table = &dio48e_wr_table,
.rd_table = &dio48e_rd_table,
.volatile_table = &dio48e_volatile_table,
.precious_table = &dio48e_precious_table,
.cache_type = REGCACHE_FLAT,
};
map = devm_regmap_init_mmio(dev, regs, &dio48e_regmap_config); map = devm_regmap_init_mmio(dev, regs, &dio48e_regmap_config);
if (IS_ERR(map)) if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map), return dev_err_probe(dev, PTR_ERR(map),
"Unable to initialize register map\n"); "Unable to initialize register map\n");
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
if (!dio48egpio)
return -ENOMEM;
dio48egpio->map = map; dio48egpio->map = map;
pit_regmap_config = (struct regmap_config) {
.name = "i8254",
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.lock = pit_regmap_lock,
.unlock = pit_regmap_unlock,
.lock_arg = dio48egpio,
.io_port = true,
.wr_table = &pit_wr_table,
.rd_table = &pit_rd_table,
};
pit_config.map = devm_regmap_init_mmio(dev, regs, &pit_regmap_config);
if (IS_ERR(pit_config.map))
return dev_err_probe(dev, PTR_ERR(pit_config.map),
"Unable to initialize i8254 register map\n");
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip) if (!chip)
return -ENOMEM; return -ENOMEM;
@@ -225,6 +313,12 @@ static int dio48e_probe(struct device *dev, unsigned int id)
if (err) if (err)
return dev_err_probe(dev, err, "IRQ registration failed\n"); return dev_err_probe(dev, err, "IRQ registration failed\n");
pit_config.parent = dev;
err = devm_i8254_regmap_register(dev, &pit_config);
if (err)
return err;
config.parent = dev; config.parent = dev;
config.map = map; config.map = map;
config.num_ppi = DIO48E_NUM_PPI; config.num_ppi = DIO48E_NUM_PPI;
@@ -245,3 +339,4 @@ module_isa_driver_with_irq(dio48e_driver, num_dio48e, num_irq);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(I8254);

View File

@@ -6,19 +6,16 @@
* This driver supports the following ACCES devices: 104-IDIO-16, * This driver supports the following ACCES devices: 104-IDIO-16,
* 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8. * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
*/ */
#include <linux/bitmap.h> #include <linux/bits.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/interrupt.h> #include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/isa.h> #include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/spinlock.h> #include <linux/regmap.h>
#include <linux/types.h> #include <linux/types.h>
#include "gpio-idio-16.h" #include "gpio-idio-16.h"
@@ -36,187 +33,62 @@ static unsigned int num_irq;
module_param_hw_array(irq, uint, irq, &num_irq, 0); module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers"); MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
/** static const struct regmap_range idio_16_wr_ranges[] = {
* struct idio_16_gpio - GPIO device private data structure regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x4),
* @chip: instance of the gpio_chip };
* @lock: synchronization lock to prevent I/O race conditions static const struct regmap_range idio_16_rd_ranges[] = {
* @irq_mask: I/O bits affected by interrupts regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x5),
* @reg: I/O address offset for the device registers };
* @state: ACCES IDIO-16 device state static const struct regmap_range idio_16_precious_ranges[] = {
*/ regmap_reg_range(0x2, 0x2),
struct idio_16_gpio { };
struct gpio_chip chip; static const struct regmap_access_table idio_16_wr_table = {
raw_spinlock_t lock; .yes_ranges = idio_16_wr_ranges,
unsigned long irq_mask; .n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges),
struct idio_16 __iomem *reg; };
struct idio_16_state state; static const struct regmap_access_table idio_16_rd_table = {
.yes_ranges = idio_16_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_16_rd_ranges),
};
static const struct regmap_access_table idio_16_precious_table = {
.yes_ranges = idio_16_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_16_precious_ranges),
};
static const struct regmap_config idio_16_regmap_config = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
.wr_table = &idio_16_wr_table,
.rd_table = &idio_16_rd_table,
.volatile_table = &idio_16_rd_table,
.precious_table = &idio_16_precious_table,
.cache_type = REGCACHE_FLAT,
.use_raw_spinlock = true,
}; };
static int idio_16_gpio_get_direction(struct gpio_chip *chip, /* Only input lines (GPIO 16-31) support interrupts */
unsigned int offset) #define IDIO_16_REGMAP_IRQ(_id) \
{ [16 + _id] = { \
if (idio_16_get_direction(offset)) .mask = BIT(_id), \
return GPIO_LINE_DIRECTION_IN; .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
return GPIO_LINE_DIRECTION_OUT;
}
static int idio_16_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
return 0;
}
static int idio_16_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
chip->set(chip, offset, value);
return 0;
}
static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset);
}
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
return 0;
}
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
}
static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
}
static void idio_16_irq_ack(struct irq_data *data)
{
}
static void idio_16_irq_mask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
const unsigned long offset = irqd_to_hwirq(data);
unsigned long flags;
idio16gpio->irq_mask &= ~BIT(offset);
gpiochip_disable_irq(chip, offset);
if (!idio16gpio->irq_mask) {
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
iowrite8(0, &idio16gpio->reg->irq_ctl);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
} }
}
static void idio_16_irq_unmask(struct irq_data *data) static const struct regmap_irq idio_16_regmap_irqs[] = {
{ IDIO_16_REGMAP_IRQ(0), IDIO_16_REGMAP_IRQ(1), IDIO_16_REGMAP_IRQ(2), /* 0-2 */
struct gpio_chip *chip = irq_data_get_irq_chip_data(data); IDIO_16_REGMAP_IRQ(3), IDIO_16_REGMAP_IRQ(4), IDIO_16_REGMAP_IRQ(5), /* 3-5 */
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); IDIO_16_REGMAP_IRQ(6), IDIO_16_REGMAP_IRQ(7), IDIO_16_REGMAP_IRQ(8), /* 6-8 */
const unsigned long offset = irqd_to_hwirq(data); IDIO_16_REGMAP_IRQ(9), IDIO_16_REGMAP_IRQ(10), IDIO_16_REGMAP_IRQ(11), /* 9-11 */
const unsigned long prev_irq_mask = idio16gpio->irq_mask; IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */
unsigned long flags; IDIO_16_REGMAP_IRQ(15), /* 15 */
gpiochip_enable_irq(chip, offset);
idio16gpio->irq_mask |= BIT(offset);
if (!prev_irq_mask) {
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
ioread8(&idio16gpio->reg->irq_ctl);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
}
}
static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
/* The only valid irq types are none and both-edges */
if (flow_type != IRQ_TYPE_NONE &&
(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
return 0;
}
static const struct irq_chip idio_16_irqchip = {
.name = "104-idio-16",
.irq_ack = idio_16_irq_ack,
.irq_mask = idio_16_irq_mask,
.irq_unmask = idio_16_irq_unmask,
.irq_set_type = idio_16_irq_set_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
}; };
static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
{
struct idio_16_gpio *const idio16gpio = dev_id;
struct gpio_chip *const chip = &idio16gpio->chip;
int gpio;
for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
generic_handle_domain_irq(chip->irq.domain, gpio);
raw_spin_lock(&idio16gpio->lock);
iowrite8(0, &idio16gpio->reg->in0_7);
raw_spin_unlock(&idio16gpio->lock);
return IRQ_HANDLED;
}
#define IDIO_16_NGPIO 32
static const char *idio_16_names[IDIO_16_NGPIO] = {
"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
};
static int idio_16_irq_init_hw(struct gpio_chip *gc)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
/* Disable IRQ by default */
iowrite8(0, &idio16gpio->reg->irq_ctl);
iowrite8(0, &idio16gpio->reg->in0_7);
return 0;
}
static int idio_16_probe(struct device *dev, unsigned int id) static int idio_16_probe(struct device *dev, unsigned int id)
{ {
struct idio_16_gpio *idio16gpio;
const char *const name = dev_name(dev); const char *const name = dev_name(dev);
struct gpio_irq_chip *girq; struct idio_16_regmap_config config = {};
int err; void __iomem *regs;
struct regmap *map;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
if (!idio16gpio)
return -ENOMEM;
if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) { if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
@@ -224,54 +96,22 @@ static int idio_16_probe(struct device *dev, unsigned int id)
return -EBUSY; return -EBUSY;
} }
idio16gpio->reg = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); regs = devm_ioport_map(dev, base[id], IDIO_16_EXTENT);
if (!idio16gpio->reg) if (!regs)
return -ENOMEM; return -ENOMEM;
idio16gpio->chip.label = name; map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config);
idio16gpio->chip.parent = dev; if (IS_ERR(map))
idio16gpio->chip.owner = THIS_MODULE; return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n");
idio16gpio->chip.base = -1;
idio16gpio->chip.ngpio = IDIO_16_NGPIO;
idio16gpio->chip.names = idio_16_names;
idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get;
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
idio_16_state_init(&idio16gpio->state); config.parent = dev;
/* FET off states are represented by bit values of "1" */ config.map = map;
bitmap_fill(idio16gpio->state.out_state, IDIO_16_NOUT); config.regmap_irqs = idio_16_regmap_irqs;
config.num_regmap_irqs = ARRAY_SIZE(idio_16_regmap_irqs);
config.irq = irq[id];
config.no_status = true;
girq = &idio16gpio->chip.irq; return devm_idio_16_regmap_register(dev, &config);
gpio_irq_chip_set_chip(girq, &idio_16_irqchip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_edge_irq;
girq->init_hw = idio_16_irq_init_hw;
raw_spin_lock_init(&idio16gpio->lock);
err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
return err;
}
err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name,
idio16gpio);
if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
return err;
}
return 0;
} }
static struct isa_driver idio_16_driver = { static struct isa_driver idio_16_driver = {

View File

@@ -135,8 +135,6 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev)
priv->gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags); priv->gc.ngpio = MMIO_74XX_BIT_CNT(priv->flags);
priv->gc.owner = THIS_MODULE; priv->gc.owner = THIS_MODULE;
platform_set_drvdata(pdev, priv);
return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
} }

View File

@@ -9,6 +9,7 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/mfd/altera-a10sr.h> #include <linux/mfd/altera-a10sr.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/property.h> #include <linux/property.h>
@@ -104,7 +105,7 @@ static struct platform_driver altr_a10sr_gpio_driver = {
.probe = altr_a10sr_gpio_probe, .probe = altr_a10sr_gpio_probe,
.driver = { .driver = {
.name = "altr_a10sr_gpio", .name = "altr_a10sr_gpio",
.of_match_table = of_match_ptr(altr_a10sr_gpio_of_match), .of_match_table = altr_a10sr_gpio_of_match,
}, },
}; };
module_platform_driver(altr_a10sr_gpio_driver); module_platform_driver(altr_a10sr_gpio_driver);

View File

@@ -9,8 +9,9 @@
*/ */
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/platform_data/gpio-ath79.h> #include <linux/platform_data/gpio-ath79.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/irq.h> #include <linux/irq.h>

View File

@@ -8,12 +8,14 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/of_device.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#define BCM_GPIO_PASSWD 0x00a5a501 #define BCM_GPIO_PASSWD 0x00a5a501
#define GPIO_PER_BANK 32 #define GPIO_PER_BANK 32
@@ -62,7 +64,6 @@ struct bcm_kona_gpio {
struct gpio_chip gpio_chip; struct gpio_chip gpio_chip;
struct irq_domain *irq_domain; struct irq_domain *irq_domain;
struct bcm_kona_gpio_bank *banks; struct bcm_kona_gpio_bank *banks;
struct platform_device *pdev;
}; };
struct bcm_kona_gpio_bank { struct bcm_kona_gpio_bank {
@@ -556,19 +557,12 @@ static void bcm_kona_gpio_reset(struct bcm_kona_gpio *kona_gpio)
static int bcm_kona_gpio_probe(struct platform_device *pdev) static int bcm_kona_gpio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct bcm_kona_gpio_bank *bank; struct bcm_kona_gpio_bank *bank;
struct bcm_kona_gpio *kona_gpio; struct bcm_kona_gpio *kona_gpio;
struct gpio_chip *chip; struct gpio_chip *chip;
int ret; int ret;
int i; int i;
match = of_match_device(bcm_kona_gpio_of_match, dev);
if (!match) {
dev_err(dev, "Failed to find gpio controller\n");
return -ENODEV;
}
kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL); kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL);
if (!kona_gpio) if (!kona_gpio)
return -ENOMEM; return -ENOMEM;
@@ -596,15 +590,13 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
if (!kona_gpio->banks) if (!kona_gpio->banks)
return -ENOMEM; return -ENOMEM;
kona_gpio->pdev = pdev;
platform_set_drvdata(pdev, kona_gpio);
chip->parent = dev; chip->parent = dev;
chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK; chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK;
kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node, kona_gpio->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
chip->ngpio, chip->ngpio,
&bcm_kona_irq_ops, &bcm_kona_irq_ops,
kona_gpio); kona_gpio);
if (!kona_gpio->irq_domain) { if (!kona_gpio->irq_domain) {
dev_err(dev, "Couldn't allocate IRQ domain\n"); dev_err(dev, "Couldn't allocate IRQ domain\n");
return -ENXIO; return -ENXIO;

View File

@@ -3,12 +3,12 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h>
enum gio_reg_index { enum gio_reg_index {
GIO_REG_ODEN = 0, GIO_REG_ODEN = 0,

View File

@@ -67,7 +67,7 @@ static int clps711x_gpio_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(&pdev->dev, gc, NULL); return devm_gpiochip_add_data(&pdev->dev, gc, NULL);
} }
static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = { static const struct of_device_id clps711x_gpio_ids[] = {
{ .compatible = "cirrus,ep7209-gpio" }, { .compatible = "cirrus,ep7209-gpio" },
{ } { }
}; };
@@ -76,7 +76,7 @@ MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
static struct platform_driver clps711x_gpio_driver = { static struct platform_driver clps711x_gpio_driver = {
.driver = { .driver = {
.name = "clps711x-gpio", .name = "clps711x-gpio",
.of_match_table = of_match_ptr(clps711x_gpio_ids), .of_match_table = clps711x_gpio_ids,
}, },
.probe = clps711x_gpio_probe, .probe = clps711x_gpio_probe,
}; };

View File

@@ -8,7 +8,7 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/platform_device.h>
#define MAX_GPIO 32 #define MAX_GPIO 32

View File

@@ -236,7 +236,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
for (i = 0; i < nirq; i++) { for (i = 0; i < nirq; i++) {
chips->irqs[i] = platform_get_irq(pdev, i); chips->irqs[i] = platform_get_irq(pdev, i);
if (chips->irqs[i] < 0) if (chips->irqs[i] < 0)
return dev_err_probe(dev, chips->irqs[i], "IRQ not populated\n"); return chips->irqs[i];
} }
chips->chip.label = dev_name(dev); chips->chip.label = dev_name(dev);

View File

@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023 Analog Devices, Inc.
* Driver for the DS4520 I/O Expander
*/
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/i2c.h>
#include <linux/property.h>
#include <linux/regmap.h>
#define DS4520_PULLUP0 0xF0
#define DS4520_IO_CONTROL0 0xF2
#define DS4520_IO_STATUS0 0xF8
static const struct regmap_config ds4520_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int ds4520_gpio_probe(struct i2c_client *client)
{
struct gpio_regmap_config config = { };
struct device *dev = &client->dev;
struct regmap *regmap;
u32 ngpio;
u32 base;
int ret;
ret = device_property_read_u32(dev, "reg", &base);
if (ret)
return dev_err_probe(dev, ret, "Missing 'reg' property.\n");
ret = device_property_read_u32(dev, "ngpios", &ngpio);
if (ret)
return dev_err_probe(dev, ret, "Missing 'ngpios' property.\n");
regmap = devm_regmap_init_i2c(client, &ds4520_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to allocate register map\n");
config.regmap = regmap;
config.parent = dev;
config.ngpio = ngpio;
config.reg_dat_base = base + DS4520_IO_STATUS0;
config.reg_set_base = base + DS4520_PULLUP0;
config.reg_dir_out_base = base + DS4520_IO_CONTROL0;
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
}
static const struct of_device_id ds4520_gpio_of_match_table[] = {
{ .compatible = "adi,ds4520-gpio" },
{ }
};
MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table);
static const struct i2c_device_id ds4520_gpio_id_table[] = {
{ "ds4520-gpio" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table);
static struct i2c_driver ds4520_gpio_driver = {
.driver = {
.name = "ds4520-gpio",
.of_match_table = ds4520_gpio_of_match_table,
},
.probe = ds4520_gpio_probe,
.id_table = ds4520_gpio_id_table,
};
module_i2c_driver(ds4520_gpio_driver);
MODULE_DESCRIPTION("DS4520 I/O Expander");
MODULE_AUTHOR("Okan Sahin <okan.sahin@analog.com>");
MODULE_LICENSE("GPL");

View File

@@ -9,7 +9,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@@ -653,7 +653,6 @@ static int sprd_eic_probe(struct platform_device *pdev)
return ret; return ret;
} }
platform_set_drvdata(pdev, sprd_eic);
return 0; return 0;
} }

View File

@@ -217,8 +217,6 @@ static int gpio_exar_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
platform_set_drvdata(pdev, exar_gpio);
return 0; return 0;
} }

View File

@@ -250,8 +250,8 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
return PTR_ERR(g->base); return PTR_ERR(g->base);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq <= 0) if (irq < 0)
return irq ? irq : -EINVAL; return irq;
g->clk = devm_clk_get(dev, NULL); g->clk = devm_clk_get(dev, NULL);
if (!IS_ERR(g->clk)) { if (!IS_ERR(g->clk)) {

View File

@@ -1,29 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* Driver for GE FPGA based GPIO * Driver for GE FPGA based GPIO
* *
* Author: Martyn Welch <martyn.welch@ge.com> * Author: Martyn Welch <martyn.welch@ge.com>
* *
* 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/ */
/* TODO /*
* TODO:
* *
* Configuration of output modes (totem-pole/open-drain) * Configuration of output modes (totem-pole/open-drain).
* Interrupt configuration - interrupts are always generated the FPGA relies on * Interrupt configuration - interrupts are always generated, the FPGA relies
* the I/O interrupt controllers mask to stop them propergating * on the I/O interrupt controllers mask to stop them from being propagated.
*/ */
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/module.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#define GEF_GPIO_DIRECT 0x00 #define GEF_GPIO_DIRECT 0x00
#define GEF_GPIO_IN 0x04 #define GEF_GPIO_IN 0x04
@@ -52,46 +51,39 @@ MODULE_DEVICE_TABLE(of, gef_gpio_ids);
static int __init gef_gpio_probe(struct platform_device *pdev) static int __init gef_gpio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct gpio_chip *gc; struct gpio_chip *gc;
void __iomem *regs; void __iomem *regs;
int ret; int ret;
gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc) if (!gc)
return -ENOMEM; return -ENOMEM;
regs = of_iomap(pdev->dev.of_node, 0); regs = devm_platform_ioremap_resource(pdev, 0);
if (!regs) if (IS_ERR(regs))
return -ENOMEM; return PTR_ERR(regs);
ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN, ret = bgpio_init(gc, dev, 4, regs + GEF_GPIO_IN, regs + GEF_GPIO_OUT,
regs + GEF_GPIO_OUT, NULL, NULL, NULL, NULL, regs + GEF_GPIO_DIRECT,
regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER); BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret) { if (ret)
dev_err(&pdev->dev, "bgpio_init failed\n"); return dev_err_probe(dev, ret, "bgpio_init failed\n");
goto err0;
}
/* Setup pointers to chip functions */ /* Setup pointers to chip functions */
gc->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOF", pdev->dev.of_node); gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));
if (!gc->label) { if (!gc->label)
ret = -ENOMEM; return -ENOMEM;
goto err0;
}
gc->base = -1; gc->base = -1;
gc->ngpio = (u16)(uintptr_t)of_device_get_match_data(&pdev->dev); gc->ngpio = (uintptr_t)device_get_match_data(dev);
/* This function adds a memory mapped GPIO chip */ /* This function adds a memory mapped GPIO chip */
ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL); ret = devm_gpiochip_add_data(dev, gc, NULL);
if (ret) if (ret)
goto err0; return dev_err_probe(dev, ret, "GPIO chip registration failed\n");
return 0; return 0;
err0:
iounmap(regs);
pr_err("%pOF: GPIO chip registration failed\n", pdev->dev.of_node);
return ret;
}; };
static struct platform_driver gef_gpio_driver = { static struct platform_driver gef_gpio_driver = {
@@ -103,5 +95,5 @@ static struct platform_driver gef_gpio_driver = {
module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe); module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe);
MODULE_DESCRIPTION("GE I/O FPGA GPIO driver"); MODULE_DESCRIPTION("GE I/O FPGA GPIO driver");
MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com"); MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@@ -19,10 +19,10 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>

View File

@@ -10,7 +10,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/platform_device.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@@ -3,143 +3,169 @@
* GPIO library for the ACCES IDIO-16 family * GPIO library for the ACCES IDIO-16 family
* Copyright (C) 2022 William Breathitt Gray * Copyright (C) 2022 William Breathitt Gray
*/ */
#include <linux/bitmap.h> #include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/io.h> #include <linux/gpio/regmap.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/regmap.h>
#include <linux/types.h> #include <linux/types.h>
#include "gpio-idio-16.h" #include "gpio-idio-16.h"
#define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16 #define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16
/** #define IDIO_16_DAT_BASE 0x0
* idio_16_get - get signal value at signal offset #define IDIO_16_OUT_BASE IDIO_16_DAT_BASE
* @reg: ACCES IDIO-16 device registers #define IDIO_16_IN_BASE (IDIO_16_DAT_BASE + 1)
* @state: ACCES IDIO-16 device state #define IDIO_16_CLEAR_INTERRUPT 0x1
* @offset: offset of signal to get #define IDIO_16_ENABLE_IRQ 0x2
* #define IDIO_16_DEACTIVATE_INPUT_FILTERS 0x3
* Returns the signal value (0=low, 1=high) for the signal at @offset. #define IDIO_16_DISABLE_IRQ IDIO_16_ENABLE_IRQ
*/ #define IDIO_16_INTERRUPT_STATUS 0x6
int idio_16_get(struct idio_16 __iomem *const reg,
struct idio_16_state *const state, const unsigned long offset) #define IDIO_16_NGPIO 32
#define IDIO_16_NGPIO_PER_REG 8
#define IDIO_16_REG_STRIDE 4
struct idio_16_data {
struct regmap *map;
unsigned int irq_mask;
};
static int idio_16_handle_mask_sync(const int index, const unsigned int mask_buf_def,
const unsigned int mask_buf, void *const irq_drv_data)
{ {
const unsigned long mask = BIT(offset); struct idio_16_data *const data = irq_drv_data;
const unsigned int prev_mask = data->irq_mask;
int err;
unsigned int val;
if (offset < IDIO_16_NOUT) /* exit early if no change since the previous mask */
return test_bit(offset, state->out_state); if (mask_buf == prev_mask)
return 0;
if (offset < 24) /* remember the current mask for the next mask sync */
return !!(ioread8(&reg->in0_7) & (mask >> IDIO_16_NOUT)); data->irq_mask = mask_buf;
if (offset < 32) /* if all previously masked, enable interrupts when unmasking */
return !!(ioread8(&reg->in8_15) & (mask >> 24)); if (prev_mask == mask_buf_def) {
err = regmap_write(data->map, IDIO_16_CLEAR_INTERRUPT, 0x00);
if (err)
return err;
return regmap_read(data->map, IDIO_16_ENABLE_IRQ, &val);
}
return -EINVAL; /* if all are currently masked, disable interrupts */
if (mask_buf == mask_buf_def)
return regmap_write(data->map, IDIO_16_DISABLE_IRQ, 0x00);
return 0;
} }
EXPORT_SYMBOL_GPL(idio_16_get);
static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
const unsigned int offset, unsigned int *const reg,
unsigned int *const mask)
{
unsigned int stride;
/* Input lines start at GPIO 16 */
if (offset < 16) {
stride = offset / IDIO_16_NGPIO_PER_REG;
*reg = IDIO_16_OUT_BASE + stride * IDIO_16_REG_STRIDE;
} else {
stride = (offset - 16) / IDIO_16_NGPIO_PER_REG;
*reg = IDIO_16_IN_BASE + stride * IDIO_16_REG_STRIDE;
}
*mask = BIT(offset % IDIO_16_NGPIO_PER_REG);
return 0;
}
static const char *idio_16_names[IDIO_16_NGPIO] = {
"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
};
/** /**
* idio_16_get_multiple - get multiple signal values at multiple signal offsets * devm_idio_16_regmap_register - Register an IDIO-16 GPIO device
* @reg: ACCES IDIO-16 device registers * @dev: device that is registering this IDIO-16 GPIO device
* @state: ACCES IDIO-16 device state * @config: configuration for idio_16_regmap_config
* @mask: mask of signals to get
* @bits: bitmap to store signal values
* *
* Stores in @bits the values (0=low, 1=high) for the signals defined by @mask. * Registers an IDIO-16 GPIO device. Returns 0 on success and negative error number on failure.
*/ */
void idio_16_get_multiple(struct idio_16 __iomem *const reg, int devm_idio_16_regmap_register(struct device *const dev,
struct idio_16_state *const state, const struct idio_16_regmap_config *const config)
const unsigned long *const mask,
unsigned long *const bits)
{ {
unsigned long flags; struct gpio_regmap_config gpio_config = {};
const unsigned long out_mask = GENMASK(IDIO_16_NOUT - 1, 0); int err;
struct idio_16_data *data;
struct regmap_irq_chip *chip;
struct regmap_irq_chip_data *chip_data;
spin_lock_irqsave(&state->lock, flags); if (!config->parent)
return -EINVAL;
bitmap_replace(bits, bits, state->out_state, &out_mask, IDIO_16_NOUT); if (!config->map)
if (*mask & GENMASK(23, 16)) return -EINVAL;
bitmap_set_value8(bits, ioread8(&reg->in0_7), 16);
if (*mask & GENMASK(31, 24))
bitmap_set_value8(bits, ioread8(&reg->in8_15), 24);
spin_unlock_irqrestore(&state->lock, flags); if (!config->regmap_irqs)
return -EINVAL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->map = config->map;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->name = dev_name(dev);
chip->status_base = IDIO_16_INTERRUPT_STATUS;
chip->mask_base = IDIO_16_ENABLE_IRQ;
chip->ack_base = IDIO_16_CLEAR_INTERRUPT;
chip->no_status = config->no_status;
chip->num_regs = 1;
chip->irqs = config->regmap_irqs;
chip->num_irqs = config->num_regmap_irqs;
chip->handle_mask_sync = idio_16_handle_mask_sync;
chip->irq_drv_data = data;
/* Disable IRQ to prevent spurious interrupts before we're ready */
err = regmap_write(data->map, IDIO_16_DISABLE_IRQ, 0x00);
if (err)
return err;
err = devm_regmap_add_irq_chip(dev, data->map, config->irq, 0, 0, chip, &chip_data);
if (err)
return dev_err_probe(dev, err, "IRQ registration failed\n");
if (config->filters) {
/* Deactivate input filters */
err = regmap_write(data->map, IDIO_16_DEACTIVATE_INPUT_FILTERS, 0x00);
if (err)
return err;
}
gpio_config.parent = config->parent;
gpio_config.regmap = data->map;
gpio_config.ngpio = IDIO_16_NGPIO;
gpio_config.names = idio_16_names;
gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE);
gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE);
gpio_config.ngpio_per_reg = IDIO_16_NGPIO_PER_REG;
gpio_config.reg_stride = IDIO_16_REG_STRIDE;
gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
gpio_config.reg_mask_xlate = idio_16_reg_mask_xlate;
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
} }
EXPORT_SYMBOL_GPL(idio_16_get_multiple); EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register);
/**
* idio_16_set - set signal value at signal offset
* @reg: ACCES IDIO-16 device registers
* @state: ACCES IDIO-16 device state
* @offset: offset of signal to set
* @value: value of signal to set
*
* Assigns output @value for the signal at @offset.
*/
void idio_16_set(struct idio_16 __iomem *const reg,
struct idio_16_state *const state, const unsigned long offset,
const unsigned long value)
{
unsigned long flags;
if (offset >= IDIO_16_NOUT)
return;
spin_lock_irqsave(&state->lock, flags);
__assign_bit(offset, state->out_state, value);
if (offset < 8)
iowrite8(bitmap_get_value8(state->out_state, 0), &reg->out0_7);
else
iowrite8(bitmap_get_value8(state->out_state, 8), &reg->out8_15);
spin_unlock_irqrestore(&state->lock, flags);
}
EXPORT_SYMBOL_GPL(idio_16_set);
/**
* idio_16_set_multiple - set signal values at multiple signal offsets
* @reg: ACCES IDIO-16 device registers
* @state: ACCES IDIO-16 device state
* @mask: mask of signals to set
* @bits: bitmap of signal output values
*
* Assigns output values defined by @bits for the signals defined by @mask.
*/
void idio_16_set_multiple(struct idio_16 __iomem *const reg,
struct idio_16_state *const state,
const unsigned long *const mask,
const unsigned long *const bits)
{
unsigned long flags;
spin_lock_irqsave(&state->lock, flags);
bitmap_replace(state->out_state, state->out_state, bits, mask,
IDIO_16_NOUT);
if (*mask & GENMASK(7, 0))
iowrite8(bitmap_get_value8(state->out_state, 0), &reg->out0_7);
if (*mask & GENMASK(15, 8))
iowrite8(bitmap_get_value8(state->out_state, 8), &reg->out8_15);
spin_unlock_irqrestore(&state->lock, flags);
}
EXPORT_SYMBOL_GPL(idio_16_set_multiple);
/**
* idio_16_state_init - initialize idio_16_state structure
* @state: ACCES IDIO-16 device state
*
* Initializes the ACCES IDIO-16 device @state for use in idio-16 library
* functions.
*/
void idio_16_state_init(struct idio_16_state *const state)
{
spin_lock_init(&state->lock);
}
EXPORT_SYMBOL_GPL(idio_16_state_init);
MODULE_AUTHOR("William Breathitt Gray"); MODULE_AUTHOR("William Breathitt Gray");
MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library"); MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library");

View File

@@ -3,69 +3,30 @@
#ifndef _IDIO_16_H_ #ifndef _IDIO_16_H_
#define _IDIO_16_H_ #define _IDIO_16_H_
#include <linux/spinlock.h> struct device;
#include <linux/types.h> struct regmap;
struct regmap_irq;
/** /**
* struct idio_16 - IDIO-16 registers structure * struct idio_16_regmap_config - Configuration for the IDIO-16 register map
* @out0_7: Read: FET Drive Outputs 0-7 * @parent: parent device
* Write: FET Drive Outputs 0-7 * @map: regmap for the IDIO-16 device
* @in0_7: Read: Isolated Inputs 0-7 * @regmap_irqs: descriptors for individual IRQs
* Write: Clear Interrupt * @num_regmap_irqs: number of IRQ descriptors
* @irq_ctl: Read: Enable IRQ * @irq: IRQ number for the IDIO-16 device
* Write: Disable IRQ * @no_status: device has no status register
* @filter_ctl: Read: Activate Input Filters 0-15 * @filters: device has input filters
* Write: Deactivate Input Filters 0-15
* @out8_15: Read: FET Drive Outputs 8-15
* Write: FET Drive Outputs 8-15
* @in8_15: Read: Isolated Inputs 8-15
* Write: Unused
* @irq_status: Read: Interrupt status
* Write: Unused
*/ */
struct idio_16 { struct idio_16_regmap_config {
u8 out0_7; struct device *parent;
u8 in0_7; struct regmap *map;
u8 irq_ctl; const struct regmap_irq *regmap_irqs;
u8 filter_ctl; int num_regmap_irqs;
u8 out8_15; unsigned int irq;
u8 in8_15; bool no_status;
u8 irq_status; bool filters;
}; };
#define IDIO_16_NOUT 16 int devm_idio_16_regmap_register(struct device *dev, const struct idio_16_regmap_config *config);
/**
* struct idio_16_state - IDIO-16 state structure
* @lock: synchronization lock for accessing device state
* @out_state: output signals state
*/
struct idio_16_state {
spinlock_t lock;
DECLARE_BITMAP(out_state, IDIO_16_NOUT);
};
/**
* idio_16_get_direction - get the I/O direction for a signal offset
* @offset: offset of signal to get direction
*
* Returns the signal direction (0=output, 1=input) for the signal at @offset.
*/
static inline int idio_16_get_direction(const unsigned long offset)
{
return (offset >= IDIO_16_NOUT) ? 1 : 0;
}
int idio_16_get(struct idio_16 __iomem *reg, struct idio_16_state *state,
unsigned long offset);
void idio_16_get_multiple(struct idio_16 __iomem *reg,
struct idio_16_state *state,
const unsigned long *mask, unsigned long *bits);
void idio_16_set(struct idio_16 __iomem *reg, struct idio_16_state *state,
unsigned long offset, unsigned long value);
void idio_16_set_multiple(struct idio_16 __iomem *reg,
struct idio_16_state *state,
const unsigned long *mask, const unsigned long *bits);
void idio_16_state_init(struct idio_16_state *state);
#endif /* _IDIO_16_H_ */ #endif /* _IDIO_16_H_ */

View File

@@ -6,6 +6,7 @@
* to control the PIN resources on SCU domain. * to control the PIN resources on SCU domain.
*/ */
#include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@@ -103,7 +104,7 @@ static int imx_scu_gpio_probe(struct platform_device *pdev)
gc = &priv->chip; gc = &priv->chip;
gc->base = -1; gc->base = -1;
gc->parent = dev; gc->parent = dev;
gc->ngpio = sizeof(scu_rsrc_arr)/sizeof(unsigned int); gc->ngpio = ARRAY_SIZE(scu_rsrc_arr);
gc->label = dev_name(dev); gc->label = dev_name(dev);
gc->get = imx_scu_gpio_get; gc->get = imx_scu_gpio_get;
gc->set = imx_scu_gpio_set; gc->set = imx_scu_gpio_set;

View File

@@ -302,7 +302,7 @@ static const struct of_device_id ixp4xx_gpio_of_match[] = {
static struct platform_driver ixp4xx_gpio_driver = { static struct platform_driver ixp4xx_gpio_driver = {
.driver = { .driver = {
.name = "ixp4xx-gpio", .name = "ixp4xx-gpio",
.of_match_table = of_match_ptr(ixp4xx_gpio_of_match), .of_match_table = ixp4xx_gpio_of_match,
}, },
.probe = ixp4xx_gpio_probe, .probe = ixp4xx_gpio_probe,
}; };

View File

@@ -8,7 +8,6 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
@@ -139,8 +138,6 @@ static int logicvc_gpio_probe(struct platform_device *pdev)
logicvc->chip.set = logicvc_gpio_set; logicvc->chip.set = logicvc_gpio_set;
logicvc->chip.direction_output = logicvc_gpio_direction_output; logicvc->chip.direction_output = logicvc_gpio_direction_output;
platform_set_drvdata(pdev, logicvc);
return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc);
} }

View File

@@ -199,8 +199,6 @@ static int lp3943_gpio_probe(struct platform_device *pdev)
lp3943_gpio->chip = lp3943_gpio_chip; lp3943_gpio->chip = lp3943_gpio_chip;
lp3943_gpio->chip.parent = &pdev->dev; lp3943_gpio->chip.parent = &pdev->dev;
platform_set_drvdata(pdev, lp3943_gpio);
return devm_gpiochip_add_data(&pdev->dev, &lp3943_gpio->chip, return devm_gpiochip_add_data(&pdev->dev, &lp3943_gpio->chip,
lp3943_gpio); lp3943_gpio);
} }

View File

@@ -525,17 +525,15 @@ static int lpc32xx_gpio_probe(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id lpc32xx_gpio_of_match[] = { static const struct of_device_id lpc32xx_gpio_of_match[] = {
{ .compatible = "nxp,lpc3220-gpio", }, { .compatible = "nxp,lpc3220-gpio", },
{ }, { },
}; };
#endif
static struct platform_driver lpc32xx_gpio_driver = { static struct platform_driver lpc32xx_gpio_driver = {
.driver = { .driver = {
.name = "lpc32xx-gpio", .name = "lpc32xx-gpio",
.of_match_table = of_match_ptr(lpc32xx_gpio_of_match), .of_match_table = lpc32xx_gpio_of_match,
}, },
.probe = lpc32xx_gpio_probe, .probe = lpc32xx_gpio_probe,
}; };

View File

@@ -457,7 +457,6 @@ static int __init max3191x_register_driver(struct spi_driver *sdrv)
return spi_register_driver(sdrv); return spi_register_driver(sdrv);
} }
#ifdef CONFIG_OF
static const struct of_device_id max3191x_of_id[] = { static const struct of_device_id max3191x_of_id[] = {
{ .compatible = "maxim,max31910" }, { .compatible = "maxim,max31910" },
{ .compatible = "maxim,max31911" }, { .compatible = "maxim,max31911" },
@@ -468,7 +467,6 @@ static const struct of_device_id max3191x_of_id[] = {
{ } { }
}; };
MODULE_DEVICE_TABLE(of, max3191x_of_id); MODULE_DEVICE_TABLE(of, max3191x_of_id);
#endif
static const struct spi_device_id max3191x_spi_id[] = { static const struct spi_device_id max3191x_spi_id[] = {
{ "max31910" }, { "max31910" },
@@ -484,7 +482,7 @@ MODULE_DEVICE_TABLE(spi, max3191x_spi_id);
static struct spi_driver max3191x_driver = { static struct spi_driver max3191x_driver = {
.driver = { .driver = {
.name = "max3191x", .name = "max3191x",
.of_match_table = of_match_ptr(max3191x_of_id), .of_match_table = max3191x_of_id,
}, },
.probe = max3191x_probe, .probe = max3191x_probe,
.remove = max3191x_remove, .remove = max3191x_remove,

View File

@@ -18,8 +18,6 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/max732x.h> #include <linux/platform_data/max732x.h>
#include <linux/of.h>
/* /*
* Each port of MAX732x (including MAX7319) falls into one of the * Each port of MAX732x (including MAX7319) falls into one of the
@@ -114,7 +112,6 @@ static const struct i2c_device_id max732x_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, max732x_id); MODULE_DEVICE_TABLE(i2c, max732x_id);
#ifdef CONFIG_OF
static const struct of_device_id max732x_of_table[] = { static const struct of_device_id max732x_of_table[] = {
{ .compatible = "maxim,max7319" }, { .compatible = "maxim,max7319" },
{ .compatible = "maxim,max7320" }, { .compatible = "maxim,max7320" },
@@ -128,7 +125,6 @@ static const struct of_device_id max732x_of_table[] = {
{ } { }
}; };
MODULE_DEVICE_TABLE(of, max732x_of_table); MODULE_DEVICE_TABLE(of, max732x_of_table);
#endif
struct max732x_chip { struct max732x_chip {
struct gpio_chip gpio_chip; struct gpio_chip gpio_chip;
@@ -709,7 +705,7 @@ static int max732x_probe(struct i2c_client *client)
static struct i2c_driver max732x_driver = { static struct i2c_driver max732x_driver = {
.driver = { .driver = {
.name = "max732x", .name = "max732x",
.of_match_table = of_match_ptr(max732x_of_table), .of_match_table = max732x_of_table,
}, },
.probe = max732x_probe, .probe = max732x_probe,
.id_table = max732x_id, .id_table = max732x_id,

View File

@@ -331,8 +331,6 @@ static int max77620_gpio_probe(struct platform_device *pdev)
girq->init_hw = max77620_gpio_irq_init_hw; girq->init_hw = max77620_gpio_irq_init_hw;
girq->threaded = true; girq->threaded = true;
platform_set_drvdata(pdev, mgpio);
ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n"); dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n");

View File

@@ -10,11 +10,11 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/of_device.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
/* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */ /* Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES */
#include <linux/bitfield.h> #include <linux/bitfield.h>
@@ -19,6 +19,8 @@
* gpio[1]: HOST_GPIO32->HOST_GPIO55 * gpio[1]: HOST_GPIO32->HOST_GPIO55
*/ */
#define MLXBF3_GPIO_MAX_PINS_PER_BLOCK 32 #define MLXBF3_GPIO_MAX_PINS_PER_BLOCK 32
#define MLXBF3_GPIO_MAX_PINS_BLOCK0 32
#define MLXBF3_GPIO_MAX_PINS_BLOCK1 24
/* /*
* fw_gpio[x] block registers and their offset * fw_gpio[x] block registers and their offset
@@ -158,6 +160,26 @@ static const struct irq_chip gpio_mlxbf3_irqchip = {
GPIOCHIP_IRQ_RESOURCE_HELPERS, GPIOCHIP_IRQ_RESOURCE_HELPERS,
}; };
static int mlxbf3_gpio_add_pin_ranges(struct gpio_chip *chip)
{
unsigned int id;
switch(chip->ngpio) {
case MLXBF3_GPIO_MAX_PINS_BLOCK0:
id = 0;
break;
case MLXBF3_GPIO_MAX_PINS_BLOCK1:
id = 1;
break;
default:
return -EINVAL;
}
return gpiochip_add_pin_range(chip, "MLNXBF34:00",
chip->base, id * MLXBF3_GPIO_MAX_PINS_PER_BLOCK,
chip->ngpio);
}
static int mlxbf3_gpio_probe(struct platform_device *pdev) static int mlxbf3_gpio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@@ -197,6 +219,7 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
gc->request = gpiochip_generic_request; gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free; gc->free = gpiochip_generic_free;
gc->owner = THIS_MODULE; gc->owner = THIS_MODULE;
gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq >= 0) { if (irq >= 0) {
@@ -243,6 +266,7 @@ static struct platform_driver mlxbf3_gpio_driver = {
}; };
module_platform_driver(mlxbf3_gpio_driver); module_platform_driver(mlxbf3_gpio_driver);
MODULE_SOFTDEP("pre: pinctrl-mlxbf3");
MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver"); MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver");
MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>"); MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
MODULE_LICENSE("Dual BSD/GPL"); MODULE_LICENSE("Dual BSD/GPL");

View File

@@ -60,6 +60,8 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include "gpiolib.h"
static void bgpio_write8(void __iomem *reg, unsigned long data) static void bgpio_write8(void __iomem *reg, unsigned long data)
{ {
writeb(data, reg); writeb(data, reg);
@@ -614,10 +616,15 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
gc->parent = dev; gc->parent = dev;
gc->label = dev_name(dev); gc->label = dev_name(dev);
gc->base = -1; gc->base = -1;
gc->ngpio = gc->bgpio_bits;
gc->request = bgpio_request; gc->request = bgpio_request;
gc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN); gc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN);
ret = gpiochip_get_ngpios(gc, dev);
if (ret)
gc->ngpio = gc->bgpio_bits;
else
gc->bgpio_bits = roundup_pow_of_two(round_up(gc->ngpio, 8));
ret = bgpio_setup_io(gc, dat, set, clr, flags); ret = bgpio_setup_io(gc, dat, set, clr, flags);
if (ret) if (ret)
return ret; return ret;

View File

@@ -10,7 +10,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/gpio/legacy-of-mm-gpiochip.h> #include <linux/gpio/legacy-of-mm-gpiochip.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_platform.h> #include <linux/platform_device.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/mpc52xx.h> #include <asm/mpc52xx.h>

View File

@@ -9,12 +9,10 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@@ -6,7 +6,6 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/module.h> #include <linux/module.h>

View File

@@ -17,12 +17,12 @@
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/bug.h> #include <linux/bug.h>
#define IMX_SCU_WAKEUP_OFF 0 #define IMX_SCU_WAKEUP_OFF 0
@@ -62,6 +62,7 @@ struct mxc_gpio_port {
struct clk *clk; struct clk *clk;
int irq; int irq;
int irq_high; int irq_high;
void (*mx_irq_handler)(struct irq_desc *desc);
struct irq_domain *domain; struct irq_domain *domain;
struct gpio_chip gc; struct gpio_chip gc;
struct device *dev; struct device *dev;
@@ -382,6 +383,41 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
return irq_find_mapping(port->domain, offset); return irq_find_mapping(port->domain, offset);
} }
static int mxc_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
int ret;
ret = gpiochip_generic_request(chip, offset);
if (ret)
return ret;
return pm_runtime_resume_and_get(chip->parent);
}
static void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset)
{
gpiochip_generic_free(chip, offset);
pm_runtime_put(chip->parent);
}
static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enable)
{
if (enable)
irq_set_chained_handler_and_data(port->irq, port->mx_irq_handler, port);
else
irq_set_chained_handler_and_data(port->irq, NULL, NULL);
/* setup handler for GPIO 16 to 31 */
if (port->irq_high > 0) {
if (enable)
irq_set_chained_handler_and_data(port->irq_high,
port->mx_irq_handler,
port);
else
irq_set_chained_handler_and_data(port->irq_high, NULL, NULL);
}
}
static int mxc_gpio_probe(struct platform_device *pdev) static int mxc_gpio_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
@@ -416,19 +452,17 @@ static int mxc_gpio_probe(struct platform_device *pdev)
return port->irq; return port->irq;
/* the controller clock is optional */ /* the controller clock is optional */
port->clk = devm_clk_get_optional(&pdev->dev, NULL); port->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
if (IS_ERR(port->clk)) if (IS_ERR(port->clk))
return PTR_ERR(port->clk); return PTR_ERR(port->clk);
err = clk_prepare_enable(port->clk);
if (err) {
dev_err(&pdev->dev, "Unable to enable clock.\n");
return err;
}
if (of_device_is_compatible(np, "fsl,imx7d-gpio")) if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
port->power_off = true; port->power_off = true;
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
/* disable the interrupt and clear the status */ /* disable the interrupt and clear the status */
writel(0, port->base + GPIO_IMR); writel(0, port->base + GPIO_IMR);
writel(~0, port->base + GPIO_ISR); writel(~0, port->base + GPIO_ISR);
@@ -439,18 +473,12 @@ static int mxc_gpio_probe(struct platform_device *pdev)
* the handler is needed only once, but doing it for every port * the handler is needed only once, but doing it for every port
* is more robust and easier. * is more robust and easier.
*/ */
irq_set_chained_handler(port->irq, mx2_gpio_irq_handler); port->irq_high = -1;
} else { port->mx_irq_handler = mx2_gpio_irq_handler;
/* setup one handler for each entry */ } else
irq_set_chained_handler_and_data(port->irq, port->mx_irq_handler = mx3_gpio_irq_handler;
mx3_gpio_irq_handler, port);
if (port->irq_high > 0)
/* setup handler for GPIO 16 to 31 */
irq_set_chained_handler_and_data(port->irq_high,
mx3_gpio_irq_handler,
port);
}
mxc_update_irq_chained_handler(port, true);
err = bgpio_init(&port->gc, &pdev->dev, 4, err = bgpio_init(&port->gc, &pdev->dev, 4,
port->base + GPIO_PSR, port->base + GPIO_PSR,
port->base + GPIO_DR, NULL, port->base + GPIO_DR, NULL,
@@ -459,8 +487,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
if (err) if (err)
goto out_bgio; goto out_bgio;
port->gc.request = gpiochip_generic_request; port->gc.request = mxc_gpio_request;
port->gc.free = gpiochip_generic_free; port->gc.free = mxc_gpio_free;
port->gc.to_irq = mxc_gpio_to_irq; port->gc.to_irq = mxc_gpio_to_irq;
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 : port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32; pdev->id * 32;
@@ -482,6 +510,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
goto out_bgio; goto out_bgio;
} }
irq_domain_set_pm_device(port->domain, &pdev->dev);
/* gpio-mxc can be a generic irq chip */ /* gpio-mxc can be a generic irq chip */
err = mxc_gpio_init_gc(port, irq_base); err = mxc_gpio_init_gc(port, irq_base);
if (err < 0) if (err < 0)
@@ -490,13 +520,15 @@ static int mxc_gpio_probe(struct platform_device *pdev)
list_add_tail(&port->node, &mxc_gpio_ports); list_add_tail(&port->node, &mxc_gpio_ports);
platform_set_drvdata(pdev, port); platform_set_drvdata(pdev, port);
pm_runtime_put_autosuspend(&pdev->dev);
return 0; return 0;
out_irqdomain_remove: out_irqdomain_remove:
irq_domain_remove(port->domain); irq_domain_remove(port->domain);
out_bgio: out_bgio:
clk_disable_unprepare(port->clk); pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err); dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
return err; return err;
} }
@@ -572,7 +604,35 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
return ret; return ret;
} }
static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) static int mxc_gpio_runtime_suspend(struct device *dev)
{
struct mxc_gpio_port *port = dev_get_drvdata(dev);
mxc_gpio_save_regs(port);
clk_disable_unprepare(port->clk);
mxc_update_irq_chained_handler(port, false);
return 0;
}
static int mxc_gpio_runtime_resume(struct device *dev)
{
struct mxc_gpio_port *port = dev_get_drvdata(dev);
int ret;
mxc_update_irq_chained_handler(port, true);
ret = clk_prepare_enable(port->clk);
if (ret) {
mxc_update_irq_chained_handler(port, false);
return ret;
}
mxc_gpio_restore_regs(port);
return 0;
}
static int mxc_gpio_noirq_suspend(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct mxc_gpio_port *port = platform_get_drvdata(pdev); struct mxc_gpio_port *port = platform_get_drvdata(pdev);
@@ -583,7 +643,7 @@ static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
return 0; return 0;
} }
static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) static int mxc_gpio_noirq_resume(struct device *dev)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct mxc_gpio_port *port = platform_get_drvdata(pdev); struct mxc_gpio_port *port = platform_get_drvdata(pdev);
@@ -596,15 +656,20 @@ static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
} }
static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL)
}; };
static int mxc_gpio_syscore_suspend(void) static int mxc_gpio_syscore_suspend(void)
{ {
struct mxc_gpio_port *port; struct mxc_gpio_port *port;
int ret;
/* walk through all ports */ /* walk through all ports */
list_for_each_entry(port, &mxc_gpio_ports, node) { list_for_each_entry(port, &mxc_gpio_ports, node) {
ret = clk_prepare_enable(port->clk);
if (ret)
return ret;
mxc_gpio_save_regs(port); mxc_gpio_save_regs(port);
clk_disable_unprepare(port->clk); clk_disable_unprepare(port->clk);
} }
@@ -625,6 +690,7 @@ static void mxc_gpio_syscore_resume(void)
return; return;
} }
mxc_gpio_restore_regs(port); mxc_gpio_restore_regs(port);
clk_disable_unprepare(port->clk);
} }
} }
@@ -638,7 +704,7 @@ static struct platform_driver mxc_gpio_driver = {
.name = "gpio-mxc", .name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids, .of_match_table = mxc_gpio_dt_ids,
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
.pm = &mxc_gpio_dev_pm_ops, .pm = pm_ptr(&mxc_gpio_dev_pm_ops),
}, },
.probe = mxc_gpio_probe, .probe = mxc_gpio_probe,
}; };

View File

@@ -14,7 +14,6 @@
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
@@ -272,7 +271,7 @@ static int mxs_gpio_probe(struct platform_device *pdev)
port->id = of_alias_get_id(np, "gpio"); port->id = of_alias_get_id(np, "gpio");
if (port->id < 0) if (port->id < 0)
return port->id; return port->id;
port->devid = (enum mxs_gpio_id)of_device_get_match_data(&pdev->dev); port->devid = (uintptr_t)of_device_get_match_data(&pdev->dev);
port->dev = &pdev->dev; port->dev = &pdev->dev;
port->irq = platform_get_irq(pdev, 0); port->irq = platform_get_irq(pdev, 0);
if (port->irq < 0) if (port->irq < 0)

View File

@@ -22,7 +22,6 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/platform_data/gpio-omap.h> #include <linux/platform_data/gpio-omap.h>
@@ -1413,11 +1412,8 @@ static int omap_gpio_probe(struct platform_device *pdev)
bank->dev = dev; bank->dev = dev;
bank->irq = platform_get_irq(pdev, 0); bank->irq = platform_get_irq(pdev, 0);
if (bank->irq <= 0) { if (bank->irq < 0)
if (!bank->irq) return bank->irq;
bank->irq = -ENXIO;
return dev_err_probe(dev, bank->irq, "can't get irq resource\n");
}
bank->chip.parent = dev; bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE; bank->chip.owner = THIS_MODULE;

View File

@@ -12,7 +12,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/mfd/palmas.h> #include <linux/mfd/palmas.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
struct palmas_gpio { struct palmas_gpio {
@@ -184,7 +183,6 @@ static int palmas_gpio_probe(struct platform_device *pdev)
return ret; return ret;
} }
platform_set_drvdata(pdev, palmas_gpio);
return ret; return ret;
} }

View File

@@ -108,6 +108,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "tca6408", 8 | PCA953X_TYPE | PCA_INT, }, { "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, }, { "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca6424", 24 | PCA953X_TYPE | PCA_INT, }, { "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
{ "tca9538", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca9539", 16 | PCA953X_TYPE | PCA_INT, }, { "tca9539", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca9554", 8 | PCA953X_TYPE | PCA_INT, }, { "tca9554", 8 | PCA953X_TYPE | PCA_INT, },
{ "xra1202", 8 | PCA953X_TYPE }, { "xra1202", 8 | PCA953X_TYPE },
@@ -1051,7 +1052,6 @@ out:
static int pca953x_probe(struct i2c_client *client) static int pca953x_probe(struct i2c_client *client)
{ {
const struct i2c_device_id *i2c_id = i2c_client_get_device_id(client);
struct pca953x_platform_data *pdata; struct pca953x_platform_data *pdata;
struct pca953x_chip *chip; struct pca953x_chip *chip;
int irq_base = 0; int irq_base = 0;
@@ -1090,6 +1090,9 @@ static int pca953x_probe(struct i2c_client *client)
} }
chip->client = client; chip->client = client;
chip->driver_data = (uintptr_t)i2c_get_match_data(client);
if (!chip->driver_data)
return -ENODEV;
reg = devm_regulator_get(&client->dev, "vcc"); reg = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(reg)) if (IS_ERR(reg))
@@ -1102,20 +1105,6 @@ static int pca953x_probe(struct i2c_client *client)
} }
chip->regulator = reg; chip->regulator = reg;
if (i2c_id) {
chip->driver_data = i2c_id->driver_data;
} else {
const void *match;
match = device_get_match_data(&client->dev);
if (!match) {
ret = -ENODEV;
goto err_exit;
}
chip->driver_data = (uintptr_t)match;
}
i2c_set_clientdata(client, chip); i2c_set_clientdata(client, chip);
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK); pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
@@ -1354,6 +1343,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), }, { .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), }, { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
{ .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), }, { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
{ .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), }, { .compatible = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },

View File

@@ -30,7 +30,7 @@ struct pca9570_chip_data {
/** /**
* struct pca9570 - GPIO driver data * struct pca9570 - GPIO driver data
* @chip: GPIO controller chip * @chip: GPIO controller chip
* @p_data: GPIO controller platform data * @chip_data: GPIO controller platform data
* @lock: Protects write sequences * @lock: Protects write sequences
* @out: Buffer for device register * @out: Buffer for device register
*/ */

View File

@@ -36,19 +36,19 @@ static const struct i2c_device_id pcf857x_id[] = {
MODULE_DEVICE_TABLE(i2c, pcf857x_id); MODULE_DEVICE_TABLE(i2c, pcf857x_id);
static const struct of_device_id pcf857x_of_table[] = { static const struct of_device_id pcf857x_of_table[] = {
{ .compatible = "nxp,pcf8574" }, { .compatible = "nxp,pcf8574", (void *)8 },
{ .compatible = "nxp,pcf8574a" }, { .compatible = "nxp,pcf8574a", (void *)8 },
{ .compatible = "nxp,pca8574" }, { .compatible = "nxp,pca8574", (void *)8 },
{ .compatible = "nxp,pca9670" }, { .compatible = "nxp,pca9670", (void *)8 },
{ .compatible = "nxp,pca9672" }, { .compatible = "nxp,pca9672", (void *)8 },
{ .compatible = "nxp,pca9674" }, { .compatible = "nxp,pca9674", (void *)8 },
{ .compatible = "nxp,pcf8575" }, { .compatible = "nxp,pcf8575", (void *)16 },
{ .compatible = "nxp,pca8575" }, { .compatible = "nxp,pca8575", (void *)16 },
{ .compatible = "nxp,pca9671" }, { .compatible = "nxp,pca9671", (void *)16 },
{ .compatible = "nxp,pca9673" }, { .compatible = "nxp,pca9673", (void *)16 },
{ .compatible = "nxp,pca9675" }, { .compatible = "nxp,pca9675", (void *)16 },
{ .compatible = "maxim,max7328" }, { .compatible = "maxim,max7328", (void *)8 },
{ .compatible = "maxim,max7329" }, { .compatible = "maxim,max7329", (void *)8 },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, pcf857x_of_table); MODULE_DEVICE_TABLE(of, pcf857x_of_table);
@@ -272,7 +272,6 @@ static const struct irq_chip pcf857x_irq_chip = {
static int pcf857x_probe(struct i2c_client *client) static int pcf857x_probe(struct i2c_client *client)
{ {
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct pcf857x *gpio; struct pcf857x *gpio;
unsigned int n_latch = 0; unsigned int n_latch = 0;
int status; int status;
@@ -296,7 +295,7 @@ static int pcf857x_probe(struct i2c_client *client)
gpio->chip.set_multiple = pcf857x_set_multiple; gpio->chip.set_multiple = pcf857x_set_multiple;
gpio->chip.direction_input = pcf857x_input; gpio->chip.direction_input = pcf857x_input;
gpio->chip.direction_output = pcf857x_output; gpio->chip.direction_output = pcf857x_output;
gpio->chip.ngpio = id->driver_data; gpio->chip.ngpio = (uintptr_t)i2c_get_match_data(client);
/* NOTE: the OnSemi jlc1562b is also largely compatible with /* NOTE: the OnSemi jlc1562b is also largely compatible with
* these parts, notably for output. It has a low-resolution * these parts, notably for output. It has a low-resolution

View File

@@ -5,214 +5,75 @@
*/ */
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/err.h>
#include <linux/gpio/driver.h> #include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdesc.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/spinlock.h> #include <linux/regmap.h>
#include <linux/types.h> #include <linux/types.h>
#include "gpio-idio-16.h" #include "gpio-idio-16.h"
/** static const struct regmap_range idio_16_wr_ranges[] = {
* struct idio_16_gpio - GPIO device private data structure regmap_reg_range(0x0, 0x2), regmap_reg_range(0x3, 0x4),
* @chip: instance of the gpio_chip };
* @lock: synchronization lock to prevent I/O race conditions static const struct regmap_range idio_16_rd_ranges[] = {
* @reg: I/O address offset for the GPIO device registers regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x6),
* @state: ACCES IDIO-16 device state };
* @irq_mask: I/O bits affected by interrupts static const struct regmap_range idio_16_precious_ranges[] = {
*/ regmap_reg_range(0x2, 0x2),
struct idio_16_gpio { };
struct gpio_chip chip; static const struct regmap_access_table idio_16_wr_table = {
raw_spinlock_t lock; .yes_ranges = idio_16_wr_ranges,
struct idio_16 __iomem *reg; .n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges),
struct idio_16_state state; };
unsigned long irq_mask; static const struct regmap_access_table idio_16_rd_table = {
.yes_ranges = idio_16_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_16_rd_ranges),
};
static const struct regmap_access_table idio_16_precious_table = {
.yes_ranges = idio_16_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_16_precious_ranges),
};
static const struct regmap_config idio_16_regmap_config = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
.wr_table = &idio_16_wr_table,
.rd_table = &idio_16_rd_table,
.volatile_table = &idio_16_rd_table,
.precious_table = &idio_16_precious_table,
.cache_type = REGCACHE_FLAT,
.use_raw_spinlock = true,
}; };
static int idio_16_gpio_get_direction(struct gpio_chip *chip, /* Only input lines (GPIO 16-31) support interrupts */
unsigned int offset) #define IDIO_16_REGMAP_IRQ(_id) \
{ [16 + _id] = { \
if (idio_16_get_direction(offset)) .mask = BIT(2), \
return GPIO_LINE_DIRECTION_IN; .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
return GPIO_LINE_DIRECTION_OUT;
}
static int idio_16_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
return 0;
}
static int idio_16_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
chip->set(chip, offset, value);
return 0;
}
static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset);
}
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
return 0;
}
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
}
static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
}
static void idio_16_irq_ack(struct irq_data *data)
{
}
static void idio_16_irq_mask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
const unsigned long mask = BIT(irqd_to_hwirq(data));
unsigned long flags;
idio16gpio->irq_mask &= ~mask;
if (!idio16gpio->irq_mask) {
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
iowrite8(0, &idio16gpio->reg->irq_ctl);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
} }
gpiochip_disable_irq(chip, irqd_to_hwirq(data)); static const struct regmap_irq idio_16_regmap_irqs[] = {
} IDIO_16_REGMAP_IRQ(0), IDIO_16_REGMAP_IRQ(1), IDIO_16_REGMAP_IRQ(2), /* 0-2 */
IDIO_16_REGMAP_IRQ(3), IDIO_16_REGMAP_IRQ(4), IDIO_16_REGMAP_IRQ(5), /* 3-5 */
static void idio_16_irq_unmask(struct irq_data *data) IDIO_16_REGMAP_IRQ(6), IDIO_16_REGMAP_IRQ(7), IDIO_16_REGMAP_IRQ(8), /* 6-8 */
{ IDIO_16_REGMAP_IRQ(9), IDIO_16_REGMAP_IRQ(10), IDIO_16_REGMAP_IRQ(11), /* 9-11 */
struct gpio_chip *chip = irq_data_get_irq_chip_data(data); IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); IDIO_16_REGMAP_IRQ(15), /* 15 */
const unsigned long mask = BIT(irqd_to_hwirq(data));
const unsigned long prev_irq_mask = idio16gpio->irq_mask;
unsigned long flags;
gpiochip_enable_irq(chip, irqd_to_hwirq(data));
idio16gpio->irq_mask |= mask;
if (!prev_irq_mask) {
raw_spin_lock_irqsave(&idio16gpio->lock, flags);
ioread8(&idio16gpio->reg->irq_ctl);
raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
}
}
static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
/* The only valid irq types are none and both-edges */
if (flow_type != IRQ_TYPE_NONE &&
(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
return 0;
}
static const struct irq_chip idio_16_irqchip = {
.name = "pci-idio-16",
.irq_ack = idio_16_irq_ack,
.irq_mask = idio_16_irq_mask,
.irq_unmask = idio_16_irq_unmask,
.irq_set_type = idio_16_irq_set_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
}; };
static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
{
struct idio_16_gpio *const idio16gpio = dev_id;
unsigned int irq_status;
struct gpio_chip *const chip = &idio16gpio->chip;
int gpio;
raw_spin_lock(&idio16gpio->lock);
irq_status = ioread8(&idio16gpio->reg->irq_status);
raw_spin_unlock(&idio16gpio->lock);
/* Make sure our device generated IRQ */
if (!(irq_status & 0x3) || !(irq_status & 0x4))
return IRQ_NONE;
for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
generic_handle_domain_irq(chip->irq.domain, gpio);
raw_spin_lock(&idio16gpio->lock);
/* Clear interrupt */
iowrite8(0, &idio16gpio->reg->in0_7);
raw_spin_unlock(&idio16gpio->lock);
return IRQ_HANDLED;
}
#define IDIO_16_NGPIO 32
static const char *idio_16_names[IDIO_16_NGPIO] = {
"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
};
static int idio_16_irq_init_hw(struct gpio_chip *gc)
{
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
/* Disable IRQ by default and clear any pending interrupt */
iowrite8(0, &idio16gpio->reg->irq_ctl);
iowrite8(0, &idio16gpio->reg->in0_7);
return 0;
}
static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct device *const dev = &pdev->dev; struct device *const dev = &pdev->dev;
struct idio_16_gpio *idio16gpio;
int err; int err;
const size_t pci_bar_index = 2; const size_t pci_bar_index = 2;
const char *const name = pci_name(pdev); const char *const name = pci_name(pdev);
struct gpio_irq_chip *girq; struct idio_16_regmap_config config = {};
void __iomem *regs;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); struct regmap *map;
if (!idio16gpio)
return -ENOMEM;
err = pcim_enable_device(pdev); err = pcim_enable_device(pdev);
if (err) { if (err) {
@@ -226,53 +87,20 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err; return err;
} }
idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; regs = pcim_iomap_table(pdev)[pci_bar_index];
/* Deactivate input filters */ map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config);
iowrite8(0, &idio16gpio->reg->filter_ctl); if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n");
idio16gpio->chip.label = name; config.parent = dev;
idio16gpio->chip.parent = dev; config.map = map;
idio16gpio->chip.owner = THIS_MODULE; config.regmap_irqs = idio_16_regmap_irqs;
idio16gpio->chip.base = -1; config.num_regmap_irqs = ARRAY_SIZE(idio_16_regmap_irqs);
idio16gpio->chip.ngpio = IDIO_16_NGPIO; config.irq = pdev->irq;
idio16gpio->chip.names = idio_16_names; config.filters = true;
idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
idio16gpio->chip.get = idio_16_gpio_get;
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
idio16gpio->chip.set = idio_16_gpio_set;
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
idio_16_state_init(&idio16gpio->state); return devm_idio_16_regmap_register(dev, &config);
girq = &idio16gpio->chip.irq;
gpio_irq_chip_set_chip(girq, &idio_16_irqchip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_edge_irq;
girq->init_hw = idio_16_irq_init_hw;
raw_spin_lock_init(&idio16gpio->lock);
err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
return err;
}
err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
name, idio16gpio);
if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
return err;
}
return 0;
} }
static const struct pci_device_id idio_16_pci_dev_id[] = { static const struct pci_device_id idio_16_pci_dev_id[] = {

View File

@@ -6,16 +6,15 @@
* This driver supports the following ACCES devices: PCIe-IDIO-24, * This driver supports the following ACCES devices: PCIe-IDIO-24,
* PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12. * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
*/ */
#include <linux/bitmap.h> #include <linux/bits.h>
#include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/err.h>
#include <linux/gpio/driver.h> #include <linux/gpio/regmap.h>
#include <linux/interrupt.h> #include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/regmap.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
@@ -59,422 +58,224 @@
#define PLX_PEX8311_PCI_LCS_INTCSR 0x68 #define PLX_PEX8311_PCI_LCS_INTCSR 0x68
#define INTCSR_INTERNAL_PCI_WIRE BIT(8) #define INTCSR_INTERNAL_PCI_WIRE BIT(8)
#define INTCSR_LOCAL_INPUT BIT(11) #define INTCSR_LOCAL_INPUT BIT(11)
#define IDIO_24_ENABLE_IRQ (INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT)
/** #define IDIO_24_OUT_BASE 0x0
* struct idio_24_gpio_reg - GPIO device registers structure #define IDIO_24_TTLCMOS_OUT_REG 0x3
* @out0_7: Read: FET Outputs 0-7 #define IDIO_24_IN_BASE 0x4
* Write: FET Outputs 0-7 #define IDIO_24_TTLCMOS_IN_REG 0x7
* @out8_15: Read: FET Outputs 8-15 #define IDIO_24_COS_STATUS_BASE 0x8
* Write: FET Outputs 8-15 #define IDIO_24_CONTROL_REG 0xC
* @out16_23: Read: FET Outputs 16-23 #define IDIO_24_COS_ENABLE 0xE
* Write: FET Outputs 16-23 #define IDIO_24_SOFT_RESET 0xF
* @ttl_out0_7: Read: TTL/CMOS Outputs 0-7
* Write: TTL/CMOS Outputs 0-7 #define CONTROL_REG_OUT_MODE BIT(1)
* @in0_7: Read: Isolated Inputs 0-7
* Write: Reserved #define COS_ENABLE_RISING BIT(1)
* @in8_15: Read: Isolated Inputs 8-15 #define COS_ENABLE_FALLING BIT(4)
* Write: Reserved #define COS_ENABLE_BOTH (COS_ENABLE_RISING | COS_ENABLE_FALLING)
* @in16_23: Read: Isolated Inputs 16-23
* Write: Reserved static const struct regmap_config pex8311_intcsr_regmap_config = {
* @ttl_in0_7: Read: TTL/CMOS Inputs 0-7 .name = "pex8311_intcsr",
* Write: Reserved .reg_bits = 32,
* @cos0_7: Read: COS Status Inputs 0-7 .reg_stride = 1,
* Write: COS Clear Inputs 0-7 .reg_base = PLX_PEX8311_PCI_LCS_INTCSR,
* @cos8_15: Read: COS Status Inputs 8-15 .val_bits = 32,
* Write: COS Clear Inputs 8-15 .io_port = true,
* @cos16_23: Read: COS Status Inputs 16-23 };
* Write: COS Clear Inputs 16-23
* @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7 static const struct regmap_range idio_24_wr_ranges[] = {
* Write: COS Clear TTL/CMOS 0-7 regmap_reg_range(0x0, 0x3), regmap_reg_range(0x8, 0xC),
* @ctl: Read: Control Register regmap_reg_range(0xE, 0xF),
* Write: Control Register };
* @reserved: Read: Reserved static const struct regmap_range idio_24_rd_ranges[] = {
* Write: Reserved regmap_reg_range(0x0, 0xC), regmap_reg_range(0xE, 0xF),
* @cos_enable: Read: COS Enable };
* Write: COS Enable static const struct regmap_range idio_24_volatile_ranges[] = {
* @soft_reset: Read: IRQ Output Pin Status regmap_reg_range(0x4, 0xB), regmap_reg_range(0xF, 0xF),
* Write: Software Board Reset };
*/ static const struct regmap_access_table idio_24_wr_table = {
struct idio_24_gpio_reg { .yes_ranges = idio_24_wr_ranges,
u8 out0_7; .n_yes_ranges = ARRAY_SIZE(idio_24_wr_ranges),
u8 out8_15; };
u8 out16_23; static const struct regmap_access_table idio_24_rd_table = {
u8 ttl_out0_7; .yes_ranges = idio_24_rd_ranges,
u8 in0_7; .n_yes_ranges = ARRAY_SIZE(idio_24_rd_ranges),
u8 in8_15; };
u8 in16_23; static const struct regmap_access_table idio_24_volatile_table = {
u8 ttl_in0_7; .yes_ranges = idio_24_volatile_ranges,
u8 cos0_7; .n_yes_ranges = ARRAY_SIZE(idio_24_volatile_ranges),
u8 cos8_15; };
u8 cos16_23;
u8 cos_ttl0_7; static const struct regmap_config idio_24_regmap_config = {
u8 ctl; .reg_bits = 8,
u8 reserved; .reg_stride = 1,
u8 cos_enable; .val_bits = 8,
u8 soft_reset; .io_port = true,
.wr_table = &idio_24_wr_table,
.rd_table = &idio_24_rd_table,
.volatile_table = &idio_24_volatile_table,
.cache_type = REGCACHE_FLAT,
.use_raw_spinlock = true,
};
#define IDIO_24_NGPIO_PER_REG 8
#define IDIO_24_REGMAP_IRQ(_id) \
[24 + _id] = { \
.reg_offset = (_id) / IDIO_24_NGPIO_PER_REG, \
.mask = BIT((_id) % IDIO_24_NGPIO_PER_REG), \
.type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
}
#define IDIO_24_IIN_IRQ(_id) IDIO_24_REGMAP_IRQ(_id)
#define IDIO_24_TTL_IRQ(_id) IDIO_24_REGMAP_IRQ(24 + _id)
static const struct regmap_irq idio_24_regmap_irqs[] = {
IDIO_24_IIN_IRQ(0), IDIO_24_IIN_IRQ(1), IDIO_24_IIN_IRQ(2), /* IIN 0-2 */
IDIO_24_IIN_IRQ(3), IDIO_24_IIN_IRQ(4), IDIO_24_IIN_IRQ(5), /* IIN 3-5 */
IDIO_24_IIN_IRQ(6), IDIO_24_IIN_IRQ(7), IDIO_24_IIN_IRQ(8), /* IIN 6-8 */
IDIO_24_IIN_IRQ(9), IDIO_24_IIN_IRQ(10), IDIO_24_IIN_IRQ(11), /* IIN 9-11 */
IDIO_24_IIN_IRQ(12), IDIO_24_IIN_IRQ(13), IDIO_24_IIN_IRQ(14), /* IIN 12-14 */
IDIO_24_IIN_IRQ(15), IDIO_24_IIN_IRQ(16), IDIO_24_IIN_IRQ(17), /* IIN 15-17 */
IDIO_24_IIN_IRQ(18), IDIO_24_IIN_IRQ(19), IDIO_24_IIN_IRQ(20), /* IIN 18-20 */
IDIO_24_IIN_IRQ(21), IDIO_24_IIN_IRQ(22), IDIO_24_IIN_IRQ(23), /* IIN 21-23 */
IDIO_24_TTL_IRQ(0), IDIO_24_TTL_IRQ(1), IDIO_24_TTL_IRQ(2), /* TTL 0-2 */
IDIO_24_TTL_IRQ(3), IDIO_24_TTL_IRQ(4), IDIO_24_TTL_IRQ(5), /* TTL 3-5 */
IDIO_24_TTL_IRQ(6), IDIO_24_TTL_IRQ(7), /* TTL 6-7 */
}; };
/** /**
* struct idio_24_gpio - GPIO device private data structure * struct idio_24_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip * @map: regmap for the device
* @lock: synchronization lock to prevent I/O race conditions * @lock: synchronization lock to prevent I/O race conditions
* @reg: I/O address offset for the GPIO device registers * @irq_type: type configuration for IRQs
* @irq_mask: I/O bits affected by interrupts
*/ */
struct idio_24_gpio { struct idio_24_gpio {
struct gpio_chip chip; struct regmap *map;
raw_spinlock_t lock; raw_spinlock_t lock;
__u8 __iomem *plx; u8 irq_type;
struct idio_24_gpio_reg __iomem *reg;
unsigned long irq_mask;
}; };
static int idio_24_gpio_get_direction(struct gpio_chip *chip, static int idio_24_handle_mask_sync(const int index, const unsigned int mask_buf_def,
unsigned int offset) const unsigned int mask_buf, void *const irq_drv_data)
{ {
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); const unsigned int type_mask = COS_ENABLE_BOTH << index;
const unsigned long out_mode_mask = BIT(1); struct idio_24_gpio *const idio24gpio = irq_drv_data;
u8 type;
int ret;
/* FET Outputs */ raw_spin_lock(&idio24gpio->lock);
if (offset < 24)
return GPIO_LINE_DIRECTION_OUT;
/* Isolated Inputs */ /* if all are masked, then disable interrupts, else set to type */
if (offset < 48) type = (mask_buf == mask_buf_def) ? ~type_mask : idio24gpio->irq_type;
return GPIO_LINE_DIRECTION_IN;
/* TTL/CMOS I/O */ ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, type_mask, type);
/* OUT MODE = 1 when TTL/CMOS Output Mode is set */
if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN; raw_spin_unlock(&idio24gpio->lock);
return ret;
} }
static int idio_24_gpio_direction_input(struct gpio_chip *chip, static int idio_24_set_type_config(unsigned int **const buf, const unsigned int type,
unsigned int offset) const struct regmap_irq *const irq_data, const int idx,
void *const irq_drv_data)
{ {
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); const unsigned int offset = irq_data->reg_offset;
unsigned long flags; const unsigned int rising = COS_ENABLE_RISING << offset;
unsigned int ctl_state; const unsigned int falling = COS_ENABLE_FALLING << offset;
const unsigned long out_mode_mask = BIT(1); const unsigned int mask = COS_ENABLE_BOTH << offset;
struct idio_24_gpio *const idio24gpio = irq_drv_data;
unsigned int new;
unsigned int cos_enable;
int ret;
/* TTL/CMOS I/O */ switch (type) {
if (offset > 47) { case IRQ_TYPE_EDGE_RISING:
raw_spin_lock_irqsave(&idio24gpio->lock, flags); new = rising;
break;
/* Clear TTL/CMOS Output Mode */ case IRQ_TYPE_EDGE_FALLING:
ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask; new = falling;
iowrite8(ctl_state, &idio24gpio->reg->ctl); break;
case IRQ_TYPE_EDGE_BOTH:
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); new = mask;
break;
default:
return -EINVAL;
} }
return 0; raw_spin_lock(&idio24gpio->lock);
}
static int idio_24_gpio_direction_output(struct gpio_chip *chip, /* replace old bitmap with new bitmap */
unsigned int offset, int value) idio24gpio->irq_type = (idio24gpio->irq_type & ~mask) | (new & mask);
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
unsigned long flags;
unsigned int ctl_state;
const unsigned long out_mode_mask = BIT(1);
/* TTL/CMOS I/O */ ret = regmap_read(idio24gpio->map, IDIO_24_COS_ENABLE, &cos_enable);
if (offset > 47) { if (ret)
raw_spin_lock_irqsave(&idio24gpio->lock, flags); goto exit_unlock;
/* Set TTL/CMOS Output Mode */ /* if COS is currently enabled then update the edge type */
ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask; if (cos_enable & mask) {
iowrite8(ctl_state, &idio24gpio->reg->ctl); ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, mask,
idio24gpio->irq_type);
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); if (ret)
goto exit_unlock;
} }
chip->set(chip, offset, value); exit_unlock:
return 0; raw_spin_unlock(&idio24gpio->lock);
return ret;
} }
static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset) static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
const unsigned int offset, unsigned int *const reg,
unsigned int *const mask)
{ {
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG;
const unsigned long offset_mask = BIT(offset % 8); const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG;
const unsigned long out_mode_mask = BIT(1); struct regmap *const map = gpio_regmap_get_drvdata(gpio);
int err;
unsigned int ctrl_reg;
/* FET Outputs */ switch (base) {
if (offset < 8) case IDIO_24_OUT_BASE:
return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask); *mask = BIT(offset % IDIO_24_NGPIO_PER_REG);
if (offset < 16) /* FET Outputs */
return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask); if (offset < 24) {
*reg = IDIO_24_OUT_BASE + out_stride;
if (offset < 24) return 0;
return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask);
/* Isolated Inputs */
if (offset < 32)
return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask);
if (offset < 40)
return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask);
if (offset < 48)
return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask);
/* TTL/CMOS Outputs */
if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask);
/* TTL/CMOS Inputs */
return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
}
static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
unsigned long offset;
unsigned long gpio_mask;
void __iomem *ports[] = {
&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
&idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
&idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
};
size_t index;
unsigned long port_state;
const unsigned long out_mode_mask = BIT(1);
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
index = offset / 8;
/* read bits from current gpio port (port 6 is TTL GPIO) */
if (index < 6)
port_state = ioread8(ports[index]);
else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
else
port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
port_state &= gpio_mask;
bitmap_set_value8(bits, port_state, offset);
}
return 0;
}
static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
const unsigned long out_mode_mask = BIT(1);
void __iomem *base;
const unsigned int mask = BIT(offset % 8);
unsigned long flags;
unsigned int out_state;
/* Isolated Inputs */
if (offset > 23 && offset < 48)
return;
/* TTL/CMOS Inputs */
if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
return;
/* TTL/CMOS Outputs */
if (offset > 47)
base = &idio24gpio->reg->ttl_out0_7;
/* FET Outputs */
else if (offset > 15)
base = &idio24gpio->reg->out16_23;
else if (offset > 7)
base = &idio24gpio->reg->out8_15;
else
base = &idio24gpio->reg->out0_7;
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
if (value)
out_state = ioread8(base) | mask;
else
out_state = ioread8(base) & ~mask;
iowrite8(out_state, base);
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
}
static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
unsigned long offset;
unsigned long gpio_mask;
void __iomem *ports[] = {
&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
&idio24gpio->reg->out16_23
};
size_t index;
unsigned long bitmask;
unsigned long flags;
unsigned long out_state;
const unsigned long out_mode_mask = BIT(1);
for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
index = offset / 8;
bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
/* read bits from current gpio port (port 6 is TTL GPIO) */
if (index < 6) {
out_state = ioread8(ports[index]);
} else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) {
out_state = ioread8(&idio24gpio->reg->ttl_out0_7);
} else {
/* skip TTL GPIO if set for input */
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
continue;
} }
/* set requested bit states */ /* Isolated Inputs */
out_state &= ~gpio_mask; if (offset < 48) {
out_state |= bitmask; *reg = IDIO_24_IN_BASE + in_stride;
return 0;
}
/* write bits for current gpio port (port 6 is TTL GPIO) */ err = regmap_read(map, IDIO_24_CONTROL_REG, &ctrl_reg);
if (index < 6) if (err)
iowrite8(out_state, ports[index]); return err;
else
iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); /* TTL/CMOS Outputs */
} if (ctrl_reg & CONTROL_REG_OUT_MODE) {
} *reg = IDIO_24_TTLCMOS_OUT_REG;
return 0;
}
static void idio_24_irq_ack(struct irq_data *data) /* TTL/CMOS Inputs */
{ *reg = IDIO_24_TTLCMOS_IN_REG;
} return 0;
case IDIO_24_CONTROL_REG:
/* We can only set direction for TTL/CMOS lines */
if (offset < 48)
return -EOPNOTSUPP;
static void idio_24_irq_mask(struct irq_data *data) *reg = IDIO_24_CONTROL_REG;
{ *mask = CONTROL_REG_OUT_MODE;
struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); return 0;
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); default:
unsigned long flags; /* Should never reach this path */
const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
unsigned char new_irq_mask;
const unsigned long bank_offset = bit_offset / 8;
unsigned char cos_enable_state;
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
idio24gpio->irq_mask &= ~BIT(bit_offset);
new_irq_mask = idio24gpio->irq_mask >> bank_offset * 8;
if (!new_irq_mask) {
cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
/* Disable Rising Edge detection */
cos_enable_state &= ~BIT(bank_offset);
/* Disable Falling Edge detection */
cos_enable_state &= ~BIT(bank_offset + 4);
iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
}
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
gpiochip_disable_irq(chip, irqd_to_hwirq(data));
}
static void idio_24_irq_unmask(struct irq_data *data)
{
struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
unsigned long flags;
unsigned char prev_irq_mask;
const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
const unsigned long bank_offset = bit_offset / 8;
unsigned char cos_enable_state;
gpiochip_enable_irq(chip, irqd_to_hwirq(data));
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
prev_irq_mask = idio24gpio->irq_mask >> bank_offset * 8;
idio24gpio->irq_mask |= BIT(bit_offset);
if (!prev_irq_mask) {
cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
/* Enable Rising Edge detection */
cos_enable_state |= BIT(bank_offset);
/* Enable Falling Edge detection */
cos_enable_state |= BIT(bank_offset + 4);
iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
}
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
}
static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
/* The only valid irq types are none and both-edges */
if (flow_type != IRQ_TYPE_NONE &&
(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
return -EINVAL; return -EINVAL;
}
return 0;
}
static const struct irq_chip idio_24_irqchip = {
.name = "pcie-idio-24",
.irq_ack = idio_24_irq_ack,
.irq_mask = idio_24_irq_mask,
.irq_unmask = idio_24_irq_unmask,
.irq_set_type = idio_24_irq_set_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
{
struct idio_24_gpio *const idio24gpio = dev_id;
unsigned long irq_status;
struct gpio_chip *const chip = &idio24gpio->chip;
unsigned long irq_mask;
int gpio;
raw_spin_lock(&idio24gpio->lock);
/* Read Change-Of-State status */
irq_status = ioread32(&idio24gpio->reg->cos0_7);
raw_spin_unlock(&idio24gpio->lock);
/* Make sure our device generated IRQ */
if (!irq_status)
return IRQ_NONE;
/* Handle only unmasked IRQ */
irq_mask = idio24gpio->irq_mask & irq_status;
for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
generic_handle_domain_irq(chip->irq.domain, gpio + 24);
raw_spin_lock(&idio24gpio->lock);
/* Clear Change-Of-State status */
iowrite32(irq_status, &idio24gpio->reg->cos0_7);
raw_spin_unlock(&idio24gpio->lock);
return IRQ_HANDLED;
} }
#define IDIO_24_NGPIO 56 #define IDIO_24_NGPIO 56
@@ -496,11 +297,12 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
const size_t pci_plx_bar_index = 1; const size_t pci_plx_bar_index = 1;
const size_t pci_bar_index = 2; const size_t pci_bar_index = 2;
const char *const name = pci_name(pdev); const char *const name = pci_name(pdev);
struct gpio_irq_chip *girq; struct gpio_regmap_config gpio_config = {};
void __iomem *pex8311_regs;
idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); void __iomem *idio_24_regs;
if (!idio24gpio) struct regmap *intcsr_map;
return -ENOMEM; struct regmap_irq_chip *chip;
struct regmap_irq_chip_data *chip_data;
err = pcim_enable_device(pdev); err = pcim_enable_device(pdev);
if (err) { if (err) {
@@ -514,57 +316,72 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err; return err;
} }
idio24gpio->plx = pcim_iomap_table(pdev)[pci_plx_bar_index]; pex8311_regs = pcim_iomap_table(pdev)[pci_plx_bar_index];
idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; idio_24_regs = pcim_iomap_table(pdev)[pci_bar_index];
idio24gpio->chip.label = name; intcsr_map = devm_regmap_init_mmio(dev, pex8311_regs, &pex8311_intcsr_regmap_config);
idio24gpio->chip.parent = dev; if (IS_ERR(intcsr_map))
idio24gpio->chip.owner = THIS_MODULE; return dev_err_probe(dev, PTR_ERR(intcsr_map),
idio24gpio->chip.base = -1; "Unable to initialize PEX8311 register map\n");
idio24gpio->chip.ngpio = IDIO_24_NGPIO;
idio24gpio->chip.names = idio_24_names;
idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
idio24gpio->chip.get = idio_24_gpio_get;
idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
idio24gpio->chip.set = idio_24_gpio_set;
idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
girq = &idio24gpio->chip.irq; idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
gpio_irq_chip_set_chip(girq, &idio_24_irqchip); if (!idio24gpio)
/* This will let us handle the parent IRQ in the driver */ return -ENOMEM;
girq->parent_handler = NULL;
girq->num_parents = 0; idio24gpio->map = devm_regmap_init_mmio(dev, idio_24_regs, &idio_24_regmap_config);
girq->parents = NULL; if (IS_ERR(idio24gpio->map))
girq->default_type = IRQ_TYPE_NONE; return dev_err_probe(dev, PTR_ERR(idio24gpio->map),
girq->handler = handle_edge_irq; "Unable to initialize register map\n");
raw_spin_lock_init(&idio24gpio->lock); raw_spin_lock_init(&idio24gpio->lock);
/* Initialize all IRQ type configuration to IRQ_TYPE_EDGE_BOTH */
idio24gpio->irq_type = GENMASK(7, 0);
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->name = name;
chip->status_base = IDIO_24_COS_STATUS_BASE;
chip->mask_base = IDIO_24_COS_ENABLE;
chip->ack_base = IDIO_24_COS_STATUS_BASE;
chip->num_regs = 4;
chip->irqs = idio_24_regmap_irqs;
chip->num_irqs = ARRAY_SIZE(idio_24_regmap_irqs);
chip->handle_mask_sync = idio_24_handle_mask_sync;
chip->set_type_config = idio_24_set_type_config;
chip->irq_drv_data = idio24gpio;
/* Software board reset */ /* Software board reset */
iowrite8(0, &idio24gpio->reg->soft_reset); err = regmap_write(idio24gpio->map, IDIO_24_SOFT_RESET, 0);
if (err)
return err;
/* /*
* enable PLX PEX8311 internal PCI wire interrupt and local interrupt * enable PLX PEX8311 internal PCI wire interrupt and local interrupt
* input * input
*/ */
iowrite8((INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT) >> 8, err = regmap_update_bits(intcsr_map, 0x0, IDIO_24_ENABLE_IRQ, IDIO_24_ENABLE_IRQ);
idio24gpio->plx + PLX_PEX8311_PCI_LCS_INTCSR + 1); if (err)
err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
return err; return err;
}
err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED, err = devm_regmap_add_irq_chip(dev, idio24gpio->map, pdev->irq, 0, 0, chip, &chip_data);
name, idio24gpio); if (err)
if (err) { return dev_err_probe(dev, err, "IRQ registration failed\n");
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
return err;
}
return 0; gpio_config.parent = dev;
gpio_config.regmap = idio24gpio->map;
gpio_config.ngpio = IDIO_24_NGPIO;
gpio_config.names = idio_24_names;
gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE);
gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_24_OUT_BASE);
gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(IDIO_24_CONTROL_REG);
gpio_config.ngpio_per_reg = IDIO_24_NGPIO_PER_REG;
gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
gpio_config.reg_mask_xlate = idio_24_reg_mask_xlate;
gpio_config.drvdata = idio24gpio->map;
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
} }
static const struct pci_device_id idio_24_pci_dev_id[] = { static const struct pci_device_id idio_24_pci_dev_id[] = {

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Copyright (C) 2015-2023 Texas Instruments Incorporated - https://www.ti.com/
* Andrew F. Davis <afd@ti.com> * Andrew Davis <afd@ti.com>
*/ */
#include <linux/bitmap.h> #include <linux/bitmap.h>
@@ -116,6 +116,11 @@ static const struct gpio_chip template_chip = {
.can_sleep = true, .can_sleep = true,
}; };
static void pisosr_mutex_destroy(void *lock)
{
mutex_destroy(lock);
}
static int pisosr_gpio_probe(struct spi_device *spi) static int pisosr_gpio_probe(struct spi_device *spi)
{ {
struct device *dev = &spi->dev; struct device *dev = &spi->dev;
@@ -126,8 +131,6 @@ static int pisosr_gpio_probe(struct spi_device *spi)
if (!gpio) if (!gpio)
return -ENOMEM; return -ENOMEM;
spi_set_drvdata(spi, gpio);
gpio->chip = template_chip; gpio->chip = template_chip;
gpio->chip.parent = dev; gpio->chip.parent = dev;
of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio); of_property_read_u16(dev->of_node, "ngpios", &gpio->chip.ngpio);
@@ -145,8 +148,11 @@ static int pisosr_gpio_probe(struct spi_device *spi)
"Unable to allocate load GPIO\n"); "Unable to allocate load GPIO\n");
mutex_init(&gpio->lock); mutex_init(&gpio->lock);
ret = devm_add_action_or_reset(dev, pisosr_mutex_destroy, &gpio->lock);
if (ret)
return ret;
ret = gpiochip_add_data(&gpio->chip, gpio); ret = devm_gpiochip_add_data(dev, &gpio->chip, gpio);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "Unable to register gpiochip\n"); dev_err(dev, "Unable to register gpiochip\n");
return ret; return ret;
@@ -155,15 +161,6 @@ static int pisosr_gpio_probe(struct spi_device *spi)
return 0; return 0;
} }
static void pisosr_gpio_remove(struct spi_device *spi)
{
struct pisosr_gpio *gpio = spi_get_drvdata(spi);
gpiochip_remove(&gpio->chip);
mutex_destroy(&gpio->lock);
}
static const struct spi_device_id pisosr_gpio_id_table[] = { static const struct spi_device_id pisosr_gpio_id_table[] = {
{ "pisosr-gpio", }, { "pisosr-gpio", },
{ /* sentinel */ } { /* sentinel */ }
@@ -182,11 +179,10 @@ static struct spi_driver pisosr_gpio_driver = {
.of_match_table = pisosr_gpio_of_match_table, .of_match_table = pisosr_gpio_of_match_table,
}, },
.probe = pisosr_gpio_probe, .probe = pisosr_gpio_probe,
.remove = pisosr_gpio_remove,
.id_table = pisosr_gpio_id_table, .id_table = pisosr_gpio_id_table,
}; };
module_spi_driver(pisosr_gpio_driver); module_spi_driver(pisosr_gpio_driver);
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); MODULE_AUTHOR("Andrew Davis <afd@ti.com>");
MODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver"); MODULE_DESCRIPTION("SPI Compatible PISO Shift Register GPIO Driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@@ -8,7 +8,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
@@ -363,7 +363,6 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)
return ret; return ret;
} }
platform_set_drvdata(pdev, pmic_eic);
return 0; return 0;
} }

View File

@@ -20,7 +20,6 @@
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>

View File

@@ -234,7 +234,7 @@ static int rpi_exp_gpio_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio); return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio);
} }
static const struct of_device_id rpi_exp_gpio_ids[] __maybe_unused = { static const struct of_device_id rpi_exp_gpio_ids[] = {
{ .compatible = "raspberrypi,firmware-gpio" }, { .compatible = "raspberrypi,firmware-gpio" },
{ } { }
}; };
@@ -243,7 +243,7 @@ MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids);
static struct platform_driver rpi_exp_gpio_driver = { static struct platform_driver rpi_exp_gpio_driver = {
.driver = { .driver = {
.name = MODULE_NAME, .name = MODULE_NAME,
.of_match_table = of_match_ptr(rpi_exp_gpio_ids), .of_match_table = rpi_exp_gpio_ids,
}, },
.probe = rpi_exp_gpio_probe, .probe = rpi_exp_gpio_probe,
}; };

View File

@@ -121,8 +121,6 @@ static int rc5t583_gpio_probe(struct platform_device *pdev)
if (pdata && pdata->gpio_base) if (pdata && pdata->gpio_base)
rc5t583_gpio->gpio_chip.base = pdata->gpio_base; rc5t583_gpio->gpio_chip.base = pdata->gpio_base;
platform_set_drvdata(pdev, rc5t583_gpio);
return devm_gpiochip_add_data(&pdev->dev, &rc5t583_gpio->gpio_chip, return devm_gpiochip_add_data(&pdev->dev, &rc5t583_gpio->gpio_chip,
rc5t583_gpio); rc5t583_gpio);
} }

View File

@@ -15,7 +15,6 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>

View File

@@ -17,10 +17,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include "../pinctrl/core.h" #include "../pinctrl/core.h"

View File

@@ -189,7 +189,6 @@ static int sama5d2_piobu_probe(struct platform_device *pdev)
if (!piobu) if (!piobu)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, piobu);
piobu->chip.label = pdev->name; piobu->chip.label = pdev->name;
piobu->chip.parent = &pdev->dev; piobu->chip.parent = &pdev->dev;
piobu->chip.owner = THIS_MODULE, piobu->chip.owner = THIS_MODULE,

View File

@@ -380,8 +380,6 @@ static int sch_gpio_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
platform_set_drvdata(pdev, sch);
girq = &sch->chip.irq; girq = &sch->chip.irq;
gpio_irq_chip_set_chip(girq, &sch_irqchip); gpio_irq_chip_set_chip(girq, &sch_irqchip);
girq->num_parents = 0; girq->num_parents = 0;

View File

@@ -6,10 +6,10 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/of_irq.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/regmap.h> #include <linux/regmap.h>
@@ -150,6 +150,7 @@ static const struct irq_chip sifive_gpio_irqchip = {
.irq_disable = sifive_gpio_irq_disable, .irq_disable = sifive_gpio_irq_disable,
.irq_eoi = sifive_gpio_irq_eoi, .irq_eoi = sifive_gpio_irq_eoi,
.irq_set_affinity = sifive_gpio_irq_set_affinity, .irq_set_affinity = sifive_gpio_irq_set_affinity,
.irq_set_wake = irq_chip_set_wake_parent,
.flags = IRQCHIP_IMMUTABLE, .flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS, GPIOCHIP_IRQ_RESOURCE_HELPERS,
}; };
@@ -180,12 +181,10 @@ static const struct regmap_config sifive_gpio_regmap_config = {
static int sifive_gpio_probe(struct platform_device *pdev) static int sifive_gpio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct device_node *irq_parent;
struct irq_domain *parent; struct irq_domain *parent;
struct gpio_irq_chip *girq; struct gpio_irq_chip *girq;
struct sifive_gpio *chip; struct sifive_gpio *chip;
int ret, ngpio, i; int ret, ngpio;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip) if (!chip)
@@ -202,31 +201,22 @@ static int sifive_gpio_probe(struct platform_device *pdev)
if (IS_ERR(chip->regs)) if (IS_ERR(chip->regs))
return PTR_ERR(chip->regs); return PTR_ERR(chip->regs);
ngpio = of_irq_count(node); for (ngpio = 0; ngpio < SIFIVE_GPIO_MAX; ngpio++) {
if (ngpio > SIFIVE_GPIO_MAX) { ret = platform_get_irq_optional(pdev, ngpio);
dev_err(dev, "Too many GPIO interrupts (max=%d)\n",
SIFIVE_GPIO_MAX);
return -ENXIO;
}
irq_parent = of_irq_find_parent(node);
if (!irq_parent) {
dev_err(dev, "no IRQ parent node\n");
return -ENODEV;
}
parent = irq_find_host(irq_parent);
of_node_put(irq_parent);
if (!parent) {
dev_err(dev, "no IRQ parent domain\n");
return -ENODEV;
}
for (i = 0; i < ngpio; i++) {
ret = platform_get_irq(pdev, i);
if (ret < 0) if (ret < 0)
return ret; break;
chip->irq_number[i] = ret; chip->irq_number[ngpio] = ret;
} }
if (!ngpio) {
dev_err(dev, "no IRQ found\n");
return -ENODEV;
}
/*
* The check above ensures at least one parent IRQ is valid.
* Assume all parent IRQs belong to the same domain.
*/
parent = irq_get_irq_data(chip->irq_number[0])->domain;
ret = bgpio_init(&chip->gc, dev, 4, ret = bgpio_init(&chip->gc, dev, 4,
chip->base + SIFIVE_GPIO_INPUT_VAL, chip->base + SIFIVE_GPIO_INPUT_VAL,
@@ -254,7 +244,7 @@ static int sifive_gpio_probe(struct platform_device *pdev)
chip->gc.owner = THIS_MODULE; chip->gc.owner = THIS_MODULE;
girq = &chip->gc.irq; girq = &chip->gc.irq;
gpio_irq_chip_set_chip(girq, &sifive_gpio_irqchip); gpio_irq_chip_set_chip(girq, &sifive_gpio_irqchip);
girq->fwnode = of_node_to_fwnode(node); girq->fwnode = dev_fwnode(dev);
girq->parent_domain = parent; girq->parent_domain = parent;
girq->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq; girq->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq;
girq->handler = handle_bad_irq; girq->handler = handle_bad_irq;
@@ -277,4 +267,8 @@ static struct platform_driver sifive_gpio_driver = {
.of_match_table = sifive_gpio_match, .of_match_table = sifive_gpio_match,
}, },
}; };
builtin_platform_driver(sifive_gpio_driver) module_platform_driver(sifive_gpio_driver)
MODULE_AUTHOR("Yash Shah <yash.shah@sifive.com>");
MODULE_DESCRIPTION("SiFive GPIO driver");
MODULE_LICENSE("GPL");

View File

@@ -8,6 +8,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/configfs.h> #include <linux/configfs.h>
#include <linux/device.h> #include <linux/device.h>
@@ -68,7 +69,7 @@ static int gpio_sim_apply_pull(struct gpio_sim_chip *chip,
gc = &chip->gc; gc = &chip->gc;
desc = &gc->gpiodev->descs[offset]; desc = &gc->gpiodev->descs[offset];
mutex_lock(&chip->lock); guard(mutex)(&chip->lock);
if (test_bit(FLAG_REQUESTED, &desc->flags) && if (test_bit(FLAG_REQUESTED, &desc->flags) &&
!test_bit(FLAG_IS_OUT, &desc->flags)) { !test_bit(FLAG_IS_OUT, &desc->flags)) {
@@ -104,29 +105,24 @@ set_value:
set_pull: set_pull:
__assign_bit(offset, chip->pull_map, value); __assign_bit(offset, chip->pull_map, value);
mutex_unlock(&chip->lock);
return 0; return 0;
} }
static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset) static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset)
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
int ret;
mutex_lock(&chip->lock); guard(mutex)(&chip->lock);
ret = !!test_bit(offset, chip->value_map);
mutex_unlock(&chip->lock);
return ret; return !!test_bit(offset, chip->value_map);
} }
static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value) static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value)
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
__assign_bit(offset, chip->value_map, value); __assign_bit(offset, chip->value_map, value);
mutex_unlock(&chip->lock);
} }
static int gpio_sim_get_multiple(struct gpio_chip *gc, static int gpio_sim_get_multiple(struct gpio_chip *gc,
@@ -134,9 +130,8 @@ static int gpio_sim_get_multiple(struct gpio_chip *gc,
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio); bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio);
mutex_unlock(&chip->lock);
return 0; return 0;
} }
@@ -146,9 +141,9 @@ static void gpio_sim_set_multiple(struct gpio_chip *gc,
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio); bitmap_replace(chip->value_map, chip->value_map, bits, mask,
mutex_unlock(&chip->lock); gc->ngpio);
} }
static int gpio_sim_direction_output(struct gpio_chip *gc, static int gpio_sim_direction_output(struct gpio_chip *gc,
@@ -156,10 +151,10 @@ static int gpio_sim_direction_output(struct gpio_chip *gc,
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock) {
__clear_bit(offset, chip->direction_map); __clear_bit(offset, chip->direction_map);
__assign_bit(offset, chip->value_map, value); __assign_bit(offset, chip->value_map, value);
mutex_unlock(&chip->lock); }
return 0; return 0;
} }
@@ -168,9 +163,8 @@ static int gpio_sim_direction_input(struct gpio_chip *gc, unsigned int offset)
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
__set_bit(offset, chip->direction_map); __set_bit(offset, chip->direction_map);
mutex_unlock(&chip->lock);
return 0; return 0;
} }
@@ -180,9 +174,8 @@ static int gpio_sim_get_direction(struct gpio_chip *gc, unsigned int offset)
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
int direction; int direction;
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
direction = !!test_bit(offset, chip->direction_map); direction = !!test_bit(offset, chip->direction_map);
mutex_unlock(&chip->lock);
return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
} }
@@ -215,9 +208,9 @@ static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset)
{ {
struct gpio_sim_chip *chip = gpiochip_get_data(gc); struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
__assign_bit(offset, chip->value_map, !!test_bit(offset, chip->pull_map)); __assign_bit(offset, chip->value_map,
mutex_unlock(&chip->lock); !!test_bit(offset, chip->pull_map));
} }
static ssize_t gpio_sim_sysfs_val_show(struct device *dev, static ssize_t gpio_sim_sysfs_val_show(struct device *dev,
@@ -227,9 +220,8 @@ static ssize_t gpio_sim_sysfs_val_show(struct device *dev,
struct gpio_sim_chip *chip = dev_get_drvdata(dev); struct gpio_sim_chip *chip = dev_get_drvdata(dev);
int val; int val;
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
val = !!test_bit(line_attr->offset, chip->value_map); val = !!test_bit(line_attr->offset, chip->value_map);
mutex_unlock(&chip->lock);
return sysfs_emit(buf, "%d\n", val); return sysfs_emit(buf, "%d\n", val);
} }
@@ -258,9 +250,8 @@ static ssize_t gpio_sim_sysfs_pull_show(struct device *dev,
struct gpio_sim_chip *chip = dev_get_drvdata(dev); struct gpio_sim_chip *chip = dev_get_drvdata(dev);
int pull; int pull;
mutex_lock(&chip->lock); scoped_guard(mutex, &chip->lock)
pull = !!test_bit(line_attr->offset, chip->pull_map); pull = !!test_bit(line_attr->offset, chip->pull_map);
mutex_unlock(&chip->lock);
return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]); return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]);
} }
@@ -502,7 +493,7 @@ struct gpio_sim_device {
* This structure however can be modified by callbacks of different * This structure however can be modified by callbacks of different
* attributes so we need another lock. * attributes so we need another lock.
* *
* We use this lock fo protecting all data structures owned by this * We use this lock for protecting all data structures owned by this
* object too. * object too.
*/ */
struct mutex lock; struct mutex lock;
@@ -656,16 +647,13 @@ static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev)
static char *gpio_sim_strdup_trimmed(const char *str, size_t count) static char *gpio_sim_strdup_trimmed(const char *str, size_t count)
{ {
char *dup, *trimmed; char *trimmed;
dup = kstrndup(str, count, GFP_KERNEL); trimmed = kstrndup(skip_spaces(str), count, GFP_KERNEL);
if (!dup) if (!trimmed)
return NULL; return NULL;
trimmed = strstrip(dup); return strim(trimmed);
memmove(dup, trimmed, strlen(trimmed) + 1);
return dup;
} }
static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item, static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
@@ -673,17 +661,14 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
{ {
struct gpio_sim_device *dev = to_gpio_sim_device(item); struct gpio_sim_device *dev = to_gpio_sim_device(item);
struct platform_device *pdev; struct platform_device *pdev;
int ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
pdev = dev->pdev; pdev = dev->pdev;
if (pdev) if (pdev)
ret = sprintf(page, "%s\n", dev_name(&pdev->dev)); return sprintf(page, "%s\n", dev_name(&pdev->dev));
else
ret = sprintf(page, "gpio-sim.%d\n", dev->id);
mutex_unlock(&dev->lock);
return ret; return sprintf(page, "gpio-sim.%d\n", dev->id);
} }
CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name); CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name);
@@ -694,9 +679,8 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page)
struct gpio_sim_device *dev = to_gpio_sim_device(item); struct gpio_sim_device *dev = to_gpio_sim_device(item);
bool live; bool live;
mutex_lock(&dev->lock); scoped_guard(mutex, &dev->lock)
live = gpio_sim_device_is_live_unlocked(dev); live = gpio_sim_device_is_live_unlocked(dev);
mutex_unlock(&dev->lock);
return sprintf(page, "%c\n", live ? '1' : '0'); return sprintf(page, "%c\n", live ? '1' : '0');
} }
@@ -851,8 +835,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
{ {
struct property_entry properties[GPIO_SIM_PROP_MAX]; struct property_entry properties[GPIO_SIM_PROP_MAX];
unsigned int prop_idx = 0, line_names_size = 0; unsigned int prop_idx = 0, line_names_size = 0;
struct fwnode_handle *swnode; char **line_names __free(kfree) = NULL;
char **line_names;
memset(properties, 0, sizeof(properties)); memset(properties, 0, sizeof(properties));
@@ -871,9 +854,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
"gpio-line-names", "gpio-line-names",
line_names, line_names_size); line_names, line_names_size);
swnode = fwnode_create_software_node(properties, parent); return fwnode_create_software_node(properties, parent);
kfree(line_names);
return swnode;
} }
static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode) static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode)
@@ -998,18 +979,15 @@ gpio_sim_device_config_live_store(struct config_item *item,
if (ret) if (ret)
return ret; return ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if ((!live && !gpio_sim_device_is_live_unlocked(dev)) || if (live == gpio_sim_device_is_live_unlocked(dev))
(live && gpio_sim_device_is_live_unlocked(dev)))
ret = -EPERM; ret = -EPERM;
else if (live) else if (live)
ret = gpio_sim_device_activate_unlocked(dev); ret = gpio_sim_device_activate_unlocked(dev);
else else
gpio_sim_device_deactivate_unlocked(dev); gpio_sim_device_deactivate_unlocked(dev);
mutex_unlock(&dev->lock);
return ret ?: count; return ret ?: count;
} }
@@ -1046,17 +1024,14 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
struct gpio_sim_bank *bank = to_gpio_sim_bank(item); struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
struct gpio_sim_chip_name_ctx ctx = { bank->swnode, page }; struct gpio_sim_chip_name_ctx ctx = { bank->swnode, page };
int ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) if (gpio_sim_device_is_live_unlocked(dev))
ret = device_for_each_child(&dev->pdev->dev, &ctx, return device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name); gpio_sim_emit_chip_name);
else
ret = sprintf(page, "none\n");
mutex_unlock(&dev->lock);
return ret; return sprintf(page, "none\n");
} }
CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name); CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name);
@@ -1066,13 +1041,10 @@ gpio_sim_bank_config_label_show(struct config_item *item, char *page)
{ {
struct gpio_sim_bank *bank = to_gpio_sim_bank(item); struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
int ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
ret = sprintf(page, "%s\n", bank->label ?: "");
mutex_unlock(&dev->lock);
return ret; return sprintf(page, "%s\n", bank->label ?: "");
} }
static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
@@ -1082,23 +1054,18 @@ static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
char *trimmed; char *trimmed;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return -EBUSY; return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count); trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) { if (!trimmed)
mutex_unlock(&dev->lock);
return -ENOMEM; return -ENOMEM;
}
kfree(bank->label); kfree(bank->label);
bank->label = trimmed; bank->label = trimmed;
mutex_unlock(&dev->lock);
return count; return count;
} }
@@ -1109,13 +1076,10 @@ gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page)
{ {
struct gpio_sim_bank *bank = to_gpio_sim_bank(item); struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
int ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
ret = sprintf(page, "%u\n", bank->num_lines);
mutex_unlock(&dev->lock);
return ret; return sprintf(page, "%u\n", bank->num_lines);
} }
static ssize_t static ssize_t
@@ -1134,16 +1098,13 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item,
if (num_lines == 0) if (num_lines == 0)
return -EINVAL; return -EINVAL;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return -EBUSY; return -EBUSY;
}
bank->num_lines = num_lines; bank->num_lines = num_lines;
mutex_unlock(&dev->lock);
return count; return count;
} }
@@ -1161,13 +1122,10 @@ gpio_sim_line_config_name_show(struct config_item *item, char *page)
{ {
struct gpio_sim_line *line = to_gpio_sim_line(item); struct gpio_sim_line *line = to_gpio_sim_line(item);
struct gpio_sim_device *dev = gpio_sim_line_get_device(line); struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
int ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
ret = sprintf(page, "%s\n", line->name ?: "");
mutex_unlock(&dev->lock);
return ret; return sprintf(page, "%s\n", line->name ?: "");
} }
static ssize_t gpio_sim_line_config_name_store(struct config_item *item, static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
@@ -1177,24 +1135,18 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
struct gpio_sim_device *dev = gpio_sim_line_get_device(line); struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
char *trimmed; char *trimmed;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return -EBUSY; return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count); trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) { if (!trimmed)
mutex_unlock(&dev->lock);
return -ENOMEM; return -ENOMEM;
}
kfree(line->name); kfree(line->name);
line->name = trimmed; line->name = trimmed;
mutex_unlock(&dev->lock);
return count; return count;
} }
@@ -1210,13 +1162,10 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item,
{ {
struct gpio_sim_hog *hog = to_gpio_sim_hog(item); struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
int ret;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
ret = sprintf(page, "%s\n", hog->name ?: "");
mutex_unlock(&dev->lock);
return ret; return sprintf(page, "%s\n", hog->name ?: "");
} }
static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
@@ -1226,24 +1175,18 @@ static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
char *trimmed; char *trimmed;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return -EBUSY; return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count); trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) { if (!trimmed)
mutex_unlock(&dev->lock);
return -ENOMEM; return -ENOMEM;
}
kfree(hog->name); kfree(hog->name);
hog->name = trimmed; hog->name = trimmed;
mutex_unlock(&dev->lock);
return count; return count;
} }
@@ -1257,9 +1200,8 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item,
char *repr; char *repr;
int dir; int dir;
mutex_lock(&dev->lock); scoped_guard(mutex, &dev->lock)
dir = hog->dir; dir = hog->dir;
mutex_unlock(&dev->lock);
switch (dir) { switch (dir) {
case GPIOD_IN: case GPIOD_IN:
@@ -1286,42 +1228,24 @@ gpio_sim_hog_config_direction_store(struct config_item *item,
{ {
struct gpio_sim_hog *hog = to_gpio_sim_hog(item); struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
char *trimmed;
int dir; int dir;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return -EBUSY; return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count); if (sysfs_streq(page, "input"))
if (!trimmed) {
mutex_unlock(&dev->lock);
return -ENOMEM;
}
if (strcmp(trimmed, "input") == 0)
dir = GPIOD_IN; dir = GPIOD_IN;
else if (strcmp(trimmed, "output-high") == 0) else if (sysfs_streq(page, "output-high"))
dir = GPIOD_OUT_HIGH; dir = GPIOD_OUT_HIGH;
else if (strcmp(trimmed, "output-low") == 0) else if (sysfs_streq(page, "output-low"))
dir = GPIOD_OUT_LOW; dir = GPIOD_OUT_LOW;
else else
dir = -EINVAL; return -EINVAL;
kfree(trimmed);
if (dir < 0) {
mutex_unlock(&dev->lock);
return dir;
}
hog->dir = dir; hog->dir = dir;
mutex_unlock(&dev->lock);
return count; return count;
} }
@@ -1339,9 +1263,8 @@ static void gpio_sim_hog_config_item_release(struct config_item *item)
struct gpio_sim_line *line = hog->parent; struct gpio_sim_line *line = hog->parent;
struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog); struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
mutex_lock(&dev->lock); scoped_guard(mutex, &dev->lock)
line->hog = NULL; line->hog = NULL;
mutex_unlock(&dev->lock);
kfree(hog->name); kfree(hog->name);
kfree(hog); kfree(hog);
@@ -1367,13 +1290,11 @@ gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name)
if (strcmp(name, "hog") != 0) if (strcmp(name, "hog") != 0)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
hog = kzalloc(sizeof(*hog), GFP_KERNEL); hog = kzalloc(sizeof(*hog), GFP_KERNEL);
if (!hog) { if (!hog)
mutex_unlock(&dev->lock);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
config_item_init_type_name(&hog->item, name, config_item_init_type_name(&hog->item, name,
&gpio_sim_hog_config_type); &gpio_sim_hog_config_type);
@@ -1383,8 +1304,6 @@ gpio_sim_line_config_make_hog_item(struct config_group *group, const char *name)
hog->parent = line; hog->parent = line;
line->hog = hog; line->hog = hog;
mutex_unlock(&dev->lock);
return &hog->item; return &hog->item;
} }
@@ -1393,9 +1312,8 @@ static void gpio_sim_line_config_group_release(struct config_item *item)
struct gpio_sim_line *line = to_gpio_sim_line(item); struct gpio_sim_line *line = to_gpio_sim_line(item);
struct gpio_sim_device *dev = gpio_sim_line_get_device(line); struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
mutex_lock(&dev->lock); scoped_guard(mutex, &dev->lock)
list_del(&line->siblings); list_del(&line->siblings);
mutex_unlock(&dev->lock);
kfree(line->name); kfree(line->name);
kfree(line); kfree(line);
@@ -1430,18 +1348,14 @@ gpio_sim_bank_config_make_line_group(struct config_group *group,
if (ret != 1 || nchar != strlen(name)) if (ret != 1 || nchar != strlen(name))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
}
line = kzalloc(sizeof(*line), GFP_KERNEL); line = kzalloc(sizeof(*line), GFP_KERNEL);
if (!line) { if (!line)
mutex_unlock(&dev->lock);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
config_group_init_type_name(&line->group, name, config_group_init_type_name(&line->group, name,
&gpio_sim_line_config_type); &gpio_sim_line_config_type);
@@ -1450,8 +1364,6 @@ gpio_sim_bank_config_make_line_group(struct config_group *group,
line->offset = offset; line->offset = offset;
list_add_tail(&line->siblings, &bank->line_list); list_add_tail(&line->siblings, &bank->line_list);
mutex_unlock(&dev->lock);
return &line->group; return &line->group;
} }
@@ -1460,9 +1372,8 @@ static void gpio_sim_bank_config_group_release(struct config_item *item)
struct gpio_sim_bank *bank = to_gpio_sim_bank(item); struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank); struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
mutex_lock(&dev->lock); scoped_guard(mutex, &dev->lock)
list_del(&bank->siblings); list_del(&bank->siblings);
mutex_unlock(&dev->lock);
kfree(bank->label); kfree(bank->label);
kfree(bank); kfree(bank);
@@ -1490,18 +1401,14 @@ gpio_sim_device_config_make_bank_group(struct config_group *group,
struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item); struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item);
struct gpio_sim_bank *bank; struct gpio_sim_bank *bank;
mutex_lock(&dev->lock); guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) { if (gpio_sim_device_is_live_unlocked(dev))
mutex_unlock(&dev->lock);
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
}
bank = kzalloc(sizeof(*bank), GFP_KERNEL); bank = kzalloc(sizeof(*bank), GFP_KERNEL);
if (!bank) { if (!bank)
mutex_unlock(&dev->lock);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
config_group_init_type_name(&bank->group, name, config_group_init_type_name(&bank->group, name,
&gpio_sim_bank_config_group_type); &gpio_sim_bank_config_group_type);
@@ -1510,8 +1417,6 @@ gpio_sim_device_config_make_bank_group(struct config_group *group,
INIT_LIST_HEAD(&bank->line_list); INIT_LIST_HEAD(&bank->line_list);
list_add_tail(&bank->siblings, &dev->bank_list); list_add_tail(&bank->siblings, &dev->bank_list);
mutex_unlock(&dev->lock);
return &bank->group; return &bank->group;
} }
@@ -1519,10 +1424,10 @@ static void gpio_sim_device_config_group_release(struct config_item *item)
{ {
struct gpio_sim_device *dev = to_gpio_sim_device(item); struct gpio_sim_device *dev = to_gpio_sim_device(item);
mutex_lock(&dev->lock); scoped_guard(mutex, &dev->lock) {
if (gpio_sim_device_is_live_unlocked(dev)) if (gpio_sim_device_is_live_unlocked(dev))
gpio_sim_device_deactivate_unlocked(dev); gpio_sim_device_deactivate_unlocked(dev);
mutex_unlock(&dev->lock); }
mutex_destroy(&dev->lock); mutex_destroy(&dev->lock);
ida_free(&gpio_sim_ida, dev->id); ida_free(&gpio_sim_ida, dev->id);
@@ -1547,7 +1452,7 @@ static const struct config_item_type gpio_sim_device_config_group_type = {
static struct config_group * static struct config_group *
gpio_sim_config_make_device_group(struct config_group *group, const char *name) gpio_sim_config_make_device_group(struct config_group *group, const char *name)
{ {
struct gpio_sim_device *dev; struct gpio_sim_device *dev __free(kfree) = NULL;
int id; int id;
dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -1555,10 +1460,8 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
id = ida_alloc(&gpio_sim_ida, GFP_KERNEL); id = ida_alloc(&gpio_sim_ida, GFP_KERNEL);
if (id < 0) { if (id < 0)
kfree(dev);
return ERR_PTR(id); return ERR_PTR(id);
}
config_group_init_type_name(&dev->group, name, config_group_init_type_name(&dev->group, name,
&gpio_sim_device_config_group_type); &gpio_sim_device_config_group_type);
@@ -1569,7 +1472,7 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name)
dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call; dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call;
init_completion(&dev->probe_completion); init_completion(&dev->probe_completion);
return &dev->group; return &no_free_ptr(dev)->group;
} }
static struct configfs_group_operations gpio_sim_config_group_ops = { static struct configfs_group_operations gpio_sim_config_group_ops = {

View File

@@ -7,8 +7,8 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>

View File

@@ -4,11 +4,12 @@
* Copyright (C) 2012 John Crispin <john@phrozen.org> * Copyright (C) 2012 John Crispin <john@phrozen.org>
*/ */
#include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/of_platform.h> #include <linux/of.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/io.h> #include <linux/io.h>

View File

@@ -9,7 +9,6 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
@@ -249,8 +248,6 @@ static int syscon_gpio_probe(struct platform_device *pdev)
priv->chip.direction_output = syscon_gpio_dir_out; priv->chip.direction_output = syscon_gpio_dir_out;
} }
platform_set_drvdata(pdev, priv);
return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
} }

View File

@@ -15,7 +15,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>

View File

@@ -11,7 +11,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>

View File

@@ -256,8 +256,6 @@ static int timbgpio_probe(struct platform_device *pdev)
if (err) if (err)
return err; return err;
platform_set_drvdata(pdev, tgpio);
/* make sure to disable interrupts */ /* make sure to disable interrupts */
iowrite32(0x0, tgpio->membase + TGPIO_IER); iowrite32(0x0, tgpio->membase + TGPIO_IER);

View File

@@ -216,7 +216,7 @@ MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table);
static struct platform_driver tps65218_gpio_driver = { static struct platform_driver tps65218_gpio_driver = {
.driver = { .driver = {
.name = "tps65218-gpio", .name = "tps65218-gpio",
.of_match_table = of_match_ptr(tps65218_dt_match) .of_match_table = tps65218_dt_match,
}, },
.probe = tps65218_gpio_probe, .probe = tps65218_gpio_probe,
.id_table = tps65218_gpio_id_table, .id_table = tps65218_gpio_id_table,

View File

@@ -15,7 +15,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/mfd/tps6586x.h> #include <linux/mfd/tps6586x.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
/* GPIO control registers */ /* GPIO control registers */

View File

@@ -15,7 +15,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mfd/tps65910.h> #include <linux/mfd/tps65910.h>
#include <linux/of_device.h> #include <linux/of.h>
struct tps65910_gpio { struct tps65910_gpio {
struct gpio_chip gpio_chip; struct gpio_chip gpio_chip;

View File

@@ -277,8 +277,6 @@ static int tqmx86_gpio_probe(struct platform_device *pdev)
tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD); tqmx86_gpio_write(gpio, (u8)~TQMX86_DIR_INPUT_MASK, TQMX86_GPIODD);
platform_set_drvdata(pdev, gpio);
chip = &gpio->chip; chip = &gpio->chip;
chip->label = "gpio-tqmx86"; chip->label = "gpio-tqmx86";
chip->owner = THIS_MODULE; chip->owner = THIS_MODULE;

View File

@@ -7,8 +7,7 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#define DEFAULT_PIN_NUMBER 16 #define DEFAULT_PIN_NUMBER 16

View File

@@ -8,7 +8,7 @@
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/regmap.h> #include <linux/regmap.h>

View File

@@ -9,7 +9,6 @@
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>

View File

@@ -17,7 +17,6 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
@@ -259,7 +258,6 @@ static void vf610_gpio_disable_clk(void *data)
static int vf610_gpio_probe(struct platform_device *pdev) static int vf610_gpio_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct vf610_gpio_port *port; struct vf610_gpio_port *port;
struct gpio_chip *gc; struct gpio_chip *gc;
struct gpio_irq_chip *girq; struct gpio_irq_chip *girq;
@@ -319,7 +317,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
gc->parent = dev; gc->parent = dev;
gc->label = dev_name(dev); gc->label = dev_name(dev);
gc->ngpio = VF610_GPIO_PER_PORT; gc->ngpio = VF610_GPIO_PER_PORT;
gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT; gc->base = -1;
gc->request = gpiochip_generic_request; gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free; gc->free = gpiochip_generic_free;

View File

@@ -240,8 +240,6 @@ static int vx855gpio_probe(struct platform_device *pdev)
if (!vg) if (!vg)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, vg);
dev_info(&pdev->dev, "found VX855 GPIO controller\n"); dev_info(&pdev->dev, "found VX855 GPIO controller\n");
vg->io_gpi = res_gpi->start; vg->io_gpi = res_gpi->start;
vg->io_gpo = res_gpo->start; vg->io_gpo = res_gpo->start;

View File

@@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019, Linaro Limited // Copyright (c) 2019, Linaro Limited
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of_device.h>
#define WCD_PIN_MASK(p) BIT(p) #define WCD_PIN_MASK(p) BIT(p)
#define WCD_REG_DIR_CTL_OFFSET 0x42 #define WCD_REG_DIR_CTL_OFFSET 0x42

View File

@@ -3,19 +3,18 @@
* GPIO driver for the WinSystems WS16C48 * GPIO driver for the WinSystems WS16C48
* Copyright (C) 2016 William Breathitt Gray * Copyright (C) 2016 William Breathitt Gray
*/ */
#include <linux/bitmap.h> #include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/errno.h> #include <linux/err.h>
#include <linux/gpio/driver.h> #include <linux/gpio/regmap.h>
#include <linux/io.h> #include <linux/irq.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/irqdesc.h>
#include <linux/isa.h> #include <linux/isa.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/types.h> #include <linux/types.h>
#define WS16C48_EXTENT 11 #define WS16C48_EXTENT 11
@@ -31,371 +30,178 @@ static unsigned int num_irq;
module_param_hw_array(irq, uint, irq, &num_irq, 0); module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
/** #define WS16C48_DAT_BASE 0x0
* struct ws16c48_reg - device register structure #define WS16C48_PAGE_LOCK 0x7
* @port: Port 0 through 5 I/O #define WS16C48_PAGE_BASE 0x8
* @int_pending: Interrupt Pending #define WS16C48_POL WS16C48_PAGE_BASE
* @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0) #define WS16C48_ENAB WS16C48_PAGE_BASE
* @pol_enab_int_id: Interrupt polarity, enable, and ID #define WS16C48_INT_ID WS16C48_PAGE_BASE
*/
struct ws16c48_reg { #define PAGE_LOCK_PAGE_FIELD GENMASK(7, 6)
u8 port[6]; #define POL_PAGE u8_encode_bits(1, PAGE_LOCK_PAGE_FIELD)
u8 int_pending; #define ENAB_PAGE u8_encode_bits(2, PAGE_LOCK_PAGE_FIELD)
u8 page_lock; #define INT_ID_PAGE u8_encode_bits(3, PAGE_LOCK_PAGE_FIELD)
u8 pol_enab_int_id[3];
static const struct regmap_range ws16c48_wr_ranges[] = {
regmap_reg_range(0x0, 0x5), regmap_reg_range(0x7, 0xA),
};
static const struct regmap_range ws16c48_rd_ranges[] = {
regmap_reg_range(0x0, 0xA),
};
static const struct regmap_range ws16c48_volatile_ranges[] = {
regmap_reg_range(0x0, 0x6), regmap_reg_range(0x8, 0xA),
};
static const struct regmap_access_table ws16c48_wr_table = {
.yes_ranges = ws16c48_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(ws16c48_wr_ranges),
};
static const struct regmap_access_table ws16c48_rd_table = {
.yes_ranges = ws16c48_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(ws16c48_rd_ranges),
};
static const struct regmap_access_table ws16c48_volatile_table = {
.yes_ranges = ws16c48_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(ws16c48_volatile_ranges),
};
static const struct regmap_config ws16c48_regmap_config = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
.wr_table = &ws16c48_wr_table,
.rd_table = &ws16c48_rd_table,
.volatile_table = &ws16c48_volatile_table,
.cache_type = REGCACHE_FLAT,
.use_raw_spinlock = true,
};
#define WS16C48_NGPIO_PER_REG 8
#define WS16C48_REGMAP_IRQ(_id) \
[_id] = { \
.reg_offset = (_id) / WS16C48_NGPIO_PER_REG, \
.mask = BIT((_id) % WS16C48_NGPIO_PER_REG), \
.type = { \
.type_reg_offset = (_id) / WS16C48_NGPIO_PER_REG, \
.types_supported = IRQ_TYPE_EDGE_BOTH, \
}, \
}
/* Only the first 24 lines (Port 0-2) support interrupts */
#define WS16C48_NUM_IRQS 24
static const struct regmap_irq ws16c48_regmap_irqs[WS16C48_NUM_IRQS] = {
WS16C48_REGMAP_IRQ(0), WS16C48_REGMAP_IRQ(1), WS16C48_REGMAP_IRQ(2), /* 0-2 */
WS16C48_REGMAP_IRQ(3), WS16C48_REGMAP_IRQ(4), WS16C48_REGMAP_IRQ(5), /* 3-5 */
WS16C48_REGMAP_IRQ(6), WS16C48_REGMAP_IRQ(7), WS16C48_REGMAP_IRQ(8), /* 6-8 */
WS16C48_REGMAP_IRQ(9), WS16C48_REGMAP_IRQ(10), WS16C48_REGMAP_IRQ(11), /* 9-11 */
WS16C48_REGMAP_IRQ(12), WS16C48_REGMAP_IRQ(13), WS16C48_REGMAP_IRQ(14), /* 12-14 */
WS16C48_REGMAP_IRQ(15), WS16C48_REGMAP_IRQ(16), WS16C48_REGMAP_IRQ(17), /* 15-17 */
WS16C48_REGMAP_IRQ(18), WS16C48_REGMAP_IRQ(19), WS16C48_REGMAP_IRQ(20), /* 18-20 */
WS16C48_REGMAP_IRQ(21), WS16C48_REGMAP_IRQ(22), WS16C48_REGMAP_IRQ(23), /* 21-23 */
}; };
/** /**
* struct ws16c48_gpio - GPIO device private data structure * struct ws16c48_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip * @map: regmap for the device
* @io_state: bit I/O state (whether bit is set to input or output)
* @out_state: output bits state
* @lock: synchronization lock to prevent I/O race conditions * @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts * @irq_mask: I/O bits affected by interrupts
* @flow_mask: IRQ flow type mask for the respective I/O bits
* @reg: I/O address offset for the device registers
*/ */
struct ws16c48_gpio { struct ws16c48_gpio {
struct gpio_chip chip; struct regmap *map;
unsigned char io_state[6];
unsigned char out_state[6];
raw_spinlock_t lock; raw_spinlock_t lock;
unsigned long irq_mask; u8 irq_mask[WS16C48_NUM_IRQS / WS16C48_NGPIO_PER_REG];
unsigned long flow_mask;
struct ws16c48_reg __iomem *reg;
}; };
static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) static int ws16c48_handle_pre_irq(void *const irq_drv_data) __acquires(&ws16c48gpio->lock)
{ {
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
if (ws16c48gpio->io_state[port] & mask) /* Lock to prevent Page/Lock register change while we handle IRQ */
return GPIO_LINE_DIRECTION_IN; raw_spin_lock(&ws16c48gpio->lock);
return GPIO_LINE_DIRECTION_OUT;
}
static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
unsigned long flags;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
ws16c48gpio->io_state[port] |= mask;
ws16c48gpio->out_state[port] &= ~mask;
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return 0; return 0;
} }
static int ws16c48_gpio_direction_output(struct gpio_chip *chip, static int ws16c48_handle_post_irq(void *const irq_drv_data) __releases(&ws16c48gpio->lock)
unsigned offset, int value)
{ {
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
unsigned long flags;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); raw_spin_unlock(&ws16c48gpio->lock);
ws16c48gpio->io_state[port] &= ~mask;
if (value)
ws16c48gpio->out_state[port] |= mask;
else
ws16c48gpio->out_state[port] &= ~mask;
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return 0; return 0;
} }
static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) static int ws16c48_handle_mask_sync(const int index, const unsigned int mask_buf_def,
const unsigned int mask_buf, void *const irq_drv_data)
{ {
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
unsigned long flags; unsigned long flags;
unsigned port_state; int ret = 0;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
/* ensure that GPIO is set for input */ /* exit early if no change since the last mask sync */
if (!(ws16c48gpio->io_state[port] & mask)) { if (mask_buf == ws16c48gpio->irq_mask[index])
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); goto exit_unlock;
return -EINVAL; ws16c48gpio->irq_mask[index] = mask_buf;
}
port_state = ioread8(ws16c48gpio->reg->port + port); ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, ENAB_PAGE);
if (ret)
goto exit_unlock;
/* Update ENAB register (inverted mask) */
ret = regmap_write(ws16c48gpio->map, WS16C48_ENAB + index, ~mask_buf);
if (ret)
goto exit_unlock;
ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
if (ret)
goto exit_unlock;
exit_unlock:
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return !!(port_state & mask); return ret;
} }
static int ws16c48_gpio_get_multiple(struct gpio_chip *chip, static int ws16c48_set_type_config(unsigned int **const buf, const unsigned int type,
unsigned long *mask, unsigned long *bits) const struct regmap_irq *const irq_data, const int idx,
void *const irq_drv_data)
{ {
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
unsigned long offset; unsigned int polarity;
unsigned long gpio_mask;
size_t index;
u8 __iomem *port_addr;
unsigned long port_state;
/* clear bits array to a clean slate */
bitmap_zero(bits, chip->ngpio);
for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
index = offset / 8;
port_addr = ws16c48gpio->reg->port + index;
port_state = ioread8(port_addr) & gpio_mask;
bitmap_set_value8(bits, port_state, offset);
}
return 0;
}
static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
unsigned long flags; unsigned long flags;
int ret;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); switch (type) {
/* ensure that GPIO is set for output */
if (ws16c48gpio->io_state[port] & mask) {
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return;
}
if (value)
ws16c48gpio->out_state[port] |= mask;
else
ws16c48gpio->out_state[port] &= ~mask;
iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
static void ws16c48_gpio_set_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
unsigned long offset;
unsigned long gpio_mask;
size_t index;
u8 __iomem *port_addr;
unsigned long bitmask;
unsigned long flags;
for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
index = offset / 8;
port_addr = ws16c48gpio->reg->port + index;
/* mask out GPIO configured for input */
gpio_mask &= ~ws16c48gpio->io_state[index];
bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
/* update output state data and set device gpio register */
ws16c48gpio->out_state[index] &= ~gpio_mask;
ws16c48gpio->out_state[index] |= bitmask;
iowrite8(ws16c48gpio->out_state[index], port_addr);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
}
static void ws16c48_irq_ack(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned long offset = irqd_to_hwirq(data);
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
unsigned long flags;
unsigned port_state;
/* only the first 3 ports support interrupts */
if (port > 2)
return;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
port_state = ws16c48gpio->irq_mask >> (8*port);
/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);
/* Clear pending interrupt */
iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port);
iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port);
/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
static void ws16c48_irq_mask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned long offset = irqd_to_hwirq(data);
const unsigned long mask = BIT(offset);
const unsigned port = offset / 8;
unsigned long flags;
unsigned long port_state;
/* only the first 3 ports support interrupts */
if (port > 2)
return;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
ws16c48gpio->irq_mask &= ~mask;
gpiochip_disable_irq(chip, offset);
port_state = ws16c48gpio->irq_mask >> (8 * port);
/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);
/* Disable interrupt */
iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
static void ws16c48_irq_unmask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned long offset = irqd_to_hwirq(data);
const unsigned long mask = BIT(offset);
const unsigned port = offset / 8;
unsigned long flags;
unsigned long port_state;
/* only the first 3 ports support interrupts */
if (port > 2)
return;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
gpiochip_enable_irq(chip, offset);
ws16c48gpio->irq_mask |= mask;
port_state = ws16c48gpio->irq_mask >> (8 * port);
/* Select Register Page 2; Unlock all I/O ports */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);
/* Enable interrupt */
iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
/* Select Register Page 3; Unlock all I/O ports */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
}
static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned long offset = irqd_to_hwirq(data);
const unsigned long mask = BIT(offset);
const unsigned port = offset / 8;
unsigned long flags;
unsigned long port_state;
/* only the first 3 ports support interrupts */
if (port > 2)
return -EINVAL;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
switch (flow_type) {
case IRQ_TYPE_NONE:
break;
case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_EDGE_RISING:
ws16c48gpio->flow_mask |= mask; polarity = irq_data->mask;
break; break;
case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_FALLING:
ws16c48gpio->flow_mask &= ~mask; polarity = 0;
break; break;
default: default:
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return -EINVAL; return -EINVAL;
} }
port_state = ws16c48gpio->flow_mask >> (8 * port); raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
/* Select Register Page 1; Unlock all I/O ports */ ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, POL_PAGE);
iowrite8(0x40, &ws16c48gpio->reg->page_lock); if (ret)
goto exit_unlock;
/* Set interrupt polarity */ /* Set interrupt polarity */
iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); ret = regmap_update_bits(ws16c48gpio->map, WS16C48_POL + idx, irq_data->mask, polarity);
if (ret)
goto exit_unlock;
/* Select Register Page 3; Unlock all I/O ports */ ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
iowrite8(0xC0, &ws16c48gpio->reg->page_lock); if (ret)
goto exit_unlock;
exit_unlock:
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return 0; return ret;
}
static const struct irq_chip ws16c48_irqchip = {
.name = "ws16c48",
.irq_ack = ws16c48_irq_ack,
.irq_mask = ws16c48_irq_mask,
.irq_unmask = ws16c48_irq_unmask,
.irq_set_type = ws16c48_irq_set_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
{
struct ws16c48_gpio *const ws16c48gpio = dev_id;
struct gpio_chip *const chip = &ws16c48gpio->chip;
struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg;
unsigned long int_pending;
unsigned long port;
unsigned long int_id;
unsigned long gpio;
int_pending = ioread8(&reg->int_pending) & 0x7;
if (!int_pending)
return IRQ_NONE;
/* loop until all pending interrupts are handled */
do {
for_each_set_bit(port, &int_pending, 3) {
int_id = ioread8(reg->pol_enab_int_id + port);
for_each_set_bit(gpio, &int_id, 8)
generic_handle_domain_irq(chip->irq.domain,
gpio + 8*port);
}
int_pending = ioread8(&reg->int_pending) & 0x7;
} while (int_pending);
return IRQ_HANDLED;
} }
#define WS16C48_NGPIO 48 #define WS16C48_NGPIO 48
@@ -414,30 +220,37 @@ static const char *ws16c48_names[WS16C48_NGPIO] = {
"Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
}; };
static int ws16c48_irq_init_hw(struct gpio_chip *gc) static int ws16c48_irq_init_hw(struct regmap *const map)
{ {
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); int err;
/* Select Register Page 2; Unlock all I/O ports */ err = regmap_write(map, WS16C48_PAGE_LOCK, ENAB_PAGE);
iowrite8(0x80, &ws16c48gpio->reg->page_lock); if (err)
return err;
/* Disable interrupts for all lines */ /* Disable interrupts for all lines */
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]); err = regmap_write(map, WS16C48_ENAB + 0, 0x00);
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]); if (err)
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]); return err;
err = regmap_write(map, WS16C48_ENAB + 1, 0x00);
if (err)
return err;
err = regmap_write(map, WS16C48_ENAB + 2, 0x00);
if (err)
return err;
/* Select Register Page 3; Unlock all I/O ports */ return regmap_write(map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
return 0;
} }
static int ws16c48_probe(struct device *dev, unsigned int id) static int ws16c48_probe(struct device *dev, unsigned int id)
{ {
struct ws16c48_gpio *ws16c48gpio; struct ws16c48_gpio *ws16c48gpio;
const char *const name = dev_name(dev); const char *const name = dev_name(dev);
struct gpio_irq_chip *girq;
int err; int err;
struct gpio_regmap_config gpio_config = {};
void __iomem *regs;
struct regmap_irq_chip *chip;
struct regmap_irq_chip_data *chip_data;
ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
if (!ws16c48gpio) if (!ws16c48gpio)
@@ -449,50 +262,55 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
return -EBUSY; return -EBUSY;
} }
ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT); regs = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
if (!ws16c48gpio->reg) if (!regs)
return -ENOMEM; return -ENOMEM;
ws16c48gpio->chip.label = name; ws16c48gpio->map = devm_regmap_init_mmio(dev, regs, &ws16c48_regmap_config);
ws16c48gpio->chip.parent = dev; if (IS_ERR(ws16c48gpio->map))
ws16c48gpio->chip.owner = THIS_MODULE; return dev_err_probe(dev, PTR_ERR(ws16c48gpio->map),
ws16c48gpio->chip.base = -1; "Unable to initialize register map\n");
ws16c48gpio->chip.ngpio = WS16C48_NGPIO;
ws16c48gpio->chip.names = ws16c48_names;
ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
ws16c48gpio->chip.get = ws16c48_gpio_get;
ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
ws16c48gpio->chip.set = ws16c48_gpio_set;
ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
girq = &ws16c48gpio->chip.irq; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
gpio_irq_chip_set_chip(girq, &ws16c48_irqchip); if (!chip)
/* This will let us handle the parent IRQ in the driver */ return -ENOMEM;
girq->parent_handler = NULL;
girq->num_parents = 0; chip->name = name;
girq->parents = NULL; chip->status_base = WS16C48_INT_ID;
girq->default_type = IRQ_TYPE_NONE; chip->mask_base = WS16C48_ENAB;
girq->handler = handle_edge_irq; chip->ack_base = WS16C48_INT_ID;
girq->init_hw = ws16c48_irq_init_hw; chip->num_regs = 3;
chip->irqs = ws16c48_regmap_irqs;
chip->num_irqs = ARRAY_SIZE(ws16c48_regmap_irqs);
chip->handle_pre_irq = ws16c48_handle_pre_irq;
chip->handle_post_irq = ws16c48_handle_post_irq;
chip->handle_mask_sync = ws16c48_handle_mask_sync;
chip->set_type_config = ws16c48_set_type_config;
chip->irq_drv_data = ws16c48gpio;
raw_spin_lock_init(&ws16c48gpio->lock); raw_spin_lock_init(&ws16c48gpio->lock);
err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); /* Initialize to prevent spurious interrupts before we're ready */
if (err) { err = ws16c48_irq_init_hw(ws16c48gpio->map);
dev_err(dev, "GPIO registering failed (%d)\n", err); if (err)
return err; return err;
}
err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, err = devm_regmap_add_irq_chip(dev, ws16c48gpio->map, irq[id], 0, 0, chip, &chip_data);
name, ws16c48gpio); if (err)
if (err) { return dev_err_probe(dev, err, "IRQ registration failed\n");
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
return err;
}
return 0; gpio_config.parent = dev;
gpio_config.regmap = ws16c48gpio->map;
gpio_config.ngpio = WS16C48_NGPIO;
gpio_config.names = ws16c48_names;
gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
gpio_config.reg_set_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
/* Setting a GPIO to 0 allows it to be used as an input */
gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
gpio_config.ngpio_per_reg = WS16C48_NGPIO_PER_REG;
gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
} }
static struct isa_driver ws16c48_driver = { static struct isa_driver ws16c48_driver = {

View File

@@ -15,8 +15,8 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@@ -8,9 +8,9 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/regmap.h> #include <linux/regmap.h>
@@ -194,7 +194,7 @@ static const struct spi_device_id xra1403_ids[] = {
}; };
MODULE_DEVICE_TABLE(spi, xra1403_ids); MODULE_DEVICE_TABLE(spi, xra1403_ids);
static const struct of_device_id xra1403_spi_of_match[] __maybe_unused = { static const struct of_device_id xra1403_spi_of_match[] = {
{ .compatible = "exar,xra1403" }, { .compatible = "exar,xra1403" },
{}, {},
}; };
@@ -205,7 +205,7 @@ static struct spi_driver xra1403_driver = {
.id_table = xra1403_ids, .id_table = xra1403_ids,
.driver = { .driver = {
.name = "xra1403", .name = "xra1403",
.of_match_table = of_match_ptr(xra1403_spi_of_match), .of_match_table = xra1403_spi_of_match,
}, },
}; };

View File

@@ -176,8 +176,6 @@ static int zevio_gpio_probe(struct platform_device *pdev)
if (!controller) if (!controller)
return -ENOMEM; return -ENOMEM;
platform_set_drvdata(pdev, controller);
/* Copy our reference */ /* Copy our reference */
controller->chip = zevio_gpio_chip; controller->chip = zevio_gpio_chip;
controller->chip.parent = &pdev->dev; controller->chip.parent = &pdev->dev;

View File

@@ -128,7 +128,7 @@ static bool acpi_gpio_deferred_req_irqs_done;
static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) static int acpi_gpiochip_find(struct gpio_chip *gc, void *data)
{ {
return ACPI_HANDLE_FWNODE(gc->fwnode) == data; return device_match_acpi_handle(&gc->gpiodev->dev, data);
} }
/** /**

View File

@@ -230,9 +230,7 @@ static long linehandle_set_config(struct linehandle_state *lh,
return ret; return ret;
} }
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
GPIO_V2_LINE_CHANGED_CONFIG,
desc);
} }
return 0; return 0;
} }
@@ -414,8 +412,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
goto out_free_lh; goto out_free_lh;
} }
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
offset); offset);
@@ -555,6 +552,7 @@ struct line {
* @label: consumer label used to tag GPIO descriptors * @label: consumer label used to tag GPIO descriptors
* @num_lines: the number of lines in the lines array * @num_lines: the number of lines in the lines array
* @wait: wait queue that handles blocking reads of events * @wait: wait queue that handles blocking reads of events
* @device_unregistered_nb: notifier block for receiving gdev unregister events
* @event_buffer_size: the number of elements allocated in @events * @event_buffer_size: the number of elements allocated in @events
* @events: KFIFO for the GPIO events * @events: KFIFO for the GPIO events
* @seqno: the sequence number for edge events generated on all lines in * @seqno: the sequence number for edge events generated on all lines in
@@ -569,6 +567,7 @@ struct linereq {
const char *label; const char *label;
u32 num_lines; u32 num_lines;
wait_queue_head_t wait; wait_queue_head_t wait;
struct notifier_block device_unregistered_nb;
u32 event_buffer_size; u32 event_buffer_size;
DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event); DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event);
atomic_t seqno; atomic_t seqno;
@@ -610,6 +609,17 @@ struct linereq {
GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
GPIO_V2_LINE_EDGE_FLAGS) GPIO_V2_LINE_EDGE_FLAGS)
static int linereq_unregistered_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct linereq *lr = container_of(nb, struct linereq,
device_unregistered_nb);
wake_up_poll(&lr->wait, EPOLLIN | EPOLLERR);
return NOTIFY_OK;
}
static void linereq_put_event(struct linereq *lr, static void linereq_put_event(struct linereq *lr,
struct gpio_v2_line_event *le) struct gpio_v2_line_event *le)
{ {
@@ -1407,9 +1417,7 @@ static long linereq_set_config_unlocked(struct linereq *lr,
WRITE_ONCE(line->edflags, edflags); WRITE_ONCE(line->edflags, edflags);
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
GPIO_V2_LINE_CHANGED_CONFIG,
desc);
} }
return 0; return 0;
} }
@@ -1567,6 +1575,10 @@ static void linereq_free(struct linereq *lr)
{ {
unsigned int i; unsigned int i;
if (lr->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&lr->gdev->device_notifier,
&lr->device_unregistered_nb);
for (i = 0; i < lr->num_lines; i++) { for (i = 0; i < lr->num_lines; i++) {
if (lr->lines[i].desc) { if (lr->lines[i].desc) {
edge_detector_stop(&lr->lines[i]); edge_detector_stop(&lr->lines[i]);
@@ -1720,13 +1732,18 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
lr->lines[i].edflags = edflags; lr->lines[i].edflags = edflags;
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
offset); offset);
} }
lr->device_unregistered_nb.notifier_call = linereq_unregistered_notify;
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&lr->device_unregistered_nb);
if (ret)
goto out_free_linereq;
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
ret = fd; ret = fd;
@@ -1779,6 +1796,7 @@ out_free_linereq:
* @eflags: the event flags this line was requested with * @eflags: the event flags this line was requested with
* @irq: the interrupt that trigger in response to events on this GPIO * @irq: the interrupt that trigger in response to events on this GPIO
* @wait: wait queue that handles blocking reads of events * @wait: wait queue that handles blocking reads of events
* @device_unregistered_nb: notifier block for receiving gdev unregister events
* @events: KFIFO for the GPIO events * @events: KFIFO for the GPIO events
* @timestamp: cache for the timestamp storing it between hardirq * @timestamp: cache for the timestamp storing it between hardirq
* and IRQ thread, used to bring the timestamp close to the actual * and IRQ thread, used to bring the timestamp close to the actual
@@ -1791,6 +1809,7 @@ struct lineevent_state {
u32 eflags; u32 eflags;
int irq; int irq;
wait_queue_head_t wait; wait_queue_head_t wait;
struct notifier_block device_unregistered_nb;
DECLARE_KFIFO(events, struct gpioevent_data, 16); DECLARE_KFIFO(events, struct gpioevent_data, 16);
u64 timestamp; u64 timestamp;
}; };
@@ -1824,6 +1843,17 @@ static __poll_t lineevent_poll(struct file *file,
return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked); return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked);
} }
static int lineevent_unregistered_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct lineevent_state *le = container_of(nb, struct lineevent_state,
device_unregistered_nb);
wake_up_poll(&le->wait, EPOLLIN | EPOLLERR);
return NOTIFY_OK;
}
struct compat_gpioeevent_data { struct compat_gpioeevent_data {
compat_u64 timestamp; compat_u64 timestamp;
u32 id; u32 id;
@@ -1909,6 +1939,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
static void lineevent_free(struct lineevent_state *le) static void lineevent_free(struct lineevent_state *le)
{ {
if (le->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&le->gdev->device_notifier,
&le->device_unregistered_nb);
if (le->irq) if (le->irq)
free_irq(le->irq, le); free_irq(le->irq, le);
if (le->desc) if (le->desc)
@@ -2117,8 +2150,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
if (ret) if (ret)
goto out_free_le; goto out_free_le;
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
irq = gpiod_to_irq(desc); irq = gpiod_to_irq(desc);
if (irq <= 0) { if (irq <= 0) {
@@ -2137,6 +2169,12 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
INIT_KFIFO(le->events); INIT_KFIFO(le->events);
init_waitqueue_head(&le->wait); init_waitqueue_head(&le->wait);
le->device_unregistered_nb.notifier_call = lineevent_unregistered_notify;
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&le->device_unregistered_nb);
if (ret)
goto out_free_le;
/* Request a thread to read the events */ /* Request a thread to read the events */
ret = request_threaded_irq(irq, ret = request_threaded_irq(irq,
lineevent_irq_handler, lineevent_irq_handler,
@@ -2320,6 +2358,7 @@ struct gpio_chardev_data {
wait_queue_head_t wait; wait_queue_head_t wait;
DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32); DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32);
struct notifier_block lineinfo_changed_nb; struct notifier_block lineinfo_changed_nb;
struct notifier_block device_unregistered_nb;
unsigned long *watched_lines; unsigned long *watched_lines;
#ifdef CONFIG_GPIO_CDEV_V1 #ifdef CONFIG_GPIO_CDEV_V1
atomic_t watch_abi_version; atomic_t watch_abi_version;
@@ -2491,16 +2530,11 @@ static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
} }
#endif #endif
static struct gpio_chardev_data *
to_gpio_chardev_data(struct notifier_block *nb)
{
return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
}
static int lineinfo_changed_notify(struct notifier_block *nb, static int lineinfo_changed_notify(struct notifier_block *nb,
unsigned long action, void *data) unsigned long action, void *data)
{ {
struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb); struct gpio_chardev_data *cdev =
container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
struct gpio_v2_line_info_changed chg; struct gpio_v2_line_info_changed chg;
struct gpio_desc *desc = data; struct gpio_desc *desc = data;
int ret; int ret;
@@ -2522,6 +2556,18 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
return NOTIFY_OK; return NOTIFY_OK;
} }
static int gpio_device_unregistered_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct gpio_chardev_data *cdev = container_of(nb,
struct gpio_chardev_data,
device_unregistered_nb);
wake_up_poll(&cdev->wait, EPOLLIN | EPOLLERR);
return NOTIFY_OK;
}
static __poll_t lineinfo_watch_poll_unlocked(struct file *file, static __poll_t lineinfo_watch_poll_unlocked(struct file *file,
struct poll_table_struct *pollt) struct poll_table_struct *pollt)
{ {
@@ -2671,23 +2717,33 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
cdev->gdev = gpio_device_get(gdev); cdev->gdev = gpio_device_get(gdev);
cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; cdev->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify;
ret = blocking_notifier_chain_register(&gdev->notifier, ret = blocking_notifier_chain_register(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb); &cdev->lineinfo_changed_nb);
if (ret) if (ret)
goto out_free_bitmap; goto out_free_bitmap;
cdev->device_unregistered_nb.notifier_call =
gpio_device_unregistered_notify;
ret = blocking_notifier_chain_register(&gdev->device_notifier,
&cdev->device_unregistered_nb);
if (ret)
goto out_unregister_line_notifier;
file->private_data = cdev; file->private_data = cdev;
ret = nonseekable_open(inode, file); ret = nonseekable_open(inode, file);
if (ret) if (ret)
goto out_unregister_notifier; goto out_unregister_device_notifier;
up_read(&gdev->sem); up_read(&gdev->sem);
return ret; return ret;
out_unregister_notifier: out_unregister_device_notifier:
blocking_notifier_chain_unregister(&gdev->notifier, blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
out_unregister_line_notifier:
blocking_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb); &cdev->lineinfo_changed_nb);
out_free_bitmap: out_free_bitmap:
gpio_device_put(gdev); gpio_device_put(gdev);
@@ -2711,7 +2767,9 @@ static int gpio_chrdev_release(struct inode *inode, struct file *file)
struct gpio_device *gdev = cdev->gdev; struct gpio_device *gdev = cdev->gdev;
bitmap_free(cdev->watched_lines); bitmap_free(cdev->watched_lines);
blocking_notifier_chain_unregister(&gdev->notifier, blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
blocking_notifier_chain_unregister(&gdev->line_state_notifier,
&cdev->lineinfo_changed_nb); &cdev->lineinfo_changed_nb);
gpio_device_put(gdev); gpio_device_put(gdev);
kfree(cdev); kfree(cdev);
@@ -2753,4 +2811,5 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
void gpiolib_cdev_unregister(struct gpio_device *gdev) void gpiolib_cdev_unregister(struct gpio_device *gdev)
{ {
cdev_device_del(&gdev->chrdev, &gdev->dev); cdev_device_del(&gdev->chrdev, &gdev->dev);
blocking_notifier_call_chain(&gdev->device_notifier, 0, NULL);
} }

View File

@@ -1094,16 +1094,16 @@ int of_gpiochip_add(struct gpio_chip *chip)
if (ret) if (ret)
return ret; return ret;
fwnode_handle_get(chip->fwnode); of_node_get(np);
ret = of_gpiochip_scan_gpios(chip); ret = of_gpiochip_scan_gpios(chip);
if (ret) if (ret)
fwnode_handle_put(chip->fwnode); of_node_put(np);
return ret; return ret;
} }
void of_gpiochip_remove(struct gpio_chip *chip) void of_gpiochip_remove(struct gpio_chip *chip)
{ {
fwnode_handle_put(chip->fwnode); of_node_put(dev_of_node(&chip->gpiodev->dev));
} }

View File

@@ -700,6 +700,40 @@ void *gpiochip_get_data(struct gpio_chip *gc)
} }
EXPORT_SYMBOL_GPL(gpiochip_get_data); EXPORT_SYMBOL_GPL(gpiochip_get_data);
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev)
{
u32 ngpios = gc->ngpio;
int ret;
if (ngpios == 0) {
ret = device_property_read_u32(dev, "ngpios", &ngpios);
if (ret == -ENODATA)
/*
* -ENODATA means that there is no property found and
* we want to issue the error message to the user.
* Besides that, we want to return different error code
* to state that supplied value is not valid.
*/
ngpios = 0;
else if (ret)
return ret;
gc->ngpio = ngpios;
}
if (gc->ngpio == 0) {
chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
return -EINVAL;
}
if (gc->ngpio > FASTPATH_NGPIO)
chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
gc->ngpio, FASTPATH_NGPIO);
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_get_ngpios);
int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct lock_class_key *lock_key, struct lock_class_key *lock_key,
struct lock_class_key *request_key) struct lock_class_key *request_key)
@@ -707,17 +741,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct gpio_device *gdev; struct gpio_device *gdev;
unsigned long flags; unsigned long flags;
unsigned int i; unsigned int i;
u32 ngpios = 0;
int base = 0; int base = 0;
int ret = 0; int ret = 0;
/*
* If the calling driver did not initialize firmware node, do it here
* using the parent device, if any.
*/
if (!gc->fwnode && gc->parent)
gc->fwnode = dev_fwnode(gc->parent);
/* /*
* First: allocate and populate the internal stat container, and * First: allocate and populate the internal stat container, and
* set up the struct device. * set up the struct device.
@@ -732,7 +758,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gc->gpiodev = gdev; gc->gpiodev = gdev;
gpiochip_set_data(gc, data); gpiochip_set_data(gc, data);
device_set_node(&gdev->dev, gc->fwnode); /*
* If the calling driver did not initialize firmware node,
* do it here using the parent device, if any.
*/
if (gc->fwnode)
device_set_node(&gdev->dev, gc->fwnode);
else if (gc->parent)
device_set_node(&gdev->dev, dev_fwnode(gc->parent));
gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
if (gdev->id < 0) { if (gdev->id < 0) {
@@ -753,36 +786,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
else else
gdev->owner = THIS_MODULE; gdev->owner = THIS_MODULE;
/* ret = gpiochip_get_ngpios(gc, &gdev->dev);
* Try the device properties if the driver didn't supply the number if (ret)
* of GPIO lines.
*/
ngpios = gc->ngpio;
if (ngpios == 0) {
ret = device_property_read_u32(&gdev->dev, "ngpios", &ngpios);
if (ret == -ENODATA)
/*
* -ENODATA means that there is no property found and
* we want to issue the error message to the user.
* Besides that, we want to return different error code
* to state that supplied value is not valid.
*/
ngpios = 0;
else if (ret)
goto err_free_dev_name;
gc->ngpio = ngpios;
}
if (gc->ngpio == 0) {
chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
ret = -EINVAL;
goto err_free_dev_name; goto err_free_dev_name;
}
if (gc->ngpio > FASTPATH_NGPIO)
chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
gc->ngpio, FASTPATH_NGPIO);
gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL); gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
if (!gdev->descs) { if (!gdev->descs) {
@@ -841,7 +847,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
init_rwsem(&gdev->sem); init_rwsem(&gdev->sem);
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
@@ -947,7 +954,7 @@ err_print_message:
/* failures here can mean systems won't boot... */ /* failures here can mean systems won't boot... */
if (ret != -EPROBE_DEFER) { if (ret != -EPROBE_DEFER) {
pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
base, base + (int)ngpios - 1, base, base + (int)gc->ngpio - 1,
gc->label ? : "generic", ret); gc->label ? : "generic", ret);
} }
return ret; return ret;
@@ -1292,12 +1299,14 @@ static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
ops->free = irq_domain_free_irqs_common; ops->free = irq_domain_free_irqs_common;
} }
static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
{ {
struct irq_domain *domain;
if (!gc->irq.child_to_parent_hwirq || if (!gc->irq.child_to_parent_hwirq ||
!gc->irq.fwnode) { !gc->irq.fwnode) {
chip_err(gc, "missing irqdomain vital data\n"); chip_err(gc, "missing irqdomain vital data\n");
return -EINVAL; return ERR_PTR(-EINVAL);
} }
if (!gc->irq.child_offset_to_irq) if (!gc->irq.child_offset_to_irq)
@@ -1309,7 +1318,7 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops);
gc->irq.domain = irq_domain_create_hierarchy( domain = irq_domain_create_hierarchy(
gc->irq.parent_domain, gc->irq.parent_domain,
0, 0,
gc->ngpio, gc->ngpio,
@@ -1317,12 +1326,12 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
&gc->irq.child_irq_domain_ops, &gc->irq.child_irq_domain_ops,
gc); gc);
if (!gc->irq.domain) if (!domain)
return -ENOMEM; return ERR_PTR(-ENOMEM);
gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip); gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
return 0; return domain;
} }
static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@@ -1366,9 +1375,9 @@ EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
#else #else
static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
{ {
return -EINVAL; return ERR_PTR(-EINVAL);
} }
static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@@ -1445,6 +1454,19 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.xlate = irq_domain_xlate_twocell, .xlate = irq_domain_xlate_twocell,
}; };
static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc)
{
struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
struct irq_domain *domain;
domain = irq_domain_create_simple(fwnode, gc->ngpio, gc->irq.first,
&gpiochip_domain_ops, gc);
if (!domain)
return ERR_PTR(-EINVAL);
return domain;
}
/* /*
* TODO: move these activate/deactivate in under the hierarchicial * TODO: move these activate/deactivate in under the hierarchicial
* irqchip implementation as static once SPMI and SSBI (all external * irqchip implementation as static once SPMI and SSBI (all external
@@ -1623,6 +1645,31 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
} }
} }
static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc,
struct irq_domain *domain,
bool allocated_externally)
{
if (!domain)
return -EINVAL;
if (gc->to_irq)
chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
gc->to_irq = gpiochip_to_irq;
gc->irq.domain = domain;
gc->irq.domain_is_allocated_externally = allocated_externally;
/*
* Using barrier() here to prevent compiler from reordering
* gc->irq.initialized before adding irqdomain.
*/
barrier();
gc->irq.initialized = true;
return 0;
}
/** /**
* gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip
* @gc: the GPIO chip to add the IRQ chip to * @gc: the GPIO chip to add the IRQ chip to
@@ -1635,8 +1682,10 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
{ {
struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev); struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
struct irq_chip *irqchip = gc->irq.chip; struct irq_chip *irqchip = gc->irq.chip;
struct irq_domain *domain;
unsigned int type; unsigned int type;
unsigned int i; unsigned int i;
int ret;
if (!irqchip) if (!irqchip)
return 0; return 0;
@@ -1657,28 +1706,18 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
"%pfw: Ignoring %u default trigger\n", fwnode, type)) "%pfw: Ignoring %u default trigger\n", fwnode, type))
type = IRQ_TYPE_NONE; type = IRQ_TYPE_NONE;
if (gc->to_irq)
chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
gc->to_irq = gpiochip_to_irq;
gc->irq.default_type = type; gc->irq.default_type = type;
gc->irq.lock_key = lock_key; gc->irq.lock_key = lock_key;
gc->irq.request_key = request_key; gc->irq.request_key = request_key;
/* If a parent irqdomain is provided, let's build a hierarchy */ /* If a parent irqdomain is provided, let's build a hierarchy */
if (gpiochip_hierarchy_is_hierarchical(gc)) { if (gpiochip_hierarchy_is_hierarchical(gc)) {
int ret = gpiochip_hierarchy_add_domain(gc); domain = gpiochip_hierarchy_create_domain(gc);
if (ret)
return ret;
} else { } else {
gc->irq.domain = irq_domain_create_simple(fwnode, domain = gpiochip_simple_create_domain(gc);
gc->ngpio,
gc->irq.first,
&gpiochip_domain_ops,
gc);
if (!gc->irq.domain)
return -EINVAL;
} }
if (IS_ERR(domain))
return PTR_ERR(domain);
if (gc->irq.parent_handler) { if (gc->irq.parent_handler) {
for (i = 0; i < gc->irq.num_parents; i++) { for (i = 0; i < gc->irq.num_parents; i++) {
@@ -1702,14 +1741,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
gpiochip_set_irq_hooks(gc); gpiochip_set_irq_hooks(gc);
/* ret = gpiochip_irqchip_add_allocated_domain(gc, domain, false);
* Using barrier() here to prevent compiler from reordering if (ret)
* gc->irq.initialized before initialization of above return ret;
* GPIO chip irq members.
*/
barrier();
gc->irq.initialized = true;
acpi_gpiochip_request_interrupts(gc); acpi_gpiochip_request_interrupts(gc);
@@ -1780,22 +1814,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc)
int gpiochip_irqchip_add_domain(struct gpio_chip *gc, int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
struct irq_domain *domain) struct irq_domain *domain)
{ {
if (!domain) return gpiochip_irqchip_add_allocated_domain(gc, domain, true);
return -EINVAL;
gc->to_irq = gpiochip_to_irq;
gc->irq.domain = domain;
gc->irq.domain_is_allocated_externally = true;
/*
* Using barrier() here to prevent compiler from reordering
* gc->irq.initialized before adding irqdomain.
*/
barrier();
gc->irq.initialized = true;
return 0;
} }
EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain); EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain);
@@ -2159,8 +2178,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
} }
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
GPIOLINE_CHANGED_RELEASED, desc);
return ret; return ret;
} }
@@ -3728,6 +3746,12 @@ int gpiod_set_array_value_cansleep(unsigned int array_size,
} }
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep); EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
{
blocking_notifier_call_chain(&desc->gdev->line_state_notifier,
action, desc);
}
/** /**
* gpiod_add_lookup_table() - register GPIO device consumers * gpiod_add_lookup_table() - register GPIO device consumers
* @table: table of consumers to register * @table: table of consumers to register
@@ -3995,8 +4019,7 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
blocking_notifier_call_chain(&desc->gdev->notifier, gpiod_line_state_notify(desc, GPIOLINE_CHANGED_REQUESTED);
GPIOLINE_CHANGED_REQUESTED, desc);
return desc; return desc;
} }

View File

@@ -9,12 +9,13 @@
#ifndef GPIOLIB_H #ifndef GPIOLIB_H
#define GPIOLIB_H #define GPIOLIB_H
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/err.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#define GPIOCHIP_NAME "gpiochip" #define GPIOCHIP_NAME "gpiochip"
@@ -38,8 +39,10 @@
* or name of the IP component in a System on Chip. * or name of the IP component in a System on Chip.
* @data: per-instance data assigned by the driver * @data: per-instance data assigned by the driver
* @list: links gpio_device:s together for traversal * @list: links gpio_device:s together for traversal
* @notifier: used to notify subscribers about lines being requested, released * @line_state_notifier: used to notify subscribers about lines being
* or reconfigured * requested, released or reconfigured
* @device_notifier: used to notify character device wait queues about the GPIO
* device being unregistered
* @sem: protects the structure from a NULL-pointer dereference of @chip by * @sem: protects the structure from a NULL-pointer dereference of @chip by
* user-space operations when the device gets unregistered during * user-space operations when the device gets unregistered during
* a hot-unplug event * a hot-unplug event
@@ -63,7 +66,8 @@ struct gpio_device {
const char *label; const char *label;
void *data; void *data;
struct list_head list; struct list_head list;
struct blocking_notifier_head notifier; struct blocking_notifier_head line_state_notifier;
struct blocking_notifier_head device_notifier;
struct rw_semaphore sem; struct rw_semaphore sem;
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
@@ -143,6 +147,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
extern spinlock_t gpio_lock; extern spinlock_t gpio_lock;
extern struct list_head gpio_devices; extern struct list_head gpio_devices;
void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
/** /**
* struct gpio_desc - Opaque descriptor for a GPIO * struct gpio_desc - Opaque descriptor for a GPIO
@@ -217,6 +222,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce); int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name, int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags); unsigned long lflags, enum gpiod_flags dflags);
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
/* /*
* Return the GPIO number of the passed descriptor relative to its chip * Return the GPIO number of the passed descriptor relative to its chip