Merge tag 'gpio-updates-for-v7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio updates from Bartosz Golaszewski:
 "There's one new driver, one legacy driver removed, a kunit test-suite
  for the GPIO core, support for new models in existing drivers and a
  slew of various changes in many places though I can't think of
  anything controversial that would stand out - it's been a relatively
  calm cycle.

  GPIO core:
   - Add an initial set of kunit test cases for the GPIO subsystem
   - Use the devres owner as the GPIO chip's parent in absence of any
     other parent
   - Fix const-correctness of GPIO chip SRCU guards
   - Provide new GPIO consumer interfaces: gpiod_is_single_ended() and
     fwnode_gpiod_get()
   - Quarantine all legacy GPIO APIs in linux/gpio/legacy.h
   - Use __ro_after_init where applicable

  New drivers:
   - Add driver for the GPIO controller on Waveshare DSI TOUCH panels

  Removed drivers:
   - Remove the obsolete ts5500 GPIO driver

  Driver updates:
   - Modernize gpio-timberdale: remove platform data support and use
     generic device property accessors
   - Extend test build coverage by enabling COMPILE_TEST for more GPIO
     drivers
   - Add some missing dependencies in Kconfig
   - Add support for sparse fixed direction to gpio-regmap
   - Remove dead code from gpio-nomadik
   - use BIT() in gpio-mxc
   - use bitmap_complement() in gpio-xilinx and gpio-pca953x
   - Use more appropriate printing functions where applicable
   - Use named initializers for platform_device_id and i2c_device_id
     arrays
   - Convert gpio-altera to using the generic GPIO chip helper library
   - Add support for new models to gpio-dwapb, gpio-zynq, gpio-usbio and
     gpio-tegra186
   - Unify the naming convention for Qualcomm in GPIO drivers
   - Fix interrupt bank mapping to GPIO chips in gpio-mt7621
   - Add support for the lines-initial-states property to gpio-74x164
   - Switch to using dynamic GPIO base in gpio-ixp4xx
   - Move the handling of an OF quirk from ASoC to gpiolib-of.c where
     other such quirks live
   - Use handle_bad_irq() in gpio-ep93xx
   - Some other minor tweaks and refactorings

  Devicetree bindings:
   - Document the Waveshare GPIO controller for DSI TOUCH panels
   - Document new models: Tegra238 in gpio-tegra186 and EIO GPIO in
     gpio-zynq
   - Add new properties for gpio-dwapb and fairchild,74hc595
   - Fix whitespace issues
   - Sort compatibles alphabetically in gpio-zynq

  Documentation:
   - Fix kerneldoc warnings in gpio-realtek-otto

  Misc:
   - Attach software nodes representing GPIO chips to the actual struct
     device objects associated with them in some legacy platforms
     enabling real firmware node lookup instead of string matching
   - Drop unneeded dependencies on OF_GPIO from bus and staging drivers"

* tag 'gpio-updates-for-v7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (62 commits)
  gpio: nomadik: remove dead DB8540 code from <gpio/gpio-nomadik.h>
  gpio: mt7621: fix interrupt banks mapping on gpio chips
  bus: ts-nbus: drop unneeded dependency on OF_GPIO
  staging: media: max96712: drop unneeded dependency on OF_GPIO
  gpiolib: Replace strcpy() with memcpy()
  gpio: remove obsolete UAF FIXMEs from lookup paths
  gpio: core: fix const-correctness of gpio_chip_guard
  gpio: mxc: use BIT() macro
  gpio: realtek-otto: fix kernel-doc warnings
  gpio: max77620: Unify usage of space and comma in platform_device_id array
  gpio: Use named initializers for platform_device_id arrays
  gpio: cros-ec: Drop unused assignment of platform_device_id driver data
  ARM: omap1: enable real software node lookup of GPIOs on Nokia 770
  ARM: omap1: use platform_device_register_full() for GPIO devices on OMAP 16xx
  ARM: omap1: drop unused variable from omap16xx_gpio_init()
  gpio: gpiolib: use seq_puts() for plain strings
  gpio: ts5500: remove obsolete driver
  gpio: add kunit test cases for the GPIO subsystem
  kunit: provide kunit_platform_device_unregister()
  kunit: provide kunit_platform_device_register_full()
  ...
This commit is contained in:
Linus Torvalds
2026-06-16 07:30:52 +05:30
75 changed files with 1721 additions and 1127 deletions

View File

@@ -45,6 +45,18 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32
description: Number of daisy-chained shift registers
lines-initial-states:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Bitmask that specifies the initial state of each output line, written
by the driver before the gpiochip is registered. Bit N corresponds to
GPIO line N, following the convention already documented for
nxp,pcf8575. Because the 74HC595/74LVC594 family is push-pull output
only, a bit set to zero drives the line low and a bit set to one
drives it high. The bitmask covers up to 32 lines (four cascaded
registers); outputs beyond that come up zeroed. When the property is
absent all outputs come up low, preserving the previous behaviour.
enable-gpios:
description: GPIO connected to the OE (Output Enable) pin.
maxItems: 1
@@ -79,6 +91,7 @@ examples:
gpio-controller;
#gpio-cells = <2>;
registers-number = <4>;
lines-initial-states = <0xffff0000>;
spi-max-frequency = <100000>;
};
};

View File

@@ -12,10 +12,11 @@ maintainers:
properties:
compatible:
enum:
- xlnx,eio-gpio-1.0
- xlnx,pmc-gpio-1.0
- xlnx,versal-gpio-1.0
- xlnx,zynq-gpio-1.0
- xlnx,zynqmp-gpio-1.0
- xlnx,versal-gpio-1.0
- xlnx,pmc-gpio-1.0
reg:
maxItems: 1
@@ -30,7 +31,7 @@ properties:
gpio-line-names:
description: strings describing the names of each gpio line
minItems: 58
minItems: 52
maxItems: 174
interrupt-controller: true
@@ -89,6 +90,16 @@ allOf:
minItems: 116
maxItems: 116
- if:
properties:
compatible:
enum:
- xlnx,eio-gpio-1.0
then:
properties:
gpio-line-names:
maxItems: 52
required:
- compatible
- reg

View File

@@ -85,6 +85,8 @@ properties:
- nvidia,tegra194-gpio-aon
- nvidia,tegra234-gpio
- nvidia,tegra234-gpio-aon
- nvidia,tegra238-gpio
- nvidia,tegra238-gpio-aon
- nvidia,tegra256-gpio
- nvidia,tegra264-gpio
- nvidia,tegra264-gpio-uphy
@@ -163,6 +165,7 @@ allOf:
- nvidia,tegra186-gpio
- nvidia,tegra194-gpio
- nvidia,tegra234-gpio
- nvidia,tegra238-gpio
- nvidia,tegra256-gpio
- nvidia,tegra264-gpio
- nvidia,tegra264-gpio-uphy
@@ -180,6 +183,7 @@ allOf:
- nvidia,tegra186-gpio-aon
- nvidia,tegra194-gpio-aon
- nvidia,tegra234-gpio-aon
- nvidia,tegra238-gpio-aon
- nvidia,tegra264-gpio-aon
then:
properties:
@@ -192,6 +196,8 @@ allOf:
compatible:
contains:
enum:
- nvidia,tegra238-gpio
- nvidia,tegra238-gpio-aon
- nvidia,tegra264-gpio
- nvidia,tegra264-gpio-uphy
- nvidia,tegra264-gpio-aon

View File

@@ -95,6 +95,12 @@ patternProperties:
'#interrupt-cells':
const: 2
patternProperties:
"^.+-hog(-[0-9]+)?$":
type: object
required:
- gpio-hog
required:
- compatible
- reg

View File

@@ -0,0 +1,73 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/waveshare,dsi-touch-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Waveshare GPIO controller on DSI TOUCH panels
maintainers:
- Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
description:
Waveshare DSI TOUCH panel kits contain separate GPIO controller for toggling
power supplies and panel / touchscreen resets.
properties:
compatible:
const: waveshare,dsi-touch-gpio
reg:
maxItems: 1
gpio-controller: true
'#gpio-cells':
const: 2
required:
- compatible
- reg
- gpio-controller
- "#gpio-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
wsgpio: gpio@45 {
compatible = "waveshare,dsi-touch-gpio";
reg = <0x45>;
gpio-controller;
#gpio-cells = <2>;
};
};
panel_avdd: regulator-panel-avdd {
compatible = "regulator-fixed";
regulator-name = "panel-avdd";
gpios = <&wsgpio 0 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
panel_iovcc: regulator-panel-iovcc {
compatible = "regulator-fixed";
regulator-name = "panel-iovcc";
gpios = <&wsgpio 4 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
panel_vcc: regulator-panel-vcc {
compatible = "regulator-fixed";
regulator-name = "panel-vcc";
gpios = <&wsgpio 8 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-always-on;
};
...

View File

@@ -36,27 +36,6 @@
#include "clock.h"
#include "mmc.h"
static const struct software_node nokia770_mpuio_gpiochip_node = {
.name = "mpuio",
};
static const struct software_node nokia770_gpiochip1_node = {
.name = "gpio-0-15",
};
static const struct software_node nokia770_gpiochip2_node = {
.name = "gpio-16-31",
};
static const struct software_node *nokia770_gpiochip_nodes[] = {
&nokia770_mpuio_gpiochip_node,
&nokia770_gpiochip1_node,
&nokia770_gpiochip2_node,
NULL
};
#define ADS7846_PENDOWN_GPIO 15
static const unsigned int nokia770_keymap[] = {
KEY(1, 0, GROUP_0 | KEY_UP),
KEY(2, 0, GROUP_1 | KEY_F5),
@@ -112,7 +91,7 @@ static const struct omap_lcd_config nokia770_lcd_config __initconst = {
};
static const struct property_entry nokia770_mipid_props[] = {
PROPERTY_ENTRY_GPIO("reset-gpios", &nokia770_gpiochip1_node,
PROPERTY_ENTRY_GPIO("reset-gpios", &omap16xx_gpio1_swnode,
13, GPIO_ACTIVE_LOW),
{ }
};
@@ -138,8 +117,7 @@ static const struct property_entry nokia770_ads7846_props[] = {
PROPERTY_ENTRY_U16("ti,x-plate-ohms", 180),
PROPERTY_ENTRY_U16("ti,debounce-tol", 3),
PROPERTY_ENTRY_U16("ti,debounce-rep", 1),
PROPERTY_ENTRY_GPIO("pendown-gpios", &nokia770_gpiochip1_node,
ADS7846_PENDOWN_GPIO, GPIO_ACTIVE_LOW),
PROPERTY_ENTRY_GPIO("pendown-gpios", &omap16xx_gpio1_swnode, 15, GPIO_ACTIVE_LOW),
{ }
};
@@ -225,9 +203,9 @@ static inline void nokia770_mmc_init(void)
#if IS_ENABLED(CONFIG_I2C_CBUS_GPIO)
static const struct software_node_ref_args nokia770_cbus_gpio_refs[] = {
SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 9, 0),
SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 10, 0),
SOFTWARE_NODE_REFERENCE(&nokia770_mpuio_gpiochip_node, 11, 0),
SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 9, 0),
SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 10, 0),
SOFTWARE_NODE_REFERENCE(&omap16xx_mpu_gpio_swnode, 11, 0),
};
static const struct property_entry nokia770_cbus_props[] = {
@@ -318,7 +296,6 @@ static void __init omap_nokia770_init(void)
/* Unmask SleepX signal */
omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004);
software_node_register_node_group(nokia770_gpiochip_nodes);
platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices));
gpiod_add_lookup_table(&nokia770_irq_gpio_table);

View File

@@ -35,6 +35,9 @@
#include "soc.h"
#include "i2c.h"
extern const struct software_node omap16xx_mpu_gpio_swnode;
extern const struct software_node omap16xx_gpio1_swnode;
#ifdef CONFIG_OMAP_SERIAL_WAKE
int omap_serial_wakeup_init(void);
#else

View File

@@ -9,6 +9,7 @@
*/
#include <linux/platform_data/gpio-omap.h>
#include <linux/property.h>
#include <linux/soc/ti/omap1-io.h>
#include "hardware.h"
@@ -55,14 +56,16 @@ static struct omap_gpio_platform_data omap16xx_mpu_gpio_config = {
.regs = &omap16xx_mpuio_regs,
};
static struct platform_device omap16xx_mpu_gpio = {
const struct software_node omap16xx_mpu_gpio_swnode = { };
static const struct platform_device_info omap16xx_mpu_gpio = {
.name = "omap_gpio",
.id = 0,
.dev = {
.platform_data = &omap16xx_mpu_gpio_config,
},
.num_resources = ARRAY_SIZE(omap16xx_mpu_gpio_resources),
.resource = omap16xx_mpu_gpio_resources,
.data = &omap16xx_mpu_gpio_config,
.size_data = sizeof(omap16xx_mpu_gpio_config),
.num_res = ARRAY_SIZE(omap16xx_mpu_gpio_resources),
.res = omap16xx_mpu_gpio_resources,
.swnode = &omap16xx_mpu_gpio_swnode,
};
/* gpio1 */
@@ -99,14 +102,16 @@ static struct omap_gpio_platform_data omap16xx_gpio1_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio1 = {
const struct software_node omap16xx_gpio1_swnode = { };
static const struct platform_device_info omap16xx_gpio1 = {
.name = "omap_gpio",
.id = 1,
.dev = {
.platform_data = &omap16xx_gpio1_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio1_resources),
.resource = omap16xx_gpio1_resources,
.data = &omap16xx_gpio1_config,
.size_data = sizeof(omap16xx_gpio1_config),
.num_res = ARRAY_SIZE(omap16xx_gpio1_resources),
.res = omap16xx_gpio1_resources,
.swnode = &omap16xx_gpio1_swnode,
};
/* gpio2 */
@@ -127,14 +132,13 @@ static struct omap_gpio_platform_data omap16xx_gpio2_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio2 = {
static const struct platform_device_info omap16xx_gpio2 = {
.name = "omap_gpio",
.id = 2,
.dev = {
.platform_data = &omap16xx_gpio2_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio2_resources),
.resource = omap16xx_gpio2_resources,
.data = &omap16xx_gpio2_config,
.size_data = sizeof(omap16xx_gpio2_config),
.num_res = ARRAY_SIZE(omap16xx_gpio2_resources),
.res = omap16xx_gpio2_resources,
};
/* gpio3 */
@@ -155,14 +159,13 @@ static struct omap_gpio_platform_data omap16xx_gpio3_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio3 = {
static const struct platform_device_info omap16xx_gpio3 = {
.name = "omap_gpio",
.id = 3,
.dev = {
.platform_data = &omap16xx_gpio3_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio3_resources),
.resource = omap16xx_gpio3_resources,
.data = &omap16xx_gpio3_config,
.size_data = sizeof(omap16xx_gpio3_config),
.num_res = ARRAY_SIZE(omap16xx_gpio3_resources),
.res = omap16xx_gpio3_resources,
};
/* gpio4 */
@@ -183,17 +186,16 @@ static struct omap_gpio_platform_data omap16xx_gpio4_config = {
.regs = &omap16xx_gpio_regs,
};
static struct platform_device omap16xx_gpio4 = {
static const struct platform_device_info omap16xx_gpio4 = {
.name = "omap_gpio",
.id = 4,
.dev = {
.platform_data = &omap16xx_gpio4_config,
},
.num_resources = ARRAY_SIZE(omap16xx_gpio4_resources),
.resource = omap16xx_gpio4_resources,
.data = &omap16xx_gpio4_config,
.size_data = sizeof(omap16xx_gpio4_config),
.num_res = ARRAY_SIZE(omap16xx_gpio4_resources),
.res = omap16xx_gpio4_resources,
};
static struct platform_device *omap16xx_gpio_dev[] __initdata = {
static const struct platform_device_info *omap16xx_gpio_dev[] __initconst = {
&omap16xx_mpu_gpio,
&omap16xx_gpio1,
&omap16xx_gpio2,
@@ -210,9 +212,8 @@ static int __init omap16xx_gpio_init(void)
{
int i;
void __iomem *base;
struct resource *res;
struct platform_device *pdev;
struct omap_gpio_platform_data *pdata;
const struct resource *res;
const struct platform_device_info *pdevinfo;
if (!cpu_is_omap16xx())
return -EINVAL;
@@ -225,25 +226,24 @@ static int __init omap16xx_gpio_init(void)
ULPD_CAM_CLK_CTRL);
for (i = 0; i < ARRAY_SIZE(omap16xx_gpio_dev); i++) {
pdev = omap16xx_gpio_dev[i];
pdata = pdev->dev.platform_data;
pdevinfo = omap16xx_gpio_dev[i];
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res = &pdevinfo->res[0];
if (unlikely(!res)) {
dev_err(&pdev->dev, "Invalid mem resource.\n");
pr_err("%s.%d: Invalid mem resource.\n", pdevinfo->name, pdevinfo->id);
return -ENODEV;
}
base = ioremap(res->start, resource_size(res));
if (unlikely(!base)) {
dev_err(&pdev->dev, "ioremap failed.\n");
pr_err("%s.%d: ioremap failed.\n", pdevinfo->name, pdevinfo->id);
return -ENOMEM;
}
__raw_writel(SYSCONFIG_WORD, base + OMAP1610_GPIO_SYSCONFIG);
iounmap(base);
platform_device_register(omap16xx_gpio_dev[i]);
platform_device_register_full(omap16xx_gpio_dev[i]);
}
return 0;

View File

@@ -216,7 +216,7 @@ config TI_SYSC
config TS_NBUS
tristate "Technologic Systems NBUS Driver"
depends on SOC_IMX28
depends on OF_GPIO && PWM
depends on PWM
help
Driver for the Technologic Systems NBUS which is used to interface
with the peripherals in the FPGA of the TS-4600 SoM.

View File

@@ -102,6 +102,14 @@ config GPIO_CDEV_V1
This ABI version is deprecated.
Please use the latest ABI for new developments.
config GPIO_KUNIT
tristate "Build GPIO Kunit test cases"
depends on KUNIT
default KUNIT_ALL_TESTS
help
Say Y here to build the module containing Kunit test cases verifying
the functionality of the GPIO subsystem.
config GPIO_GENERIC
depends on HAS_IOMEM # Only for IOMEM drivers
tristate
@@ -156,6 +164,7 @@ config GPIO_74XX_MMIO
config GPIO_ALTERA
tristate "Altera GPIO"
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
help
Say Y or M here to build support for the Altera PIO device.
@@ -301,7 +310,7 @@ config GPIO_EM
config GPIO_EN7523
tristate "Airoha GPIO support"
depends on ARCH_AIROHA
depends on ARCH_AIROHA || COMPILE_TEST
default ARCH_AIROHA
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
@@ -698,7 +707,7 @@ config GPIO_SPACEMIT_K1
config GPIO_SPEAR_SPICS
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
depends on PLAT_SPEAR
depends on PLAT_SPEAR || COMPILE_TEST
select GENERIC_IRQ_CHIP
help
Say yes here to support ST SPEAr SPI Chip Select as GPIO device.
@@ -805,8 +814,18 @@ config GPIO_VISCONTI
help
Say yes here to support GPIO on Tohisba Visconti.
config GPIO_WAVESHARE_DSI_TOUCH
tristate "Waveshare GPIO controller for DSI panels"
depends on BACKLIGHT_CLASS_DEVICE
depends on I2C
select REGMAP_I2C
help
Enable support for the GPIO and PWM controller found on Waveshare DSI
TOUCH panel kits. It provides GPIOs (used for regulator control and
resets) and backlight support.
config GPIO_WCD934X
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver"
tristate "Qualcomm WCD9340/WCD9341 GPIO controller driver"
depends on MFD_WCD934X
help
This driver is to support GPIO block found on the Qualcomm Technologies
@@ -814,7 +833,7 @@ config GPIO_WCD934X
config GPIO_XGENE
bool "APM X-Gene GPIO controller support"
depends on ARM64
depends on ARM64 || COMPILE_TEST
help
This driver is to support the GPIO block within the APM X-Gene SoC
platform's generic flash controller. The GPIO pins are muxed with
@@ -858,7 +877,7 @@ config GPIO_XTENSA
config GPIO_ZEVIO
bool "LSI ZEVIO SoC memory mapped GPIOs"
depends on ARM
depends on ARM || COMPILE_TEST
help
Say yes here to support the GPIO controller in LSI ZEVIO SoCs.
@@ -1093,15 +1112,6 @@ config GPIO_SCH311X
To compile this driver as a module, choose M here: the module will
be called gpio-sch311x.
config GPIO_TS5500
tristate "TS-5500 DIO blocks and compatibles"
depends on TS5500 || COMPILE_TEST
help
This driver supports Digital I/O exposed by pin blocks found on some
Technologic Systems platforms. It includes, but is not limited to, 3
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
config GPIO_WINBOND
tristate "Winbond Super I/O GPIO support"
select ISA_BUS_API
@@ -2060,6 +2070,7 @@ config GPIO_VIRTIO
config GPIO_SIM
tristate "GPIO Simulator Module"
depends on SYSFS
select IRQ_SIM
select CONFIGFS_FS
help

View File

@@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o
obj-$(CONFIG_GPIO_KUNIT) += gpiolib-kunit.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
@@ -194,7 +195,6 @@ obj-$(CONFIG_GPIO_TPS68470) += gpio-tps68470.o
obj-$(CONFIG_GPIO_TQMX86) += gpio-tqmx86.o
obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o
obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
@@ -205,6 +205,7 @@ obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o
obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WAVESHARE_DSI_TOUCH) += gpio-waveshare-dsi.o
obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o
obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o
obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o

View File

@@ -112,7 +112,7 @@ static int gen_74x164_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct gen_74x164_chip *chip;
u32 nregs;
u32 nregs, init_state;
int ret;
/*
@@ -134,6 +134,21 @@ static int gen_74x164_probe(struct spi_device *spi)
chip->registers = nregs;
/*
* Optionally seed the chain with a board-specified pattern so the
* outputs come up in a known state on the first SPI write. The
* property follows the nxp,pcf8575 convention where bit N maps to
* GPIO line N. On this output-only device, bit=0 drives the line
* low and bit=1 drives it high. The bitmask covers up to 32 lines;
* any further outputs come up zeroed by devm_kzalloc().
*/
if (!device_property_read_u32(dev, "lines-initial-states", &init_state)) {
unsigned int i;
for (i = 0; i < min(nregs, 4U); i++)
chip->buffer[nregs - 1 - i] = (init_state >> (i * 8)) & 0xff;
}
chip->gpiod_oe = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(chip->gpiod_oe))
return PTR_ERR(chip->gpiod_oe);

View File

@@ -501,8 +501,8 @@ static int adnp_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id adnp_i2c_id[] = {
{ "gpio-adnp" },
{ },
{ .name = "gpio-adnp" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adnp_i2c_id);

View File

@@ -507,8 +507,8 @@ static const struct adp5585_gpio_chip adp5589_gpio_chip_info = {
};
static const struct platform_device_id adp5585_gpio_id_table[] = {
{ "adp5585-gpio", (kernel_ulong_t)&adp5585_gpio_chip_info },
{ "adp5589-gpio", (kernel_ulong_t)&adp5589_gpio_chip_info },
{ .name = "adp5585-gpio", .driver_data = (kernel_ulong_t)&adp5585_gpio_chip_info },
{ .name = "adp5589-gpio", .driver_data = (kernel_ulong_t)&adp5589_gpio_chip_info },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(platform, adp5585_gpio_id_table);

View File

@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/generic.h>
#define ALTERA_GPIO_MAX_NGPIO 32
#define ALTERA_GPIO_DATA 0x0
@@ -26,7 +27,7 @@
/**
* struct altera_gpio_chip
* @gc : GPIO chip structure.
* @chip : Generic GPIO chip structure.
* @regs : memory mapped IO address for the controller registers.
* @gpio_lock : synchronization lock so that new irq/set/get requests
* will be blocked until the current one completes.
@@ -34,7 +35,7 @@
* (rising, falling, both, high)
*/
struct altera_gpio_chip {
struct gpio_chip gc;
struct gpio_generic_chip chip;
void __iomem *regs;
raw_spinlock_t gpio_lock;
int interrupt_trigger;
@@ -106,72 +107,6 @@ static unsigned int altera_gpio_irq_startup(struct irq_data *d)
return 0;
}
static int altera_gpio_get(struct gpio_chip *gc, unsigned offset)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
return !!(readl(altera_gc->regs + ALTERA_GPIO_DATA) & BIT(offset));
}
static int altera_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
unsigned long flags;
unsigned int data_reg;
raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);
data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA);
if (value)
data_reg |= BIT(offset);
else
data_reg &= ~BIT(offset);
writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA);
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
return 0;
}
static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
unsigned long flags;
unsigned int gpio_ddr;
raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);
/* Set pin as input, assumes software controlled IP */
gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR);
gpio_ddr &= ~BIT(offset);
writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR);
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
return 0;
}
static int altera_gpio_direction_output(struct gpio_chip *gc,
unsigned offset, int value)
{
struct altera_gpio_chip *altera_gc = gpiochip_get_data(gc);
unsigned long flags;
unsigned int data_reg, gpio_ddr;
raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);
/* Sets the GPIO value */
data_reg = readl(altera_gc->regs + ALTERA_GPIO_DATA);
if (value)
data_reg |= BIT(offset);
else
data_reg &= ~BIT(offset);
writel(data_reg, altera_gc->regs + ALTERA_GPIO_DATA);
/* Set pin as output, assumes software controlled IP */
gpio_ddr = readl(altera_gc->regs + ALTERA_GPIO_DIR);
gpio_ddr |= BIT(offset);
writel(gpio_ddr, altera_gc->regs + ALTERA_GPIO_DIR);
raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);
return 0;
}
static void altera_gpio_irq_edge_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
@@ -231,9 +166,12 @@ static const struct irq_chip altera_gpio_irq_chip = {
static int altera_gpio_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct device *dev = &pdev->dev;
int reg, ret;
struct altera_gpio_chip *altera_gc;
struct gpio_generic_chip *chip;
struct gpio_chip *gc;
struct gpio_irq_chip *girq;
int mapped_irq;
@@ -243,35 +181,45 @@ static int altera_gpio_probe(struct platform_device *pdev)
raw_spin_lock_init(&altera_gc->gpio_lock);
altera_gc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(altera_gc->regs))
return dev_err_probe(dev, PTR_ERR(altera_gc->regs),
"failed to ioremap memory resource\n");
chip = &altera_gc->chip;
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
.dat = altera_gc->regs + ALTERA_GPIO_DATA,
.set = altera_gc->regs + ALTERA_GPIO_DATA,
.dirout = altera_gc->regs + ALTERA_GPIO_DIR,
};
ret = gpio_generic_chip_init(chip, &config);
if (ret)
return dev_err_probe(dev, ret, "unable to init generic GPIO\n");
gc = &chip->gc;
if (device_property_read_u32(dev, "altr,ngpio", &reg))
/* By default assume maximum ngpio */
altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
gc->ngpio = ALTERA_GPIO_MAX_NGPIO;
else
altera_gc->gc.ngpio = reg;
gc->ngpio = reg;
if (altera_gc->gc.ngpio > ALTERA_GPIO_MAX_NGPIO) {
if (gc->ngpio > ALTERA_GPIO_MAX_NGPIO) {
dev_warn(&pdev->dev,
"ngpio is greater than %d, defaulting to %d\n",
ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO);
altera_gc->gc.ngpio = ALTERA_GPIO_MAX_NGPIO;
gc->ngpio = ALTERA_GPIO_MAX_NGPIO;
}
altera_gc->gc.direction_input = altera_gpio_direction_input;
altera_gc->gc.direction_output = altera_gpio_direction_output;
altera_gc->gc.get = altera_gpio_get;
altera_gc->gc.set = altera_gpio_set;
altera_gc->gc.owner = THIS_MODULE;
altera_gc->gc.parent = &pdev->dev;
altera_gc->gc.base = -1;
altera_gc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));
if (!altera_gc->gc.label)
gc->base = -1;
gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pfw", dev_fwnode(dev));
if (!gc->label)
return -ENOMEM;
altera_gc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(altera_gc->regs))
return dev_err_probe(dev, PTR_ERR(altera_gc->regs), "failed to ioremap memory resource\n");
mapped_irq = platform_get_irq_optional(pdev, 0);
if (mapped_irq < 0)
goto skip_irq;
@@ -283,7 +231,7 @@ static int altera_gpio_probe(struct platform_device *pdev)
}
altera_gc->interrupt_trigger = reg;
girq = &altera_gc->gc.irq;
girq = &gc->irq;
gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip);
if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH)
@@ -300,7 +248,7 @@ static int altera_gpio_probe(struct platform_device *pdev)
girq->parents[0] = mapped_irq;
skip_irq:
ret = devm_gpiochip_add_data(dev, &altera_gc->gc, altera_gc);
ret = devm_gpiochip_add_data(dev, gc, altera_gc);
if (ret) {
dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");
return ret;

View File

@@ -59,8 +59,8 @@
* want to register another driver on the same PCI id.
*/
static const struct pci_device_id pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS), 0 },
{ 0, }, /* terminate list */
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS) },
{ }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);

View File

@@ -263,8 +263,8 @@ static int gpo_bd72720_probe(struct platform_device *pdev)
}
static const struct platform_device_id bd72720_gpio_id[] = {
{ "bd72720-gpio" },
{ },
{ .name = "bd72720-gpio" },
{ }
};
MODULE_DEVICE_TABLE(platform, bd72720_gpio_id);

View File

@@ -110,8 +110,8 @@ static int bd9571mwv_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id bd9571mwv_gpio_id_table[] = {
{ "bd9571mwv-gpio", ROHM_CHIP_TYPE_BD9571 },
{ "bd9574mwf-gpio", ROHM_CHIP_TYPE_BD9574 },
{ .name = "bd9571mwv-gpio", .driver_data = ROHM_CHIP_TYPE_BD9571 },
{ .name = "bd9574mwf-gpio", .driver_data = ROHM_CHIP_TYPE_BD9574 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, bd9571mwv_gpio_id_table);

View File

@@ -196,8 +196,8 @@ static int cros_ec_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id cros_ec_gpio_id[] = {
{ "cros-ec-gpio", 0 },
{}
{ .name = "cros-ec-gpio" },
{ }
};
MODULE_DEVICE_TABLE(platform, cros_ec_gpio_id);

View File

@@ -54,7 +54,7 @@ static const struct of_device_id ds4520_gpio_of_match_table[] = {
MODULE_DEVICE_TABLE(of, ds4520_gpio_of_match_table);
static const struct i2c_device_id ds4520_gpio_id_table[] = {
{ "ds4520-gpio" },
{ .name = "ds4520-gpio" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds4520_gpio_id_table);

View File

@@ -694,6 +694,7 @@ static const struct acpi_device_id dwapb_acpi_match[] = {
{"APMC0D07", GPIO_REG_OFFSET_V1},
{"APMC0D81", GPIO_REG_OFFSET_V2},
{"FUJI200A", GPIO_REG_OFFSET_V1},
{"LECA0001", GPIO_REG_OFFSET_V1},
{ }
};
MODULE_DEVICE_TABLE(acpi, dwapb_acpi_match);

View File

@@ -323,8 +323,7 @@ static int ep93xx_setup_irqs(struct platform_device *pdev,
}
girq->default_type = IRQ_TYPE_NONE;
/* TODO: replace with handle_bad_irq() once we are fully hierarchical */
girq->handler = handle_simple_irq;
girq->handler = handle_bad_irq;
return 0;
}

View File

@@ -150,7 +150,7 @@ static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = {
MODULE_DEVICE_TABLE(of, fxl6408_dt_ids);
static const struct i2c_device_id fxl6408_id[] = {
{ "fxl6408" },
{ .name = "fxl6408" },
{ }
};
MODULE_DEVICE_TABLE(i2c, fxl6408_id);

View File

@@ -109,7 +109,7 @@ static int gw_pld_probe(struct i2c_client *client)
}
static const struct i2c_device_id gw_pld_id[] = {
{ "gw-pld", },
{ .name = "gw-pld" },
{ }
};
MODULE_DEVICE_TABLE(i2c, gw_pld_id);

View File

@@ -311,12 +311,7 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev)
}
g->chip.gc.ngpio = 16;
g->chip.gc.label = "IXP4XX_GPIO_CHIP";
/*
* TODO: when we have migrated to device tree and all GPIOs
* are fetched using phandles, set this to -1 to get rid of
* the fixed gpiochip base.
*/
g->chip.gc.base = 0;
g->chip.gc.base = -1;
g->chip.gc.parent = &pdev->dev;
g->chip.gc.owner = THIS_MODULE;

View File

@@ -156,7 +156,7 @@ static int lp873x_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id lp873x_gpio_id_table[] = {
{ "lp873x-gpio", },
{ .name = "lp873x-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table);

View File

@@ -171,7 +171,7 @@ static int lp87565_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id lp87565_gpio_id_table[] = {
{ "lp87565-q1-gpio", },
{ .name = "lp87565-q1-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table);

View File

@@ -53,7 +53,7 @@ static void max7300_remove(struct i2c_client *client)
}
static const struct i2c_device_id max7300_id[] = {
{ "max7300" },
{ .name = "max7300" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max7300_id);

View File

@@ -103,16 +103,16 @@ static uint64_t max732x_features[] = {
};
static const struct i2c_device_id max732x_id[] = {
{ "max7319", MAX7319 },
{ "max7320", MAX7320 },
{ "max7321", MAX7321 },
{ "max7322", MAX7322 },
{ "max7323", MAX7323 },
{ "max7324", MAX7324 },
{ "max7325", MAX7325 },
{ "max7326", MAX7326 },
{ "max7327", MAX7327 },
{ },
{ .name = "max7319", .driver_data = MAX7319 },
{ .name = "max7320", .driver_data = MAX7320 },
{ .name = "max7321", .driver_data = MAX7321 },
{ .name = "max7322", .driver_data = MAX7322 },
{ .name = "max7323", .driver_data = MAX7323 },
{ .name = "max7324", .driver_data = MAX7324 },
{ .name = "max7325", .driver_data = MAX7325 },
{ .name = "max7326", .driver_data = MAX7326 },
{ .name = "max7327", .driver_data = MAX7327 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max732x_id);

View File

@@ -367,7 +367,7 @@ static int max77620_gpio_probe(struct platform_device *pdev)
static const struct platform_device_id max77620_gpio_devtype[] = {
{ .name = "max77620-gpio", },
{ .name = "max20024-gpio", },
{},
{ }
};
MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype);

View File

@@ -502,7 +502,7 @@ static const struct of_device_id max77759_gpio_of_id[] = {
MODULE_DEVICE_TABLE(of, max77759_gpio_of_id);
static const struct platform_device_id max77759_gpio_platform_id[] = {
{ "max77759-gpio", },
{ .name = "max77759-gpio" },
{ }
};
MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id);

View File

@@ -29,8 +29,8 @@
#define GPIO_REG_EDGE 0xA0
struct mtk_gc {
struct irq_chip irq_chip;
struct gpio_generic_chip chip;
struct mtk *parent_priv;
int bank;
u32 rising;
u32 falling;
@@ -41,20 +41,32 @@ struct mtk_gc {
/**
* struct mtk - state container for
* data of the platform driver. It is 3
* separate gpio-chip each one with its
* own irq_chip.
* @dev: device instance
* separate gpio-chip having an IRQ
* linear domain shared for all of them
* @pdev: platform device instance
* @base: memory base address
* @irq_domain: IRQ linear domain shared across the three gpio chips
* @gpio_irq: irq number from the device tree
* @num_gpios: total number of gpio pins on the three gpio chips
* @gc_map: array of the gpio chips
*/
struct mtk {
struct device *dev;
struct platform_device *pdev;
void __iomem *base;
struct irq_domain *irq_domain;
int gpio_irq;
int num_gpios;
struct mtk_gc gc_map[MTK_BANK_CNT];
};
static inline struct mtk *
mt7621_gpio_gc_to_priv(struct gpio_chip *gc)
{
struct mtk_gc *bank = gpiochip_get_data(gc);
return bank->parent_priv;
}
static inline struct mtk_gc *
to_mediatek_gpio(struct gpio_chip *chip)
{
@@ -67,7 +79,7 @@ static inline void
mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val)
{
struct gpio_chip *gc = &rg->chip.gc;
struct mtk *mtk = gpiochip_get_data(gc);
struct mtk *mtk = mt7621_gpio_gc_to_priv(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
gpio_generic_write_reg(&rg->chip, mtk->base + offset, val);
@@ -77,41 +89,62 @@ static inline u32
mtk_gpio_r32(struct mtk_gc *rg, u32 offset)
{
struct gpio_chip *gc = &rg->chip.gc;
struct mtk *mtk = gpiochip_get_data(gc);
struct mtk *mtk = mt7621_gpio_gc_to_priv(gc);
offset = (rg->bank * GPIO_BANK_STRIDE) + offset;
return gpio_generic_read_reg(&rg->chip, mtk->base + offset);
}
static irqreturn_t
mediatek_gpio_irq_handler(int irq, void *data)
static void
mt7621_gpio_irq_bank_handler(struct mtk_gc *bank)
{
struct gpio_chip *gc = data;
struct mtk_gc *rg = to_mediatek_gpio(gc);
irqreturn_t ret = IRQ_NONE;
struct mtk *priv = bank->parent_priv;
struct irq_domain *domain = priv->irq_domain;
int hwbase = bank->chip.gc.offset;
unsigned long pending;
int bit;
unsigned int offset;
pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
pending = mtk_gpio_r32(bank, GPIO_REG_STAT);
if (!pending)
return;
for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
generic_handle_domain_irq(gc->irq.domain, bit);
mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
ret |= IRQ_HANDLED;
mtk_gpio_w32(bank, GPIO_REG_STAT, pending);
for_each_set_bit(offset, &pending, MTK_BANK_WIDTH)
generic_handle_domain_irq(domain, hwbase + offset);
}
static void
mt7621_gpio_irq_handler(struct irq_desc *desc)
{
struct mtk *priv = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
int i;
chained_irq_enter(chip, desc);
for (i = 0; i < MTK_BANK_CNT; i++) {
struct mtk_gc *bank = &priv->gc_map[i];
mt7621_gpio_irq_bank_handler(bank);
}
chained_irq_exit(chip, desc);
}
return ret;
static int
mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank)
{
return hwirq - bank->chip.gc.offset;
}
static void
mediatek_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
struct mtk_gc *rg = gpiochip_get_data(gc);
u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
u32 rise, fall, high, low;
gpiochip_enable_irq(gc, d->hwirq);
gpiochip_enable_irq(gc, mask);
guard(gpio_generic_lock_irqsave)(&rg->chip);
@@ -119,18 +152,18 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising));
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(mask) & rg->rising));
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(mask) & rg->falling));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(mask) & rg->hlevel));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(mask) & rg->llevel));
}
static void
mediatek_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
struct mtk_gc *rg = gpiochip_get_data(gc);
u32 mask = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
u32 rise, fall, high, low;
scoped_guard(gpio_generic_lock_irqsave, &rg->chip) {
@@ -138,22 +171,21 @@ mediatek_gpio_irq_mask(struct irq_data *d)
fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
high = mtk_gpio_r32(rg, GPIO_REG_HLVL);
low = mtk_gpio_r32(rg, GPIO_REG_LLVL);
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin));
mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(mask));
mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(mask));
mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(mask));
mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(mask));
}
gpiochip_disable_irq(gc, d->hwirq);
gpiochip_disable_irq(gc, mask);
}
static int
mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = to_mediatek_gpio(gc);
int pin = d->hwirq;
u32 mask = BIT(pin);
struct mtk_gc *rg = gpiochip_get_data(gc);
u32 mask = BIT(mt7621_gpio_hwirq_to_offset(d->hwirq, rg));
if (type == IRQ_TYPE_PROBE) {
if ((rg->rising | rg->falling |
@@ -190,6 +222,26 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
return 0;
}
static int
mt7621_gpio_irq_reqres(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
return gpiochip_reqres_irq(gc, irq);
}
static void
mt7621_gpio_irq_relres(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
unsigned int irq = mt7621_gpio_hwirq_to_offset(d->hwirq, rg);
gpiochip_relres_irq(gc, irq);
}
static int
mediatek_gpio_xlate(struct gpio_chip *chip,
const struct of_phandle_args *spec, u32 *flags)
@@ -208,14 +260,123 @@ mediatek_gpio_xlate(struct gpio_chip *chip,
static const struct irq_chip mt7621_irq_chip = {
.name = "mt7621-gpio",
.irq_request_resources = mt7621_gpio_irq_reqres,
.irq_release_resources = mt7621_gpio_irq_relres,
.irq_mask_ack = mediatek_gpio_irq_mask,
.irq_mask = mediatek_gpio_irq_mask,
.irq_unmask = mediatek_gpio_irq_unmask,
.irq_set_type = mediatek_gpio_irq_type,
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static void
mt7621_gpio_remove(struct platform_device *pdev)
{
struct mtk *priv = platform_get_drvdata(pdev);
int offset, virq;
if (priv->gpio_irq > 0)
irq_set_chained_handler_and_data(priv->gpio_irq, NULL, NULL);
/* Remove all IRQ mappings and delete the domain */
if (priv->irq_domain) {
for (offset = 0; offset < priv->num_gpios; offset++) {
virq = irq_find_mapping(priv->irq_domain, offset);
irq_dispose_mapping(virq);
}
irq_domain_remove(priv->irq_domain);
}
}
static struct mtk_gc *
mt7621_gpio_hwirq_to_bank(struct mtk *priv, irq_hw_number_t hwirq)
{
int i;
for (i = 0; i < MTK_BANK_CNT; i++) {
struct mtk_gc *bank = &priv->gc_map[i];
if (hwirq >= bank->chip.gc.offset &&
hwirq < (bank->chip.gc.offset + bank->chip.gc.ngpio))
return bank;
}
return NULL;
}
static int
mt7621_gpio_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hwirq)
{
struct mtk *priv = d->host_data;
struct mtk_gc *bank = mt7621_gpio_hwirq_to_bank(priv, hwirq);
struct platform_device *pdev = priv->pdev;
int ret;
if (!bank)
return -EINVAL;
dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n",
irq, (int)hwirq, bank->bank);
ret = irq_set_chip_data(irq, &bank->chip.gc);
if (ret < 0)
return ret;
irq_set_chip_and_handler(irq, &mt7621_irq_chip, handle_simple_irq);
irq_set_noprobe(irq);
return 0;
}
static void
mt7621_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
{
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
}
static const struct irq_domain_ops mt7621_gpio_irq_domain_ops = {
.map = mt7621_gpio_irq_map,
.unmap = mt7621_gpio_irq_unmap,
.xlate = irq_domain_xlate_twocell,
};
static int
mt7621_gpio_irq_setup(struct platform_device *pdev,
struct mtk *priv)
{
struct device *dev = &pdev->dev;
priv->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
priv->num_gpios,
&mt7621_gpio_irq_domain_ops,
priv);
if (!priv->irq_domain) {
dev_err(dev, "Couldn't allocate IRQ domain\n");
return -ENXIO;
}
irq_set_chained_handler_and_data(priv->gpio_irq,
mt7621_gpio_irq_handler, priv);
irq_set_status_flags(priv->gpio_irq, IRQ_DISABLE_UNLAZY);
return 0;
}
static int
mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
{
struct mtk *priv = mt7621_gpio_gc_to_priv(gc);
/* gc_offset is relative to this gpio_chip; want real offset */
int hwirq = offset + gc->offset;
if (hwirq >= priv->num_gpios)
return -ENXIO;
return irq_create_mapping(priv->irq_domain, hwirq);
}
static int
mediatek_gpio_bank_probe(struct device *dev, int bank)
{
@@ -228,6 +389,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg = &mtk->gc_map[bank];
memset(rg, 0, sizeof(*rg));
rg->parent_priv = mtk;
rg->bank = bank;
dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE);
@@ -253,41 +415,17 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
rg->chip.gc.of_gpio_n_cells = 2;
rg->chip.gc.of_xlate = mediatek_gpio_xlate;
rg->chip.gc.ngpio = MTK_BANK_WIDTH;
rg->chip.gc.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d",
dev_name(dev), bank);
if (!rg->chip.gc.label)
return -ENOMEM;
rg->chip.gc.offset = bank * MTK_BANK_WIDTH;
if (mtk->gpio_irq > 0)
rg->chip.gc.to_irq = mt7621_gpio_to_irq;
if (mtk->gpio_irq) {
struct gpio_irq_chip *girq;
/*
* Directly request the irq here instead of passing
* a flow-handler because the irq is shared.
*/
ret = devm_request_irq(dev, mtk->gpio_irq,
mediatek_gpio_irq_handler, IRQF_SHARED,
rg->chip.gc.label, &rg->chip.gc);
if (ret) {
dev_err(dev, "Error requesting IRQ %d: %d\n",
mtk->gpio_irq, ret);
return ret;
}
girq = &rg->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &mt7621_irq_chip);
/* 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_simple_irq;
}
ret = devm_gpiochip_add_data(dev, &rg->chip.gc, mtk);
ret = devm_gpiochip_add_data(dev, &rg->chip.gc, rg);
if (ret < 0) {
dev_err(dev, "Could not register gpio %d, ret=%d\n",
rg->chip.gc.ngpio, ret);
@@ -322,7 +460,8 @@ mediatek_gpio_probe(struct platform_device *pdev)
if (mtk->gpio_irq < 0)
return mtk->gpio_irq;
mtk->dev = dev;
mtk->pdev = pdev;
mtk->num_gpios = MTK_BANK_WIDTH * MTK_BANK_CNT;
platform_set_drvdata(pdev, mtk);
for (i = 0; i < MTK_BANK_CNT; i++) {
@@ -331,7 +470,17 @@ mediatek_gpio_probe(struct platform_device *pdev)
return ret;
}
if (mtk->gpio_irq > 0) {
ret = mt7621_gpio_irq_setup(pdev, mtk);
if (ret)
goto fail;
}
return 0;
fail:
mt7621_gpio_remove(pdev);
return ret;
}
static const struct of_device_id mediatek_gpio_match[] = {
@@ -342,6 +491,7 @@ MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
static struct platform_driver mediatek_gpio_driver = {
.probe = mediatek_gpio_probe,
.remove = mt7621_gpio_remove,
.driver = {
.name = "mt7621_gpio",
.of_match_table = mediatek_gpio_match,

View File

@@ -330,13 +330,13 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
ret = enable_irq_wake(port->irq_high);
else
ret = enable_irq_wake(port->irq);
port->wakeup_pads |= (1 << gpio_idx);
port->wakeup_pads |= BIT(gpio_idx);
} else {
if (port->irq_high && (gpio_idx >= 16))
ret = disable_irq_wake(port->irq_high);
else
ret = disable_irq_wake(port->irq);
port->wakeup_pads &= ~(1 << gpio_idx);
port->wakeup_pads &= ~BIT(gpio_idx);
}
return ret;

View File

@@ -86,49 +86,49 @@
#define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK)
static const struct i2c_device_id pca953x_id[] = {
{ "pca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9505", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9506", 40 | PCA953X_TYPE | PCA_INT, },
{ "pca9534", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9535", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9536", 4 | PCA953X_TYPE, },
{ "pca9537", 4 | PCA953X_TYPE | PCA_INT, },
{ "pca9538", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9539", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9554", 8 | PCA953X_TYPE | PCA_INT, },
{ "pca9555", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca9556", 8 | PCA953X_TYPE, },
{ "pca9557", 8 | PCA953X_TYPE, },
{ "pca9574", 8 | PCA957X_TYPE | PCA_INT, },
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
{ "pca9698", 40 | PCA953X_TYPE, },
{ .name = "pca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9505", .driver_data = 40 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9506", .driver_data = 40 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9534", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9535", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9536", .driver_data = 4 | PCA953X_TYPE },
{ .name = "pca9537", .driver_data = 4 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9555", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca9556", .driver_data = 8 | PCA953X_TYPE },
{ .name = "pca9557", .driver_data = 8 | PCA953X_TYPE },
{ .name = "pca9574", .driver_data = 8 | PCA957X_TYPE | PCA_INT },
{ .name = "pca9575", .driver_data = 16 | PCA957X_TYPE | PCA_INT },
{ .name = "pca9698", .driver_data = 40 | PCA953X_TYPE },
{ "pcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal6534", 34 | PCAL653X_TYPE | PCA_LATCH_INT, },
{ "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal9554b", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ .name = "pcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal6524", .driver_data = 24 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal6534", .driver_data = 34 | PCAL653X_TYPE | PCA_LATCH_INT },
{ .name = "pcal9535", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal9554b", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "pcal9555a", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ "max7310", 8 | PCA953X_TYPE, },
{ "max7312", 16 | PCA953X_TYPE | PCA_INT, },
{ "max7313", 16 | PCA953X_TYPE | PCA_INT, },
{ "max7315", 8 | PCA953X_TYPE | PCA_INT, },
{ "max7318", 16 | PCA953X_TYPE | PCA_INT, },
{ "pca6107", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6408", 8 | PCA953X_TYPE | PCA_INT, },
{ "tca6416", 16 | PCA953X_TYPE | PCA_INT, },
{ "tca6418", 18 | TCA6418_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 },
{ .name = "max7310", .driver_data = 8 | PCA953X_TYPE },
{ .name = "max7312", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "max7313", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "max7315", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "max7318", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "pca6107", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "tca6408", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "tca6416", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "tca6418", .driver_data = 18 | TCA6418_TYPE | PCA_INT },
{ .name = "tca6424", .driver_data = 24 | PCA953X_TYPE | PCA_INT },
{ .name = "tca9538", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "tca9539", .driver_data = 16 | PCA953X_TYPE | PCA_INT },
{ .name = "tca9554", .driver_data = 8 | PCA953X_TYPE | PCA_INT },
{ .name = "xra1202", .driver_data = 8 | PCA953X_TYPE },
{ "tcal6408", 8 | PCA953X_TYPE | PCA_LATCH_INT, },
{ "tcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, },
{ .name = "tcal6408", .driver_data = 8 | PCA953X_TYPE | PCA_LATCH_INT },
{ .name = "tcal6416", .driver_data = 16 | PCA953X_TYPE | PCA_LATCH_INT },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca953x_id);
@@ -877,11 +877,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio);
bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio);
bitmap_complement(reg_direction, reg_direction, gc->ngpio);
bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio);
/* Look for any newly setup interrupt */
for_each_set_bit(level, irq_mask, gc->ngpio)
for_each_andnot_bit(level, irq_mask, reg_direction, gc->ngpio)
pca953x_gpio_direction_input(&chip->gpio_chip, level);
mutex_unlock(&chip->irq_lock);
@@ -1005,8 +1003,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio);
bitmap_or(pending, pending, cur_stat, gc->ngpio);
bitmap_complement(cur_stat, new_stat, gc->ngpio);
bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio);
bitmap_andnot(cur_stat, reg_direction, new_stat, gc->ngpio);
bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio);
bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio);
bitmap_or(pending, pending, old_stat, gc->ngpio);

View File

@@ -163,9 +163,9 @@ static const struct pca9570_chip_data slg7xl45106_gpio = {
};
static const struct i2c_device_id pca9570_id_table[] = {
{ "pca9570", (kernel_ulong_t)&pca9570_gpio},
{ "pca9571", (kernel_ulong_t)&pca9571_gpio },
{ "slg7xl45106", (kernel_ulong_t)&slg7xl45106_gpio },
{ .name = "pca9570", .driver_data = (kernel_ulong_t)&pca9570_gpio },
{ .name = "pca9571", .driver_data = (kernel_ulong_t)&pca9571_gpio },
{ .name = "slg7xl45106", .driver_data = (kernel_ulong_t)&slg7xl45106_gpio },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, pca9570_id_table);

View File

@@ -20,19 +20,19 @@
#include <linux/spinlock.h>
static const struct i2c_device_id pcf857x_id[] = {
{ "pcf8574", 8 },
{ "pcf8574a", 8 },
{ "pca8574", 8 },
{ "pca9670", 8 },
{ "pca9672", 8 },
{ "pca9674", 8 },
{ "pcf8575", 16 },
{ "pca8575", 16 },
{ "pca9671", 16 },
{ "pca9673", 16 },
{ "pca9675", 16 },
{ "max7328", 8 },
{ "max7329", 8 },
{ .name = "pcf8574", .driver_data = 8 },
{ .name = "pcf8574a", .driver_data = 8 },
{ .name = "pca8574", .driver_data = 8 },
{ .name = "pca9670", .driver_data = 8 },
{ .name = "pca9672", .driver_data = 8 },
{ .name = "pca9674", .driver_data = 8 },
{ .name = "pcf8575", .driver_data = 16 },
{ .name = "pca8575", .driver_data = 16 },
{ .name = "pca9671", .driver_data = 16 },
{ .name = "pca9673", .driver_data = 16 },
{ .name = "pca9675", .driver_data = 16 },
{ .name = "max7328", .driver_data = 8 },
{ .name = "max7329", .driver_data = 8 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcf857x_id);

View File

@@ -708,15 +708,15 @@ static int pxa_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id gpio_id_table[] = {
{ "pxa25x-gpio", (unsigned long)&pxa25x_id },
{ "pxa26x-gpio", (unsigned long)&pxa26x_id },
{ "pxa27x-gpio", (unsigned long)&pxa27x_id },
{ "pxa3xx-gpio", (unsigned long)&pxa3xx_id },
{ "pxa93x-gpio", (unsigned long)&pxa93x_id },
{ "mmp-gpio", (unsigned long)&mmp_id },
{ "mmp2-gpio", (unsigned long)&mmp2_id },
{ "pxa1928-gpio", (unsigned long)&pxa1928_id },
{ },
{ .name = "pxa25x-gpio", .driver_data = (unsigned long)&pxa25x_id },
{ .name = "pxa26x-gpio", .driver_data = (unsigned long)&pxa26x_id },
{ .name = "pxa27x-gpio", .driver_data = (unsigned long)&pxa27x_id },
{ .name = "pxa3xx-gpio", .driver_data = (unsigned long)&pxa3xx_id },
{ .name = "pxa93x-gpio", .driver_data = (unsigned long)&pxa93x_id },
{ .name = "mmp-gpio", .driver_data = (unsigned long)&mmp_id },
{ .name = "mmp2-gpio", .driver_data = (unsigned long)&mmp2_id },
{ .name = "pxa1928-gpio", .driver_data = (unsigned long)&pxa1928_id },
{ }
};
static struct platform_driver pxa_gpio_driver = {

View File

@@ -40,16 +40,18 @@
#define REALTEK_GPIO_PORTS_PER_BANK 4
/**
* realtek_gpio_ctrl - Realtek Otto GPIO driver data
* struct realtek_gpio_ctrl - Realtek Otto GPIO driver data
*
* @chip: Associated gpio_generic_chip instance
* @base: Base address of the register block for a GPIO bank
* @cpumask_base: Base address of the per-CPU interrupt mask registers
* @cpu_irq_maskable: CPUs that can receive GPIO interrupts
* @lock: Lock for accessing the IRQ registers and values
* @intr_mask: Mask for interrupts lines
* @intr_type: Interrupt type selection
* @bank_read: Read a bank setting as a single 32-bit value
* @bank_write: Write a bank setting as a single 32-bit value
* @imr_line_pos: Bit shift of an IRQ line's IMR value.
* @line_imr_pos: Bit shift of an IRQ line's IMR value.
*
* The DIR, DATA, and ISR registers consist of four 8-bit port values, packed
* into a single 32-bit register. Use @bank_read (@bank_write) to get (assign)

View File

@@ -31,6 +31,7 @@ struct gpio_regmap {
unsigned int reg_clr_base;
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
unsigned long *fixed_direction_mask;
unsigned long *fixed_direction_output;
#ifdef CONFIG_REGMAP_IRQ
@@ -138,6 +139,20 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
return regmap_write(gpio->regmap, reg, mask);
}
static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio,
unsigned int offset)
{
if (!gpio->fixed_direction_output)
return false;
/* In this case only some GPIOs are fixed as input/output */
if (gpio->fixed_direction_mask &&
!test_bit(offset, gpio->fixed_direction_mask))
return false;
return true;
}
static int gpio_regmap_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
@@ -145,7 +160,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
unsigned int base, val, reg, mask;
int invert, ret;
if (gpio->fixed_direction_output) {
if (gpio_regmap_fixed_direction(gpio, offset)) {
if (test_bit(offset, gpio->fixed_direction_output))
return GPIO_LINE_DIRECTION_OUT;
else
@@ -181,6 +196,22 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return GPIO_LINE_DIRECTION_IN;
}
static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio,
unsigned int offset, bool output)
{
if (test_bit(offset, gpio->fixed_direction_output)) {
if (output)
return 0;
else
return -EINVAL;
} else {
if (output)
return -EINVAL;
else
return 0;
}
}
static int gpio_regmap_set_direction(struct gpio_chip *chip,
unsigned int offset, bool output)
{
@@ -188,6 +219,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
unsigned int base, val, reg, mask;
int invert, ret;
/*
* If the direction is fixed, only accept the fixed
* direction in this call.
*/
if (gpio_regmap_fixed_direction(gpio, offset))
return gpio_regmap_try_direction_fixed(gpio, offset, output);
if (gpio->reg_dir_out_base) {
base = gpio_regmap_addr(gpio->reg_dir_out_base);
invert = 0;
@@ -219,6 +257,20 @@ static int gpio_regmap_direction_input(struct gpio_chip *chip,
static int gpio_regmap_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
/*
* First check if this is gonna work on a fixed direction line,
* if it doesn't (i.e. this is a fixed input line), then do not
* attempt to set the output value either and just bail out.
*/
if (gpio_regmap_fixed_direction(gpio, offset)) {
ret = gpio_regmap_try_direction_fixed(gpio, offset, true);
if (ret)
return ret;
}
gpio_regmap_set(chip, offset, value);
return gpio_regmap_set_direction(chip, offset, true);
@@ -302,12 +354,23 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
goto err_free_gpio;
}
if (config->fixed_direction_mask) {
gpio->fixed_direction_mask = bitmap_alloc(chip->ngpio,
GFP_KERNEL);
if (!gpio->fixed_direction_mask) {
ret = -ENOMEM;
goto err_free_gpio;
}
bitmap_copy(gpio->fixed_direction_mask,
config->fixed_direction_mask, chip->ngpio);
}
if (config->fixed_direction_output) {
gpio->fixed_direction_output = bitmap_alloc(chip->ngpio,
GFP_KERNEL);
if (!gpio->fixed_direction_output) {
ret = -ENOMEM;
goto err_free_gpio;
goto err_free_bitmap_dirmask;
}
bitmap_copy(gpio->fixed_direction_output,
config->fixed_direction_output, chip->ngpio);
@@ -329,7 +392,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
ret = gpiochip_add_data(chip, gpio);
if (ret < 0)
goto err_free_bitmap;
goto err_free_bitmap_output;
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
@@ -355,8 +418,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
err_remove_gpiochip:
gpiochip_remove(chip);
err_free_bitmap:
err_free_bitmap_output:
bitmap_free(gpio->fixed_direction_output);
err_free_bitmap_dirmask:
bitmap_free(gpio->fixed_direction_mask);
err_free_gpio:
kfree(gpio);
return ERR_PTR(ret);
@@ -376,6 +441,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio)
gpiochip_remove(&gpio->gpio_chip);
bitmap_free(gpio->fixed_direction_output);
bitmap_free(gpio->fixed_direction_mask);
kfree(gpio);
}
EXPORT_SYMBOL_GPL(gpio_regmap_unregister);

View File

@@ -695,9 +695,9 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
pdev = dev->pdev;
if (pdev)
return sprintf(page, "%s\n", dev_name(&pdev->dev));
return sysfs_emit(page, "%s\n", dev_name(&pdev->dev));
return sprintf(page, "gpio-sim.%d\n", dev->id);
return sysfs_emit(page, "gpio-sim.%d\n", dev->id);
}
CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name);
@@ -711,7 +711,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page)
scoped_guard(mutex, &dev->lock)
live = gpio_sim_device_is_live(dev);
return sprintf(page, "%c\n", live ? '1' : '0');
return sysfs_emit(page, "%c\n", live ? '1' : '0');
}
static unsigned int gpio_sim_get_line_names_size(struct gpio_sim_bank *bank)
@@ -1059,7 +1059,7 @@ static int gpio_sim_emit_chip_name(struct device *dev, void *data)
return 0;
if (device_match_fwnode(dev, ctx->swnode))
return sprintf(ctx->page, "%s\n", dev_name(dev));
return sysfs_emit(ctx->page, "%s\n", dev_name(dev));
return 0;
}
@@ -1077,7 +1077,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
return device_for_each_child(&dev->pdev->dev, &ctx,
gpio_sim_emit_chip_name);
return sprintf(page, "none\n");
return sysfs_emit(page, "none\n");
}
CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name);
@@ -1090,7 +1090,7 @@ gpio_sim_bank_config_label_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%s\n", bank->label ?: "");
return sysfs_emit(page, "%s\n", bank->label ?: "");
}
static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
@@ -1125,7 +1125,7 @@ gpio_sim_bank_config_num_lines_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%u\n", bank->num_lines);
return sysfs_emit(page, "%u\n", bank->num_lines);
}
static ssize_t
@@ -1171,7 +1171,7 @@ gpio_sim_line_config_name_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%s\n", line->name ?: "");
return sysfs_emit(page, "%s\n", line->name ?: "");
}
static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
@@ -1206,7 +1206,7 @@ gpio_sim_line_config_valid_show(struct config_item *item, char *page)
guard(mutex)(&dev->lock);
return sprintf(page, "%c\n", line->valid ? '1' : '0');
return sysfs_emit(page, "%c\n", line->valid ? '1' : '0');
}
static ssize_t gpio_sim_line_config_valid_store(struct config_item *item,
@@ -1244,7 +1244,7 @@ static ssize_t gpio_sim_hog_config_name_show(struct config_item *item,
guard(mutex)(&dev->lock);
return sprintf(page, "%s\n", hog->name ?: "");
return sysfs_emit(page, "%s\n", hog->name ?: "");
}
static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
@@ -1298,7 +1298,7 @@ static ssize_t gpio_sim_hog_config_direction_show(struct config_item *item,
return -EINVAL;
}
return sprintf(page, "%s\n", repr);
return sysfs_emit(page, "%s\n", repr);
}
static ssize_t
@@ -1338,7 +1338,7 @@ static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item,
guard(mutex)(&dev->lock);
return sprintf(page, "%c\n", hog->active_low ? '1' : '0');
return sysfs_emit(page, "%c\n", hog->active_low ? '1' : '0');
}
static ssize_t

View File

@@ -19,6 +19,7 @@
#include <dt-bindings/gpio/tegra186-gpio.h>
#include <dt-bindings/gpio/tegra194-gpio.h>
#include <dt-bindings/gpio/tegra234-gpio.h>
#include <dt-bindings/gpio/nvidia,tegra238-gpio.h>
#include <dt-bindings/gpio/tegra241-gpio.h>
#include <dt-bindings/gpio/tegra256-gpio.h>
#include <dt-bindings/gpio/nvidia,tegra264-gpio.h>
@@ -1239,6 +1240,67 @@ static const struct tegra_gpio_soc tegra234_aon_soc = {
.has_vm_support = false,
};
#define TEGRA238_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA238_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra238_main_ports[] = {
TEGRA238_MAIN_GPIO_PORT(A, 0, 0, 8),
TEGRA238_MAIN_GPIO_PORT(B, 0, 1, 5),
TEGRA238_MAIN_GPIO_PORT(C, 0, 2, 8),
TEGRA238_MAIN_GPIO_PORT(D, 0, 3, 8),
TEGRA238_MAIN_GPIO_PORT(E, 0, 4, 4),
TEGRA238_MAIN_GPIO_PORT(F, 0, 5, 8),
TEGRA238_MAIN_GPIO_PORT(G, 0, 6, 8),
TEGRA238_MAIN_GPIO_PORT(H, 0, 7, 6),
TEGRA238_MAIN_GPIO_PORT(J, 1, 0, 8),
TEGRA238_MAIN_GPIO_PORT(K, 1, 1, 4),
TEGRA238_MAIN_GPIO_PORT(L, 1, 2, 8),
TEGRA238_MAIN_GPIO_PORT(M, 1, 3, 8),
TEGRA238_MAIN_GPIO_PORT(N, 1, 4, 3),
TEGRA238_MAIN_GPIO_PORT(P, 1, 5, 8),
TEGRA238_MAIN_GPIO_PORT(Q, 1, 6, 3),
TEGRA238_MAIN_GPIO_PORT(R, 2, 0, 8),
TEGRA238_MAIN_GPIO_PORT(S, 2, 1, 8),
TEGRA238_MAIN_GPIO_PORT(T, 2, 2, 8),
TEGRA238_MAIN_GPIO_PORT(U, 2, 3, 6),
TEGRA238_MAIN_GPIO_PORT(V, 2, 4, 2),
TEGRA238_MAIN_GPIO_PORT(W, 3, 0, 8),
TEGRA238_MAIN_GPIO_PORT(X, 3, 1, 2)
};
static const struct tegra_gpio_soc tegra238_main_soc = {
.num_ports = ARRAY_SIZE(tegra238_main_ports),
.ports = tegra238_main_ports,
.name = "tegra238-gpio",
.instance = 0,
.num_irqs_per_bank = 8,
.has_vm_support = true,
};
#define TEGRA238_AON_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA238_AON, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra238_aon_ports[] = {
TEGRA238_AON_GPIO_PORT(AA, 0, 0, 8),
TEGRA238_AON_GPIO_PORT(BB, 0, 1, 1),
TEGRA238_AON_GPIO_PORT(CC, 0, 2, 8),
TEGRA238_AON_GPIO_PORT(DD, 0, 3, 8),
TEGRA238_AON_GPIO_PORT(EE, 0, 4, 6),
TEGRA238_AON_GPIO_PORT(FF, 0, 5, 8),
TEGRA238_AON_GPIO_PORT(GG, 0, 6, 8),
TEGRA238_AON_GPIO_PORT(HH, 0, 7, 4)
};
static const struct tegra_gpio_soc tegra238_aon_soc = {
.num_ports = ARRAY_SIZE(tegra238_aon_ports),
.ports = tegra238_aon_ports,
.name = "tegra238-gpio-aon",
.instance = 1,
.num_irqs_per_bank = 8,
.has_gte = true,
.has_vm_support = false,
};
#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins)
@@ -1333,6 +1395,7 @@ static const struct tegra_gpio_soc tegra264_aon_soc = {
.name = "tegra264-gpio-aon",
.instance = 1,
.num_irqs_per_bank = 8,
.has_gte = true,
.has_vm_support = true,
};
@@ -1447,6 +1510,12 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
}, {
.compatible = "nvidia,tegra234-gpio-aon",
.data = &tegra234_aon_soc
}, {
.compatible = "nvidia,tegra238-gpio",
.data = &tegra238_main_soc
}, {
.compatible = "nvidia,tegra238-gpio-aon",
.data = &tegra238_aon_soc
}, {
.compatible = "nvidia,tegra256-gpio",
.data = &tegra256_main_soc

View File

@@ -14,7 +14,6 @@
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/timb_gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
@@ -225,19 +224,21 @@ static int timbgpio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
struct timbgpio *tgpio;
struct timbgpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
int irq = platform_get_irq(pdev, 0);
if (!pdata || pdata->nr_pins > 32) {
dev_err(dev, "Invalid platform data\n");
return -EINVAL;
}
tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL);
if (!tgpio)
return -EINVAL;
tgpio->irq_base = pdata->irq_base;
gc = &tgpio->gpio;
err = device_property_read_u32(dev, "irq-base", &tgpio->irq_base);
if (err)
return err;
err = device_property_read_u32(dev, "gpio-base", &gc->base);
if (err)
return err;
spin_lock_init(&tgpio->lock);
@@ -245,8 +246,6 @@ static int timbgpio_probe(struct platform_device *pdev)
if (IS_ERR(tgpio->membase))
return PTR_ERR(tgpio->membase);
gc = &tgpio->gpio;
gc->label = dev_name(&pdev->dev);
gc->owner = THIS_MODULE;
gc->parent = &pdev->dev;
@@ -256,21 +255,22 @@ static int timbgpio_probe(struct platform_device *pdev)
gc->set = timbgpio_gpio_set;
gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL;
gc->dbg_show = NULL;
gc->base = pdata->gpio_base;
gc->ngpio = pdata->nr_pins;
gc->can_sleep = false;
err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio);
if (err)
return err;
if (gc->ngpio > 32)
return dev_err_probe(dev, -EINVAL, "Invalid number of pins\n");
/* make sure to disable interrupts */
iowrite32(0x0, tgpio->membase + TGPIO_IER);
if (irq < 0 || tgpio->irq_base <= 0)
return 0;
for (i = 0; i < pdata->nr_pins; i++) {
for (i = 0; i < gc->ngpio; i++) {
irq_set_chip_and_handler(tgpio->irq_base + i,
&timbgpio_irqchip, handle_simple_irq);
irq_set_chip_data(tgpio->irq_base + i, tgpio);

View File

@@ -112,7 +112,7 @@ static int tpic2810_probe(struct i2c_client *client)
}
static const struct i2c_device_id tpic2810_id_table[] = {
{ "tpic2810", },
{ .name = "tpic2810" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tpic2810_id_table);

View File

@@ -91,7 +91,7 @@ static int tps65086_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps65086_gpio_id_table[] = {
{ "tps65086-gpio", },
{ .name = "tps65086-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65086_gpio_id_table);

View File

@@ -201,7 +201,7 @@ static const struct of_device_id tps65218_dt_match[] = {
MODULE_DEVICE_TABLE(of, tps65218_dt_match);
static const struct platform_device_id tps65218_gpio_id_table[] = {
{ "tps65218-gpio", },
{ .name = "tps65218-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65218_gpio_id_table);

View File

@@ -249,8 +249,8 @@ static int tps65219_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps6521x_gpio_id_table[] = {
{ "tps65214-gpio", TPS65214 },
{ "tps65219-gpio", TPS65219 },
{ .name = "tps65214-gpio", .driver_data = TPS65214 },
{ .name = "tps65219-gpio", .driver_data = TPS65219 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps6521x_gpio_id_table);

View File

@@ -115,7 +115,7 @@ static int tps65912_gpio_probe(struct platform_device *pdev)
}
static const struct platform_device_id tps65912_gpio_id_table[] = {
{ "tps65912-gpio", },
{ .name = "tps65912-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, tps65912_gpio_id_table);

View File

@@ -175,7 +175,7 @@ static int ts4900_gpio_probe(struct i2c_client *client)
}
static const struct i2c_device_id ts4900_gpio_id_table[] = {
{ "ts4900-gpio", },
{ .name = "ts4900-gpio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table);

View File

@@ -1,446 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Digital I/O driver for Technologic Systems TS-5500
*
* Copyright (c) 2012 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* Technologic Systems platforms have pin blocks, exposing several Digital
* Input/Output lines (DIO). This driver aims to support single pin blocks.
* In that sense, the support is not limited to the TS-5500 blocks.
* Actually, the following platforms have DIO support:
*
* TS-5500:
* Documentation: https://docs.embeddedts.com/TS-5500
* Blocks: DIO1, DIO2 and LCD port.
*
* TS-5600:
* Documentation: https://docs.embeddedts.com/TS-5600
* Blocks: LCD port (identical to TS-5500 LCD).
*/
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* List of supported Technologic Systems platforms DIO blocks */
enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
struct ts5500_priv {
const struct ts5500_dio *pinout;
struct gpio_chip gpio_chip;
spinlock_t lock;
bool strap;
u8 hwirq;
};
/*
* Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
* This flag ensures that the region has been requested by this driver.
*/
static bool hex7d_reserved;
/*
* This structure is used to describe capabilities of DIO lines,
* such as available directions and connected interrupt (if any).
*/
struct ts5500_dio {
const u8 value_addr;
const u8 value_mask;
const u8 control_addr;
const u8 control_mask;
const bool no_input;
const bool no_output;
const u8 irq;
};
#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit) \
{ \
.value_addr = vaddr, \
.value_mask = BIT(vbit), \
.control_addr = caddr, \
.control_mask = BIT(cbit), \
}
#define TS5500_DIO_IN(addr, bit) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_output = true, \
}
#define TS5500_DIO_IN_IRQ(addr, bit, _irq) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_output = true, \
.irq = _irq, \
}
#define TS5500_DIO_OUT(addr, bit) \
{ \
.value_addr = addr, \
.value_mask = BIT(bit), \
.no_input = true, \
}
/*
* Input/Output DIO lines are programmed in groups of 4. Their values are
* available through 4 consecutive bits in a value port, whereas the direction
* of these 4 lines is driven by only 1 bit in a control port.
*/
#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit) \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit), \
TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
/*
* TS-5500 DIO1 block
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x7b 0 0x7a 0 x x DIO1_0 1 0
* 0x7b 1 0x7a 0 x x DIO1_1 3 1
* 0x7b 2 0x7a 0 x x DIO1_2 5 2
* 0x7b 3 0x7a 0 x x DIO1_3 7 3
* 0x7b 4 0x7a 1 x x DIO1_4 9 4
* 0x7b 5 0x7a 1 x x DIO1_5 11 5
* 0x7b 6 0x7a 1 x x DIO1_6 13 6
* 0x7b 7 0x7a 1 x x DIO1_7 15 7
* 0x7c 0 0x7a 5 x x DIO1_8 4 8
* 0x7c 1 0x7a 5 x x DIO1_9 6 9
* 0x7c 2 0x7a 5 x x DIO1_10 8 10
* 0x7c 3 0x7a 5 x x DIO1_11 10 11
* 0x7c 4 x DIO1_12 12 12
* 0x7c 5 x 7 DIO1_13 14 13
*/
static const struct ts5500_dio ts5500_dio1[] = {
TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
TS5500_DIO_IN(0x7c, 4),
TS5500_DIO_IN_IRQ(0x7c, 5, 7),
};
/*
* TS-5500 DIO2 block
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x7e 0 0x7d 0 x x DIO2_0 1 0
* 0x7e 1 0x7d 0 x x DIO2_1 3 1
* 0x7e 2 0x7d 0 x x DIO2_2 5 2
* 0x7e 3 0x7d 0 x x DIO2_3 7 3
* 0x7e 4 0x7d 1 x x DIO2_4 9 4
* 0x7e 5 0x7d 1 x x DIO2_5 11 5
* 0x7e 6 0x7d 1 x x DIO2_6 13 6
* 0x7e 7 0x7d 1 x x DIO2_7 15 7
* 0x7f 0 0x7d 5 x x DIO2_8 4 8
* 0x7f 1 0x7d 5 x x DIO2_9 6 9
* 0x7f 2 0x7d 5 x x DIO2_10 8 10
* 0x7f 3 0x7d 5 x x DIO2_11 10 11
* 0x7f 4 x 6 DIO2_13 14 12
*/
static const struct ts5500_dio ts5500_dio2[] = {
TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
TS5500_DIO_IN_IRQ(0x7f, 4, 6),
};
/*
* TS-5500 LCD port used as DIO block
* TS-5600 LCD port is identical
*
* value control dir hw
* addr bit addr bit in out irq name pin offset
*
* 0x72 0 0x7d 2 x x LCD_0 8 0
* 0x72 1 0x7d 2 x x LCD_1 7 1
* 0x72 2 0x7d 2 x x LCD_2 10 2
* 0x72 3 0x7d 2 x x LCD_3 9 3
* 0x72 4 0x7d 3 x x LCD_4 12 4
* 0x72 5 0x7d 3 x x LCD_5 11 5
* 0x72 6 0x7d 3 x x LCD_6 14 6
* 0x72 7 0x7d 3 x x LCD_7 13 7
* 0x73 0 x LCD_EN 5 8
* 0x73 6 x LCD_WR 6 9
* 0x73 7 x 1 LCD_RS 3 10
*/
static const struct ts5500_dio ts5500_lcd[] = {
TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
TS5500_DIO_OUT(0x73, 0),
TS5500_DIO_IN(0x73, 6),
TS5500_DIO_IN_IRQ(0x73, 7, 1),
};
static inline void ts5500_set_mask(u8 mask, u8 addr)
{
u8 val = inb(addr);
val |= mask;
outb(val, addr);
}
static inline void ts5500_clear_mask(u8 mask, u8 addr)
{
u8 val = inb(addr);
val &= ~mask;
outb(val, addr);
}
static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
if (line.no_input)
return -ENXIO;
if (line.no_output)
return 0;
spin_lock_irqsave(&priv->lock, flags);
ts5500_clear_mask(line.control_mask, line.control_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
return !!(inb(line.value_addr) & line.value_mask);
}
static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
if (line.no_output)
return -ENXIO;
spin_lock_irqsave(&priv->lock, flags);
if (!line.no_input)
ts5500_set_mask(line.control_mask, line.control_addr);
if (val)
ts5500_set_mask(line.value_mask, line.value_addr);
else
ts5500_clear_mask(line.value_mask, line.value_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio line = priv->pinout[offset];
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (val)
ts5500_set_mask(line.value_mask, line.value_addr);
else
ts5500_clear_mask(line.value_mask, line.value_addr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct ts5500_priv *priv = gpiochip_get_data(chip);
const struct ts5500_dio *block = priv->pinout;
const struct ts5500_dio line = block[offset];
/* Only one pin is connected to an interrupt */
if (line.irq)
return line.irq;
/* As this pin is input-only, we may strap it to another in/out pin */
if (priv->strap)
return priv->hwirq;
return -ENXIO;
}
static int ts5500_enable_irq(struct ts5500_priv *priv)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (priv->hwirq == 7)
ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
else if (priv->hwirq == 6)
ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
else if (priv->hwirq == 1)
ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
else
ret = -EINVAL;
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
static void ts5500_disable_irq(struct ts5500_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (priv->hwirq == 7)
ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
else if (priv->hwirq == 6)
ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
else if (priv->hwirq == 1)
ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
else
dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n",
priv->hwirq);
spin_unlock_irqrestore(&priv->lock, flags);
}
static int ts5500_dio_probe(struct platform_device *pdev)
{
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
struct ts5500_priv *priv;
unsigned long flags;
int ret;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->hwirq = ret;
spin_lock_init(&priv->lock);
priv->gpio_chip.owner = THIS_MODULE;
priv->gpio_chip.label = name;
priv->gpio_chip.parent = dev;
priv->gpio_chip.direction_input = ts5500_gpio_input;
priv->gpio_chip.direction_output = ts5500_gpio_output;
priv->gpio_chip.get = ts5500_gpio_get;
priv->gpio_chip.set = ts5500_gpio_set;
priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
priv->gpio_chip.base = -1;
switch (block) {
case TS5500_DIO1:
priv->pinout = ts5500_dio1;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
if (!devm_request_region(dev, 0x7a, 3, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
break;
case TS5500_DIO2:
priv->pinout = ts5500_dio2;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
if (!devm_request_region(dev, 0x7e, 2, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
if (hex7d_reserved)
break;
if (!devm_request_region(dev, 0x7d, 1, name)) {
dev_err(dev, "failed to request %s 7D\n", name);
return -EBUSY;
}
hex7d_reserved = true;
break;
case TS5500_LCD:
case TS5600_LCD:
priv->pinout = ts5500_lcd;
priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
if (!devm_request_region(dev, 0x72, 2, name)) {
dev_err(dev, "failed to request %s ports\n", name);
return -EBUSY;
}
if (!hex7d_reserved) {
if (!devm_request_region(dev, 0x7d, 1, name)) {
dev_err(dev, "failed to request %s 7D\n", name);
return -EBUSY;
}
hex7d_reserved = true;
}
/* Ensure usage of LCD port as DIO */
spin_lock_irqsave(&priv->lock, flags);
ts5500_clear_mask(BIT(4), 0x7d);
spin_unlock_irqrestore(&priv->lock, flags);
break;
}
ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
if (ret) {
dev_err(dev, "failed to register the gpio chip\n");
return ret;
}
ret = ts5500_enable_irq(priv);
if (ret) {
dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
return ret;
}
return 0;
}
static void ts5500_dio_remove(struct platform_device *pdev)
{
struct ts5500_priv *priv = platform_get_drvdata(pdev);
ts5500_disable_irq(priv);
}
static const struct platform_device_id ts5500_dio_ids[] = {
{ "ts5500-dio1", TS5500_DIO1 },
{ "ts5500-dio2", TS5500_DIO2 },
{ "ts5500-dio-lcd", TS5500_LCD },
{ "ts5600-dio-lcd", TS5600_LCD },
{ }
};
MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
static struct platform_driver ts5500_dio_driver = {
.driver = {
.name = "ts5500-dio",
},
.probe = ts5500_dio_probe,
.remove = ts5500_dio_remove,
.id_table = ts5500_dio_ids,
};
module_platform_driver(ts5500_dio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");

View File

@@ -31,6 +31,7 @@ static const struct acpi_device_id usbio_gpio_acpi_hids[] = {
{ "INTC10B5" }, /* LNL */
{ "INTC10D1" }, /* MTL-CVF */
{ "INTC10E2" }, /* PTL */
{ "INTC1116" }, /* NVL */
{ }
};

View File

@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2024 Waveshare International Limited
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
/* I2C registers of the microcontroller. */
#define REG_TP 0x94
#define REG_LCD 0x95
#define REG_PWM 0x96
#define REG_SIZE 0x97
#define REG_ID 0x98
#define REG_VERSION 0x99
enum {
GPIO_AVDD = 0,
GPIO_PANEL_RESET = 1,
GPIO_BL_ENABLE = 2,
GPIO_IOVCC = 4,
GPIO_VCC = 8,
GPIO_TS_RESET = 9,
};
#define NUM_GPIO 16
struct waveshare_gpio {
struct mutex dir_lock;
struct mutex pwr_lock;
struct regmap *regmap;
u16 poweron_state;
struct gpio_chip gc;
};
static const struct regmap_config waveshare_gpio_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = REG_VERSION,
};
static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset)
{
u16 pwr_state;
guard(mutex)(&state->pwr_lock);
pwr_state = state->poweron_state & BIT(offset);
return !!pwr_state;
}
static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value)
{
u16 last_val;
int err;
guard(mutex)(&state->pwr_lock);
last_val = state->poweron_state;
if (value)
last_val |= BIT(offset);
else
last_val &= ~BIT(offset);
state->poweron_state = last_val;
err = regmap_write(state->regmap, REG_TP, last_val >> 8);
if (!err)
err = regmap_write(state->regmap, REG_LCD, last_val & 0xff);
return err;
}
static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}
static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct waveshare_gpio *state = gpiochip_get_data(gc);
return waveshare_gpio_get(state, offset);
}
static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct waveshare_gpio *state = gpiochip_get_data(gc);
return waveshare_gpio_set(state, offset, value);
}
static int waveshare_gpio_update_status(struct backlight_device *bl)
{
struct waveshare_gpio *state = bl_get_data(bl);
int brightness = backlight_get_brightness(bl);
waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness);
return regmap_write(state->regmap, REG_PWM, brightness);
}
static const struct backlight_ops waveshare_gpio_bl = {
.update_status = waveshare_gpio_update_status,
};
static int waveshare_gpio_probe(struct i2c_client *i2c)
{
struct backlight_properties props = {};
struct waveshare_gpio *state;
struct device *dev = &i2c->dev;
struct backlight_device *bl;
struct regmap *regmap;
unsigned int data;
int ret;
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
ret = devm_mutex_init(dev, &state->dir_lock);
if (ret)
return ret;
ret = devm_mutex_init(dev, &state->pwr_lock);
if (ret)
return ret;
regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
state->regmap = regmap;
i2c_set_clientdata(i2c, state);
ret = regmap_read(regmap, REG_ID, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data);
ret = regmap_read(regmap, REG_SIZE, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel size = %d\n", data);
ret = regmap_read(regmap, REG_VERSION, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data);
ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1);
if (ret)
return dev_err_probe(dev, ret, "Failed to program GPIOs\n");
msleep(20);
state->gc.parent = dev;
state->gc.label = i2c->name;
state->gc.owner = THIS_MODULE;
state->gc.base = -1;
state->gc.ngpio = NUM_GPIO;
/* it is output only */
state->gc.get = waveshare_gpio_gpio_get;
state->gc.set = waveshare_gpio_gpio_set;
state->gc.get_direction = waveshare_gpio_gpio_get_direction;
state->gc.can_sleep = true;
ret = devm_gpiochip_add_data(dev, &state->gc, state);
if (ret)
return dev_err_probe(dev, ret, "Failed to create gpiochip\n");
props.type = BACKLIGHT_RAW;
props.max_brightness = 255;
props.brightness = 255;
bl = devm_backlight_device_register(dev, dev_name(dev), dev, state,
&waveshare_gpio_bl, &props);
return PTR_ERR_OR_ZERO(bl);
}
static const struct of_device_id waveshare_gpio_dt_ids[] = {
{ .compatible = "waveshare,dsi-touch-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids);
static struct i2c_driver waveshare_gpio_regulator_driver = {
.driver = {
.name = "waveshare-regulator",
.of_match_table = of_match_ptr(waveshare_gpio_dt_ids),
},
.probe = waveshare_gpio_probe,
};
module_i2c_driver(waveshare_gpio_regulator_driver);
MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels");
MODULE_LICENSE("GPL");

View File

@@ -495,13 +495,11 @@ static void xgpio_irqhandler(struct irq_desc *desc)
xgpio_read_ch_all(chip, XGPIO_DATA_OFFSET, hw);
bitmap_complement(rising, chip->last_irq_read, 64);
bitmap_and(rising, rising, hw, 64);
bitmap_andnot(rising, hw, chip->last_irq_read, 64);
bitmap_and(rising, rising, chip->enable, 64);
bitmap_and(rising, rising, chip->rising_edge, 64);
bitmap_complement(falling, hw, 64);
bitmap_and(falling, falling, chip->last_irq_read, 64);
bitmap_andnot(falling, chip->last_irq_read, hw, 64);
bitmap_and(falling, falling, chip->enable, 64);
bitmap_and(falling, falling, chip->falling_edge, 64);

View File

@@ -64,14 +64,14 @@ static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin,
unsigned port_offset)
{
unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
return readl(IOMEM(c->regs + section_offset + port_offset));
return readl(c->regs + section_offset + port_offset);
}
static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin,
unsigned port_offset, u32 val)
{
unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
writel(val, IOMEM(c->regs + section_offset + port_offset));
writel(val, c->regs + section_offset + port_offset);
}
/* Functions for struct gpio_chip */

View File

@@ -25,6 +25,7 @@
#define VERSAL_GPIO_MAX_BANK 4
#define PMC_GPIO_MAX_BANK 5
#define VERSAL_UNUSED_BANKS 2
#define EIO_GPIO_MAX_BANK 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
@@ -818,6 +819,16 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
};
static const struct zynq_platform_data eio_gpio_def = {
.label = "eio_gpio",
.ngpio = 52,
.max_bank = EIO_GPIO_MAX_BANK,
.bank_min[0] = 0,
.bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */
.bank_min[1] = 26,
.bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */
};
static const struct zynq_platform_data versal_gpio_def = {
.label = "versal_gpio",
.quirks = GPIO_QUIRK_VERSAL,
@@ -882,6 +893,7 @@ static const struct of_device_id zynq_gpio_of_match[] = {
{ .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def },
{ .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def },
{ .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def },
{ .compatible = "xlnx,eio-gpio-1.0", .data = &eio_gpio_def },
{ /* end of table */ }
};
MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);

View File

@@ -142,10 +142,6 @@ static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin)
if (!gdev)
return ERR_PTR(-EPROBE_DEFER);
/*
* FIXME: keep track of the reference to the GPIO device somehow
* instead of putting it here.
*/
return gpio_device_get_desc(gdev, pin);
}

View File

@@ -353,6 +353,13 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo
{
int ret;
/*
* We are passing the devres device here so if the user did not pass
* another parent, it's this one.
*/
if (!gc->parent)
gc->parent = dev;
ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key);
if (ret < 0)
return ret;

View File

@@ -0,0 +1,358 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
*/
#include <linux/fwnode.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <kunit/platform_device.h>
#include <kunit/test.h>
#define GPIO_TEST_PROVIDER "gpio-test-provider"
#define GPIO_SWNODE_TEST_CONSUMER "gpio-swnode-test-consumer"
#define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer"
static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}
static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value)
{
return 0;
}
static int gpio_test_provider_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
return -ENOMEM;
gc->base = -1;
gc->ngpio = 4;
gc->label = "gpio-swnode-consumer-test-device";
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->get_direction = gpio_test_provider_get_direction;
gc->set = gpio_test_provider_set;
return devm_gpiochip_add_data(dev, gc, NULL);
}
static struct platform_driver gpio_test_provider_driver = {
.probe = gpio_test_provider_probe,
.driver = {
.name = GPIO_TEST_PROVIDER,
},
};
static const struct software_node gpio_test_provider_swnode = {
.name = "gpio-test-provider-primary",
};
struct gpio_swnode_consumer_pdata {
bool gpio_ok;
};
static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = {
.gpio_ok = false,
};
static int gpio_swnode_consumer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_swnode_consumer_pdata *pdata = dev_get_platdata(dev);
struct gpio_desc *desc;
desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
if (IS_ERR(desc))
return PTR_ERR(desc);
pdata->gpio_ok = true;
return 0;
}
static struct platform_driver gpio_swnode_consumer_driver = {
.probe = gpio_swnode_consumer_probe,
.driver = {
.name = GPIO_SWNODE_TEST_CONSUMER,
},
};
static void gpio_swnode_lookup_by_primary(struct kunit *test)
{
struct gpio_swnode_consumer_pdata *pdata;
struct platform_device_info pdevinfo;
struct property_entry properties[2];
struct platform_device *pdev;
bool bound = false;
int ret;
ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
pdevinfo = (struct platform_device_info){
.name = GPIO_TEST_PROVIDER,
.id = PLATFORM_DEVID_NONE,
.swnode = &gpio_test_provider_swnode,
};
pdev = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
&gpio_test_provider_swnode,
0, GPIO_ACTIVE_HIGH);
properties[1] = (struct property_entry){ };
pdevinfo = (struct platform_device_info){
.name = GPIO_SWNODE_TEST_CONSUMER,
.id = PLATFORM_DEVID_NONE,
.data = &gpio_swnode_pdata_template,
.size_data = sizeof(gpio_swnode_pdata_template),
.properties = properties,
};
pdev = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
wait_for_device_probe();
scoped_guard(device, &pdev->dev)
bound = device_is_bound(&pdev->dev);
KUNIT_ASSERT_TRUE(test, bound);
pdata = dev_get_platdata(&pdev->dev);
KUNIT_ASSERT_TRUE(test, pdata->gpio_ok);
}
static void gpio_swnode_lookup_by_secondary(struct kunit *test)
{
struct gpio_swnode_consumer_pdata *pdata;
struct platform_device_info pdevinfo;
struct property_entry properties[2];
struct fwnode_handle *primary;
struct platform_device *pdev;
bool bound = false;
int ret;
/*
* Can't live on the stack as it will still get referenced in cleanup
* path after this function returns.
*/
primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary);
ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
fwnode_init(primary, NULL);
pdevinfo = (struct platform_device_info){
.name = GPIO_TEST_PROVIDER,
.id = PLATFORM_DEVID_NONE,
.fwnode = primary,
.swnode = &gpio_test_provider_swnode,
};
pdev = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
&gpio_test_provider_swnode,
0, GPIO_ACTIVE_HIGH);
properties[1] = (struct property_entry){ };
pdevinfo = (struct platform_device_info){
.name = GPIO_SWNODE_TEST_CONSUMER,
.id = PLATFORM_DEVID_NONE,
.data = &gpio_swnode_pdata_template,
.size_data = sizeof(gpio_swnode_pdata_template),
.properties = properties,
};
pdev = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
wait_for_device_probe();
scoped_guard(device, &pdev->dev)
bound = device_is_bound(&pdev->dev);
KUNIT_ASSERT_TRUE(test, bound);
pdata = dev_get_platdata(&pdev->dev);
KUNIT_ASSERT_TRUE(test, pdata->gpio_ok);
}
static struct kunit_case gpio_swnode_lookup_tests[] = {
KUNIT_CASE(gpio_swnode_lookup_by_primary),
KUNIT_CASE(gpio_swnode_lookup_by_secondary),
{ }
};
static struct kunit_suite gpio_swnode_lookup_test_suite = {
.name = "gpio-swnode-lookup",
.test_cases = gpio_swnode_lookup_tests,
};
static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier);
struct gpio_unbind_consumer_drvdata {
struct device *dev;
struct gpio_desc *desc;
struct notifier_block nb;
int set_retval;
};
static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct gpio_unbind_consumer_drvdata *drvdata =
container_of(nb, struct gpio_unbind_consumer_drvdata, nb);
struct device *dev = data;
if (dev != drvdata->dev)
return NOTIFY_DONE;
drvdata->set_retval = gpiod_set_value_cansleep(drvdata->desc, 0);
return NOTIFY_OK;
}
static void gpio_unbind_unregister_notifier(void *data)
{
struct notifier_block *nb = data;
blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb);
}
static int gpio_unbind_consumer_probe(struct platform_device *pdev)
{
struct gpio_unbind_consumer_drvdata *data;
struct device *dev = &pdev->dev;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = dev;
data->desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
if (IS_ERR(data->desc))
return PTR_ERR(data->desc);
data->nb.notifier_call = gpio_unbind_notify;
ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb);
if (ret)
return ret;
platform_set_drvdata(pdev, data);
return 0;
}
static struct platform_driver gpio_unbind_consumer_driver = {
.probe = gpio_unbind_consumer_probe,
.driver = {
.name = GPIO_UNBIND_TEST_CONSUMER,
},
};
static void gpio_unbind_with_consumers(struct kunit *test)
{
struct gpio_unbind_consumer_drvdata *cons_data;
struct platform_device_info pdevinfo;
struct property_entry properties[2];
struct platform_device *prvd, *cons;
bool bound = false;
int ret;
ret = kunit_platform_driver_register(test, &gpio_test_provider_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_platform_driver_register(test, &gpio_unbind_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
pdevinfo = (struct platform_device_info){
.name = GPIO_TEST_PROVIDER,
.id = PLATFORM_DEVID_NONE,
.swnode = &gpio_test_provider_swnode,
};
prvd = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prvd);
properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
&gpio_test_provider_swnode,
0, GPIO_ACTIVE_HIGH);
properties[1] = (struct property_entry){ };
pdevinfo = (struct platform_device_info){
.name = GPIO_UNBIND_TEST_CONSUMER,
.id = PLATFORM_DEVID_NONE,
.properties = properties,
};
cons = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons);
wait_for_device_probe();
scoped_guard(device, &cons->dev)
bound = device_is_bound(&cons->dev);
KUNIT_ASSERT_TRUE(test, bound);
kunit_platform_device_unregister(test, prvd);
ret = blocking_notifier_call_chain(&gpio_unbind_notifier, 0, &cons->dev);
KUNIT_ASSERT_EQ(test, ret, NOTIFY_OK);
scoped_guard(device, &cons->dev) {
cons_data = platform_get_drvdata(cons);
ret = cons_data->set_retval;
}
KUNIT_ASSERT_EQ(test, ret, -ENODEV);
}
static struct kunit_case gpio_unbind_with_consumers_tests[] = {
KUNIT_CASE(gpio_unbind_with_consumers),
{ }
};
static struct kunit_suite gpio_unbind_with_consumers_test_suite = {
.name = "gpio-unbind-with-consumers",
.test_cases = gpio_unbind_with_consumers_tests,
};
kunit_test_suites(
&gpio_swnode_lookup_test_suite,
&gpio_unbind_with_consumers_test_suite,
);
MODULE_DESCRIPTION("Test module for the GPIO subsystem");
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>");
MODULE_LICENSE("GPL");

View File

@@ -240,6 +240,14 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np,
* treats it as "active low".
*/
{ "ti,tsc2005", "reset-gpios", false },
#endif
#if IS_ENABLED(CONFIG_SND_SOC_WSA881X)
/*
* WSA881 powerdown is always active low, but some device trees
* missed this when first contributed. It also has a very strange
* compatible.
*/
{ "sdw10217201000", "powerdown-gpios", false },
#endif
};
unsigned int i;

View File

@@ -114,10 +114,6 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
if (IS_ERR(gdev))
return ERR_CAST(gdev);
/*
* FIXME: The GPIO device reference is put at return but the descriptor
* is passed on. Find a proper solution.
*/
desc = gpio_device_get_desc(gdev, args.args[0]);
*flags = args.args[1]; /* We expect native GPIO flags */

View File

@@ -55,7 +55,7 @@
/* Device and char device-related information */
static DEFINE_IDA(gpio_ida);
static dev_t gpio_devt;
static dev_t gpio_devt __ro_after_init;
#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
static int gpio_bus_match(struct device *dev, const struct device_driver *drv)
@@ -114,7 +114,7 @@ static int gpiochip_irqchip_init_hw(struct gpio_chip *gc);
static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc);
static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc);
static bool gpiolib_initialized;
static bool gpiolib_initialized __ro_after_init;
const char *gpiod_get_label(struct gpio_desc *desc)
{
@@ -143,13 +143,15 @@ static void desc_free_label(struct rcu_head *rh)
static int desc_set_label(struct gpio_desc *desc, const char *label)
{
struct gpio_desc_label *new = NULL, *old;
size_t len;
if (label) {
new = kzalloc_flex(*new, str, strlen(label) + 1);
len = strlen(label);
new = kzalloc_flex(*new, str, len + 1);
if (!new)
return -ENOMEM;
strcpy(new->str, label);
memcpy(new->str, label, len);
}
old = rcu_replace_pointer(desc->label, new, 1);
@@ -491,6 +493,28 @@ int gpiod_get_direction(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_get_direction);
/**
* gpiod_is_single_ended - check if the GPIO is configured as single-ended
* @desc: the GPIO descriptor to check
*
* Returns true if the GPIO is configured as either Open Drain or Open Source.
* In these modes, the direction of the line cannot always be reliably
* determined by reading hardware registers, as the "off" state (High-Z)
* is physically indistinguishable from an input state.
*/
bool gpiod_is_single_ended(struct gpio_desc *desc)
{
if (!desc)
return false;
if (test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags) ||
test_bit(GPIOD_FLAG_OPEN_SOURCE, &desc->flags))
return true;
return false;
}
EXPORT_SYMBOL_GPL(gpiod_is_single_ended);
/*
* Add a new chip to the global chips list, keeping the list of chips sorted
* by range(means [base, base + ngpio - 1]) order.
@@ -3414,20 +3438,13 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
struct gpio_device *gdev;
struct gpio_chip *gc;
int value;
/* FIXME Unable to use gpio_chip_guard due to const desc. */
gdev = desc->gdev;
guard(srcu)(&gdev->srcu);
gc = srcu_dereference(gdev->chip, &gdev->srcu);
if (!gc)
CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc)
return -ENODEV;
value = gpio_chip_get_value(gc, desc);
value = gpio_chip_get_value(guard.gc, desc);
value = value < 0 ? value : !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
@@ -4134,8 +4151,6 @@ EXPORT_SYMBOL_GPL(gpiod_is_shared);
*/
int gpiod_to_irq(const struct gpio_desc *desc)
{
struct gpio_device *gdev;
struct gpio_chip *gc;
int offset;
int ret;
@@ -4143,16 +4158,13 @@ int gpiod_to_irq(const struct gpio_desc *desc)
if (ret <= 0)
return -EINVAL;
gdev = desc->gdev;
/* FIXME Cannot use gpio_chip_guard due to const desc. */
guard(srcu)(&gdev->srcu);
gc = srcu_dereference(gdev->chip, &gdev->srcu);
if (!gc)
CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc)
return -ENODEV;
offset = gpiod_hwgpio(desc);
if (gc->to_irq) {
ret = gc->to_irq(gc, offset);
if (guard.gc->to_irq) {
ret = guard.gc->to_irq(guard.gc, offset);
if (ret)
return ret;
@@ -4160,7 +4172,7 @@ int gpiod_to_irq(const struct gpio_desc *desc)
return -ENXIO;
}
#ifdef CONFIG_GPIOLIB_IRQCHIP
if (gc->irq.chip) {
if (guard.gc->irq.chip) {
/*
* Avoid race condition with other code, which tries to lookup
* an IRQ before the irqchip has been properly registered,
@@ -5348,7 +5360,7 @@ EXPORT_SYMBOL_GPL(gpiod_put_array);
* gpio_device of the GPIO chip with the firmware node and then simply
* bind it to this stub driver.
*/
static struct device_driver gpio_stub_drv = {
static struct device_driver gpio_stub_drv __ro_after_init = {
.name = "gpio_stub_drv",
.bus = &gpio_bus_type,
};
@@ -5506,8 +5518,8 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
if (gc->label)
seq_printf(s, ", %s", gc->label);
if (gc->can_sleep)
seq_printf(s, ", can sleep");
seq_printf(s, ":\n");
seq_puts(s, ", can sleep");
seq_puts(s, ":\n");
if (gc->dbg_show)
gc->dbg_show(s, gc);

View File

@@ -244,7 +244,7 @@ DEFINE_CLASS(gpio_chip_guard,
_guard;
}),
struct gpio_desc *desc)
const struct gpio_desc *desc)
int gpiod_request(struct gpio_desc *desc, const char *label);
int gpiod_request_commit(struct gpio_desc *desc, const char *label);

View File

@@ -15,8 +15,6 @@
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/timb_gpio.h>
#include <linux/i2c.h>
#include <linux/platform_data/i2c-ocores.h>
#include <linux/platform_data/i2c-xiic.h>
@@ -37,6 +35,10 @@
#define DRIVER_NAME "timberdale"
#define GPIO_NR_PINS 16
#define GPIO_BASE 0
#define IRQ_BASE 200
struct timberdale_device {
resource_size_t ctl_mapbase;
unsigned char __iomem *ctl_membase;
@@ -174,11 +176,16 @@ static const struct resource timberdale_eth_resources[] = {
},
};
static struct timbgpio_platform_data
timberdale_gpio_platform_data = {
.gpio_base = 0,
.nr_pins = GPIO_NR_PINS,
.irq_base = 200,
static const struct property_entry timberdale_gpio_properties[] = {
PROPERTY_ENTRY_U32("ngpios", GPIO_NR_PINS),
PROPERTY_ENTRY_U32("gpio-base", GPIO_BASE),
PROPERTY_ENTRY_U32("irq-base", IRQ_BASE),
{ }
};
static const struct software_node timberdale_gpio_swnode = {
.name = "timb-gpio",
.properties = timberdale_gpio_properties,
};
static const struct resource timberdale_gpio_resources[] = {
@@ -390,8 +397,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg0[] = {
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
.platform_data = &timberdale_gpio_platform_data,
.pdata_size = sizeof(timberdale_gpio_platform_data),
.swnode = &timberdale_gpio_swnode,
},
{
.name = "timb-video",
@@ -452,8 +458,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg1[] = {
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
.platform_data = &timberdale_gpio_platform_data,
.pdata_size = sizeof(timberdale_gpio_platform_data),
.swnode = &timberdale_gpio_swnode,
},
{
.name = "timb-mlogicore",
@@ -514,8 +519,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg2[] = {
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
.platform_data = &timberdale_gpio_platform_data,
.pdata_size = sizeof(timberdale_gpio_platform_data),
.swnode = &timberdale_gpio_swnode,
},
{
.name = "timb-video",
@@ -564,8 +568,7 @@ static const struct mfd_cell timberdale_cells_bar0_cfg3[] = {
.name = "timb-gpio",
.num_resources = ARRAY_SIZE(timberdale_gpio_resources),
.resources = timberdale_gpio_resources,
.platform_data = &timberdale_gpio_platform_data,
.pdata_size = sizeof(timberdale_gpio_platform_data),
.swnode = &timberdale_gpio_swnode,
},
{
.name = "timb-video",

View File

@@ -113,7 +113,6 @@
#define GPIO_PIN_ASCB 8
#define GPIO_PIN_INIC_RST 14
#define GPIO_PIN_BT_RST 15
#define GPIO_NR_PINS 16
/* DMA Channels */
#define DMA_UART_RX 0

View File

@@ -2,7 +2,6 @@
config VIDEO_MAX96712
tristate "Maxim MAX96712 Quad GMSL2 Deserializer support"
depends on I2C
depends on OF_GPIO
depends on VIDEO_DEV
select V4L2_FWNODE
select VIDEO_V4L2_SUBDEV_API

View File

@@ -23,7 +23,7 @@
#define GPIOAO_11 11
#define GPIOAO_12 12
#define GPIOAO_13 13
#define GPIO_TEST_N 14
#define GPIO_TEST_N 14
/* Second GPIO chip */
#define GPIOZ_0 0
@@ -52,7 +52,7 @@
#define BOOT_12 23
#define BOOT_13 24
#define BOOT_14 25
#define GPIOA_0 26
#define GPIOA_0 26
#define GPIOA_1 27
#define GPIOA_2 28
#define GPIOA_3 29

View File

@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
/* Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. */
/*
* This header provides constants for binding nvidia,tegra238-gpio*.
*
* The first cell in Tegra's GPIO specifier is the GPIO ID. The macros below
* provide names for this.
*
* The second cell contains standard flag values specified in gpio.h.
*/
#ifndef _DT_BINDINGS_GPIO_TEGRA238_GPIO_H
#define _DT_BINDINGS_GPIO_TEGRA238_GPIO_H
#include <dt-bindings/gpio/gpio.h>
/* GPIOs implemented by main GPIO controller */
#define TEGRA238_MAIN_GPIO_PORT_A 0
#define TEGRA238_MAIN_GPIO_PORT_B 1
#define TEGRA238_MAIN_GPIO_PORT_C 2
#define TEGRA238_MAIN_GPIO_PORT_D 3
#define TEGRA238_MAIN_GPIO_PORT_E 4
#define TEGRA238_MAIN_GPIO_PORT_F 5
#define TEGRA238_MAIN_GPIO_PORT_G 6
#define TEGRA238_MAIN_GPIO_PORT_H 7
#define TEGRA238_MAIN_GPIO_PORT_J 8
#define TEGRA238_MAIN_GPIO_PORT_K 9
#define TEGRA238_MAIN_GPIO_PORT_L 10
#define TEGRA238_MAIN_GPIO_PORT_M 11
#define TEGRA238_MAIN_GPIO_PORT_N 12
#define TEGRA238_MAIN_GPIO_PORT_P 13
#define TEGRA238_MAIN_GPIO_PORT_Q 14
#define TEGRA238_MAIN_GPIO_PORT_R 15
#define TEGRA238_MAIN_GPIO_PORT_S 16
#define TEGRA238_MAIN_GPIO_PORT_T 17
#define TEGRA238_MAIN_GPIO_PORT_U 18
#define TEGRA238_MAIN_GPIO_PORT_V 19
#define TEGRA238_MAIN_GPIO_PORT_W 20
#define TEGRA238_MAIN_GPIO_PORT_X 21
#define TEGRA238_MAIN_GPIO(port, offset) \
((TEGRA238_MAIN_GPIO_PORT_##port * 8) + (offset))
/* GPIOs implemented by AON GPIO controller */
#define TEGRA238_AON_GPIO_PORT_AA 0
#define TEGRA238_AON_GPIO_PORT_BB 1
#define TEGRA238_AON_GPIO_PORT_CC 2
#define TEGRA238_AON_GPIO_PORT_DD 3
#define TEGRA238_AON_GPIO_PORT_EE 4
#define TEGRA238_AON_GPIO_PORT_FF 5
#define TEGRA238_AON_GPIO_PORT_GG 6
#define TEGRA238_AON_GPIO_PORT_HH 7
#define TEGRA238_AON_GPIO(port, offset) \
((TEGRA238_AON_GPIO_PORT_##port * 8) + (offset))
#endif

View File

@@ -6,10 +6,16 @@ struct completion;
struct kunit;
struct platform_device;
struct platform_driver;
struct platform_device_info;
struct platform_device *
kunit_platform_device_alloc(struct kunit *test, const char *name, int id);
int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev);
struct platform_device *
kunit_platform_device_register_full(struct kunit *test,
const struct platform_device_info *pdevinfo);
void kunit_platform_device_unregister(struct kunit *test,
struct platform_device *pdev);
int kunit_platform_device_prepare_wait_for_probe(struct kunit *test,
struct platform_device *pdev,

View File

@@ -2,13 +2,11 @@
/*
* NOTE: This header *must not* be included.
*
* This is the LEGACY GPIO bulk include file, including legacy APIs. It is
* used for GPIO drivers still referencing the global GPIO numberspace,
* and should not be included in new code.
*
* If you're implementing a GPIO driver, only include <linux/gpio/driver.h>
* If you're implementing a GPIO consumer, only include <linux/gpio/consumer.h>
* If you're using the legacy interfaces, include <linux/gpio/legacy.h>
*/
#ifndef __LINUX_GPIO_H
#define __LINUX_GPIO_H
@@ -18,159 +16,7 @@
#endif
#ifdef CONFIG_GPIOLIB_LEGACY
struct device;
/* make these flag values available regardless of GPIO kconfig options */
#define GPIOF_IN ((1 << 0))
#define GPIOF_OUT_INIT_LOW ((0 << 0) | (0 << 1))
#define GPIOF_OUT_INIT_HIGH ((0 << 0) | (1 << 1))
#ifdef CONFIG_GPIOLIB
/*
* "valid" GPIO numbers are nonnegative and may be passed to
* setup routines like gpio_request(). Only some valid numbers
* can successfully be requested and used.
*
* Invalid GPIO numbers are useful for indicating no-such-GPIO in
* platform data and other tables.
*/
static inline bool gpio_is_valid(int number)
{
/* only non-negative numbers are valid */
return number >= 0;
}
/*
* Platforms may implement their GPIO interface with library code,
* at a small performance cost for non-inlined operations and some
* extra memory (for code and for per-GPIO table entries).
*/
/* Always use the library code for GPIO management calls,
* or when sleeping may be involved.
*/
int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
static inline int gpio_get_value_cansleep(unsigned gpio)
{
return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio));
}
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
{
gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value);
}
static inline int gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
static inline void gpio_set_value(unsigned gpio, int value)
{
gpiod_set_raw_value(gpio_to_desc(gpio), value);
}
static inline int gpio_to_irq(unsigned gpio)
{
return gpiod_to_irq(gpio_to_desc(gpio));
}
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);
#else /* ! CONFIG_GPIOLIB */
#include <linux/kernel.h>
#include <asm/bug.h>
#include <asm/errno.h>
static inline bool gpio_is_valid(int number)
{
return false;
}
static inline int gpio_request(unsigned gpio, const char *label)
{
return -ENOSYS;
}
static inline int gpio_request_one(unsigned gpio,
unsigned long flags, const char *label)
{
return -ENOSYS;
}
static inline void gpio_free(unsigned gpio)
{
might_sleep();
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpio_direction_input(unsigned gpio)
{
return -ENOSYS;
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return -ENOSYS;
}
static inline int gpio_get_value(unsigned gpio)
{
/* GPIO can never have been requested or set as {in,out}put */
WARN_ON(1);
return 0;
}
static inline void gpio_set_value(unsigned gpio, int value)
{
/* GPIO can never have been requested or set as output */
WARN_ON(1);
}
static inline int gpio_get_value_cansleep(unsigned gpio)
{
/* GPIO can never have been requested or set as {in,out}put */
WARN_ON(1);
return 0;
}
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
{
/* GPIO can never have been requested or set as output */
WARN_ON(1);
}
static inline int gpio_to_irq(unsigned gpio)
{
/* GPIO can never have been requested or set as input */
WARN_ON(1);
return -EINVAL;
}
static inline int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label)
{
WARN_ON(1);
return -EINVAL;
}
#endif /* ! CONFIG_GPIOLIB */
#include <linux/gpio/legacy.h>
#endif /* CONFIG_GPIOLIB_LEGACY */
#endif /* __LINUX_GPIO_H */

View File

@@ -111,6 +111,7 @@ void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc);
void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
int gpiod_get_direction(struct gpio_desc *desc);
bool gpiod_is_single_ended(struct gpio_desc *desc);
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
@@ -337,6 +338,10 @@ static inline int gpiod_get_direction(const struct gpio_desc *desc)
WARN_ON(desc);
return -ENOSYS;
}
static inline bool gpiod_is_single_ended(struct gpio_desc *desc)
{
return false;
}
static inline int gpiod_direction_input(struct gpio_desc *desc)
{
/* GPIO can never have been requested */
@@ -598,6 +603,15 @@ static inline int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc,
}
#endif /* CONFIG_GPIOLIB && CONFIG_HTE */
static inline
struct gpio_desc *fwnode_gpiod_get(struct fwnode_handle *fwnode,
const char *con_id,
enum gpiod_flags flags,
const char *label)
{
return fwnode_gpiod_get_index(fwnode, con_id, 0, flags, label);
}
static inline
struct gpio_desc *devm_fwnode_gpiod_get(struct device *dev,
struct fwnode_handle *fwnode,

View File

@@ -32,9 +32,6 @@ struct fwnode_handle;
#define NMK_GPIO_RWIMSC 0x50
#define NMK_GPIO_FWIMSC 0x54
#define NMK_GPIO_WKS 0x58
/* These appear in DB8540 and later ASICs */
#define NMK_GPIO_EDGELEVEL 0x5C
#define NMK_GPIO_LEVEL 0x60
/* Pull up/down values */
enum nmk_gpio_pull {
@@ -236,19 +233,6 @@ nmk_pinctrl_db8500_init(const struct nmk_pinctrl_soc_data **soc)
#endif
#ifdef CONFIG_PINCTRL_DB8540
void nmk_pinctrl_db8540_init(const struct nmk_pinctrl_soc_data **soc);
#else
static inline void
nmk_pinctrl_db8540_init(const struct nmk_pinctrl_soc_data **soc)
{
}
#endif
struct platform_device;
#ifdef CONFIG_DEBUG_FS

173
include/linux/gpio/legacy.h Normal file
View File

@@ -0,0 +1,173 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This is the LEGACY GPIO include file, used only for legacy APIs.
*
* No new code should use this, but instead use the linux/gpio/consumer.h
* interfaces directly.
*/
#ifndef __LINUX_GPIO_LEGACY_H
#define __LINUX_GPIO_LEGACY_H
#include <linux/types.h>
#ifdef CONFIG_GPIOLIB_LEGACY
struct device;
/* make these flag values available regardless of GPIO kconfig options */
#define GPIOF_IN ((1 << 0))
#define GPIOF_OUT_INIT_LOW ((0 << 0) | (0 << 1))
#define GPIOF_OUT_INIT_HIGH ((0 << 0) | (1 << 1))
#ifdef CONFIG_GPIOLIB
#include <linux/gpio/consumer.h>
/*
* "valid" GPIO numbers are nonnegative and may be passed to
* setup routines like gpio_request(). Only some valid numbers
* can successfully be requested and used.
*
* Invalid GPIO numbers are useful for indicating no-such-GPIO in
* platform data and other tables.
*/
static inline bool gpio_is_valid(int number)
{
/* only non-negative numbers are valid */
return number >= 0;
}
/*
* Platforms may implement their GPIO interface with library code,
* at a small performance cost for non-inlined operations and some
* extra memory (for code and for per-GPIO table entries).
*/
/* Always use the library code for GPIO management calls,
* or when sleeping may be involved.
*/
int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
static inline int gpio_direction_input(unsigned gpio)
{
return gpiod_direction_input(gpio_to_desc(gpio));
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}
static inline int gpio_get_value_cansleep(unsigned gpio)
{
return gpiod_get_raw_value_cansleep(gpio_to_desc(gpio));
}
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
{
gpiod_set_raw_value_cansleep(gpio_to_desc(gpio), value);
}
static inline int gpio_get_value(unsigned gpio)
{
return gpiod_get_raw_value(gpio_to_desc(gpio));
}
static inline void gpio_set_value(unsigned gpio, int value)
{
gpiod_set_raw_value(gpio_to_desc(gpio), value);
}
static inline int gpio_to_irq(unsigned gpio)
{
return gpiod_to_irq(gpio_to_desc(gpio));
}
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);
#else /* ! CONFIG_GPIOLIB */
#include <linux/kernel.h>
#include <asm/bug.h>
#include <asm/errno.h>
static inline bool gpio_is_valid(int number)
{
return false;
}
static inline int gpio_request(unsigned gpio, const char *label)
{
return -ENOSYS;
}
static inline int gpio_request_one(unsigned gpio,
unsigned long flags, const char *label)
{
return -ENOSYS;
}
static inline void gpio_free(unsigned gpio)
{
might_sleep();
/* GPIO can never have been requested */
WARN_ON(1);
}
static inline int gpio_direction_input(unsigned gpio)
{
return -ENOSYS;
}
static inline int gpio_direction_output(unsigned gpio, int value)
{
return -ENOSYS;
}
static inline int gpio_get_value(unsigned gpio)
{
/* GPIO can never have been requested or set as {in,out}put */
WARN_ON(1);
return 0;
}
static inline void gpio_set_value(unsigned gpio, int value)
{
/* GPIO can never have been requested or set as output */
WARN_ON(1);
}
static inline int gpio_get_value_cansleep(unsigned gpio)
{
/* GPIO can never have been requested or set as {in,out}put */
WARN_ON(1);
return 0;
}
static inline void gpio_set_value_cansleep(unsigned gpio, int value)
{
/* GPIO can never have been requested or set as output */
WARN_ON(1);
}
static inline int gpio_to_irq(unsigned gpio)
{
/* GPIO can never have been requested or set as input */
WARN_ON(1);
return -EINVAL;
}
static inline int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label)
{
WARN_ON(1);
return -EINVAL;
}
#endif /* ! CONFIG_GPIOLIB */
#endif /* CONFIG_GPIOLIB_LEGACY */
#endif /* __LINUX_GPIO_LEGAGY_H */

View File

@@ -38,6 +38,12 @@ struct regmap;
* offset to a register/bitmask pair. If not
* given the default gpio_regmap_simple_xlate()
* is used.
* @fixed_direction_mask:
* (Optional) Bitmap representing the GPIO lines that
* make use of the @fixed_direction_output list to
* enforce direction of the GPIO. If this is NULL
* and @fixed_direction_output is defined, ALL GPIOs
* are assumed to be fixed direction (out or in).
* @fixed_direction_output:
* (Optional) Bitmap representing the fixed direction of
* the GPIO lines. Useful when there are GPIO lines with a
@@ -89,6 +95,7 @@ struct gpio_regmap_config {
int reg_stride;
int ngpio_per_reg;
struct irq_domain *irq_domain;
unsigned long *fixed_direction_mask;
unsigned long *fixed_direction_output;
#ifdef CONFIG_REGMAP_IRQ

View File

@@ -1,25 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* timb_gpio.h timberdale FPGA GPIO driver, platform data definition
* Copyright (c) 2009 Intel Corporation
*/
#ifndef _LINUX_TIMB_GPIO_H
#define _LINUX_TIMB_GPIO_H
/**
* struct timbgpio_platform_data - Platform data of the Timberdale GPIO driver
* @gpio_base: The number of the first GPIO pin, set to -1 for
* dynamic number allocation.
* @nr_pins: Number of pins that is supported by the hardware (1-32)
* @irq_base: If IRQ is supported by the hardware, this is the base
* number of IRQ:s. One IRQ per pin will be used. Set to
* -1 if IRQ:s is not supported.
*/
struct timbgpio_platform_data {
int gpio_base;
int nr_pins;
int irq_base;
};
#endif

View File

@@ -6,6 +6,7 @@
#include <linux/completion.h>
#include <linux/device/bus.h>
#include <linux/device/driver.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <kunit/platform_device.h>
@@ -130,6 +131,69 @@ int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(kunit_platform_device_add);
/**
* kunit_platform_device_register_full() - Register a KUnit test-managed platform
* device described by platform device info
* @test: test context
* @pdevinfo: platform device information describing the new device
*
* Register a test-managed platform device. The device is unregistered when the
* test completes.
*
* Return: New platform device on success, IS_ERR() on error.
*/
struct platform_device *
kunit_platform_device_register_full(struct kunit *test,
const struct platform_device_info *pdevinfo)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_register_full(pdevinfo);
if (IS_ERR(pdev))
return pdev;
ret = kunit_add_action_or_reset(test, platform_device_unregister_wrapper, pdev);
if (ret)
return ERR_PTR(ret);
return pdev;
}
EXPORT_SYMBOL_GPL(kunit_platform_device_register_full);
static bool
kunit_platform_device_add_match(struct kunit *test, struct kunit_resource *res,
void *match_data)
{
struct platform_device *pdev = match_data;
return res->data == pdev && res->free == kunit_platform_device_add_exit;
}
/**
* kunit_platform_device_unregister() - Unregister a KUnit-managed platform device
* @test: test context
* @pdev: platform device to unregister
*
* Unregister a test-managed platform device and cancel its release action.
*/
void kunit_platform_device_unregister(struct kunit *test,
struct platform_device *pdev)
{
struct kunit_resource *res;
res = kunit_find_resource(test, kunit_platform_device_add_match, pdev);
if (res) {
res->free = NULL;
kunit_put_resource(res);
} else {
kunit_remove_action(test, platform_device_unregister_wrapper, pdev);
}
platform_device_unregister(pdev);
}
EXPORT_SYMBOL_GPL(kunit_platform_device_unregister);
struct kunit_platform_device_probe_nb {
struct completion *x;
struct device *dev;

View File

@@ -3,7 +3,6 @@
// Copyright (c) 2019, Linaro Limited
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -672,11 +671,6 @@ struct wsa881x_priv {
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS];
struct gpio_desc *sd_n;
/*
* Logical state for SD_N GPIO: high for shutdown, low for enable.
* For backwards compatibility.
*/
unsigned int sd_n_val;
int active_ports;
bool hw_init;
bool port_prepared[WSA881X_MAX_SWR_PORTS];
@@ -1121,31 +1115,11 @@ static int wsa881x_probe(struct sdw_slave *pdev,
if (!wsa881x)
return -ENOMEM;
wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", 0);
wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW);
if (IS_ERR(wsa881x->sd_n))
return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n),
"Shutdown Control GPIO not found\n");
/*
* Backwards compatibility work-around.
*
* The SD_N GPIO is active low, however upstream DTS used always active
* high. Changing the flag in driver and DTS will break backwards
* compatibility, so add a simple value inversion to work with both old
* and new DTS.
*
* This won't work properly with DTS using the flags properly in cases:
* 1. Old DTS with proper ACTIVE_LOW, however such case was broken
* before as the driver required the active high.
* 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case
* (not existing upstream) but possible. This is the price of
* backwards compatibility, therefore this hack should be removed at
* some point.
*/
wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n);
if (!wsa881x->sd_n_val)
dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO.");
dev_set_drvdata(dev, wsa881x);
wsa881x->slave = pdev;
wsa881x->dev = dev;
@@ -1158,7 +1132,6 @@ static int wsa881x_probe(struct sdw_slave *pdev,
pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
pdev->prop.clk_stop_mode1 = true;
gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config);
if (IS_ERR(wsa881x->regmap))
@@ -1181,7 +1154,7 @@ static int wsa881x_runtime_suspend(struct device *dev)
struct regmap *regmap = dev_get_regmap(dev, NULL);
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
gpiod_direction_output(wsa881x->sd_n, 1);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
@@ -1196,13 +1169,13 @@ static int wsa881x_runtime_resume(struct device *dev)
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
unsigned long time;
gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
gpiod_direction_output(wsa881x->sd_n, 0);
time = wait_for_completion_timeout(&slave->initialization_complete,
msecs_to_jiffies(WSA881X_PROBE_TIMEOUT));
if (!time) {
dev_err(dev, "Initialization not complete, timed out\n");
gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
gpiod_direction_output(wsa881x->sd_n, 1);
return -ETIMEDOUT;
}