gpio updates for v6.6

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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmTt18QACgkQEacuoBRx
 13IkWw/7Bri9D6XJZSaqITu1tXf0mCRskEpQDjgvEW5MpGt8K5nHcg08h5e5KB3j
 1ppsWUZfCCzWexPkQlIeaXDnxJNoz4qfmq5gjJI1IP5BDs/Cvr6IxGLTMptPwsT8
 381TH8eFIky1L7Q37PXL4Mc4lFO6lI9ZuH0kAgn7NpDNVTYnfLj7BUlR8kMBYhqc
 yzeDVrUqO7NtxxWHpSz511EihmF1vWQmtvk8YI1aglbFyVaWI0sZgMHHzyENArR+
 EIzNd49GHFOZpLdmATRsN885Aa6nFSEKnRNP1gqcDt8VziTdujs/L93Rqh4h3oCt
 VbUobg1Sb4qSlVX0YapNWX9FyTSXupgVMPyjRd52O+X01Yjau4I8YSOMNKQXqudl
 Y2jN7MoEdPYtYj7JGiTM035VZ6rKxZkY5k4Dx4ZKqoBMT+QSCvqBbBapwamW5+8U
 EBrnpiJfQmqltQP54sb7vIRQ0j925kR0HYruDhjgxMaow1m8KzUJ5dG1yVuT1NGr
 8PnMn+QjxO6IB2BbZX0656lDq0QRE7qyrlmQQ+8vI7nHqs9nINP4HHpht8T+FsHC
 sJ2HN6HnzJlFZVFw3Zu6pn4Fhdkto0tiYaWxtFg/1oi0G6tl1imLx08UIvS101Xg
 QkjaQQ+kaBL4KnmXiysP9zT/mOPlZneeE8ephqsXVtQc7YP/+sk=
 =t+zD
 -----END PGP SIGNATURE-----

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
commit f97e18a3f2
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,imx6ul-gpio
- fsl,imx7d-gpio
- fsl,imx8dxl-gpio
- fsl,imx8mm-gpio
- fsl,imx8mn-gpio
- fsl,imx8mp-gpio
- fsl,imx8mq-gpio
- fsl,imx8qm-gpio
- fsl,imx8qxp-gpio
- fsl,imxrt1050-gpio
- fsl,imxrt1170-gpio

View File

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

View File

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

View File

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

View File

@ -4195,7 +4195,7 @@ BROADCOM KONA GPIO DRIVER
M: Ray Jui <rjui@broadcom.com>
R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
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
BROADCOM MPI3 STORAGE CONTROLLER DRIVER

View File

@ -111,6 +111,9 @@ config GPIO_MAX730X
config GPIO_IDIO_16
tristate
select REGMAP_IRQ
select GPIOLIB_IRQCHIP
select GPIO_REGMAP
help
Enables support for the idio-16 library functions. The idio-16 library
provides functions to facilitate communication with devices within the
@ -191,7 +194,7 @@ config GPIO_RASPBERRYPI_EXP
config GPIO_BCM_KONA
bool "Broadcom Kona GPIO"
depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
depends on ARCH_BCM_MOBILE || COMPILE_TEST
help
Turn on GPIO support for Broadcom "Kona" chips.
@ -283,7 +286,7 @@ config GPIO_EXAR
config GPIO_GE_FPGA
bool "GE FPGA based GPIO"
depends on GE_FPGA
depends on GE_FPGA || COMPILE_TEST
select GPIO_GENERIC
help
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.
config GPIO_SIFIVE
bool "SiFive GPIO support"
tristate "SiFive GPIO support"
depends on OF_GPIO
select IRQ_DOMAIN_HIERARCHY
select GPIO_GENERIC
@ -858,6 +861,7 @@ config GPIO_104_DIO_48E
select REGMAP_IRQ
select GPIOLIB_IRQCHIP
select GPIO_I8255
select I8254
help
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
@ -868,7 +872,7 @@ config GPIO_104_IDIO_16
tristate "ACCES 104-IDIO-16 GPIO support"
depends on PC104
select ISA_BUS_API
select GPIOLIB_IRQCHIP
select REGMAP_MMIO
select GPIO_IDIO_16
help
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
@ -994,7 +998,10 @@ config GPIO_WINBOND
config GPIO_WS16C48
tristate "WinSystems WS16C48 GPIO support"
select ISA_BUS_API
select REGMAP_IRQ
select REGMAP_MMIO
select GPIOLIB_IRQCHIP
select GPIO_REGMAP
help
Enables GPIO support for the WinSystems WS16C48. The base port
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
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
tristate "Gateworks PLD GPIO Expander"
depends on OF_GPIO
@ -1640,7 +1658,7 @@ config GPIO_PCH
config GPIO_PCI_IDIO_16
tristate "ACCES PCI-IDIO-16 GPIO support"
select GPIOLIB_IRQCHIP
select REGMAP_MMIO
select GPIO_IDIO_16
help
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
tristate "ACCES PCIe-IDIO-24 GPIO support"
select REGMAP_IRQ
select REGMAP_MMIO
select GPIOLIB_IRQCHIP
select GPIO_REGMAP
help
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

View File

@ -52,6 +52,7 @@ obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.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_EIC_SPRD) += gpio-eic-sprd.o
obj-$(CONFIG_GPIO_ELKHARTLAKE) += gpio-elkhartlake.o

View File

@ -9,6 +9,7 @@
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i8254.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/isa.h>
@ -16,6 +17,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.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_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_NUM_PPI 2
@ -75,18 +79,20 @@ static const struct regmap_access_table dio48e_precious_table = {
.yes_ranges = dio48e_precious_ranges,
.n_yes_ranges = ARRAY_SIZE(dio48e_precious_ranges),
};
static const struct regmap_config dio48e_regmap_config = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
.max_register = 0xF,
.wr_table = &dio48e_wr_table,
.rd_table = &dio48e_rd_table,
.volatile_table = &dio48e_volatile_table,
.precious_table = &dio48e_precious_table,
.cache_type = REGCACHE_FLAT,
.use_raw_spinlock = true,
static const struct regmap_range pit_wr_ranges[] = {
regmap_reg_range(0x0, 0x3),
};
static const struct regmap_range pit_rd_ranges[] = {
regmap_reg_range(0x0, 0x2),
};
static const struct regmap_access_table pit_wr_table = {
.yes_ranges = pit_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(pit_wr_ranges),
};
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 */
@ -102,14 +108,56 @@ static const struct regmap_irq dio48e_regmap_irqs[] = {
/**
* struct dio48e_gpio - GPIO device private data structure
* @lock: synchronization lock to prevent I/O race conditions
* @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
*/
struct dio48e_gpio {
raw_spinlock_t lock;
struct regmap *map;
void __iomem *regs;
unsigned long flags;
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,
const unsigned int mask_buf_def,
const unsigned int mask_buf,
@ -176,6 +224,9 @@ static int dio48e_probe(struct device *dev, unsigned int id)
struct i8255_regmap_config config = {};
void __iomem *regs;
struct regmap *map;
struct regmap_config dio48e_regmap_config;
struct regmap_config pit_regmap_config;
struct i8254_regmap_config pit_config;
int err;
struct regmap_irq_chip *chip;
struct dio48e_gpio *dio48egpio;
@ -187,21 +238,58 @@ static int dio48e_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
if (!dio48egpio)
return -ENOMEM;
regs = devm_ioport_map(dev, base[id], DIO48E_EXTENT);
if (!regs)
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);
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map),
"Unable to initialize register map\n");
dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL);
if (!dio48egpio)
return -ENOMEM;
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);
if (!chip)
return -ENOMEM;
@ -225,6 +313,12 @@ static int dio48e_probe(struct device *dev, unsigned int id)
if (err)
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.map = map;
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_DESCRIPTION("ACCES 104-DIO-48E GPIO driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(I8254);

View File

@ -6,19 +6,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.
*/
#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/irqdesc.h>
#include <linux/irq.h>
#include <linux/isa.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/types.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_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
/**
* struct idio_16_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
* @lock: synchronization lock to prevent I/O race conditions
* @irq_mask: I/O bits affected by interrupts
* @reg: I/O address offset for the device registers
* @state: ACCES IDIO-16 device state
*/
struct idio_16_gpio {
struct gpio_chip chip;
raw_spinlock_t lock;
unsigned long irq_mask;
struct idio_16 __iomem *reg;
struct idio_16_state state;
static const struct regmap_range idio_16_wr_ranges[] = {
regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x4),
};
static const struct regmap_range idio_16_rd_ranges[] = {
regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x5),
};
static const struct regmap_range idio_16_precious_ranges[] = {
regmap_reg_range(0x2, 0x2),
};
static const struct regmap_access_table idio_16_wr_table = {
.yes_ranges = idio_16_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges),
};
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,
unsigned int offset)
{
if (idio_16_get_direction(offset))
return GPIO_LINE_DIRECTION_IN;
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);
/* Only input lines (GPIO 16-31) support interrupts */
#define IDIO_16_REGMAP_IRQ(_id) \
[16 + _id] = { \
.mask = BIT(_id), \
.type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
}
}
static void idio_16_irq_unmask(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);
const unsigned long prev_irq_mask = idio16gpio->irq_mask;
unsigned long flags;
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 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 */
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 */
IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */
IDIO_16_REGMAP_IRQ(15), /* 15 */
};
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)
{
struct idio_16_gpio *idio16gpio;
const char *const name = dev_name(dev);
struct gpio_irq_chip *girq;
int err;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
if (!idio16gpio)
return -ENOMEM;
struct idio_16_regmap_config config = {};
void __iomem *regs;
struct regmap *map;
if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
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;
}
idio16gpio->reg = devm_ioport_map(dev, base[id], IDIO_16_EXTENT);
if (!idio16gpio->reg)
regs = devm_ioport_map(dev, base[id], IDIO_16_EXTENT);
if (!regs)
return -ENOMEM;
idio16gpio->chip.label = name;
idio16gpio->chip.parent = dev;
idio16gpio->chip.owner = THIS_MODULE;
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;
map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config);
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n");
idio_16_state_init(&idio16gpio->state);
/* FET off states are represented by bit values of "1" */
bitmap_fill(idio16gpio->state.out_state, IDIO_16_NOUT);
config.parent = dev;
config.map = map;
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;
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;
return devm_idio_16_regmap_register(dev, &config);
}
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.owner = THIS_MODULE;
platform_set_drvdata(pdev, priv);
return devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv);
}

View File

@ -9,6 +9,7 @@
#include <linux/gpio/driver.h>
#include <linux/mfd/altera-a10sr.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
@ -104,7 +105,7 @@ static struct platform_driver altr_a10sr_gpio_driver = {
.probe = altr_a10sr_gpio_probe,
.driver = {
.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);

View File

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

View File

@ -8,12 +8,14 @@
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/gpio/driver.h>
#include <linux/of_device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irqdomain.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 GPIO_PER_BANK 32
@ -62,7 +64,6 @@ struct bcm_kona_gpio {
struct gpio_chip gpio_chip;
struct irq_domain *irq_domain;
struct bcm_kona_gpio_bank *banks;
struct platform_device *pdev;
};
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)
{
struct device *dev = &pdev->dev;
const struct of_device_id *match;
struct bcm_kona_gpio_bank *bank;
struct bcm_kona_gpio *kona_gpio;
struct gpio_chip *chip;
int ret;
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);
if (!kona_gpio)
return -ENOMEM;
@ -596,15 +590,13 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
if (!kona_gpio->banks)
return -ENOMEM;
kona_gpio->pdev = pdev;
platform_set_drvdata(pdev, kona_gpio);
chip->parent = dev;
chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK;
kona_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
chip->ngpio,
&bcm_kona_irq_ops,
kona_gpio);
kona_gpio->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
chip->ngpio,
&bcm_kona_irq_ops,
kona_gpio);
if (!kona_gpio->irq_domain) {
dev_err(dev, "Couldn't allocate IRQ domain\n");
return -ENXIO;

View File

@ -3,12 +3,12 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
enum gio_reg_index {
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);
}
static const struct of_device_id __maybe_unused clps711x_gpio_ids[] = {
static const struct of_device_id clps711x_gpio_ids[] = {
{ .compatible = "cirrus,ep7209-gpio" },
{ }
};
@ -76,7 +76,7 @@ MODULE_DEVICE_TABLE(of, clps711x_gpio_ids);
static struct platform_driver clps711x_gpio_driver = {
.driver = {
.name = "clps711x-gpio",
.of_match_table = of_match_ptr(clps711x_gpio_ids),
.of_match_table = clps711x_gpio_ids,
},
.probe = clps711x_gpio_probe,
};

View File

@ -8,7 +8,7 @@
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#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++) {
chips->irqs[i] = platform_get_irq(pdev, i);
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);

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

View File

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

View File

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

View File

@ -1,29 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for GE FPGA based GPIO
*
* Author: Martyn Welch <martyn.welch@ge.com>
*
* 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)
* Interrupt configuration - interrupts are always generated the FPGA relies on
* the I/O interrupt controllers mask to stop them propergating
* Configuration of output modes (totem-pole/open-drain).
* Interrupt configuration - interrupts are always generated, the FPGA relies
* 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/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_IN 0x04
@ -52,46 +51,39 @@ MODULE_DEVICE_TABLE(of, gef_gpio_ids);
static int __init gef_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
void __iomem *regs;
int ret;
gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
return -ENOMEM;
regs = of_iomap(pdev->dev.of_node, 0);
if (!regs)
return -ENOMEM;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
ret = bgpio_init(gc, &pdev->dev, 4, regs + GEF_GPIO_IN,
regs + GEF_GPIO_OUT, NULL, NULL,
regs + GEF_GPIO_DIRECT, BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret) {
dev_err(&pdev->dev, "bgpio_init failed\n");
goto err0;
}
ret = bgpio_init(gc, dev, 4, regs + GEF_GPIO_IN, regs + GEF_GPIO_OUT,
NULL, NULL, regs + GEF_GPIO_DIRECT,
BGPIOF_BIG_ENDIAN_BYTE_ORDER);
if (ret)
return dev_err_probe(dev, ret, "bgpio_init failed\n");
/* Setup pointers to chip functions */
gc->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
if (!gc->label) {
ret = -ENOMEM;
goto err0;
}
gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));
if (!gc->label)
return -ENOMEM;
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 */
ret = devm_gpiochip_add_data(&pdev->dev, gc, NULL);
ret = devm_gpiochip_add_data(dev, gc, NULL);
if (ret)
goto err0;
return dev_err_probe(dev, ret, "GPIO chip registration failed\n");
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 = {
@ -103,5 +95,5 @@ static struct platform_driver gef_gpio_driver = {
module_platform_driver_probe(gef_gpio_driver, gef_gpio_probe);
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");

View File

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

View File

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

View File

@ -3,143 +3,169 @@
* GPIO library for the ACCES IDIO-16 family
* 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/io.h>
#include <linux/gpio/regmap.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include "gpio-idio-16.h"
#define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16
/**
* idio_16_get - get signal value at signal offset
* @reg: ACCES IDIO-16 device registers
* @state: ACCES IDIO-16 device state
* @offset: offset of signal to get
*
* Returns the signal value (0=low, 1=high) for the signal at @offset.
*/
int idio_16_get(struct idio_16 __iomem *const reg,
struct idio_16_state *const state, const unsigned long offset)
#define IDIO_16_DAT_BASE 0x0
#define IDIO_16_OUT_BASE IDIO_16_DAT_BASE
#define IDIO_16_IN_BASE (IDIO_16_DAT_BASE + 1)
#define IDIO_16_CLEAR_INTERRUPT 0x1
#define IDIO_16_ENABLE_IRQ 0x2
#define IDIO_16_DEACTIVATE_INPUT_FILTERS 0x3
#define IDIO_16_DISABLE_IRQ IDIO_16_ENABLE_IRQ
#define IDIO_16_INTERRUPT_STATUS 0x6
#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)
return test_bit(offset, state->out_state);
/* exit early if no change since the previous mask */
if (mask_buf == prev_mask)
return 0;
if (offset < 24)
return !!(ioread8(&reg->in0_7) & (mask >> IDIO_16_NOUT));
/* remember the current mask for the next mask sync */
data->irq_mask = mask_buf;
if (offset < 32)
return !!(ioread8(&reg->in8_15) & (mask >> 24));
/* if all previously masked, enable interrupts when unmasking */
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
* @reg: ACCES IDIO-16 device registers
* @state: ACCES IDIO-16 device state
* @mask: mask of signals to get
* @bits: bitmap to store signal values
* devm_idio_16_regmap_register - Register an IDIO-16 GPIO device
* @dev: device that is registering this IDIO-16 GPIO device
* @config: configuration for idio_16_regmap_config
*
* 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,
struct idio_16_state *const state,
const unsigned long *const mask,
unsigned long *const bits)
int devm_idio_16_regmap_register(struct device *const dev,
const struct idio_16_regmap_config *const config)
{
unsigned long flags;
const unsigned long out_mask = GENMASK(IDIO_16_NOUT - 1, 0);
struct gpio_regmap_config gpio_config = {};
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 (*mask & GENMASK(23, 16))
bitmap_set_value8(bits, ioread8(&reg->in0_7), 16);
if (*mask & GENMASK(31, 24))
bitmap_set_value8(bits, ioread8(&reg->in8_15), 24);
if (!config->map)
return -EINVAL;
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);
/**
* 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);
EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register);
MODULE_AUTHOR("William Breathitt Gray");
MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library");

View File

@ -3,69 +3,30 @@
#ifndef _IDIO_16_H_
#define _IDIO_16_H_
#include <linux/spinlock.h>
#include <linux/types.h>
struct device;
struct regmap;
struct regmap_irq;
/**
* struct idio_16 - IDIO-16 registers structure
* @out0_7: Read: FET Drive Outputs 0-7
* Write: FET Drive Outputs 0-7
* @in0_7: Read: Isolated Inputs 0-7
* Write: Clear Interrupt
* @irq_ctl: Read: Enable IRQ
* Write: Disable IRQ
* @filter_ctl: Read: Activate Input Filters 0-15
* 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_regmap_config - Configuration for the IDIO-16 register map
* @parent: parent device
* @map: regmap for the IDIO-16 device
* @regmap_irqs: descriptors for individual IRQs
* @num_regmap_irqs: number of IRQ descriptors
* @irq: IRQ number for the IDIO-16 device
* @no_status: device has no status register
* @filters: device has input filters
*/
struct idio_16 {
u8 out0_7;
u8 in0_7;
u8 irq_ctl;
u8 filter_ctl;
u8 out8_15;
u8 in8_15;
u8 irq_status;
struct idio_16_regmap_config {
struct device *parent;
struct regmap *map;
const struct regmap_irq *regmap_irqs;
int num_regmap_irqs;
unsigned int irq;
bool no_status;
bool filters;
};
#define IDIO_16_NOUT 16
/**
* 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);
int devm_idio_16_regmap_register(struct device *dev, const struct idio_16_regmap_config *config);
#endif /* _IDIO_16_H_ */

View File

@ -6,6 +6,7 @@
* to control the PIN resources on SCU domain.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
@ -103,7 +104,7 @@ static int imx_scu_gpio_probe(struct platform_device *pdev)
gc = &priv->chip;
gc->base = -1;
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->get = imx_scu_gpio_get;
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 = {
.driver = {
.name = "ixp4xx-gpio",
.of_match_table = of_match_ptr(ixp4xx_gpio_of_match),
.of_match_table = ixp4xx_gpio_of_match,
},
.probe = ixp4xx_gpio_probe,
};

View File

@ -8,7 +8,6 @@
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.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.direction_output = logicvc_gpio_direction_output;
platform_set_drvdata(pdev, 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.parent = &pdev->dev;
platform_set_drvdata(pdev, lp3943_gpio);
return devm_gpiochip_add_data(&pdev->dev, &lp3943_gpio->chip,
lp3943_gpio);
}

View File

@ -525,17 +525,15 @@ static int lpc32xx_gpio_probe(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id lpc32xx_gpio_of_match[] = {
{ .compatible = "nxp,lpc3220-gpio", },
{ },
};
#endif
static struct platform_driver lpc32xx_gpio_driver = {
.driver = {
.name = "lpc32xx-gpio",
.of_match_table = of_match_ptr(lpc32xx_gpio_of_match),
.of_match_table = lpc32xx_gpio_of_match,
},
.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);
}
#ifdef CONFIG_OF
static const struct of_device_id max3191x_of_id[] = {
{ .compatible = "maxim,max31910" },
{ .compatible = "maxim,max31911" },
@ -468,7 +467,6 @@ static const struct of_device_id max3191x_of_id[] = {
{ }
};
MODULE_DEVICE_TABLE(of, max3191x_of_id);
#endif
static const struct spi_device_id max3191x_spi_id[] = {
{ "max31910" },
@ -484,7 +482,7 @@ MODULE_DEVICE_TABLE(spi, max3191x_spi_id);
static struct spi_driver max3191x_driver = {
.driver = {
.name = "max3191x",
.of_match_table = of_match_ptr(max3191x_of_id),
.of_match_table = max3191x_of_id,
},
.probe = max3191x_probe,
.remove = max3191x_remove,

View File

@ -18,8 +18,6 @@
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/platform_data/max732x.h>
#include <linux/of.h>
/*
* 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);
#ifdef CONFIG_OF
static const struct of_device_id max732x_of_table[] = {
{ .compatible = "maxim,max7319" },
{ .compatible = "maxim,max7320" },
@ -128,7 +125,6 @@ static const struct of_device_id max732x_of_table[] = {
{ }
};
MODULE_DEVICE_TABLE(of, max732x_of_table);
#endif
struct max732x_chip {
struct gpio_chip gpio_chip;
@ -709,7 +705,7 @@ static int max732x_probe(struct i2c_client *client)
static struct i2c_driver max732x_driver = {
.driver = {
.name = "max732x",
.of_match_table = of_match_ptr(max732x_of_table),
.of_match_table = max732x_of_table,
},
.probe = max732x_probe,
.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->threaded = true;
platform_set_drvdata(pdev, mgpio);
ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio);
if (ret < 0) {
dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n");

View File

@ -10,11 +10,11 @@
#include <linux/io.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/of_device.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.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 */
#include <linux/bitfield.h>
@ -19,6 +19,8 @@
* gpio[1]: HOST_GPIO32->HOST_GPIO55
*/
#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
@ -158,6 +160,26 @@ static const struct irq_chip gpio_mlxbf3_irqchip = {
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)
{
struct device *dev = &pdev->dev;
@ -197,6 +219,7 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
gc->request = gpiochip_generic_request;
gc->free = gpiochip_generic_free;
gc->owner = THIS_MODULE;
gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges;
irq = platform_get_irq(pdev, 0);
if (irq >= 0) {
@ -243,6 +266,7 @@ static struct platform_driver mlxbf3_gpio_driver = {
};
module_platform_driver(mlxbf3_gpio_driver);
MODULE_SOFTDEP("pre: pinctrl-mlxbf3");
MODULE_DESCRIPTION("NVIDIA BlueField-3 GPIO Driver");
MODULE_AUTHOR("Asmaa Mnebhi <asmaa@nvidia.com>");
MODULE_LICENSE("Dual BSD/GPL");

View File

@ -60,6 +60,8 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/of.h>
#include <linux/of_device.h>
#include "gpiolib.h"
static void bgpio_write8(void __iomem *reg, unsigned long data)
{
writeb(data, reg);
@ -614,10 +616,15 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
gc->parent = dev;
gc->label = dev_name(dev);
gc->base = -1;
gc->ngpio = gc->bgpio_bits;
gc->request = bgpio_request;
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);
if (ret)
return ret;

View File

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

View File

@ -9,12 +9,10 @@
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/io.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/mod_devicetable.h>
#include <linux/slab.h>

View File

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

View File

@ -17,12 +17,12 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
#include <linux/gpio/driver.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/bug.h>
#define IMX_SCU_WAKEUP_OFF 0
@ -62,6 +62,7 @@ struct mxc_gpio_port {
struct clk *clk;
int irq;
int irq_high;
void (*mx_irq_handler)(struct irq_desc *desc);
struct irq_domain *domain;
struct gpio_chip gc;
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);
}
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)
{
struct device_node *np = pdev->dev.of_node;
@ -416,19 +452,17 @@ static int mxc_gpio_probe(struct platform_device *pdev)
return port->irq;
/* 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))
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"))
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 */
writel(0, port->base + GPIO_IMR);
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
* is more robust and easier.
*/
irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
} else {
/* setup one handler for each entry */
irq_set_chained_handler_and_data(port->irq,
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);
}
port->irq_high = -1;
port->mx_irq_handler = mx2_gpio_irq_handler;
} else
port->mx_irq_handler = mx3_gpio_irq_handler;
mxc_update_irq_chained_handler(port, true);
err = bgpio_init(&port->gc, &pdev->dev, 4,
port->base + GPIO_PSR,
port->base + GPIO_DR, NULL,
@ -459,8 +487,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
if (err)
goto out_bgio;
port->gc.request = gpiochip_generic_request;
port->gc.free = gpiochip_generic_free;
port->gc.request = mxc_gpio_request;
port->gc.free = mxc_gpio_free;
port->gc.to_irq = mxc_gpio_to_irq;
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
@ -482,6 +510,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
goto out_bgio;
}
irq_domain_set_pm_device(port->domain, &pdev->dev);
/* gpio-mxc can be a generic irq chip */
err = mxc_gpio_init_gc(port, irq_base);
if (err < 0)
@ -490,13 +520,15 @@ static int mxc_gpio_probe(struct platform_device *pdev)
list_add_tail(&port->node, &mxc_gpio_ports);
platform_set_drvdata(pdev, port);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
out_irqdomain_remove:
irq_domain_remove(port->domain);
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);
return err;
}
@ -572,7 +604,35 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
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 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;
}
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 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 = {
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)
{
struct mxc_gpio_port *port;
int ret;
/* walk through all ports */
list_for_each_entry(port, &mxc_gpio_ports, node) {
ret = clk_prepare_enable(port->clk);
if (ret)
return ret;
mxc_gpio_save_regs(port);
clk_disable_unprepare(port->clk);
}
@ -625,6 +690,7 @@ static void mxc_gpio_syscore_resume(void)
return;
}
mxc_gpio_restore_regs(port);
clk_disable_unprepare(port->clk);
}
}
@ -638,7 +704,7 @@ static struct platform_driver mxc_gpio_driver = {
.name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids,
.suppress_bind_attrs = true,
.pm = &mxc_gpio_dev_pm_ops,
.pm = pm_ptr(&mxc_gpio_dev_pm_ops),
},
.probe = mxc_gpio_probe,
};

View File

@ -14,7 +14,6 @@
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.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");
if (port->id < 0)
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->irq = platform_get_irq(pdev, 0);
if (port->irq < 0)

View File

@ -22,7 +22,6 @@
#include <linux/pm_runtime.h>
#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.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->irq = platform_get_irq(pdev, 0);
if (bank->irq <= 0) {
if (!bank->irq)
bank->irq = -ENXIO;
return dev_err_probe(dev, bank->irq, "can't get irq resource\n");
}
if (bank->irq < 0)
return bank->irq;
bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE;

View File

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

View File

@ -108,6 +108,7 @@ static const struct i2c_device_id pca953x_id[] = {
{ "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca6424", 24 | PCA953X_TYPE | PCA_INT, },
{ "tca9538", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca9539", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca9554", 8 | PCA953X_TYPE | PCA_INT, },
{ "xra1202", 8 | PCA953X_TYPE },
@ -1051,7 +1052,6 @@ out:
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_chip *chip;
int irq_base = 0;
@ -1090,6 +1090,9 @@ static int pca953x_probe(struct i2c_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");
if (IS_ERR(reg))
@ -1102,20 +1105,6 @@ static int pca953x_probe(struct i2c_client *client)
}
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);
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,tca6416", .data = OF_953X(16, 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 = "onnn,cat9554", .data = OF_953X( 8, PCA_INT), },

View File

@ -30,7 +30,7 @@ struct pca9570_chip_data {
/**
* struct pca9570 - GPIO driver data
* @chip: GPIO controller chip
* @p_data: GPIO controller platform data
* @chip_data: GPIO controller platform data
* @lock: Protects write sequences
* @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);
static const struct of_device_id pcf857x_of_table[] = {
{ .compatible = "nxp,pcf8574" },
{ .compatible = "nxp,pcf8574a" },
{ .compatible = "nxp,pca8574" },
{ .compatible = "nxp,pca9670" },
{ .compatible = "nxp,pca9672" },
{ .compatible = "nxp,pca9674" },
{ .compatible = "nxp,pcf8575" },
{ .compatible = "nxp,pca8575" },
{ .compatible = "nxp,pca9671" },
{ .compatible = "nxp,pca9673" },
{ .compatible = "nxp,pca9675" },
{ .compatible = "maxim,max7328" },
{ .compatible = "maxim,max7329" },
{ .compatible = "nxp,pcf8574", (void *)8 },
{ .compatible = "nxp,pcf8574a", (void *)8 },
{ .compatible = "nxp,pca8574", (void *)8 },
{ .compatible = "nxp,pca9670", (void *)8 },
{ .compatible = "nxp,pca9672", (void *)8 },
{ .compatible = "nxp,pca9674", (void *)8 },
{ .compatible = "nxp,pcf8575", (void *)16 },
{ .compatible = "nxp,pca8575", (void *)16 },
{ .compatible = "nxp,pca9671", (void *)16 },
{ .compatible = "nxp,pca9673", (void *)16 },
{ .compatible = "nxp,pca9675", (void *)16 },
{ .compatible = "maxim,max7328", (void *)8 },
{ .compatible = "maxim,max7329", (void *)8 },
{ }
};
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)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct pcf857x *gpio;
unsigned int n_latch = 0;
int status;
@ -296,7 +295,7 @@ static int pcf857x_probe(struct i2c_client *client)
gpio->chip.set_multiple = pcf857x_set_multiple;
gpio->chip.direction_input = pcf857x_input;
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
* these parts, notably for output. It has a low-resolution

View File

@ -5,214 +5,75 @@
*/
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irqdesc.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include "gpio-idio-16.h"
/**
* struct idio_16_gpio - GPIO device private data structure
* @chip: instance of the gpio_chip
* @lock: synchronization lock to prevent I/O race conditions
* @reg: I/O address offset for the GPIO device registers
* @state: ACCES IDIO-16 device state
* @irq_mask: I/O bits affected by interrupts
*/
struct idio_16_gpio {
struct gpio_chip chip;
raw_spinlock_t lock;
struct idio_16 __iomem *reg;
struct idio_16_state state;
unsigned long irq_mask;
static const struct regmap_range idio_16_wr_ranges[] = {
regmap_reg_range(0x0, 0x2), regmap_reg_range(0x3, 0x4),
};
static const struct regmap_range idio_16_rd_ranges[] = {
regmap_reg_range(0x1, 0x2), regmap_reg_range(0x5, 0x6),
};
static const struct regmap_range idio_16_precious_ranges[] = {
regmap_reg_range(0x2, 0x2),
};
static const struct regmap_access_table idio_16_wr_table = {
.yes_ranges = idio_16_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_16_wr_ranges),
};
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,
unsigned int offset)
{
if (idio_16_get_direction(offset))
return GPIO_LINE_DIRECTION_IN;
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);
/* Only input lines (GPIO 16-31) support interrupts */
#define IDIO_16_REGMAP_IRQ(_id) \
[16 + _id] = { \
.mask = BIT(2), \
.type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
}
gpiochip_disable_irq(chip, irqd_to_hwirq(data));
}
static void idio_16_irq_unmask(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));
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 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 */
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 */
IDIO_16_REGMAP_IRQ(12), IDIO_16_REGMAP_IRQ(13), IDIO_16_REGMAP_IRQ(14), /* 12-14 */
IDIO_16_REGMAP_IRQ(15), /* 15 */
};
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)
{
struct device *const dev = &pdev->dev;
struct idio_16_gpio *idio16gpio;
int err;
const size_t pci_bar_index = 2;
const char *const name = pci_name(pdev);
struct gpio_irq_chip *girq;
idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
if (!idio16gpio)
return -ENOMEM;
struct idio_16_regmap_config config = {};
void __iomem *regs;
struct regmap *map;
err = pcim_enable_device(pdev);
if (err) {
@ -226,53 +87,20 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
regs = pcim_iomap_table(pdev)[pci_bar_index];
/* Deactivate input filters */
iowrite8(0, &idio16gpio->reg->filter_ctl);
map = devm_regmap_init_mmio(dev, regs, &idio_16_regmap_config);
if (IS_ERR(map))
return dev_err_probe(dev, PTR_ERR(map), "Unable to initialize register map\n");
idio16gpio->chip.label = name;
idio16gpio->chip.parent = dev;
idio16gpio->chip.owner = THIS_MODULE;
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;
config.parent = dev;
config.map = map;
config.regmap_irqs = idio_16_regmap_irqs;
config.num_regmap_irqs = ARRAY_SIZE(idio_16_regmap_irqs);
config.irq = pdev->irq;
config.filters = true;
idio_16_state_init(&idio16gpio->state);
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;
return devm_idio_16_regmap_register(dev, &config);
}
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,
* PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/irqdesc.h>
#include <linux/err.h>
#include <linux/gpio/regmap.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@ -59,422 +58,224 @@
#define PLX_PEX8311_PCI_LCS_INTCSR 0x68
#define INTCSR_INTERNAL_PCI_WIRE BIT(8)
#define INTCSR_LOCAL_INPUT BIT(11)
#define IDIO_24_ENABLE_IRQ (INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT)
/**
* struct idio_24_gpio_reg - GPIO device registers structure
* @out0_7: Read: FET Outputs 0-7
* Write: FET Outputs 0-7
* @out8_15: Read: FET Outputs 8-15
* Write: FET Outputs 8-15
* @out16_23: Read: FET Outputs 16-23
* Write: FET Outputs 16-23
* @ttl_out0_7: Read: TTL/CMOS Outputs 0-7
* Write: TTL/CMOS Outputs 0-7
* @in0_7: Read: Isolated Inputs 0-7
* Write: Reserved
* @in8_15: Read: Isolated Inputs 8-15
* Write: Reserved
* @in16_23: Read: Isolated Inputs 16-23
* Write: Reserved
* @ttl_in0_7: Read: TTL/CMOS Inputs 0-7
* Write: Reserved
* @cos0_7: Read: COS Status Inputs 0-7
* Write: COS Clear Inputs 0-7
* @cos8_15: Read: COS Status Inputs 8-15
* Write: COS Clear Inputs 8-15
* @cos16_23: Read: COS Status Inputs 16-23
* Write: COS Clear Inputs 16-23
* @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7
* Write: COS Clear TTL/CMOS 0-7
* @ctl: Read: Control Register
* Write: Control Register
* @reserved: Read: Reserved
* Write: Reserved
* @cos_enable: Read: COS Enable
* Write: COS Enable
* @soft_reset: Read: IRQ Output Pin Status
* Write: Software Board Reset
*/
struct idio_24_gpio_reg {
u8 out0_7;
u8 out8_15;
u8 out16_23;
u8 ttl_out0_7;
u8 in0_7;
u8 in8_15;
u8 in16_23;
u8 ttl_in0_7;
u8 cos0_7;
u8 cos8_15;
u8 cos16_23;
u8 cos_ttl0_7;
u8 ctl;
u8 reserved;
u8 cos_enable;
u8 soft_reset;
#define IDIO_24_OUT_BASE 0x0
#define IDIO_24_TTLCMOS_OUT_REG 0x3
#define IDIO_24_IN_BASE 0x4
#define IDIO_24_TTLCMOS_IN_REG 0x7
#define IDIO_24_COS_STATUS_BASE 0x8
#define IDIO_24_CONTROL_REG 0xC
#define IDIO_24_COS_ENABLE 0xE
#define IDIO_24_SOFT_RESET 0xF
#define CONTROL_REG_OUT_MODE BIT(1)
#define COS_ENABLE_RISING BIT(1)
#define COS_ENABLE_FALLING BIT(4)
#define COS_ENABLE_BOTH (COS_ENABLE_RISING | COS_ENABLE_FALLING)
static const struct regmap_config pex8311_intcsr_regmap_config = {
.name = "pex8311_intcsr",
.reg_bits = 32,
.reg_stride = 1,
.reg_base = PLX_PEX8311_PCI_LCS_INTCSR,
.val_bits = 32,
.io_port = true,
};
static const struct regmap_range idio_24_wr_ranges[] = {
regmap_reg_range(0x0, 0x3), regmap_reg_range(0x8, 0xC),
regmap_reg_range(0xE, 0xF),
};
static const struct regmap_range idio_24_rd_ranges[] = {
regmap_reg_range(0x0, 0xC), regmap_reg_range(0xE, 0xF),
};
static const struct regmap_range idio_24_volatile_ranges[] = {
regmap_reg_range(0x4, 0xB), regmap_reg_range(0xF, 0xF),
};
static const struct regmap_access_table idio_24_wr_table = {
.yes_ranges = idio_24_wr_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_24_wr_ranges),
};
static const struct regmap_access_table idio_24_rd_table = {
.yes_ranges = idio_24_rd_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_24_rd_ranges),
};
static const struct regmap_access_table idio_24_volatile_table = {
.yes_ranges = idio_24_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(idio_24_volatile_ranges),
};
static const struct regmap_config idio_24_regmap_config = {
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
.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
* @chip: instance of the gpio_chip
* @map: regmap for the device
* @lock: synchronization lock to prevent I/O race conditions
* @reg: I/O address offset for the GPIO device registers
* @irq_mask: I/O bits affected by interrupts
* @irq_type: type configuration for IRQs
*/
struct idio_24_gpio {
struct gpio_chip chip;
struct regmap *map;
raw_spinlock_t lock;
__u8 __iomem *plx;
struct idio_24_gpio_reg __iomem *reg;
unsigned long irq_mask;
u8 irq_type;
};
static int idio_24_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
static int idio_24_handle_mask_sync(const int index, const unsigned int mask_buf_def,
const unsigned int mask_buf, void *const irq_drv_data)
{
struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
const unsigned long out_mode_mask = BIT(1);
const unsigned int type_mask = COS_ENABLE_BOTH << index;
struct idio_24_gpio *const idio24gpio = irq_drv_data;
u8 type;
int ret;
/* FET Outputs */
if (offset < 24)
return GPIO_LINE_DIRECTION_OUT;
raw_spin_lock(&idio24gpio->lock);
/* Isolated Inputs */
if (offset < 48)
return GPIO_LINE_DIRECTION_IN;
/* if all are masked, then disable interrupts, else set to type */
type = (mask_buf == mask_buf_def) ? ~type_mask : idio24gpio->irq_type;
/* TTL/CMOS I/O */
/* OUT MODE = 1 when TTL/CMOS Output Mode is set */
if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
return GPIO_LINE_DIRECTION_OUT;
ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, type_mask, type);
return GPIO_LINE_DIRECTION_IN;
raw_spin_unlock(&idio24gpio->lock);
return ret;
}
static int idio_24_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
static int idio_24_set_type_config(unsigned int **const buf, const unsigned int type,
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);
unsigned long flags;
unsigned int ctl_state;
const unsigned long out_mode_mask = BIT(1);
const unsigned int offset = irq_data->reg_offset;
const unsigned int rising = COS_ENABLE_RISING << offset;
const unsigned int falling = COS_ENABLE_FALLING << offset;
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 */
if (offset > 47) {
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
/* Clear TTL/CMOS Output Mode */
ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask;
iowrite8(ctl_state, &idio24gpio->reg->ctl);
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
new = rising;
break;
case IRQ_TYPE_EDGE_FALLING:
new = falling;
break;
case IRQ_TYPE_EDGE_BOTH:
new = mask;
break;
default:
return -EINVAL;
}
return 0;
}
raw_spin_lock(&idio24gpio->lock);
static int idio_24_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
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);
/* replace old bitmap with new bitmap */
idio24gpio->irq_type = (idio24gpio->irq_type & ~mask) | (new & mask);
/* TTL/CMOS I/O */
if (offset > 47) {
raw_spin_lock_irqsave(&idio24gpio->lock, flags);
ret = regmap_read(idio24gpio->map, IDIO_24_COS_ENABLE, &cos_enable);
if (ret)
goto exit_unlock;
/* Set TTL/CMOS Output Mode */
ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask;
iowrite8(ctl_state, &idio24gpio->reg->ctl);
raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
/* if COS is currently enabled then update the edge type */
if (cos_enable & mask) {
ret = regmap_update_bits(idio24gpio->map, IDIO_24_COS_ENABLE, mask,
idio24gpio->irq_type);
if (ret)
goto exit_unlock;
}
chip->set(chip, offset, value);
return 0;
exit_unlock:
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 long offset_mask = BIT(offset % 8);
const unsigned long out_mode_mask = BIT(1);
const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG;
const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG;
struct regmap *const map = gpio_regmap_get_drvdata(gpio);
int err;
unsigned int ctrl_reg;
/* FET Outputs */
if (offset < 8)
return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask);
switch (base) {
case IDIO_24_OUT_BASE:
*mask = BIT(offset % IDIO_24_NGPIO_PER_REG);
if (offset < 16)
return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask);
if (offset < 24)
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;
/* FET Outputs */
if (offset < 24) {
*reg = IDIO_24_OUT_BASE + out_stride;
return 0;
}
/* set requested bit states */
out_state &= ~gpio_mask;
out_state |= bitmask;
/* Isolated Inputs */
if (offset < 48) {
*reg = IDIO_24_IN_BASE + in_stride;
return 0;
}
/* write bits for current gpio port (port 6 is TTL GPIO) */
if (index < 6)
iowrite8(out_state, ports[index]);
else
iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
err = regmap_read(map, IDIO_24_CONTROL_REG, &ctrl_reg);
if (err)
return err;
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)
{
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;
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)
*reg = IDIO_24_CONTROL_REG;
*mask = CONTROL_REG_OUT_MODE;
return 0;
default:
/* Should never reach this path */
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
@ -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_bar_index = 2;
const char *const name = pci_name(pdev);
struct gpio_irq_chip *girq;
idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
if (!idio24gpio)
return -ENOMEM;
struct gpio_regmap_config gpio_config = {};
void __iomem *pex8311_regs;
void __iomem *idio_24_regs;
struct regmap *intcsr_map;
struct regmap_irq_chip *chip;
struct regmap_irq_chip_data *chip_data;
err = pcim_enable_device(pdev);
if (err) {
@ -514,57 +316,72 @@ static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}
idio24gpio->plx = pcim_iomap_table(pdev)[pci_plx_bar_index];
idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
pex8311_regs = pcim_iomap_table(pdev)[pci_plx_bar_index];
idio_24_regs = pcim_iomap_table(pdev)[pci_bar_index];
idio24gpio->chip.label = name;
idio24gpio->chip.parent = dev;
idio24gpio->chip.owner = THIS_MODULE;
idio24gpio->chip.base = -1;
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;
intcsr_map = devm_regmap_init_mmio(dev, pex8311_regs, &pex8311_intcsr_regmap_config);
if (IS_ERR(intcsr_map))
return dev_err_probe(dev, PTR_ERR(intcsr_map),
"Unable to initialize PEX8311 register map\n");
girq = &idio24gpio->chip.irq;
gpio_irq_chip_set_chip(girq, &idio_24_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;
idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
if (!idio24gpio)
return -ENOMEM;
idio24gpio->map = devm_regmap_init_mmio(dev, idio_24_regs, &idio_24_regmap_config);
if (IS_ERR(idio24gpio->map))
return dev_err_probe(dev, PTR_ERR(idio24gpio->map),
"Unable to initialize register map\n");
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 */
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
* input
*/
iowrite8((INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT) >> 8,
idio24gpio->plx + PLX_PEX8311_PCI_LCS_INTCSR + 1);
err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
err = regmap_update_bits(intcsr_map, 0x0, IDIO_24_ENABLE_IRQ, IDIO_24_ENABLE_IRQ);
if (err)
return err;
}
err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
name, idio24gpio);
if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
return err;
}
err = devm_regmap_add_irq_chip(dev, idio24gpio->map, pdev->irq, 0, 0, chip, &chip_data);
if (err)
return dev_err_probe(dev, err, "IRQ registration failed\n");
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[] = {

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
* Copyright (C) 2015-2023 Texas Instruments Incorporated - https://www.ti.com/
* Andrew Davis <afd@ti.com>
*/
#include <linux/bitmap.h>
@ -116,6 +116,11 @@ static const struct gpio_chip template_chip = {
.can_sleep = true,
};
static void pisosr_mutex_destroy(void *lock)
{
mutex_destroy(lock);
}
static int pisosr_gpio_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
@ -126,8 +131,6 @@ static int pisosr_gpio_probe(struct spi_device *spi)
if (!gpio)
return -ENOMEM;
spi_set_drvdata(spi, gpio);
gpio->chip = template_chip;
gpio->chip.parent = dev;
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");
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) {
dev_err(dev, "Unable to register gpiochip\n");
return ret;
@ -155,15 +161,6 @@ static int pisosr_gpio_probe(struct spi_device *spi)
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[] = {
{ "pisosr-gpio", },
{ /* sentinel */ }
@ -182,11 +179,10 @@ static struct spi_driver pisosr_gpio_driver = {
.of_match_table = pisosr_gpio_of_match_table,
},
.probe = pisosr_gpio_probe,
.remove = pisosr_gpio_remove,
.id_table = pisosr_gpio_id_table,
};
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_LICENSE("GPL v2");

View File

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

View File

@ -20,7 +20,6 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.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);
}
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" },
{ }
};
@ -243,7 +243,7 @@ MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids);
static struct platform_driver rpi_exp_gpio_driver = {
.driver = {
.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,
};

View File

@ -121,8 +121,6 @@ static int rc5t583_gpio_probe(struct platform_device *pdev)
if (pdata && 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,
rc5t583_gpio);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -6,10 +6,10 @@
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/of_irq.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
@ -150,6 +150,7 @@ static const struct irq_chip sifive_gpio_irqchip = {
.irq_disable = sifive_gpio_irq_disable,
.irq_eoi = sifive_gpio_irq_eoi,
.irq_set_affinity = sifive_gpio_irq_set_affinity,
.irq_set_wake = irq_chip_set_wake_parent,
.flags = IRQCHIP_IMMUTABLE,
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)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct device_node *irq_parent;
struct irq_domain *parent;
struct gpio_irq_chip *girq;
struct sifive_gpio *chip;
int ret, ngpio, i;
int ret, ngpio;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@ -202,31 +201,22 @@ static int sifive_gpio_probe(struct platform_device *pdev)
if (IS_ERR(chip->regs))
return PTR_ERR(chip->regs);
ngpio = of_irq_count(node);
if (ngpio > SIFIVE_GPIO_MAX) {
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);
for (ngpio = 0; ngpio < SIFIVE_GPIO_MAX; ngpio++) {
ret = platform_get_irq_optional(pdev, ngpio);
if (ret < 0)
return ret;
chip->irq_number[i] = ret;
break;
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,
chip->base + SIFIVE_GPIO_INPUT_VAL,
@ -254,7 +244,7 @@ static int sifive_gpio_probe(struct platform_device *pdev)
chip->gc.owner = THIS_MODULE;
girq = &chip->gc.irq;
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->child_to_parent_hwirq = sifive_gpio_child_to_parent_hwirq;
girq->handler = handle_bad_irq;
@ -277,4 +267,8 @@ static struct platform_driver sifive_gpio_driver = {
.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
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/configfs.h>
#include <linux/device.h>
@ -68,7 +69,7 @@ static int gpio_sim_apply_pull(struct gpio_sim_chip *chip,
gc = &chip->gc;
desc = &gc->gpiodev->descs[offset];
mutex_lock(&chip->lock);
guard(mutex)(&chip->lock);
if (test_bit(FLAG_REQUESTED, &desc->flags) &&
!test_bit(FLAG_IS_OUT, &desc->flags)) {
@ -104,29 +105,24 @@ set_value:
set_pull:
__assign_bit(offset, chip->pull_map, value);
mutex_unlock(&chip->lock);
return 0;
}
static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_sim_chip *chip = gpiochip_get_data(gc);
int ret;
mutex_lock(&chip->lock);
ret = !!test_bit(offset, chip->value_map);
mutex_unlock(&chip->lock);
guard(mutex)(&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)
{
struct gpio_sim_chip *chip = gpiochip_get_data(gc);
mutex_lock(&chip->lock);
__assign_bit(offset, chip->value_map, value);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
__assign_bit(offset, chip->value_map, value);
}
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);
mutex_lock(&chip->lock);
bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio);
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);
mutex_lock(&chip->lock);
bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
bitmap_replace(chip->value_map, chip->value_map, bits, mask,
gc->ngpio);
}
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);
mutex_lock(&chip->lock);
__clear_bit(offset, chip->direction_map);
__assign_bit(offset, chip->value_map, value);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock) {
__clear_bit(offset, chip->direction_map);
__assign_bit(offset, chip->value_map, value);
}
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);
mutex_lock(&chip->lock);
__set_bit(offset, chip->direction_map);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
__set_bit(offset, chip->direction_map);
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);
int direction;
mutex_lock(&chip->lock);
direction = !!test_bit(offset, chip->direction_map);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
direction = !!test_bit(offset, chip->direction_map);
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);
mutex_lock(&chip->lock);
__assign_bit(offset, chip->value_map, !!test_bit(offset, chip->pull_map));
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
__assign_bit(offset, chip->value_map,
!!test_bit(offset, chip->pull_map));
}
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);
int val;
mutex_lock(&chip->lock);
val = !!test_bit(line_attr->offset, chip->value_map);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
val = !!test_bit(line_attr->offset, chip->value_map);
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);
int pull;
mutex_lock(&chip->lock);
pull = !!test_bit(line_attr->offset, chip->pull_map);
mutex_unlock(&chip->lock);
scoped_guard(mutex, &chip->lock)
pull = !!test_bit(line_attr->offset, chip->pull_map);
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
* 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.
*/
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)
{
char *dup, *trimmed;
char *trimmed;
dup = kstrndup(str, count, GFP_KERNEL);
if (!dup)
trimmed = kstrndup(skip_spaces(str), count, GFP_KERNEL);
if (!trimmed)
return NULL;
trimmed = strstrip(dup);
memmove(dup, trimmed, strlen(trimmed) + 1);
return dup;
return strim(trimmed);
}
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 platform_device *pdev;
int ret;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
pdev = dev->pdev;
if (pdev)
ret = sprintf(page, "%s\n", dev_name(&pdev->dev));
else
ret = sprintf(page, "gpio-sim.%d\n", dev->id);
mutex_unlock(&dev->lock);
return sprintf(page, "%s\n", dev_name(&pdev->dev));
return ret;
return sprintf(page, "gpio-sim.%d\n", dev->id);
}
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);
bool live;
mutex_lock(&dev->lock);
live = gpio_sim_device_is_live_unlocked(dev);
mutex_unlock(&dev->lock);
scoped_guard(mutex, &dev->lock)
live = gpio_sim_device_is_live_unlocked(dev);
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];
unsigned int prop_idx = 0, line_names_size = 0;
struct fwnode_handle *swnode;
char **line_names;
char **line_names __free(kfree) = NULL;
memset(properties, 0, sizeof(properties));
@ -871,9 +854,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
"gpio-line-names",
line_names, line_names_size);
swnode = fwnode_create_software_node(properties, parent);
kfree(line_names);
return swnode;
return fwnode_create_software_node(properties, parent);
}
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)
return ret;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if ((!live && !gpio_sim_device_is_live_unlocked(dev)) ||
(live && gpio_sim_device_is_live_unlocked(dev)))
if (live == gpio_sim_device_is_live_unlocked(dev))
ret = -EPERM;
else if (live)
ret = gpio_sim_device_activate_unlocked(dev);
else
gpio_sim_device_deactivate_unlocked(dev);
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_bank_get_device(bank);
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))
ret = device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name);
else
ret = sprintf(page, "none\n");
mutex_unlock(&dev->lock);
return device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name);
return ret;
return sprintf(page, "none\n");
}
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_device *dev = gpio_sim_bank_get_device(bank);
int ret;
mutex_lock(&dev->lock);
ret = sprintf(page, "%s\n", bank->label ?: "");
mutex_unlock(&dev->lock);
guard(mutex)(&dev->lock);
return ret;
return sprintf(page, "%s\n", bank->label ?: "");
}
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);
char *trimmed;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) {
mutex_unlock(&dev->lock);
if (!trimmed)
return -ENOMEM;
}
kfree(bank->label);
bank->label = trimmed;
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_bank_get_device(bank);
int ret;
mutex_lock(&dev->lock);
ret = sprintf(page, "%u\n", bank->num_lines);
mutex_unlock(&dev->lock);
guard(mutex)(&dev->lock);
return ret;
return sprintf(page, "%u\n", bank->num_lines);
}
static ssize_t
@ -1134,16 +1098,13 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item,
if (num_lines == 0)
return -EINVAL;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return -EBUSY;
}
bank->num_lines = num_lines;
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_line_get_device(line);
int ret;
mutex_lock(&dev->lock);
ret = sprintf(page, "%s\n", line->name ?: "");
mutex_unlock(&dev->lock);
guard(mutex)(&dev->lock);
return ret;
return sprintf(page, "%s\n", line->name ?: "");
}
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);
char *trimmed;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) {
mutex_unlock(&dev->lock);
if (!trimmed)
return -ENOMEM;
}
kfree(line->name);
line->name = trimmed;
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_hog_get_device(hog);
int ret;
mutex_lock(&dev->lock);
ret = sprintf(page, "%s\n", hog->name ?: "");
mutex_unlock(&dev->lock);
guard(mutex)(&dev->lock);
return ret;
return sprintf(page, "%s\n", hog->name ?: "");
}
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);
char *trimmed;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) {
mutex_unlock(&dev->lock);
if (!trimmed)
return -ENOMEM;
}
kfree(hog->name);
hog->name = trimmed;
mutex_unlock(&dev->lock);
return count;
}
@ -1257,9 +1200,8 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item,
char *repr;
int dir;
mutex_lock(&dev->lock);
dir = hog->dir;
mutex_unlock(&dev->lock);
scoped_guard(mutex, &dev->lock)
dir = hog->dir;
switch (dir) {
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_device *dev = gpio_sim_hog_get_device(hog);
char *trimmed;
int dir;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return -EBUSY;
}
trimmed = gpio_sim_strdup_trimmed(page, count);
if (!trimmed) {
mutex_unlock(&dev->lock);
return -ENOMEM;
}
if (strcmp(trimmed, "input") == 0)
if (sysfs_streq(page, "input"))
dir = GPIOD_IN;
else if (strcmp(trimmed, "output-high") == 0)
else if (sysfs_streq(page, "output-high"))
dir = GPIOD_OUT_HIGH;
else if (strcmp(trimmed, "output-low") == 0)
else if (sysfs_streq(page, "output-low"))
dir = GPIOD_OUT_LOW;
else
dir = -EINVAL;
kfree(trimmed);
if (dir < 0) {
mutex_unlock(&dev->lock);
return dir;
}
return -EINVAL;
hog->dir = dir;
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_hog_get_device(hog);
mutex_lock(&dev->lock);
line->hog = NULL;
mutex_unlock(&dev->lock);
scoped_guard(mutex, &dev->lock)
line->hog = NULL;
kfree(hog->name);
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)
return ERR_PTR(-EINVAL);
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
hog = kzalloc(sizeof(*hog), GFP_KERNEL);
if (!hog) {
mutex_unlock(&dev->lock);
if (!hog)
return ERR_PTR(-ENOMEM);
}
config_item_init_type_name(&hog->item, name,
&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;
line->hog = hog;
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_line_get_device(line);
mutex_lock(&dev->lock);
list_del(&line->siblings);
mutex_unlock(&dev->lock);
scoped_guard(mutex, &dev->lock)
list_del(&line->siblings);
kfree(line->name);
kfree(line);
@ -1430,18 +1348,14 @@ gpio_sim_bank_config_make_line_group(struct config_group *group,
if (ret != 1 || nchar != strlen(name))
return ERR_PTR(-EINVAL);
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return ERR_PTR(-EBUSY);
}
line = kzalloc(sizeof(*line), GFP_KERNEL);
if (!line) {
mutex_unlock(&dev->lock);
if (!line)
return ERR_PTR(-ENOMEM);
}
config_group_init_type_name(&line->group, name,
&gpio_sim_line_config_type);
@ -1450,8 +1364,6 @@ gpio_sim_bank_config_make_line_group(struct config_group *group,
line->offset = offset;
list_add_tail(&line->siblings, &bank->line_list);
mutex_unlock(&dev->lock);
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_device *dev = gpio_sim_bank_get_device(bank);
mutex_lock(&dev->lock);
list_del(&bank->siblings);
mutex_unlock(&dev->lock);
scoped_guard(mutex, &dev->lock)
list_del(&bank->siblings);
kfree(bank->label);
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_bank *bank;
mutex_lock(&dev->lock);
guard(mutex)(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev)) {
mutex_unlock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
return ERR_PTR(-EBUSY);
}
bank = kzalloc(sizeof(*bank), GFP_KERNEL);
if (!bank) {
mutex_unlock(&dev->lock);
if (!bank)
return ERR_PTR(-ENOMEM);
}
config_group_init_type_name(&bank->group, name,
&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);
list_add_tail(&bank->siblings, &dev->bank_list);
mutex_unlock(&dev->lock);
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);
mutex_lock(&dev->lock);
if (gpio_sim_device_is_live_unlocked(dev))
gpio_sim_device_deactivate_unlocked(dev);
mutex_unlock(&dev->lock);
scoped_guard(mutex, &dev->lock) {
if (gpio_sim_device_is_live_unlocked(dev))
gpio_sim_device_deactivate_unlocked(dev);
}
mutex_destroy(&dev->lock);
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 *
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;
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);
id = ida_alloc(&gpio_sim_ida, GFP_KERNEL);
if (id < 0) {
kfree(dev);
if (id < 0)
return ERR_PTR(id);
}
config_group_init_type_name(&dev->group, name,
&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;
init_completion(&dev->probe_completion);
return &dev->group;
return &no_free_ptr(dev)->group;
}
static struct configfs_group_operations gpio_sim_config_group_ops = {

View File

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

View File

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

View File

@ -9,7 +9,6 @@
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.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;
}
platform_set_drvdata(pdev, priv);
return devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
}

View File

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

View File

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

View File

@ -256,8 +256,6 @@ static int timbgpio_probe(struct platform_device *pdev)
if (err)
return err;
platform_set_drvdata(pdev, tgpio);
/* make sure to disable interrupts */
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 = {
.driver = {
.name = "tps65218-gpio",
.of_match_table = of_match_ptr(tps65218_dt_match)
.of_match_table = tps65218_dt_match,
},
.probe = tps65218_gpio_probe,
.id_table = tps65218_gpio_id_table,

View File

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

View File

@ -15,7 +15,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/mfd/tps65910.h>
#include <linux/of_device.h>
#include <linux/of.h>
struct tps65910_gpio {
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);
platform_set_drvdata(pdev, gpio);
chip = &gpio->chip;
chip->label = "gpio-tqmx86";
chip->owner = THIS_MODULE;

View File

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

View File

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

View File

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

View File

@ -17,7 +17,6 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.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)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct vf610_gpio_port *port;
struct gpio_chip *gc;
struct gpio_irq_chip *girq;
@ -319,7 +317,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
gc->parent = dev;
gc->label = dev_name(dev);
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->free = gpiochip_generic_free;

View File

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

View File

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

View File

@ -3,19 +3,18 @@
* GPIO driver for the WinSystems WS16C48
* Copyright (C) 2016 William Breathitt Gray
*/
#include <linux/bitmap.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/irqdesc.h>
#include <linux/err.h>
#include <linux/gpio/regmap.h>
#include <linux/irq.h>
#include <linux/isa.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/regmap.h>
#include <linux/types.h>
#define WS16C48_EXTENT 11
@ -31,371 +30,178 @@ static unsigned int num_irq;
module_param_hw_array(irq, uint, irq, &num_irq, 0);
MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
/**
* struct ws16c48_reg - device register structure
* @port: Port 0 through 5 I/O
* @int_pending: Interrupt Pending
* @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0)
* @pol_enab_int_id: Interrupt polarity, enable, and ID
*/
struct ws16c48_reg {
u8 port[6];
u8 int_pending;
u8 page_lock;
u8 pol_enab_int_id[3];
#define WS16C48_DAT_BASE 0x0
#define WS16C48_PAGE_LOCK 0x7
#define WS16C48_PAGE_BASE 0x8
#define WS16C48_POL WS16C48_PAGE_BASE
#define WS16C48_ENAB WS16C48_PAGE_BASE
#define WS16C48_INT_ID WS16C48_PAGE_BASE
#define PAGE_LOCK_PAGE_FIELD GENMASK(7, 6)
#define POL_PAGE u8_encode_bits(1, PAGE_LOCK_PAGE_FIELD)
#define ENAB_PAGE u8_encode_bits(2, PAGE_LOCK_PAGE_FIELD)
#define INT_ID_PAGE u8_encode_bits(3, PAGE_LOCK_PAGE_FIELD)
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
* @chip: instance of the gpio_chip
* @io_state: bit I/O state (whether bit is set to input or output)
* @out_state: output bits state
* @map: regmap for the device
* @lock: synchronization lock to prevent I/O race conditions
* @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 gpio_chip chip;
unsigned char io_state[6];
unsigned char out_state[6];
struct regmap *map;
raw_spinlock_t lock;
unsigned long irq_mask;
unsigned long flow_mask;
struct ws16c48_reg __iomem *reg;
u8 irq_mask[WS16C48_NUM_IRQS / WS16C48_NGPIO_PER_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);
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
if (ws16c48gpio->io_state[port] & mask)
return GPIO_LINE_DIRECTION_IN;
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);
/* Lock to prevent Page/Lock register change while we handle IRQ */
raw_spin_lock(&ws16c48gpio->lock);
return 0;
}
static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
static int ws16c48_handle_post_irq(void *const irq_drv_data) __releases(&ws16c48gpio->lock)
{
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
unsigned long flags;
struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
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);
raw_spin_unlock(&ws16c48gpio->lock);
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);
const unsigned port = offset / 8;
const unsigned mask = BIT(offset % 8);
struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
unsigned long flags;
unsigned port_state;
int ret = 0;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
/* ensure that GPIO is set for input */
if (!(ws16c48gpio->io_state[port] & mask)) {
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
return -EINVAL;
}
/* exit early if no change since the last mask sync */
if (mask_buf == ws16c48gpio->irq_mask[index])
goto exit_unlock;
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);
return !!(port_state & mask);
return ret;
}
static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
unsigned long *mask, unsigned long *bits)
static int ws16c48_set_type_config(unsigned int **const buf, const unsigned int type,
const struct regmap_irq *const irq_data, const int idx,
void *const irq_drv_data)
{
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 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);
struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
unsigned int polarity;
unsigned long flags;
int ret;
raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
/* 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;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
ws16c48gpio->flow_mask |= mask;
polarity = irq_data->mask;
break;
case IRQ_TYPE_EDGE_FALLING:
ws16c48gpio->flow_mask &= ~mask;
polarity = 0;
break;
default:
raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
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 */
iowrite8(0x40, &ws16c48gpio->reg->page_lock);
ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, POL_PAGE);
if (ret)
goto exit_unlock;
/* 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 */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
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);
return 0;
}
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;
return ret;
}
#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"
};
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 */
iowrite8(0x80, &ws16c48gpio->reg->page_lock);
err = regmap_write(map, WS16C48_PAGE_LOCK, ENAB_PAGE);
if (err)
return err;
/* Disable interrupts for all lines */
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]);
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]);
iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]);
err = regmap_write(map, WS16C48_ENAB + 0, 0x00);
if (err)
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 */
iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
return 0;
return regmap_write(map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
}
static int ws16c48_probe(struct device *dev, unsigned int id)
{
struct ws16c48_gpio *ws16c48gpio;
const char *const name = dev_name(dev);
struct gpio_irq_chip *girq;
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);
if (!ws16c48gpio)
@ -449,50 +262,55 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
return -EBUSY;
}
ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
if (!ws16c48gpio->reg)
regs = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
if (!regs)
return -ENOMEM;
ws16c48gpio->chip.label = name;
ws16c48gpio->chip.parent = dev;
ws16c48gpio->chip.owner = THIS_MODULE;
ws16c48gpio->chip.base = -1;
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;
ws16c48gpio->map = devm_regmap_init_mmio(dev, regs, &ws16c48_regmap_config);
if (IS_ERR(ws16c48gpio->map))
return dev_err_probe(dev, PTR_ERR(ws16c48gpio->map),
"Unable to initialize register map\n");
girq = &ws16c48gpio->chip.irq;
gpio_irq_chip_set_chip(girq, &ws16c48_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 = ws16c48_irq_init_hw;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->name = name;
chip->status_base = WS16C48_INT_ID;
chip->mask_base = WS16C48_ENAB;
chip->ack_base = WS16C48_INT_ID;
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);
err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio);
if (err) {
dev_err(dev, "GPIO registering failed (%d)\n", err);
/* Initialize to prevent spurious interrupts before we're ready */
err = ws16c48_irq_init_hw(ws16c48gpio->map);
if (err)
return err;
}
err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED,
name, ws16c48gpio);
if (err) {
dev_err(dev, "IRQ handler registering failed (%d)\n", err);
return err;
}
err = devm_regmap_add_irq_chip(dev, ws16c48gpio->map, irq[id], 0, 0, chip, &chip_data);
if (err)
return dev_err_probe(dev, err, "IRQ registration failed\n");
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 = {

View File

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

View File

@ -8,9 +8,9 @@
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/seq_file.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
@ -194,7 +194,7 @@ static const struct spi_device_id 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" },
{},
};
@ -205,7 +205,7 @@ static struct spi_driver xra1403_driver = {
.id_table = xra1403_ids,
.driver = {
.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)
return -ENOMEM;
platform_set_drvdata(pdev, controller);
/* Copy our reference */
controller->chip = zevio_gpio_chip;
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)
{
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;
}
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIO_V2_LINE_CHANGED_CONFIG,
desc);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
}
return 0;
}
@ -414,8 +412,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
goto out_free_lh;
}
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
offset);
@ -555,6 +552,7 @@ struct line {
* @label: consumer label used to tag GPIO descriptors
* @num_lines: the number of lines in the lines array
* @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
* @events: KFIFO for the GPIO events
* @seqno: the sequence number for edge events generated on all lines in
@ -569,6 +567,7 @@ struct linereq {
const char *label;
u32 num_lines;
wait_queue_head_t wait;
struct notifier_block device_unregistered_nb;
u32 event_buffer_size;
DECLARE_KFIFO_PTR(events, struct gpio_v2_line_event);
atomic_t seqno;
@ -610,6 +609,17 @@ struct linereq {
GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
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,
struct gpio_v2_line_event *le)
{
@ -1407,9 +1417,7 @@ static long linereq_set_config_unlocked(struct linereq *lr,
WRITE_ONCE(line->edflags, edflags);
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIO_V2_LINE_CHANGED_CONFIG,
desc);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_CONFIG);
}
return 0;
}
@ -1567,6 +1575,10 @@ static void linereq_free(struct linereq *lr)
{
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++) {
if (lr->lines[i].desc) {
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;
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
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);
if (fd < 0) {
ret = fd;
@ -1779,6 +1796,7 @@ out_free_linereq:
* @eflags: the event flags this line was requested with
* @irq: the interrupt that trigger in response to events on this GPIO
* @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
* @timestamp: cache for the timestamp storing it between hardirq
* and IRQ thread, used to bring the timestamp close to the actual
@ -1791,6 +1809,7 @@ struct lineevent_state {
u32 eflags;
int irq;
wait_queue_head_t wait;
struct notifier_block device_unregistered_nb;
DECLARE_KFIFO(events, struct gpioevent_data, 16);
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);
}
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 {
compat_u64 timestamp;
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)
{
if (le->device_unregistered_nb.notifier_call)
blocking_notifier_chain_unregister(&le->gdev->device_notifier,
&le->device_unregistered_nb);
if (le->irq)
free_irq(le->irq, le);
if (le->desc)
@ -2117,8 +2150,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
if (ret)
goto out_free_le;
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIO_V2_LINE_CHANGED_REQUESTED, desc);
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
irq = gpiod_to_irq(desc);
if (irq <= 0) {
@ -2137,6 +2169,12 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
INIT_KFIFO(le->events);
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 */
ret = request_threaded_irq(irq,
lineevent_irq_handler,
@ -2320,6 +2358,7 @@ struct gpio_chardev_data {
wait_queue_head_t wait;
DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32);
struct notifier_block lineinfo_changed_nb;
struct notifier_block device_unregistered_nb;
unsigned long *watched_lines;
#ifdef CONFIG_GPIO_CDEV_V1
atomic_t watch_abi_version;
@ -2491,16 +2530,11 @@ static long gpio_ioctl_compat(struct file *file, unsigned int cmd,
}
#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,
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_desc *desc = data;
int ret;
@ -2522,6 +2556,18 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
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,
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->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);
if (ret)
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;
ret = nonseekable_open(inode, file);
if (ret)
goto out_unregister_notifier;
goto out_unregister_device_notifier;
up_read(&gdev->sem);
return ret;
out_unregister_notifier:
blocking_notifier_chain_unregister(&gdev->notifier,
out_unregister_device_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);
out_free_bitmap:
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;
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);
gpio_device_put(gdev);
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)
{
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)
return ret;
fwnode_handle_get(chip->fwnode);
of_node_get(np);
ret = of_gpiochip_scan_gpios(chip);
if (ret)
fwnode_handle_put(chip->fwnode);
of_node_put(np);
return ret;
}
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);
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,
struct lock_class_key *lock_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;
unsigned long flags;
unsigned int i;
u32 ngpios = 0;
int base = 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
* set up the struct device.
@ -732,7 +758,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gc->gpiodev = gdev;
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);
if (gdev->id < 0) {
@ -753,36 +786,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
else
gdev->owner = THIS_MODULE;
/*
* Try the device properties if the driver didn't supply the number
* 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;
ret = gpiochip_get_ngpios(gc, &gdev->dev);
if (ret)
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);
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);
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);
#ifdef CONFIG_PINCTRL
@ -947,7 +954,7 @@ err_print_message:
/* failures here can mean systems won't boot... */
if (ret != -EPROBE_DEFER) {
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);
}
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;
}
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 ||
!gc->irq.fwnode) {
chip_err(gc, "missing irqdomain vital data\n");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
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);
gc->irq.domain = irq_domain_create_hierarchy(
domain = irq_domain_create_hierarchy(
gc->irq.parent_domain,
0,
gc->ngpio,
@ -1317,12 +1326,12 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
&gc->irq.child_irq_domain_ops,
gc);
if (!gc->irq.domain)
return -ENOMEM;
if (!domain)
return ERR_PTR(-ENOMEM);
gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
return 0;
return domain;
}
static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@ -1366,9 +1375,9 @@ EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
#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)
@ -1445,6 +1454,19 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.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
* 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
* @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 irq_chip *irqchip = gc->irq.chip;
struct irq_domain *domain;
unsigned int type;
unsigned int i;
int ret;
if (!irqchip)
return 0;
@ -1657,28 +1706,18 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
"%pfw: Ignoring %u default trigger\n", fwnode, type))
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.lock_key = lock_key;
gc->irq.request_key = request_key;
/* If a parent irqdomain is provided, let's build a hierarchy */
if (gpiochip_hierarchy_is_hierarchical(gc)) {
int ret = gpiochip_hierarchy_add_domain(gc);
if (ret)
return ret;
domain = gpiochip_hierarchy_create_domain(gc);
} else {
gc->irq.domain = irq_domain_create_simple(fwnode,
gc->ngpio,
gc->irq.first,
&gpiochip_domain_ops,
gc);
if (!gc->irq.domain)
return -EINVAL;
domain = gpiochip_simple_create_domain(gc);
}
if (IS_ERR(domain))
return PTR_ERR(domain);
if (gc->irq.parent_handler) {
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);
/*
* Using barrier() here to prevent compiler from reordering
* gc->irq.initialized before initialization of above
* GPIO chip irq members.
*/
barrier();
gc->irq.initialized = true;
ret = gpiochip_irqchip_add_allocated_domain(gc, domain, false);
if (ret)
return ret;
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,
struct irq_domain *domain)
{
if (!domain)
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;
return gpiochip_irqchip_add_allocated_domain(gc, domain, true);
}
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);
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIOLINE_CHANGED_RELEASED, desc);
gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
return ret;
}
@ -3728,6 +3746,12 @@ int gpiod_set_array_value_cansleep(unsigned int array_size,
}
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
* @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);
}
blocking_notifier_call_chain(&desc->gdev->notifier,
GPIOLINE_CHANGED_REQUESTED, desc);
gpiod_line_state_notify(desc, GPIOLINE_CHANGED_REQUESTED);
return desc;
}

View File

@ -9,12 +9,13 @@
#ifndef 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/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>
#define GPIOCHIP_NAME "gpiochip"
@ -38,8 +39,10 @@
* or name of the IP component in a System on Chip.
* @data: per-instance data assigned by the driver
* @list: links gpio_device:s together for traversal
* @notifier: used to notify subscribers about lines being requested, released
* or reconfigured
* @line_state_notifier: used to notify subscribers about lines being
* 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
* user-space operations when the device gets unregistered during
* a hot-unplug event
@ -63,7 +66,8 @@ struct gpio_device {
const char *label;
void *data;
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;
#ifdef CONFIG_PINCTRL
@ -143,6 +147,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
extern spinlock_t gpio_lock;
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
@ -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 gpiod_hog(struct gpio_desc *desc, const char *name,
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