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

Pull gpio updates from Bartosz Golaszewski:
 "GPIO core:
   - extend the gpio-regmap abstraction layer with more features
     allowing users to override configuration setting, translate
     register values and masks and enable/disable interrupts
   - extend GPIO kunit tests with suites verifying probe ordering by
     software node devlink support and software node hogs
   - shrink GPIO kunit initialization code
   - coding style updates (remove commas from sentinels where
     applicable)
   - with all users now converted treewide to using real firmware node
     links for software node GPIO lookup: remove the deprecated
     label-matching mechanism from from GPIO core
   - drop redundant return value check of nonseekable_open() in
     gpiolib-cdev
   - use IRQ trigger helpers where applicable

  Driver updates:
   - refactor error paths and logging in gpio-nomadik
   - use more modern interfaces for getting resources in gpio-rockchip,
     gpio-bt8xx and gpio-pca9570
   - add missing MODULE_DEVICE_TABLE() to gpio-sifive and gpio-vf610
   - drop unused FILONOFF macro from gpio-rcar
   - extend build coverage of ioport GPIO drivers with COMPILE_TEST=y
   - only enable the gpio-rtd driver by default with ARCH_REALTEK=y to
     avoid bloating the build
   - refactor coding style in several drivers
   - use correct endianess translation in gpio-pcf85x
   - add wake-up interrupt support to gpio-mvebu
   - apply initial value in direction output setter in gpio-by-pinctrl

  Misc:
   - replace linux/gpio.h inclusions treewide with linux/gpio/legacy.h
     which now exports all the deprecated APIs
   - select GPIOLIB_LEGACY in Kconfig where required treewide
   - use software nodes for gpio-keys in MFD drivers

  Devicetree bindings:
   - describe the realtek rtd1625 GPIO controller
   - document new models for gpio-pca95xx and gpio-cadence
   - document new property in gpio-rockchip"

* tag 'gpio-updates-for-v7.3-rc1-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (61 commits)
  gpio: gpio-by-pinctrl: Apply initial value in direction output wrapper
  dt-bindings: gpio: rockchip,gpio-bank: Add rockchip,grf property
  gpio: Use IRQ trigger mask helpers
  gpio: allow COMPILE_TEST for IOPORT drivers
  gpio: realtek: Add driver for Realtek DHC RTD1625 SoC
  gpio: regmap: Add IRQ enable/disable helpers
  gpio: regmap: Add set_config callback
  gpio: regmap: Add value_xlate callback
  gpio: regmap: Add gpio_regmap_operation to extend reg_mask_xlate callback
  gpio: regmap: Order kernel-doc descriptions with the actual appearance
  gpio: regmap: Apply default resource callbacks for regmap IRQ chip
  gpio: regmap: Provide default IRQ resource request and release callbacks
  Revert "gpio: realtek: Add driver for Realtek DHC RTD1625 SoC"
  gpib: gpio: replace linux/gpio.h inclusion
  Input: matrix_keyboard - replace linux/gpio.h inclusion
  phy: replace linux/gpio.h inclusions
  pcmcia: replace linux/gpio.h inclusions
  ASoC: replace linux/gpio.h inclusions
  mfd: replace linux/gpio.h inclusions
  sh: replace linux/gpio.h inclusions
  ...
This commit is contained in:
Linus Torvalds
2026-08-19 09:10:07 -07:00
115 changed files with 2363 additions and 297 deletions
@@ -14,8 +14,11 @@ properties:
oneOf:
- const: cdns,gpio-r1p02
- items:
- enum:
- axiado,ax3000-gpio
- const: axiado,ax3000-gpio
- const: cdns,gpio-r1p02
- items:
- const: axiado,ax3005-gpio
- const: axiado,ax3000-gpio
- const: cdns,gpio-r1p02
reg:
@@ -22,6 +22,9 @@ properties:
- items:
- const: diodes,pi4ioe5v6534q
- const: nxp,pcal6534
- items:
- const: kinetic,kts1622
- const: ti,tcal6416
- items:
- enum:
- exar,xra1202
@@ -0,0 +1,71 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2023 Realtek Semiconductor Corporation
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/realtek,rtd1625-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek DHC RTD1625 GPIO controller
maintainers:
- Tzuyi Chang <tychang@realtek.com>
description: |
GPIO controller for the Realtek RTD1625 SoC, featuring a per-pin register
architecture that differs significantly from earlier RTD series controllers.
Each GPIO has dedicated registers for configuration (direction, input/output
values, debounce), and interrupt control supporting edge and level detection
modes.
properties:
compatible:
enum:
- realtek,rtd1625-iso-gpio
- realtek,rtd1625-isom-gpio
reg:
maxItems: 1
interrupts:
items:
- description: Interrupt number of the assert GPIO interrupt, which is
triggered when there is a rising edge.
- description: Interrupt number of the deassert GPIO interrupt, which is
triggered when there is a falling edge.
- description: Interrupt number of the level-sensitive GPIO interrupt,
triggered by a configured logic level.
interrupt-controller: true
"#interrupt-cells":
const: 2
gpio-ranges: true
gpio-controller: true
"#gpio-cells":
const: 2
required:
- compatible
- reg
- gpio-ranges
- gpio-controller
- "#gpio-cells"
additionalProperties: false
examples:
- |
gpio@89100 {
compatible = "realtek,rtd1625-isom-gpio";
reg = <0x89100 0x30>;
interrupt-parent = <&iso_m_irq_mux>;
interrupts = <0>, <1>, <2>;
interrupt-controller;
#interrupt-cells = <2>;
gpio-ranges = <&isom_pinctrl 0 0 4>;
gpio-controller;
#gpio-cells = <2>;
};
@@ -44,6 +44,13 @@ properties:
power-domains:
maxItems: 1
rockchip,grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
The phandle of the syscon node managing the IO control registers
of this bank, on SoCs such as the RV1106 where each GPIO bank has
its own IOC block.
patternProperties:
"^.+-hog(-[0-9]+)?$":
type: object
+2
View File
@@ -25094,10 +25094,12 @@ R: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
R: Daniel Scally <djrscally@gmail.com>
R: Heikki Krogerus <heikki.krogerus@linux.intel.com>
R: Sakari Ailus <sakari.ailus@linux.intel.com>
R: Bartosz Golaszewski <brgl@kernel.org>
L: linux-acpi@vger.kernel.org
S: Maintained
F: drivers/base/property.c
F: drivers/base/swnode.c
F: drivers/base/test/swnode-devlink-test.c
F: include/linux/fwnode.h
F: include/linux/property.h
+1 -1
View File
@@ -11,7 +11,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <asm/bootinfo.h>
+1 -1
View File
@@ -8,7 +8,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/gpio/property.h>
#include <linux/init.h>
+1 -1
View File
@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+1 -1
View File
@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/io.h>
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "bcm47xx_private.h"
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <bcm47xx_board.h>
#include <bcm47xx.h>
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/gpio/legacy.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/platform_device.h>
@@ -3,7 +3,7 @@
#define BOARD_BCM963XX_H_
#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/leds.h>
#include <bcm63xx_dev_enet.h>
#include <bcm63xx_dev_usb_usbd.h>
+1
View File
@@ -462,6 +462,7 @@ config CPU_SUBTYPE_SHX3
select CPU_SHX3
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select GPIOLIB
select GPIOLIB_LEGACY
select PINCTRL
# SH4AL-DSP Processor Support
+8
View File
@@ -80,6 +80,7 @@ config SH_7724_SOLUTION_ENGINE
select SOLUTION_ENGINE
depends on CPU_SUBTYPE_SH7724
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
imply SND_SOC_AK4642 if SND_SIMPLE_CARD
help
@@ -199,6 +200,7 @@ config SH_SH7757LCR
bool "SH7757LCR"
depends on CPU_SUBTYPE_SH7757
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
config SH_SH7785LCR
@@ -226,6 +228,7 @@ config SH_URQUELL
bool "Urquell"
depends on CPU_SUBTYPE_SH7786
select GPIOLIB
select GPIOLIB_LEGACY
select HAVE_PCI
select NO_IOPORT_MAP if !PCI
@@ -233,6 +236,7 @@ config SH_MIGOR
bool "Migo-R"
depends on CPU_SUBTYPE_SH7722
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Migo-R if configuring for the SH7722 Migo-R platform
@@ -242,6 +246,7 @@ config SH_AP325RXA
bool "AP-325RXA"
depends on CPU_SUBTYPE_SH7723
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Renesas "AP-325RXA" support.
@@ -251,6 +256,7 @@ config SH_KFR2R09
bool "KFR2R09"
depends on CPU_SUBTYPE_SH7724
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
"Kit For R2R for 2009" support.
@@ -259,6 +265,7 @@ config SH_ECOVEC
bool "EcoVec"
depends on CPU_SUBTYPE_SH7724
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
imply SND_SOC_DA7210 if SND_SIMPLE_CARD
help
@@ -329,6 +336,7 @@ config SH_MAGIC_PANEL_R2
bool "Magic Panel R2"
depends on CPU_SUBTYPE_SH7720
select GPIOLIB
select GPIOLIB_LEGACY
select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Magic Panel R2 if configuring for Magic Panel R2.
+1 -1
View File
@@ -10,7 +10,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
+1 -1
View File
@@ -7,7 +7,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/irq.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
+1 -1
View File
@@ -14,7 +14,7 @@
#include <linux/smc91x.h>
#include <linux/mtd/physmap.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/sh_intc.h>
+1 -1
View File
@@ -17,7 +17,7 @@
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/i2c.h>
+1 -1
View File
@@ -20,7 +20,7 @@
#include <linux/mmc/host.h>
#include <linux/platform_data/sh_mmcif.h>
#include <linux/mtd/physmap.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/platform_data/gpio_backlight.h>
#include <linux/platform_data/tmio.h>
+1
View File
@@ -20,6 +20,7 @@ config SH_R7785RP
bool "R7785RP board support"
depends on CPU_SUBTYPE_SH7785
select GPIOLIB
select GPIOLIB_LEGACY
endchoice
@@ -3,7 +3,7 @@
* Copyright (C) 2008 Paul Mundt
*/
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <cpu/sh7785.h>
#include <mach/highlander.h>
+1 -1
View File
@@ -14,7 +14,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <video/sh_mobile_lcdc.h>
#include <mach/kfr2r09.h>
#include <cpu/sh7724.h>
+1 -1
View File
@@ -14,7 +14,7 @@
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/i2c.h>
#include <linux/init.h>
+1 -1
View File
@@ -14,7 +14,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <video/sh_mobile_lcdc.h>
#include <cpu/sh7722.h>
#include <mach/migor.h>
+1 -1
View File
@@ -22,7 +22,7 @@
#include <linux/smc91x.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/gpio/machine.h>
#include <linux/videodev2.h>
#include <linux/sh_intc.h>
+3
View File
@@ -12,16 +12,19 @@ config SH_RSK7201
config SH_RSK7203
bool "RSK7203"
select GPIOLIB
select GPIOLIB_LEGACY
depends on CPU_SUBTYPE_SH7203
config SH_RSK7264
bool "RSK2+SH7264"
select GPIOLIB
select GPIOLIB_LEGACY
depends on CPU_SUBTYPE_SH7264
config SH_RSK7269
bool "RSK2+SH7269"
select GPIOLIB
select GPIOLIB_LEGACY
depends on CPU_SUBTYPE_SH7269
endchoice
@@ -11,7 +11,6 @@
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/smsc911x.h>
#include <linux/gpio.h>
#include <asm/machvec.h>
#include <asm/io.h>
+1 -1
View File
@@ -15,7 +15,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
@@ -10,8 +10,6 @@
#ifndef __ASM_SH_MAGICPANELR2_H
#define __ASM_SH_MAGICPANELR2_H
#include <linux/gpio.h>
#define __IO_PREFIX mpr2
#include <asm/io_generic.h>
+1 -1
View File
@@ -9,7 +9,7 @@
#include <linux/serial.h>
#include <linux/serial_sci.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/sh_timer.h>
#include <linux/sh_intc.h>
#include <cpu/shx3.h>
+1
View File
@@ -2978,6 +2978,7 @@ config OLPC
bool "One Laptop Per Child support"
depends on !X86_PAE
select GPIOLIB
select GPIOLIB_LEGACY
select OF
select OF_PROMTREE
select IRQ_DOMAIN
+1 -1
View File
@@ -9,7 +9,7 @@
#include <linux/cs5535.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+22
View File
@@ -296,6 +296,26 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
static int regmap_irq_reqres(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
if (d->chip->irq_reqres)
return d->chip->irq_reqres(d->chip->irq_drv_data, hwirq);
return 0;
}
static void regmap_irq_relres(struct irq_data *data)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
irq_hw_number_t hwirq = irqd_to_hwirq(data);
if (d->chip->irq_relres)
d->chip->irq_relres(d->chip->irq_drv_data, hwirq);
}
static const struct irq_chip regmap_irq_chip = {
.irq_bus_lock = regmap_irq_lock,
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
@@ -303,6 +323,8 @@ static const struct irq_chip regmap_irq_chip = {
.irq_enable = regmap_irq_enable,
.irq_set_type = regmap_irq_set_type,
.irq_set_wake = regmap_irq_set_wake,
.irq_request_resources = regmap_irq_reqres,
.irq_release_resources = regmap_irq_relres,
};
static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,
+79
View File
@@ -699,6 +699,62 @@ software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode,
return 0;
}
static int software_node_add_links(struct fwnode_handle *fwnode)
{
const struct software_node_ref_args *ref, *ref_array;
struct swnode *swnode = to_swnode(fwnode);
const struct property_entry *prop;
struct fwnode_handle *refnode;
unsigned int count;
if (!swnode || !swnode->node->properties)
return 0;
/*
* Unlike Device Tree, where phandles appear in many non-supplier
* contexts and a curated allowlist is required, a software node only
* carries a DEV_PROP_REF property when the author explicitly describes
* a reference to another node. Every such reference is therefore an
* intentional supplier dependency, so we create fwnode links for all
* of them.
*/
for (prop = swnode->node->properties; prop->name; prop++) {
if (prop->type != DEV_PROP_REF || prop->is_inline)
continue;
/*
* TODO: Graph "remote-endpoint" references go both ways
* between endpoint child nodes and would create endpoint
* cycles. Let's leave it out for now until we have potential
* users.
*/
if (!strcmp(prop->name, "remote-endpoint"))
continue;
ref_array = prop->pointer;
count = prop->length / sizeof(*ref_array);
for (unsigned int i = 0; i < count; i++) {
ref = &ref_array[i];
if (ref->swnode)
refnode = software_node_fwnode(ref->swnode);
else if (ref->fwnode)
refnode = ref->fwnode;
else
continue;
/* Supplier not registered yet, or self-reference. */
if (!refnode || refnode == &swnode->fwnode)
continue;
fwnode_link_add(&swnode->fwnode, refnode, 0);
}
}
return 0;
}
static const struct fwnode_operations software_node_ops = {
.get = software_node_get,
.put = software_node_put,
@@ -716,6 +772,7 @@ static const struct fwnode_operations software_node_ops = {
.graph_get_remote_endpoint = software_node_graph_get_remote_endpoint,
.graph_get_port_parent = software_node_graph_get_port_parent,
.graph_parse_endpoint = software_node_graph_parse_endpoint,
.add_links = software_node_add_links,
};
/* -------------------------------------------------------------------------- */
@@ -787,6 +844,8 @@ static void software_node_release(struct kobject *kobj)
{
struct swnode *swnode = kobj_to_swnode(kobj);
fwnode_links_purge(&swnode->fwnode);
if (swnode->parent) {
ida_free(&swnode->parent->child_ids, swnode->id);
list_del(&swnode->entry);
@@ -1105,6 +1164,17 @@ void software_node_notify(struct device *dev)
if (!swnode)
return;
/*
* When the software node is the device's secondary firmware node,
* the core only records the owning device on the primary fwnode
* (see device_add()). fw_devlink resolves a supplier device through
* fwnode->dev, so without this a consumer referencing the software
* node could never find the supplier device and would defer forever.
* Make fwnode.dev point to its owner in that case.
*/
if (!device_match_fwnode(dev, &swnode->fwnode) && !swnode->fwnode.dev)
swnode->fwnode.dev = dev;
swnode_get(swnode);
ret = sysfs_create_link(&dev->kobj, &swnode->kobj, "software_node");
if (ret)
@@ -1127,6 +1197,15 @@ void software_node_notify_remove(struct device *dev)
sysfs_remove_link(&swnode->kobj, dev_name(dev));
sysfs_remove_link(&dev->kobj, "software_node");
/*
* Drop the device pointer mirrored onto a secondary software node in
* software_node_notify(). For a primary software node the core owns
* fwnode->dev and clears it in device_del().
*/
if (!device_match_fwnode(dev, &swnode->fwnode) && swnode->fwnode.dev == dev)
swnode->fwnode.dev = NULL;
swnode_put(swnode);
if (swnode->managed) {
+5
View File
@@ -18,3 +18,8 @@ config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config DRIVER_SWNODE_KUNIT_TEST
tristate "KUnit Tests for software node fw_devlink links" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
+2
View File
@@ -6,3 +6,5 @@ obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
obj-$(CONFIG_DRIVER_SWNODE_KUNIT_TEST) += swnode-devlink-test.o
+337
View File
@@ -0,0 +1,337 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
*/
#include <linux/array_size.h>
#include <linux/device.h>
#include <linux/fwnode.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <kunit/fwnode.h>
#include <kunit/platform_device.h>
#include <kunit/test.h>
static int swnode_count_suppliers(struct fwnode_handle *fwnode)
{
struct fwnode_link *link;
unsigned int count = 0;
/*
* The suppliers and consumers lists should typically only be accessed
* with the fwnode_link_lock taken but it's private to the driver core.
*
* These are tests and at this point nobody should be modifying them so
* let's just access the list.
*/
list_for_each_entry(link, &fwnode->suppliers, c_hook)
count++;
return count;
}
/* True if a supplier link con->sup exists, checked from both list ends. */
static bool swnode_has_link(struct fwnode_handle *consumer,
struct fwnode_handle *supplier)
{
bool from_con = false, from_sup = false;
struct fwnode_link *link;
list_for_each_entry(link, &consumer->suppliers, c_hook) {
if (link->supplier == supplier && link->consumer == consumer)
from_con = true;
}
list_for_each_entry(link, &supplier->consumers, s_hook) {
if (link->supplier == supplier && link->consumer == consumer)
from_sup = true;
}
return from_con && from_sup;
}
/* A single reference creates exactly one supplier link, on both list ends. */
static void swnode_devlink_test_single_ref(struct kunit *test)
{
static const struct software_node supp_swnode = {
.name = "swnode-devlink-test-supplier",
};
struct fwnode_handle *cons_fwnode, *supp_fwnode;
int ret;
const struct property_entry props[] = {
PROPERTY_ENTRY_REF("supplier", &supp_swnode),
{ }
};
supp_fwnode = kunit_software_node_register(test, &supp_swnode);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
ret = fwnode_call_int_op(cons_fwnode, add_links);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 1);
KUNIT_EXPECT_TRUE(test, swnode_has_link(cons_fwnode, supp_fwnode));
}
/* Multiple distinct references create multiple supplier links. */
static void swnode_devlink_test_multiple_refs(struct kunit *test)
{
static const struct software_node supp1_swnode = {
.name = "swnode-devlink-test-supplier-1",
};
static const struct software_node supp2_swnode = {
.name = "swnode-devlink-test-supplier-2",
};
static const struct software_node *supp_nodes[] = {
&supp1_swnode, &supp2_swnode, NULL
};
const struct property_entry props[] = {
PROPERTY_ENTRY_REF("foo", &supp1_swnode),
PROPERTY_ENTRY_REF("bar", &supp2_swnode),
{ }
};
struct fwnode_handle *fwnode;
int ret;
ret = kunit_software_node_register_node_group(test, supp_nodes);
KUNIT_ASSERT_EQ(test, ret, 0);
fwnode = kunit_fwnode_create_software_node(test, props, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
ret = fwnode_call_int_op(fwnode, add_links);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
}
/* A reference to an unregistered node creates no link (graceful skip). */
static void swnode_devlink_test_unregistered_ref(struct kunit *test)
{
static const struct software_node supp_swnode = {
.name = "swnode-devlink-test-supplier",
};
const struct property_entry props[] = {
PROPERTY_ENTRY_REF("supplier", &supp_swnode),
{ }
};
struct fwnode_handle *fwnode;
int ret;
fwnode = kunit_fwnode_create_software_node(test, props, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
ret = fwnode_call_int_op(fwnode, add_links);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 0);
}
/* Graph "remote-endpoint" references are excluded. */
static void swnode_devlink_test_remote_endpoint_excluded(struct kunit *test)
{
static const struct software_node ep_swnode = {
.name = "swnode-devlink-test-end-point"
};
const struct property_entry props[] = {
PROPERTY_ENTRY_REF("remote-endpoint", &ep_swnode),
{ }
};
struct fwnode_handle *cons_fwnode, *supp_fwnode;
int ret;
supp_fwnode = kunit_software_node_register(test, &ep_swnode);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode);
cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode);
ret = fwnode_call_int_op(cons_fwnode, add_links);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 0);
}
/* A reference array creates one link per registered element. */
static void swnode_devlink_test_ref_array(struct kunit *test)
{
static const struct software_node supp1_swnode = {
.name = "swnode-devlink-test-supplier-1",
};
static const struct software_node supp2_swnode = {
.name = "swnode-devlink-test-supplier-2",
};
static const struct software_node *supp_nodes[] = {
&supp1_swnode, &supp2_swnode, NULL
};
static const struct software_node_ref_args refs[] = {
SOFTWARE_NODE_REFERENCE(&supp1_swnode),
SOFTWARE_NODE_REFERENCE(&supp2_swnode, 4, 2),
};
const struct property_entry props[] = {
PROPERTY_ENTRY_REF_ARRAY("suppliers", refs),
{ }
};
struct fwnode_handle *fwnode;
int ret;
ret = kunit_software_node_register_node_group(test, supp_nodes);
KUNIT_ASSERT_EQ(test, ret, 0);
fwnode = kunit_fwnode_create_software_node(test, props, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
ret = fwnode_call_int_op(fwnode, add_links);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2);
KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode)));
KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode)));
}
/*
* End-to-end test: fw_devlink must defer a consumer's probe until its
* supplier has probed.
*
* The reference created by software_node_add_links() is only useful if the
* driver core promotes it to a real device_link and uses it to order probing.
* This test drives actual probing through the platform bus and asserts the
* supplier binds before the consumer.
*/
#define SWNODE_DEVLINK_TEST_SUPPLIER "swnode-link-supplier"
#define SWNODE_DEVLINK_TEST_CONSUMER "swnode-link-consumer"
#define SWNODE_DEVLINK_TEST_TIMEOUT_MS (2 * MSEC_PER_SEC)
struct swnode_test_probe_order {
/* Names in the order their drivers' .probe ran. */
const char *probed[2];
unsigned int count;
wait_queue_head_t wq;
};
static int swnode_test_record_probe(struct platform_device *pdev)
{
struct swnode_test_probe_order *order = platform_get_drvdata(pdev);
if (order && order->count < ARRAY_SIZE(order->probed)) {
order->probed[order->count++] = dev_name(&pdev->dev);
wake_up_interruptible(&order->wq);
}
return 0;
}
static struct platform_driver swnode_test_supplier_driver = {
.probe = swnode_test_record_probe,
.driver = {
.name = SWNODE_DEVLINK_TEST_SUPPLIER,
},
};
static struct platform_driver swnode_test_consumer_driver = {
.probe = swnode_test_record_probe,
.driver = {
.name = SWNODE_DEVLINK_TEST_CONSUMER,
},
};
static void swnode_devlink_test_probe_order(struct kunit *test)
{
static const struct software_node supplier_swnode = {
.name = "swnode-devlink-test-supplier",
};
const struct property_entry consumer_props[] = {
PROPERTY_ENTRY_REF("supplier-ref", &supplier_swnode),
{ }
};
struct platform_device *supplier, *consumer;
struct swnode_test_probe_order *order;
struct fwnode_handle *fwnode;
int ret;
order = kunit_kzalloc(test, sizeof(*order), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, order);
init_waitqueue_head(&order->wq);
fwnode = kunit_software_node_register(test, &supplier_swnode);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
ret = kunit_platform_driver_register(test, &swnode_test_supplier_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_platform_driver_register(test, &swnode_test_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
supplier = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_SUPPLIER,
PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier);
consumer = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_CONSUMER,
PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer);
platform_set_drvdata(supplier, order);
platform_set_drvdata(consumer, order);
ret = kunit_device_add_software_node(test, &supplier->dev, &supplier_swnode);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = device_create_managed_software_node(&consumer->dev,
consumer_props, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_platform_device_add(test, consumer);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_platform_device_add(test, supplier);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = wait_event_interruptible_timeout(order->wq,
order->count == 2,
msecs_to_jiffies(SWNODE_DEVLINK_TEST_TIMEOUT_MS));
KUNIT_ASSERT_GT(test, ret, 0);
KUNIT_EXPECT_STREQ(test, order->probed[0], SWNODE_DEVLINK_TEST_SUPPLIER);
KUNIT_EXPECT_STREQ(test, order->probed[1], SWNODE_DEVLINK_TEST_CONSUMER);
/* Tear down the consumer (and its device link) before the supplier. */
kunit_platform_device_unregister(test, consumer);
}
static struct kunit_case swnode_test_cases[] = {
KUNIT_CASE(swnode_devlink_test_single_ref),
KUNIT_CASE(swnode_devlink_test_multiple_refs),
KUNIT_CASE(swnode_devlink_test_unregistered_ref),
KUNIT_CASE(swnode_devlink_test_remote_endpoint_excluded),
KUNIT_CASE(swnode_devlink_test_ref_array),
KUNIT_CASE(swnode_devlink_test_probe_order),
{ }
};
static struct kunit_suite swnode_test_suite = {
.name = "software-node-links",
.test_cases = swnode_test_cases,
};
kunit_test_suite(swnode_test_suite);
MODULE_DESCRIPTION("Test module for software node fw_devlink support");
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>");
MODULE_LICENSE("GPL");
-1
View File
@@ -64,7 +64,6 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/gpio.h>
#include <linux/irq.h>
static int sn7516x_used = 1, sn7516x;
+17 -2
View File
@@ -653,7 +653,7 @@ config GPIO_ROCKCHIP
config GPIO_RTD
tristate "Realtek DHC GPIO support"
depends on ARCH_REALTEK || COMPILE_TEST
default y
default ARCH_REALTEK
select GPIOLIB_IRQCHIP
help
This option enables support for GPIOs found on Realtek DHC(Digital
@@ -663,6 +663,20 @@ config GPIO_RTD
Say yes here to support GPIO functionality and GPIO interrupt on
Realtek DHC SoCs.
config GPIO_RTD1625
tristate "Realtek DHC RTD1625 GPIO support"
depends on ARCH_REALTEK || COMPILE_TEST
default ARCH_REALTEK
select GPIOLIB_IRQCHIP
select GPIO_REGMAP
select REGMAP_MMIO
help
This option enables support for the GPIO controller on Realtek
DHC (Digital Home Center) RTD1625 SoC.
Say yes here to support both basic GPIO line functionality
and GPIO interrupt handling capabilities for this platform.
config GPIO_SAMA5D2_PIOBU
tristate "SAMA5D2 PIOBU GPIO support"
depends on OF
@@ -949,7 +963,8 @@ config GPIO_IDT3243X
endmenu
menu "Port-mapped I/O GPIO drivers"
depends on X86 && HAS_IOPORT # I/O space access
depends on X86 || COMPILE_TEST
depends on HAS_IOPORT # I/O space access
config GPIO_VX855
tristate "VIA VX855/VX875 GPIO"
+1
View File
@@ -161,6 +161,7 @@ obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o
obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o
obj-$(CONFIG_GPIO_RTD) += gpio-rtd.o
obj-$(CONFIG_GPIO_RTD1625) += gpio-rtd1625.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
+4 -3
View File
@@ -36,9 +36,10 @@ MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
#define IDI48_IRQ_STATUS 0x7
#define IDI48_IRQ_ENABLE IDI48_IRQ_STATUS
static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
unsigned int *mask)
static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
const unsigned int line = offset % 8;
const unsigned int stride = offset / 8;
+5 -17
View File
@@ -48,13 +48,11 @@ static void __iomem *amd_fch_gpio_addr(struct amd_fch_gpio_priv *priv,
static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
unsigned int offset)
{
unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
spin_lock_irqsave(&priv->lock, flags);
guard(spinlock_irqsave)(&priv->lock);
writel_relaxed(readl_relaxed(ptr) & ~AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -62,12 +60,11 @@ static int amd_fch_gpio_direction_input(struct gpio_chip *gc,
static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
unsigned int gpio, int value)
{
unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
u32 val;
spin_lock_irqsave(&priv->lock, flags);
guard(spinlock_irqsave)(&priv->lock);
val = readl_relaxed(ptr);
if (value)
@@ -77,33 +74,28 @@ static int amd_fch_gpio_direction_output(struct gpio_chip *gc,
writel_relaxed(val | AMD_FCH_GPIO_FLAG_DIRECTION, ptr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio)
{
int ret;
unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
spin_lock_irqsave(&priv->lock, flags);
guard(spinlock_irqsave)(&priv->lock);
ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION);
spin_unlock_irqrestore(&priv->lock, flags);
return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
{
unsigned long flags;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, gpio);
u32 mask;
spin_lock_irqsave(&priv->lock, flags);
guard(spinlock_irqsave)(&priv->lock);
mask = readl_relaxed(ptr);
if (value)
@@ -112,22 +104,18 @@ static int amd_fch_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
mask &= ~AMD_FCH_GPIO_FLAG_WRITE;
writel_relaxed(mask, ptr);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int amd_fch_gpio_get(struct gpio_chip *gc,
unsigned int offset)
{
unsigned long flags;
u32 val;
struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
void __iomem *ptr = amd_fch_gpio_addr(priv, offset);
spin_lock_irqsave(&priv->lock, flags);
guard(spinlock_irqsave)(&priv->lock);
val = readl_relaxed(ptr);
spin_unlock_irqrestore(&priv->lock, flags);
return FIELD_GET(AMD_FCH_GPIO_FLAG_READ, val);
}
+15 -9
View File
@@ -85,9 +85,11 @@ static int bd72720gpi_get(struct bd72720_gpio *bdgpio, unsigned int reg_offset)
static int bd72720gpo_get(struct bd72720_gpio *bdgpio,
unsigned int offset)
{
const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
static const int regs[] = {
BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL
};
int ret, val;
ret = regmap_read(bdgpio->regmap, regs[offset], &val);
@@ -111,9 +113,11 @@ static int bd72720gpo_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
static const int regs[] = {
BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL
};
if (BIT(offset) & bdgpio->gpio_is_input) {
dev_dbg(bdgpio->dev, "pin %d not output.\n", offset);
@@ -132,9 +136,11 @@ static int bd72720_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
unsigned long config)
{
struct bd72720_gpio *bdgpio = gpiochip_get_data(chip);
const int regs[] = { BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL };
static const int regs[] = {
BD72720_REG_GPIO1_CTRL, BD72720_REG_GPIO2_CTRL,
BD72720_REG_GPIO3_CTRL, BD72720_REG_GPIO4_CTRL,
BD72720_REG_GPIO5_CTRL, BD72720_REG_EPDEN_CTRL
};
/*
* We can only set the output mode, which makes sense only when output
+6 -15
View File
@@ -154,12 +154,18 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
const struct pci_device_id *pci_id)
{
struct bt8xxgpio *bg;
void __iomem *mmio;
int err;
mmio = devm_ioremap_resource(&dev->dev, pci_resource_n(dev, 0));
if (IS_ERR(mmio))
return PTR_ERR(mmio);
bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL);
if (!bg)
return -ENOMEM;
bg->mmio = mmio;
bg->pdev = dev;
spin_lock_init(&bg->lock);
@@ -168,24 +174,9 @@ static int bt8xxgpio_probe(struct pci_dev *dev,
dev_err(&dev->dev, "can't enable device.\n");
return err;
}
if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0),
pci_resource_len(dev, 0),
"bt8xxgpio")) {
dev_warn(&dev->dev, "can't request iomem (0x%llx).\n",
(unsigned long long)pci_resource_start(dev, 0));
err = -EBUSY;
goto err_disable;
}
pci_set_master(dev);
pci_set_drvdata(dev, bg);
bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000);
if (!bg->mmio) {
dev_err(&dev->dev, "ioremap() failed\n");
err = -EIO;
goto err_disable;
}
/* Disable interrupts */
bgwrite(0, BT848_INT_MASK);
+2 -2
View File
@@ -54,11 +54,11 @@ static int pin_control_gpio_direction_output(struct gpio_chip *chip,
{
int ret;
ret = pinctrl_gpio_direction_output(chip, offset);
ret = pin_control_gpio_set(chip, offset, val);
if (ret)
return ret;
return pin_control_gpio_set(chip, offset, val);
return pinctrl_gpio_direction_output(chip, offset);
}
static int pin_control_gpio_probe(struct platform_device *pdev)
+4 -4
View File
@@ -285,9 +285,9 @@ static void gpio_irq_unmask(struct irq_data *d)
gpiochip_enable_irq(&chips->chip, hwirq);
status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
status &= IRQ_TYPE_EDGE_BOTH;
if (!status)
status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
status = IRQ_TYPE_EDGE_BOTH;
if (status & IRQ_TYPE_EDGE_FALLING)
writel_relaxed(mask, &g->set_falling);
@@ -297,7 +297,7 @@ static void gpio_irq_unmask(struct irq_data *d)
static int gpio_irq_type(struct irq_data *d, unsigned trigger)
{
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
if (trigger & ~IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
return 0;
@@ -400,7 +400,7 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
mask = __gpio_mask(i);
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
if (trigger & ~IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_FALLING)
+2 -2
View File
@@ -67,8 +67,8 @@ static int i8255_ppi_init(struct regmap *const map, const unsigned int base)
return regmap_write(map, base + I8255_PORTC, 0x00);
}
static int i8255_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
static int i8255_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
const unsigned int ppi = offset / I8255_NGPIO;
+3 -3
View File
@@ -66,9 +66,9 @@ static int idio_16_handle_mask_sync(const int index, const unsigned int mask_buf
return 0;
}
static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
const unsigned int offset, unsigned int *const reg,
unsigned int *const mask)
static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, enum gpio_regmap_operation op,
const unsigned int base, const unsigned int offset,
unsigned int *const reg, unsigned int *const mask)
{
unsigned int stride;
+5 -3
View File
@@ -93,6 +93,7 @@ static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap)
}
static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
@@ -127,7 +128,7 @@ static int max7360_handle_mask_sync(const int index,
const unsigned int mask_buf,
void *const irq_drv_data)
{
struct regmap *regmap = irq_drv_data;
struct regmap *regmap = gpio_regmap_get_drvdata(irq_drv_data);
int ret;
for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
@@ -170,7 +171,6 @@ static int max7360_gpio_probe(struct platform_device *pdev)
/* Create custom IRQ configuration. */
irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);
gpio_config.regmap_irq_chip = irq_chip;
if (!irq_chip)
return -ENOMEM;
@@ -181,7 +181,9 @@ static int max7360_gpio_probe(struct platform_device *pdev)
irq_chip->num_irqs = MAX7360_MAX_GPIO;
irq_chip->irqs = max7360_regmap_irqs;
irq_chip->handle_mask_sync = max7360_handle_mask_sync;
irq_chip->irq_drv_data = regmap;
gpio_config.regmap_irq_chip = irq_chip;
gpio_config.drvdata = regmap;
for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {
ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),
+21 -21
View File
@@ -68,7 +68,7 @@ mt7621_gpio_gc_to_priv(struct gpio_chip *gc)
}
static inline struct mtk_gc *
to_mediatek_gpio(struct gpio_chip *chip)
to_mt7621_gpio(struct gpio_chip *chip)
{
struct gpio_generic_chip *gen_gc = to_gpio_generic_chip(chip);
@@ -137,7 +137,7 @@ mt7621_gpio_hwirq_to_offset(irq_hw_number_t hwirq, struct mtk_gc *bank)
}
static void
mediatek_gpio_irq_unmask(struct irq_data *d)
mt7621_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
@@ -159,7 +159,7 @@ mediatek_gpio_irq_unmask(struct irq_data *d)
}
static void
mediatek_gpio_irq_mask(struct irq_data *d)
mt7621_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct mtk_gc *rg = gpiochip_get_data(gc);
@@ -181,7 +181,7 @@ mediatek_gpio_irq_mask(struct irq_data *d)
}
static int
mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
mt7621_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 = gpiochip_get_data(gc);
@@ -194,7 +194,7 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
rg->hlevel | rg->llevel) & mask)
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
type = IRQ_TYPE_EDGE_BOTH;
}
rg->rising &= ~mask;
@@ -245,11 +245,11 @@ mt7621_gpio_irq_relres(struct irq_data *d)
}
static int
mediatek_gpio_xlate(struct gpio_chip *chip,
mt7621_gpio_xlate(struct gpio_chip *chip,
const struct of_phandle_args *spec, u32 *flags)
{
int gpio = spec->args[0];
struct mtk_gc *rg = to_mediatek_gpio(chip);
struct mtk_gc *rg = to_mt7621_gpio(chip);
if (rg->bank != gpio / MTK_BANK_WIDTH)
return -EINVAL;
@@ -264,10 +264,10 @@ 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,
.irq_mask_ack = mt7621_gpio_irq_mask,
.irq_mask = mt7621_gpio_irq_mask,
.irq_unmask = mt7621_gpio_irq_unmask,
.irq_set_type = mt7621_gpio_irq_type,
.flags = IRQCHIP_IMMUTABLE,
};
@@ -380,7 +380,7 @@ mt7621_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
}
static int
mediatek_gpio_bank_probe(struct device *dev, int bank)
mt7621_gpio_bank_probe(struct device *dev, int bank)
{
struct gpio_generic_chip_config config;
struct mtk *mtk = dev_get_drvdata(dev);
@@ -416,7 +416,7 @@ 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.of_xlate = mt7621_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);
@@ -443,7 +443,7 @@ mediatek_gpio_bank_probe(struct device *dev, int bank)
}
static int
mediatek_gpio_probe(struct platform_device *pdev)
mt7621_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk *mtk;
@@ -477,7 +477,7 @@ mediatek_gpio_probe(struct platform_device *pdev)
return ret;
for (i = 0; i < MTK_BANK_CNT; i++) {
ret = mediatek_gpio_bank_probe(dev, i);
ret = mt7621_gpio_bank_probe(dev, i);
if (ret)
return ret;
}
@@ -485,18 +485,18 @@ mediatek_gpio_probe(struct platform_device *pdev)
return 0;
}
static const struct of_device_id mediatek_gpio_match[] = {
static const struct of_device_id mt7621_gpio_match[] = {
{ .compatible = "mediatek,mt7621-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
MODULE_DEVICE_TABLE(of, mt7621_gpio_match);
static struct platform_driver mediatek_gpio_driver = {
.probe = mediatek_gpio_probe,
static struct platform_driver mt7621_gpio_driver = {
.probe = mt7621_gpio_probe,
.driver = {
.name = "mt7621_gpio",
.of_match_table = mediatek_gpio_match,
.of_match_table = mt7621_gpio_match,
},
};
builtin_platform_driver(mediatek_gpio_driver);
builtin_platform_driver(mt7621_gpio_driver);
+39 -5
View File
@@ -37,6 +37,7 @@
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
@@ -114,7 +115,7 @@ struct mvebu_gpio_chip {
struct regmap *regs;
u32 offset;
struct regmap *percpu_regs;
int irqbase;
int bank_irq[4];
struct irq_domain *domain;
int soc_variant;
@@ -603,6 +604,34 @@ static const struct regmap_config mvebu_gpio_regmap_config = {
.val_bits = 32,
};
/*
* Forward wake-up configuration to the parent bank IRQ.
* @d: interrupt data
* @enable: enable as wake-up if non-zero
*
* Return: 0 on success, or a negative error code.
*/
static int mvebu_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mvebu_gpio_chip *mvchip = gc->private;
int bank;
int irq;
bank = d->hwirq / 8;
if (bank >= ARRAY_SIZE(mvchip->bank_irq))
return -EINVAL;
irq = mvchip->bank_irq[bank];
if (irq <= 0)
return -EINVAL;
if (enable)
return enable_irq_wake(irq);
return disable_irq_wake(irq);
}
/*
* Functions implementing the pwm_chip methods
*/
@@ -1253,7 +1282,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
err = irq_alloc_domain_generic_chips(
mvchip->domain, ngpios, 2, np->name, handle_level_irq,
IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, 0);
IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_LEVEL, 0, IRQ_GC_INIT_NESTED_LOCK);
if (err) {
dev_err(&pdev->dev, "couldn't allocate irq chips %s (DT).\n",
mvchip->chip.label);
@@ -1267,18 +1296,22 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
gc->private = mvchip;
ct = &gc->chip_types[0];
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->chip.irq_mask = mvebu_gpio_level_irq_mask;
ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask;
ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
ct->chip.irq_set_wake = mvebu_gpio_set_wake_irq;
ct->chip.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
ct->chip.name = mvchip->chip.label;
ct = &gc->chip_types[1];
ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
ct->type = IRQ_TYPE_EDGE_BOTH;
ct->chip.irq_ack = mvebu_gpio_irq_ack;
ct->chip.irq_mask = mvebu_gpio_edge_irq_mask;
ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask;
ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
ct->chip.irq_set_wake = mvebu_gpio_set_wake_irq;
ct->chip.flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_MASK_ON_SUSPEND;
ct->handler = handle_edge_irq;
ct->chip.name = mvchip->chip.label;
@@ -1287,13 +1320,14 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
* interrupt handlers, with each handler dealing with 8 GPIO
* pins.
*/
for (i = 0; i < 4; i++) {
for (i = 0; i < ARRAY_SIZE(mvchip->bank_irq); i++) {
int irq = platform_get_irq_optional(pdev, i);
if (irq < 0)
continue;
irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler,
mvchip);
mvchip->bank_irq[i] = irq;
}
return 0;
+2 -2
View File
@@ -198,7 +198,7 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
gc->private = port;
ct = &gc->chip_types[0];
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
@@ -210,7 +210,7 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
ct->regs.disable = PINCTRL_PIN2IRQ(port) + MXS_CLR;
ct = &gc->chip_types[1];
ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
ct->type = IRQ_TYPE_EDGE_BOTH;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+32 -32
View File
@@ -519,22 +519,23 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
gpio_dev = bus_find_device_by_fwnode(&platform_bus_type, fwnode);
if (!gpio_dev) {
dev_err(dev, "populate \"%pfwP\": device not found\n", fwnode);
return ERR_PTR(-ENODEV);
ret = -ENODEV;
dev_err_probe(dev, ret, "populate \"%pfwP\": device not found\n", fwnode);
return ERR_PTR(ret);
}
gpio_pdev = to_platform_device(gpio_dev);
if (device_property_read_u32(gpio_dev, "gpio-bank", &id)) {
dev_err(dev, "populate: gpio-bank property not found\n");
platform_device_put(gpio_pdev);
return ERR_PTR(-EINVAL);
ret = -EINVAL;
dev_err_probe(dev, ret, "populate: gpio-bank property not found\n");
goto err_put_pdev;
}
#ifdef CONFIG_PINCTRL_NOMADIK
if (id >= ARRAY_SIZE(nmk_gpio_chips)) {
dev_err(dev, "populate: invalid id: %u\n", id);
platform_device_put(gpio_pdev);
return ERR_PTR(-EINVAL);
ret = -EINVAL;
dev_err_probe(dev, ret, "populate: invalid id: %u\n", id);
goto err_put_pdev;
}
/* Already populated? */
nmk_chip = nmk_gpio_chips[id];
@@ -546,8 +547,8 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
nmk_chip = devm_kzalloc(dev, sizeof(*nmk_chip), GFP_KERNEL);
if (!nmk_chip) {
platform_device_put(gpio_pdev);
return ERR_PTR(-ENOMEM);
ret = -ENOMEM;
goto err_put_pdev;
}
if (device_property_read_u32(gpio_dev, "ngpios", &ngpio)) {
@@ -568,16 +569,17 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
res = platform_get_resource(gpio_pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
platform_device_put(gpio_pdev);
return ERR_CAST(base);
ret = PTR_ERR(base);
goto err_put_pdev;
}
nmk_chip->addr = base;
/* NOTE: do not use devm_ here! */
clk = clk_get_optional(gpio_dev, NULL);
if (IS_ERR(clk)) {
platform_device_put(gpio_pdev);
return ERR_CAST(clk);
ret = PTR_ERR(clk);
dev_err_probe(dev, ret, "failed getting clock\n");
goto err_put_pdev;
}
clk_prepare(clk);
nmk_chip->clk = clk;
@@ -585,12 +587,9 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
/* NOTE: do not use devm_ here! */
reset = reset_control_get_optional_shared(gpio_dev, NULL);
if (IS_ERR(reset)) {
clk_unprepare(clk);
clk_put(clk);
platform_device_put(gpio_pdev);
dev_err(dev, "failed getting reset control: %pe\n",
reset);
return ERR_CAST(reset);
ret = PTR_ERR(reset);
dev_err_probe(dev, ret, "failed getting reset control\n");
goto err_unprepare_clk;
}
/*
@@ -600,18 +599,23 @@ struct nmk_gpio_chip *nmk_gpio_populate_chip(struct fwnode_handle *fwnode,
*/
ret = reset_control_deassert(reset);
if (ret) {
reset_control_put(reset);
clk_unprepare(clk);
clk_put(clk);
platform_device_put(gpio_pdev);
dev_err(dev, "failed reset deassert: %d\n", ret);
return ERR_PTR(ret);
dev_err_probe(dev, ret, "failed reset deassert\n");
goto err_put_reset;
}
#ifdef CONFIG_PINCTRL_NOMADIK
nmk_gpio_chips[id] = nmk_chip;
#endif
return nmk_chip;
err_put_reset:
reset_control_put(reset);
err_unprepare_clk:
clk_unprepare(clk);
clk_put(clk);
err_put_pdev:
platform_device_put(gpio_pdev);
return ERR_PTR(ret);
}
static void nmk_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p)
@@ -647,10 +651,8 @@ static int nmk_gpio_probe(struct platform_device *pdev)
int ret;
nmk_chip = nmk_gpio_populate_chip(dev_fwnode(dev), pdev);
if (IS_ERR(nmk_chip)) {
dev_err(dev, "could not populate nmk chip struct\n");
if (IS_ERR(nmk_chip))
return PTR_ERR(nmk_chip);
}
supports_sleepmode =
device_property_read_bool(dev, "st,supports-sleepmode");
@@ -693,7 +695,7 @@ static int nmk_gpio_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, nmk_gpio_irq_handler, IRQF_SHARED,
dev_name(dev), nmk_chip);
if (ret) {
dev_err(dev, "failed requesting IRQ\n");
dev_err_probe(dev, ret, "failed requesting IRQ\n");
return ret;
}
@@ -709,8 +711,6 @@ static int nmk_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, nmk_chip);
dev_info(dev, "chip registered\n");
return 0;
}
+4 -5
View File
@@ -432,8 +432,7 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
if (!bank->regs->leveldetect0 &&
(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
if (!bank->regs->leveldetect0 && (type & IRQ_TYPE_LEVEL_MASK))
return -EINVAL;
raw_spin_lock_irqsave(&bank->lock, flags);
@@ -450,9 +449,9 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
else if (type & IRQ_TYPE_EDGE_BOTH)
/*
* Edge IRQs are already cleared/acked in irq_handler and
* not need to be masked, as result handle_edge_irq()
@@ -702,7 +701,7 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
* after enabing the interrupt to clear the wakeup status.
*/
if (bank->regs->leveldetect0 && bank->regs->wkup_en &&
trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
trigger & IRQ_TYPE_LEVEL_MASK)
omap_clear_gpio_irqstatus(bank, offset);
if (trigger)
+1 -1
View File
@@ -133,7 +133,7 @@ static int pca9570_probe(struct i2c_client *client)
gpio->chip.get = pca9570_get;
gpio->chip.set = pca9570_set;
gpio->chip.base = -1;
gpio->chip_data = device_get_match_data(&client->dev);
gpio->chip_data = i2c_get_match_data(client);
gpio->chip.ngpio = gpio->chip_data->ngpio;
gpio->chip.can_sleep = true;
+8 -5
View File
@@ -18,6 +18,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/byteorder.h>
static const struct i2c_device_id pcf857x_id[] = {
{ .name = "pcf8574", .driver_data = 8 },
{ .name = "pcf8574a", .driver_data = 8 },
@@ -96,22 +98,23 @@ static int i2c_read_le8(struct i2c_client *client)
static int i2c_write_le16(struct i2c_client *client, unsigned int word)
{
u8 buf[2] = { word & 0xff, word >> 8, };
__le16 buf = cpu_to_le16(word);
int status;
status = i2c_master_send(client, buf, 2);
status = i2c_master_send(client, (char *)&buf, sizeof(buf));
return (status < 0) ? status : 0;
}
static int i2c_read_le16(struct i2c_client *client)
{
u8 buf[2];
__le16 buf;
int status;
status = i2c_master_recv(client, buf, 2);
status = i2c_master_recv(client, (char *)&buf, sizeof(buf));
if (status < 0)
return status;
return (buf[1] << 8) | buf[0];
return le16_to_cpu(buf);
}
/*-------------------------------------------------------------------------*/
+3 -3
View File
@@ -225,9 +225,9 @@ exit_unlock:
return ret;
}
static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
const unsigned int offset, unsigned int *const reg,
unsigned int *const mask)
static int idio_24_reg_mask_xlate(struct gpio_regmap *const gpio, enum gpio_regmap_operation op,
const unsigned int base, const unsigned int offset,
unsigned int *const reg, unsigned int *const mask)
{
const unsigned int out_stride = offset / IDIO_24_NGPIO_PER_REG;
const unsigned int in_stride = (offset - 24) / IDIO_24_NGPIO_PER_REG;
+2 -3
View File
@@ -132,8 +132,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
if (offset < 0 || offset >= PL061_GPIO_NR)
return -EINVAL;
if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
(trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
if ((trigger & IRQ_TYPE_LEVEL_MASK) && (trigger & IRQ_TYPE_EDGE_BOTH))
{
dev_err(gc->parent,
"trying to configure line %d for both level and edge "
@@ -149,7 +148,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
gpiois = readb(pl061->base + GPIOIS);
gpioibe = readb(pl061->base + GPIOIBE);
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
if (trigger & IRQ_TYPE_LEVEL_MASK) {
bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
/* Disable edge detection */
+1 -1
View File
@@ -406,7 +406,7 @@ static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type)
if (__gpio_is_occupied(pchip, gpio))
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
type = IRQ_TYPE_EDGE_BOTH;
}
gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
-1
View File
@@ -59,7 +59,6 @@ struct gpio_rcar_priv {
#define MSKCLR 0x1c /* Interrupt Mask Clear Register */
#define POSNEG 0x20 /* Positive/Negative Logic Select Register */
#define EDGLEVEL 0x24 /* Edge/level Select Register */
#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */
#define OUTDTSEL 0x40 /* Output Data Select Register */
#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
#define INEN 0x50 /* General Input Enable Register */
+2 -2
View File
@@ -169,9 +169,9 @@ static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
if (ret)
return ret;
if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
if (flow_type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(data, handle_level_irq);
else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
else if (flow_type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(data, handle_edge_irq);
return 0;
+95 -9
View File
@@ -6,6 +6,7 @@
*/
#include <linux/bits.h>
#include <linux/compiler_attributes.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -39,9 +40,19 @@ struct gpio_regmap {
struct regmap_irq_chip_data *irq_chip_data;
#endif
int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
unsigned int *mask);
int (*reg_mask_xlate)(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask);
int (*value_xlate)(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int reg, unsigned int *mask,
unsigned int *val);
int (*set_config)(struct gpio_regmap *gpio, struct gpio_chip *chip,
unsigned int offset, unsigned long config);
void *driver_data;
};
@@ -55,6 +66,7 @@ static unsigned int gpio_regmap_addr(unsigned int addr)
}
static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
enum gpio_regmap_operation __maybe_unused op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
@@ -79,7 +91,7 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
else
base = gpio_regmap_addr(gpio->reg_set_base);
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -102,7 +114,7 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
unsigned int reg, mask, mask_val;
int ret;
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -111,6 +123,13 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
else
mask_val = 0;
if (gpio->value_xlate) {
ret = gpio->value_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset,
reg, &mask, &mask_val);
if (ret)
return ret;
}
/* ignore input values which shadow the old output value */
if (gpio->reg_dat_base == gpio->reg_set_base)
ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val);
@@ -124,7 +143,7 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
unsigned int offset, int val)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
unsigned int base, reg, mask;
unsigned int base, reg, mask, value = 0;
int ret;
if (val)
@@ -132,10 +151,17 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
else
base = gpio_regmap_addr(gpio->reg_clr_base);
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, &reg, &mask);
if (ret)
return ret;
if (gpio->value_xlate) {
ret = gpio->value_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset,
reg, &mask, &value);
if (ret)
return ret;
}
return regmap_write(gpio->regmap, reg, mask);
}
@@ -182,7 +208,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -236,7 +262,7 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset, &reg, &mask);
if (ret)
return ret;
@@ -245,6 +271,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
else
val = output ? mask : 0;
if (gpio->value_xlate) {
ret = gpio->value_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset,
reg, &mask, &val);
if (ret)
return ret;
}
return regmap_update_bits(gpio->regmap, reg, mask, val);
}
@@ -276,12 +309,55 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
return gpio_regmap_set_direction(chip, offset, true);
}
static int gpio_regmap_set_config(struct gpio_chip *chip,
unsigned int offset,
unsigned long cfg)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
return gpio->set_config(gpio, chip, offset, cfg);
}
int gpio_regmap_reqres_irq(struct gpio_regmap *gpio, unsigned int offset)
{
return gpiochip_reqres_irq(&gpio->gpio_chip, offset);
}
EXPORT_SYMBOL_GPL(gpio_regmap_reqres_irq);
void gpio_regmap_relres_irq(struct gpio_regmap *gpio, unsigned int offset)
{
gpiochip_relres_irq(&gpio->gpio_chip, offset);
}
EXPORT_SYMBOL_GPL(gpio_regmap_relres_irq);
static int __maybe_unused gpio_regmap_irq_reqres(void *irq_drv_data, irq_hw_number_t hwirq)
{
return gpio_regmap_reqres_irq(irq_drv_data, hwirq);
}
static void __maybe_unused gpio_regmap_irq_relres(void *irq_drv_data, irq_hw_number_t hwirq)
{
gpio_regmap_relres_irq(irq_drv_data, hwirq);
}
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
{
return gpio->driver_data;
}
EXPORT_SYMBOL_GPL(gpio_regmap_get_drvdata);
void gpio_regmap_enable_irq(struct gpio_regmap *gpio, irq_hw_number_t hwirq)
{
gpiochip_enable_irq(&gpio->gpio_chip, hwirq);
}
EXPORT_SYMBOL_GPL(gpio_regmap_enable_irq);
void gpio_regmap_disable_irq(struct gpio_regmap *gpio, irq_hw_number_t hwirq)
{
gpiochip_disable_irq(&gpio->gpio_chip, hwirq);
}
EXPORT_SYMBOL_GPL(gpio_regmap_disable_irq);
/**
* gpio_regmap_register() - Register a generic regmap GPIO controller
* @config: configuration for gpio_regmap
@@ -390,6 +466,13 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
if (!gpio->reg_mask_xlate)
gpio->reg_mask_xlate = gpio_regmap_simple_xlate;
gpio->value_xlate = config->value_xlate;
if (config->set_config) {
gpio->set_config = config->set_config;
chip->set_config = gpio_regmap_set_config;
}
ret = gpiochip_add_data(chip, gpio);
if (ret < 0)
goto err_free_bitmap_output;
@@ -397,6 +480,9 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
gpio->regmap_irq_line = config->regmap_irq_line;
config->regmap_irq_chip->irq_reqres = gpio_regmap_irq_reqres;
config->regmap_irq_chip->irq_relres = gpio_regmap_irq_relres;
config->regmap_irq_chip->irq_drv_data = gpio;
ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,
config->regmap_irq_line, config->regmap_irq_flags,
0, config->regmap_irq_chip, &gpio->irq_chip_data);
+6 -10
View File
@@ -647,21 +647,17 @@ static void rockchip_clk_put(void *data)
static int rockchip_get_bank_data(struct rockchip_pin_bank *bank)
{
struct resource res;
struct platform_device *pdev = to_platform_device(bank->dev);
int id = 0, ret;
if (of_address_to_resource(bank->of_node, 0, &res)) {
dev_err(bank->dev, "cannot find IO resource for bank\n");
return -ENOENT;
}
bank->reg_base = devm_ioremap_resource(bank->dev, &res);
bank->reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bank->reg_base))
return PTR_ERR(bank->reg_base);
bank->irq = irq_of_parse_and_map(bank->of_node, 0);
if (!bank->irq)
return -EINVAL;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
bank->irq = ret;
bank->clk = devm_clk_get_enabled(bank->dev, NULL);
if (IS_ERR(bank->clk))
+677
View File
@@ -0,0 +1,677 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Realtek DHC RTD1625 gpio driver
*
* Copyright (c) 2023-2026 Realtek Semiconductor Corp.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/interrupt.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#define RTD1625_GPIO_DIR BIT(0)
#define RTD1625_GPIO_OUT BIT(2)
#define RTD1625_GPIO_IN BIT(4)
#define RTD1625_GPIO_EDGE_INT_DP BIT(6)
#define RTD1625_GPIO_EDGE_INT_EN BIT(8)
#define RTD1625_GPIO_LEVEL_INT_EN BIT(16)
#define RTD1625_GPIO_LEVEL_INT_DP BIT(18)
#define RTD1625_GPIO_DEBOUNCE GENMASK(30, 28)
#define RTD1625_GPIO_DEBOUNCE_WREN BIT(31)
#define RTD1625_GPIO_WREN(x) ((x) << 1)
/* Write-enable masks for all GPIO configs and reserved hardware bits */
#define RTD1625_ISO_GPIO_WREN_ALL 0x8000aa8a
#define RTD1625_ISOM_GPIO_WREN_ALL 0x800aaa8a
#define RTD1625_GPIO_DEBOUNCE_1US 0
#define RTD1625_GPIO_DEBOUNCE_10US 1
#define RTD1625_GPIO_DEBOUNCE_100US 2
#define RTD1625_GPIO_DEBOUNCE_1MS 3
#define RTD1625_GPIO_DEBOUNCE_10MS 4
#define RTD1625_GPIO_DEBOUNCE_20MS 5
#define RTD1625_GPIO_DEBOUNCE_30MS 6
#define RTD1625_GPIO_DEBOUNCE_50MS 7
#define GPIO_CONTROL(gpio) ((gpio) * 4)
static struct lock_class_key rtd1625_gpio_irq_lock_class;
static struct lock_class_key rtd1625_gpio_irq_request_class;
enum rtd1625_irq_index {
RTD1625_IRQ_ASSERT,
RTD1625_IRQ_DEASSERT,
RTD1625_IRQ_LEVEL,
RTD1625_MAX_IRQS
};
/**
* struct rtd1625_gpio_info - Specific GPIO register information
* @num_gpios: The number of GPIOs
* @irq_type_support: Supported IRQ types
* @base_offset: Offset for GPIO controller register
* @gpa_offset: Offset for GPIO assert interrupt status register
* @gpda_offset: Offset for GPIO deassert interrupt status register
* @level_offset: Offset of level interrupt status register
* @write_en_all: Write-enable mask for all configurable bits
*/
struct rtd1625_gpio_info {
unsigned int num_gpios;
unsigned int irq_type_support;
unsigned int base_offset;
unsigned int gpa_offset;
unsigned int gpda_offset;
unsigned int level_offset;
unsigned int write_en_all;
};
struct rtd1625_gpio {
struct gpio_regmap *gpio_reg;
const struct rtd1625_gpio_info *info;
struct regmap *regmap;
unsigned int irqs[RTD1625_MAX_IRQS];
raw_spinlock_t lock;
struct irq_domain *domain;
unsigned int *save_regs;
};
static unsigned int rtd1625_gpio_gpa_offset(struct rtd1625_gpio *data, unsigned int offset)
{
return data->info->gpa_offset + ((offset / 32) * 4);
}
static unsigned int rtd1625_gpio_gpda_offset(struct rtd1625_gpio *data, unsigned int offset)
{
return data->info->gpda_offset + ((offset / 32) * 4);
}
static unsigned int rtd1625_gpio_level_offset(struct rtd1625_gpio *data, unsigned int offset)
{
return data->info->level_offset + ((offset / 32) * 4);
}
static int rtd1625_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
/* Each GPIO has its own dedicated 32-bit register */
struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio);
int val = 0, ret = 0;
*reg = base + offset * 4;
switch (op) {
case GPIO_REGMAP_SET_OP:
*mask = RTD1625_GPIO_OUT;
return 0;
case GPIO_REGMAP_GET_OP:
ret = regmap_read(data->regmap, *reg, &val);
if (ret)
return ret;
if (val & RTD1625_GPIO_DIR)
*mask = RTD1625_GPIO_OUT;
else
*mask = RTD1625_GPIO_IN;
return 0;
case GPIO_REGMAP_GET_DIR_OP:
case GPIO_REGMAP_SET_DIR_OP:
*mask = RTD1625_GPIO_DIR;
return 0;
default:
return -ENOTSUPP;
}
}
static int rtd1625_value_xlate(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int reg, unsigned int *mask,
unsigned int *val)
{
switch (op) {
case GPIO_REGMAP_SET_OP:
*val |= RTD1625_GPIO_WREN(RTD1625_GPIO_OUT);
*mask |= RTD1625_GPIO_WREN(RTD1625_GPIO_OUT);
return 0;
case GPIO_REGMAP_SET_DIR_OP:
*val |= RTD1625_GPIO_WREN(RTD1625_GPIO_DIR);
*mask |= RTD1625_GPIO_WREN(RTD1625_GPIO_DIR);
return 0;
default:
return -ENOTSUPP;
}
}
static int rtd1625_gpio_set_debounce(struct rtd1625_gpio *data, unsigned int offset,
unsigned int debounce)
{
u8 deb_val;
u32 val;
switch (debounce) {
case 1:
deb_val = RTD1625_GPIO_DEBOUNCE_1US;
break;
case 10:
deb_val = RTD1625_GPIO_DEBOUNCE_10US;
break;
case 100:
deb_val = RTD1625_GPIO_DEBOUNCE_100US;
break;
case 1000:
deb_val = RTD1625_GPIO_DEBOUNCE_1MS;
break;
case 10000:
deb_val = RTD1625_GPIO_DEBOUNCE_10MS;
break;
case 20000:
deb_val = RTD1625_GPIO_DEBOUNCE_20MS;
break;
case 30000:
deb_val = RTD1625_GPIO_DEBOUNCE_30MS;
break;
case 50000:
deb_val = RTD1625_GPIO_DEBOUNCE_50MS;
break;
default:
return -ENOTSUPP;
}
val = FIELD_PREP(RTD1625_GPIO_DEBOUNCE, deb_val) | RTD1625_GPIO_DEBOUNCE_WREN;
guard(raw_spinlock_irqsave)(&data->lock);
return regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(offset), val);
}
static int rtd1625_gpio_set_config(struct gpio_regmap *gpio, struct gpio_chip *chip,
unsigned int offset, unsigned long config)
{
struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio);
u32 debounce;
if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) {
debounce = pinconf_to_config_argument(config);
return rtd1625_gpio_set_debounce(data, offset, debounce);
}
return gpiochip_generic_config(chip, offset, config);
}
static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
{
unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset);
struct rtd1625_gpio *data = irq_desc_get_handler_data(desc);
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int irq = irq_desc_get_irq(desc);
struct irq_domain *domain = data->domain;
unsigned int reg_offset, i, j, val;
irq_hw_number_t hwirq;
unsigned long status;
u32 irq_type;
int ret;
if (irq == data->irqs[RTD1625_IRQ_ASSERT])
get_reg_offset = &rtd1625_gpio_gpa_offset;
else if (irq == data->irqs[RTD1625_IRQ_DEASSERT])
get_reg_offset = &rtd1625_gpio_gpda_offset;
else if (irq == data->irqs[RTD1625_IRQ_LEVEL])
get_reg_offset = &rtd1625_gpio_level_offset;
else
return;
chained_irq_enter(chip, desc);
for (i = 0; i < data->info->num_gpios; i += 32) {
reg_offset = get_reg_offset(data, i);
ret = regmap_read(data->regmap, reg_offset, &val);
if (ret) {
pr_err_ratelimited("Failed to read IRQ status for GPIO %u: %d\n", i, ret);
continue;
}
status = val;
/*
* Hardware quirk: The controller fires both "assert" and "de-assert"
* interrupts simultaneously on any edge toggle.
* We must pre-clear edge interrupts here. If we drop an unwanted
* de-assert interrupt below, it will never reach the IRQ core
* (generic_handle_domain_irq), meaning ->irq_ack() won't be called.
* Failing to clear it here leads to an interrupt storm.
*/
if (irq != data->irqs[RTD1625_IRQ_LEVEL]) {
ret = regmap_write(data->regmap, reg_offset, status);
if (ret)
pr_err_ratelimited("Failed to clear edge IRQ for GPIO %u: %d\n",
i, ret);
}
for_each_set_bit(j, &status, 32) {
hwirq = i + j;
irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq));
/*
* Filter out the hardware-forced de-assert interrupt unless
* the user explicitly requested IRQ_TYPE_EDGE_BOTH.
*/
if (irq == data->irqs[RTD1625_IRQ_DEASSERT] &&
irq_type != IRQ_TYPE_EDGE_BOTH)
continue;
generic_handle_domain_irq(domain, hwirq);
}
}
chained_irq_exit(chip, desc);
}
static void rtd1625_gpio_ack_irq(struct irq_data *d)
{
struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u32 irq_type = irqd_get_trigger_type(d);
u32 bit_mask = BIT(hwirq % 32);
int reg_offset;
if (irq_type & IRQ_TYPE_LEVEL_MASK) {
reg_offset = rtd1625_gpio_level_offset(data, hwirq);
regmap_write(data->regmap, reg_offset, bit_mask);
}
}
static void rtd1625_gpio_enable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
{
int gpda_reg_offset = rtd1625_gpio_gpda_offset(data, hwirq);
int gpa_reg_offset = rtd1625_gpio_gpa_offset(data, hwirq);
u32 clr_mask = BIT(hwirq % 32);
u32 val;
guard(raw_spinlock_irqsave)(&data->lock);
regmap_write(data->regmap, gpa_reg_offset, clr_mask);
regmap_write(data->regmap, gpda_reg_offset, clr_mask);
val = RTD1625_GPIO_EDGE_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN);
regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
}
static void rtd1625_gpio_disable_edge_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
{
u32 val;
guard(raw_spinlock_irqsave)(&data->lock);
val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_EN);
regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
}
static void rtd1625_gpio_enable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
{
int level_reg_offset = rtd1625_gpio_level_offset(data, hwirq);
u32 clr_mask = BIT(hwirq % 32);
u32 val;
guard(raw_spinlock_irqsave)(&data->lock);
regmap_write(data->regmap, level_reg_offset, clr_mask);
val = RTD1625_GPIO_LEVEL_INT_EN | RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN);
regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
}
static void rtd1625_gpio_disable_level_irq(struct rtd1625_gpio *data, irq_hw_number_t hwirq)
{
u32 val;
guard(raw_spinlock_irqsave)(&data->lock);
val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_EN);
regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq), val);
}
static void rtd1625_gpio_enable_irq(struct irq_data *d)
{
struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u32 irq_type = irqd_get_trigger_type(d);
gpio_regmap_enable_irq(data->gpio_reg, hwirq);
if (irq_type & IRQ_TYPE_EDGE_BOTH)
rtd1625_gpio_enable_edge_irq(data, hwirq);
else if (irq_type & IRQ_TYPE_LEVEL_MASK)
rtd1625_gpio_enable_level_irq(data, hwirq);
}
static void rtd1625_gpio_disable_irq(struct irq_data *d)
{
struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
u32 irq_type = irqd_get_trigger_type(d);
if (irq_type & IRQ_TYPE_EDGE_BOTH)
rtd1625_gpio_disable_edge_irq(data, hwirq);
else if (irq_type & IRQ_TYPE_LEVEL_MASK)
rtd1625_gpio_disable_level_irq(data, hwirq);
gpio_regmap_disable_irq(data->gpio_reg, hwirq);
}
static int rtd1625_gpio_irq_set_level_type(struct irq_data *d, bool level)
{
u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_LEVEL_INT_DP);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct rtd1625_gpio *data;
int ret;
data = irq_data_get_irq_chip_data(d);
if (!(data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK))
return -EINVAL;
if (level)
val |= RTD1625_GPIO_LEVEL_INT_DP;
scoped_guard(raw_spinlock_irqsave, &data->lock) {
ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq),
val);
if (ret)
return ret;
}
irq_set_handler_locked(d, handle_level_irq);
return 0;
}
static int rtd1625_gpio_irq_set_edge_type(struct irq_data *d, bool polarity)
{
struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
u32 val = RTD1625_GPIO_WREN(RTD1625_GPIO_EDGE_INT_DP);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
int ret;
if (!(data->info->irq_type_support & IRQ_TYPE_EDGE_BOTH))
return -EINVAL;
if (polarity)
val |= RTD1625_GPIO_EDGE_INT_DP;
scoped_guard(raw_spinlock_irqsave, &data->lock) {
ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(hwirq),
val);
if (ret)
return ret;
}
irq_set_handler_locked(d, handle_edge_irq);
return 0;
}
static int rtd1625_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_RISING:
return rtd1625_gpio_irq_set_edge_type(d, 1);
case IRQ_TYPE_EDGE_FALLING:
return rtd1625_gpio_irq_set_edge_type(d, 0);
case IRQ_TYPE_EDGE_BOTH:
return rtd1625_gpio_irq_set_edge_type(d, 1);
case IRQ_TYPE_LEVEL_HIGH:
return rtd1625_gpio_irq_set_level_type(d, 0);
case IRQ_TYPE_LEVEL_LOW:
return rtd1625_gpio_irq_set_level_type(d, 1);
default:
return -EINVAL;
}
}
static int rtd1625_gpio_irq_request_resources(struct irq_data *d)
{
struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
return gpio_regmap_reqres_irq(data->gpio_reg, d->hwirq);
}
static void rtd1625_gpio_irq_release_resources(struct irq_data *d)
{
struct rtd1625_gpio *data = irq_data_get_irq_chip_data(d);
gpio_regmap_relres_irq(data->gpio_reg, d->hwirq);
}
static struct irq_chip rtd1625_iso_gpio_irq_chip = {
.name = "rtd1625-gpio",
.irq_ack = rtd1625_gpio_ack_irq,
.irq_mask = rtd1625_gpio_disable_irq,
.irq_unmask = rtd1625_gpio_enable_irq,
.irq_set_type = rtd1625_gpio_irq_set_type,
.flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
.irq_request_resources = rtd1625_gpio_irq_request_resources,
.irq_release_resources = rtd1625_gpio_irq_release_resources,
};
static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_gpio *data)
{
unsigned int num_irqs;
int irq;
/* IRQ is optional; operate as basic GPIO if absent */
irq = platform_get_irq_optional(pdev, 0);
if (irq == -ENXIO)
return 0;
if (irq < 0)
return irq;
num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2;
for (unsigned int i = 0; i < num_irqs; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
return irq;
data->irqs[i] = irq;
irq_set_chained_handler_and_data(data->irqs[i], rtd1625_gpio_irq_handle, data);
}
return 0;
}
static int rtd1625_gpio_irq_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{
struct rtd1625_gpio *data = domain->host_data;
irq_set_chip_data(irq, data);
irq_set_chip_and_handler(irq, &rtd1625_iso_gpio_irq_chip, handle_bad_irq);
irq_set_noprobe(irq);
irq_set_lockdep_class(irq, &rtd1625_gpio_irq_lock_class,
&rtd1625_gpio_irq_request_class);
return 0;
}
static const struct irq_domain_ops rtd1625_gpio_irq_domain_ops = {
.map = rtd1625_gpio_irq_map,
.xlate = irq_domain_xlate_twocell,
};
static const struct regmap_config rtd1625_gpio_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
#ifndef CONFIG_DEBUG_GPIO
.disable_locking = true,
#endif
};
static int rtd1625_gpio_probe(struct platform_device *pdev)
{
struct gpio_regmap_config config = {};
struct irq_domain_info d_info = {};
struct device *dev = &pdev->dev;
struct gpio_regmap *gpio_reg;
struct rtd1625_gpio *data;
void __iomem *irq_base;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->info = device_get_match_data(dev);
if (!data->info)
return -ENODATA;
irq_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(irq_base))
return PTR_ERR(irq_base);
data->regmap = devm_regmap_init_mmio(dev, irq_base, &rtd1625_gpio_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
raw_spin_lock_init(&data->lock);
platform_set_drvdata(pdev, data);
data->save_regs = devm_kcalloc(dev, data->info->num_gpios, sizeof(*data->save_regs),
GFP_KERNEL);
if (!data->save_regs)
return -ENOMEM;
config.parent = dev;
config.regmap = data->regmap;
config.ngpio = data->info->num_gpios;
config.reg_dat_base = data->info->base_offset;
config.reg_set_base = data->info->base_offset;
config.reg_dir_out_base = data->info->base_offset;
config.reg_mask_xlate = rtd1625_reg_mask_xlate;
config.set_config = rtd1625_gpio_set_config;
config.value_xlate = rtd1625_value_xlate;
d_info.fwnode = dev_fwnode(&pdev->dev);
d_info.size = data->info->num_gpios;
d_info.hwirq_max = data->info->num_gpios;
d_info.ops = &rtd1625_gpio_irq_domain_ops;
d_info.host_data = data;
data->domain = devm_irq_domain_instantiate(dev, &d_info);
if (IS_ERR(data->domain))
return PTR_ERR(data->domain);
ret = rtd1625_gpio_setup_irq(pdev, data);
if (ret)
return ret;
config.irq_domain = data->domain;
config.drvdata = data;
gpio_reg = devm_gpio_regmap_register(dev, &config);
if (IS_ERR(gpio_reg))
return PTR_ERR(gpio_reg);
data->gpio_reg = gpio_reg;
return 0;
}
static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = {
.num_gpios = 166,
.irq_type_support = IRQ_TYPE_EDGE_BOTH,
.base_offset = 0x100,
.gpa_offset = 0x000,
.gpda_offset = 0x020,
.write_en_all = RTD1625_ISO_GPIO_WREN_ALL,
};
static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = {
.num_gpios = 4,
.irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW |
IRQ_TYPE_LEVEL_HIGH,
.base_offset = 0x20,
.gpa_offset = 0x00,
.gpda_offset = 0x04,
.level_offset = 0x18,
.write_en_all = RTD1625_ISOM_GPIO_WREN_ALL,
};
static int rtd1625_gpio_suspend(struct device *dev)
{
struct rtd1625_gpio *data = dev_get_drvdata(dev);
const struct rtd1625_gpio_info *info = data->info;
int ret;
for (unsigned int i = 0; i < info->num_gpios; i++) {
ret = regmap_read(data->regmap, data->info->base_offset + GPIO_CONTROL(i),
&data->save_regs[i]);
if (ret)
return ret;
}
return 0;
}
static int rtd1625_gpio_resume(struct device *dev)
{
struct rtd1625_gpio *data = dev_get_drvdata(dev);
const struct rtd1625_gpio_info *info = data->info;
int ret;
for (unsigned int i = 0; i < info->num_gpios; i++) {
ret = regmap_write(data->regmap, data->info->base_offset + GPIO_CONTROL(i),
data->save_regs[i] | info->write_en_all);
if (ret)
return ret;
}
return 0;
}
static DEFINE_NOIRQ_DEV_PM_OPS(rtd1625_gpio_pm_ops, rtd1625_gpio_suspend, rtd1625_gpio_resume);
static const struct of_device_id rtd1625_gpio_of_matches[] = {
{ .compatible = "realtek,rtd1625-iso-gpio", .data = &rtd1625_iso_gpio_info },
{ .compatible = "realtek,rtd1625-isom-gpio", .data = &rtd1625_isom_gpio_info },
{ }
};
MODULE_DEVICE_TABLE(of, rtd1625_gpio_of_matches);
static struct platform_driver rtd1625_gpio_platform_driver = {
.driver = {
.name = "gpio-rtd1625",
.of_match_table = rtd1625_gpio_of_matches,
.pm = pm_sleep_ptr(&rtd1625_gpio_pm_ops),
},
.probe = rtd1625_gpio_probe,
};
module_platform_driver(rtd1625_gpio_platform_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Realtek Semiconductor Corporation");
MODULE_DESCRIPTION("Realtek DHC SoC RTD1625 gpio driver");
+1 -1
View File
@@ -139,7 +139,7 @@ static int sa1100_gpio_type(struct irq_data *d, unsigned int type)
if (type == IRQ_TYPE_PROBE) {
if ((sgc->irqrising | sgc->irqfalling) & mask)
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
type = IRQ_TYPE_EDGE_BOTH;
}
if (type & IRQ_TYPE_EDGE_RISING)
+1
View File
@@ -260,6 +260,7 @@ static const struct of_device_id sifive_gpio_match[] = {
{ .compatible = "sifive,fu540-c000-gpio" },
{ },
};
MODULE_DEVICE_TABLE(of, sifive_gpio_match);
static struct platform_driver sifive_gpio_driver = {
.probe = sifive_gpio_probe,
@@ -244,6 +244,7 @@ static int gpio_la_poll_probe(struct platform_device *pdev)
if (ret)
return ret;
/* Initially allocate a buffer. It currently is NULL */
fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
ret = devm_add_action_or_reset(dev, fops_buf_release, priv);
if (ret)
+1 -1
View File
@@ -158,7 +158,7 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
sd->gc->private = sd;
ct = sd->gc->chip_types;
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->regs.eoi = GPSTR;
ct->regs.mask = GPIO_INT;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
+1 -1
View File
@@ -149,7 +149,7 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int regoffset = offset / 8;
int mask = BIT(offset % 8);
if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
if (type & IRQ_TYPE_LEVEL_MASK)
return -EINVAL;
/* STMPE801 and STMPE 1600 don't have RE and FE registers */
+2 -3
View File
@@ -51,7 +51,7 @@ static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
return ioread32(gpio->base + offs);
}
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
@@ -167,9 +167,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
tb10x_gpio->domain = irq_domain_create_linear(dev_fwnode(dev),
tb10x_gpio->chip.gc.ngpio,
&irq_generic_chip_ops, NULL);
if (!tb10x_gpio->domain) {
if (!tb10x_gpio->domain)
return -ENOMEM;
}
ret = irq_alloc_domain_generic_chips(tb10x_gpio->domain,
tb10x_gpio->chip.gc.ngpio, 1, tb10x_gpio->chip.gc.label,
+2 -2
View File
@@ -342,9 +342,9 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return ret;
}
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
else if (type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(d, handle_edge_irq);
if (d->parent_data)
+1 -1
View File
@@ -148,7 +148,7 @@ static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
if (ver > 2)
bflr = ioread32(tgpio->membase + TGPIO_BFLR);
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
if (trigger & IRQ_TYPE_LEVEL_MASK) {
bflr &= ~(1 << offset);
flr &= ~(1 << offset);
if (trigger & IRQ_TYPE_LEVEL_HIGH)
+1
View File
@@ -83,6 +83,7 @@ static const struct of_device_id vf610_gpio_dt_ids[] = {
{ .compatible = "fsl,imx8ulp-gpio", .data = &imx8ulp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vf610_gpio_dt_ids);
static inline void vf610_gpio_writel(u32 val, void __iomem *reg)
{
+1 -8
View File
@@ -2682,15 +2682,8 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
file->private_data = cdev;
cdev->fp = file;
ret = nonseekable_open(inode, file);
if (ret)
goto out_unregister_device_notifier;
return nonseekable_open(inode, file);
return ret;
out_unregister_device_notifier:
blocking_notifier_chain_unregister(&gdev->device_notifier,
&cdev->device_unregistered_nb);
out_unregister_line_notifier:
scoped_guard(write_lock_irqsave, &gdev->line_state_lock)
raw_notifier_chain_unregister(&gdev->line_state_notifier,
+546 -25
View File
@@ -3,6 +3,8 @@
* Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
*/
#include <linux/cleanup.h>
#include <linux/err.h>
#include <linux/fwnode.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
@@ -11,26 +13,78 @@
#include <linux/notifier.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/types.h>
#include <kunit/fwnode.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_PROBE_ORDER_TEST_CONSUMER "gpio-probe-order-test-consumer"
#define GPIO_PROBE_DEFER_TEST_CONSUMER "gpio-probe-defer-test-consumer"
#define GPIO_UNBIND_TEST_CONSUMER "gpio-unbind-test-consumer"
#define GPIO_CONSUMER_NAME "gpio-swnode-consumer-test-device"
#define GPIO_TEST_PROVIDER_NGPIO 4
/*
* The test provider tracks per-line direction and value so that lines can be
* driven as both inputs and outputs - this is needed to exercise input as well
* as output GPIO hogs.
*/
struct gpio_test_provider_data {
DECLARE_BITMAP(is_output, GPIO_TEST_PROVIDER_NGPIO);
DECLARE_BITMAP(values, GPIO_TEST_PROVIDER_NGPIO);
};
static int gpio_test_provider_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
struct gpio_test_provider_data *data = gpiochip_get_data(gc);
return test_bit(offset, data->is_output) ?
GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
}
static int gpio_test_provider_direction_input(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_test_provider_data *data = gpiochip_get_data(gc);
clear_bit(offset, data->is_output);
return 0;
}
static int gpio_test_provider_direction_output(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct gpio_test_provider_data *data = gpiochip_get_data(gc);
set_bit(offset, data->is_output);
__assign_bit(offset, data->values, value);
return 0;
}
static int gpio_test_provider_get(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_test_provider_data *data = gpiochip_get_data(gc);
return test_bit(offset, data->values);
}
static int gpio_test_provider_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct gpio_test_provider_data *data = gpiochip_get_data(gc);
__assign_bit(offset, data->values, value);
return 0;
}
static int gpio_test_provider_probe(struct platform_device *pdev)
{
struct gpio_test_provider_data *data;
struct device *dev = &pdev->dev;
struct gpio_chip *gc;
@@ -38,16 +92,26 @@ static int gpio_test_provider_probe(struct platform_device *pdev)
if (!gc)
return -ENOMEM;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
/* Lines start as outputs to preserve the default for lookup tests. */
bitmap_fill(data->is_output, GPIO_TEST_PROVIDER_NGPIO);
gc->base = -1;
gc->ngpio = 4;
gc->label = "gpio-swnode-consumer-test-device";
gc->ngpio = GPIO_TEST_PROVIDER_NGPIO;
gc->label = GPIO_CONSUMER_NAME;
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->get_direction = gpio_test_provider_get_direction;
gc->direction_input = gpio_test_provider_direction_input;
gc->direction_output = gpio_test_provider_direction_output;
gc->get = gpio_test_provider_get;
gc->set = gpio_test_provider_set;
return devm_gpiochip_add_data(dev, gc, NULL);
return devm_gpiochip_add_data(dev, gc, data);
}
static struct platform_driver gpio_test_provider_driver = {
@@ -63,10 +127,12 @@ static const struct software_node gpio_test_provider_swnode = {
struct gpio_swnode_consumer_pdata {
bool gpio_ok;
int errno;
};
static const struct gpio_swnode_consumer_pdata gpio_swnode_pdata_template = {
.gpio_ok = false,
.errno = 0,
};
static int gpio_swnode_consumer_probe(struct platform_device *pdev)
@@ -76,8 +142,10 @@ static int gpio_swnode_consumer_probe(struct platform_device *pdev)
struct gpio_desc *desc;
desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
if (IS_ERR(desc))
if (IS_ERR(desc)) {
pdata->errno = PTR_ERR(desc);
return PTR_ERR(desc);
}
pdata->gpio_ok = true;
@@ -91,13 +159,8 @@ static struct platform_driver gpio_swnode_consumer_driver = {
},
};
static void gpio_swnode_lookup_by_primary(struct kunit *test)
static int gpio_swnode_register_drivers(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);
@@ -106,6 +169,17 @@ static void gpio_swnode_lookup_by_primary(struct kunit *test)
ret = kunit_platform_driver_register(test, &gpio_swnode_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
return 0;
}
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;
pdevinfo = (struct platform_device_info){
.name = GPIO_TEST_PROVIDER,
.id = PLATFORM_DEVID_NONE,
@@ -149,7 +223,6 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test)
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
@@ -158,12 +231,6 @@ static void gpio_swnode_lookup_by_secondary(struct kunit *test)
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){
@@ -211,6 +278,266 @@ static struct kunit_case gpio_swnode_lookup_tests[] = {
static struct kunit_suite gpio_swnode_lookup_test_suite = {
.name = "gpio-swnode-lookup",
.test_cases = gpio_swnode_lookup_tests,
.init = gpio_swnode_register_drivers,
};
static void gpio_swnode_unregister_swnode(void *data)
{
software_node_unregister(data);
}
struct gpio_probe_order_pdata {
unsigned int probe_count;
bool gpio_ok;
};
static const struct gpio_probe_order_pdata gpio_probe_order_pdata_template = {
.probe_count = 0,
.gpio_ok = false,
};
static int gpio_probe_order_consumer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_probe_order_pdata *pdata = dev_get_platdata(dev);
struct gpio_desc *desc;
pdata->probe_count++;
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_probe_order_consumer_driver = {
.probe = gpio_probe_order_consumer_probe,
.driver = {
.name = GPIO_PROBE_ORDER_TEST_CONSUMER,
},
};
/*
* Verify that fw_devlink orders the probe of a GPIO consumer after its
* provider. The consumer references the provider through a software node and
* is registered first. fw_devlink must defer it before its driver's probe()
* is ever entered, so the consumer probes exactly once - only after the
* provider is added and bound.
*/
static void gpio_swnode_probe_order(struct kunit *test)
{
struct property_entry properties[2] = { };
struct gpio_probe_order_pdata *pdata;
struct platform_device_info pdevinfo;
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_probe_order_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = software_node_register(&gpio_test_provider_swnode);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = kunit_add_action_or_reset(test, gpio_swnode_unregister_swnode,
(void *)&gpio_test_provider_swnode);
KUNIT_ASSERT_EQ(test, ret, 0);
properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
&gpio_test_provider_swnode,
0, GPIO_ACTIVE_HIGH);
pdevinfo = (struct platform_device_info){
.name = GPIO_PROBE_ORDER_TEST_CONSUMER,
.id = PLATFORM_DEVID_NONE,
.data = &gpio_probe_order_pdata_template,
.size_data = sizeof(gpio_probe_order_pdata_template),
.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_FALSE(test, bound);
pdata = dev_get_platdata(&cons->dev);
KUNIT_ASSERT_EQ(test, pdata->probe_count, 0);
KUNIT_ASSERT_FALSE(test, pdata->gpio_ok);
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);
wait_for_device_probe();
scoped_guard(device, &prvd->dev)
bound = device_is_bound(&prvd->dev);
KUNIT_ASSERT_TRUE(test, bound);
scoped_guard(device, &cons->dev)
bound = device_is_bound(&cons->dev);
KUNIT_ASSERT_TRUE(test, bound);
pdata = dev_get_platdata(&cons->dev);
KUNIT_ASSERT_EQ(test, pdata->probe_count, 1);
KUNIT_ASSERT_TRUE(test, pdata->gpio_ok);
}
struct gpio_probe_defer_pdata {
unsigned int probe_count;
int gpio_err;
};
static const struct gpio_probe_defer_pdata gpio_probe_defer_pdata_template = {
.probe_count = 0,
.gpio_err = 0,
};
static int gpio_probe_defer_consumer_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_probe_defer_pdata *pdata = dev_get_platdata(dev);
struct gpio_desc *desc;
pdata->probe_count++;
desc = devm_gpiod_get(dev, "foo", GPIOD_OUT_HIGH);
if (IS_ERR(desc)) {
pdata->gpio_err = PTR_ERR(desc);
return pdata->gpio_err;
}
pdata->gpio_err = 0;
return 0;
}
static struct platform_driver gpio_probe_defer_consumer_driver = {
.probe = gpio_probe_defer_consumer_probe,
.driver = {
.name = GPIO_PROBE_DEFER_TEST_CONSUMER,
},
};
/*
* Verify that a GPIO consumer referencing a provider whose software node is
* not registered yet, defers its probe instead of failing.
*
* The provider software node is deliberately left unregistered when the
* consumer is added. fw_devlink cannot resolve the reference, so it creates no
* supplier link and does not order the consumer - the consumer's probe() runs
* and reaches devm_gpiod_get(). The swnode GPIO lookup returns -ENOTCONN for a
* reference to an unregistered node, which gpiolib maps to -EPROBE_DEFER. Once
* the provider software node and device appear, the deferred consumer probes
* again and binds.
*/
static void gpio_swnode_probe_defer_on_unregistered(struct kunit *test)
{
struct property_entry properties[2] = { };
struct gpio_probe_defer_pdata *pdata;
struct platform_device_info pdevinfo;
struct platform_device *prvd, *cons;
struct fwnode_handle *fwnode;
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_probe_defer_consumer_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
&gpio_test_provider_swnode,
0, GPIO_ACTIVE_HIGH);
pdevinfo = (struct platform_device_info){
.name = GPIO_PROBE_DEFER_TEST_CONSUMER,
.id = PLATFORM_DEVID_NONE,
.data = &gpio_probe_defer_pdata_template,
.size_data = sizeof(gpio_probe_defer_pdata_template),
.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_FALSE(test, bound);
pdata = dev_get_platdata(&cons->dev);
KUNIT_ASSERT_GT(test, pdata->probe_count, 0);
KUNIT_ASSERT_EQ(test, pdata->gpio_err, -EPROBE_DEFER);
fwnode = kunit_software_node_register(test, &gpio_test_provider_swnode);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
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);
wait_for_device_probe();
scoped_guard(device, &prvd->dev)
bound = device_is_bound(&prvd->dev);
KUNIT_ASSERT_TRUE(test, bound);
scoped_guard(device, &cons->dev)
bound = device_is_bound(&cons->dev);
KUNIT_ASSERT_TRUE(test, bound);
pdata = dev_get_platdata(&cons->dev);
KUNIT_ASSERT_EQ(test, pdata->gpio_err, 0);
/* Tear down the consumer before the provider to free the GPIO. */
kunit_platform_device_unregister(test, cons);
}
static int gpio_swnode_probe_order_test_init(struct kunit *test)
{
/*
* A prior test may have left a managed device link teardown queued on
* the device_link_mq. Flush it so that software_node_register()
* doesn't spuriously see the node as registered and fail with -EEXIST.
*/
device_link_wait_removal();
return 0;
}
static struct kunit_case gpio_swnode_probe_order_tests[] = {
KUNIT_CASE(gpio_swnode_probe_order),
KUNIT_CASE(gpio_swnode_probe_defer_on_unregistered),
{ }
};
static struct kunit_suite gpio_swnode_probe_order_test_suite = {
.name = "gpio-swnode-probe-order",
.test_cases = gpio_swnode_probe_order_tests,
.init = gpio_swnode_probe_order_test_init,
};
static BLOCKING_NOTIFIER_HEAD(gpio_unbind_notifier);
@@ -310,15 +637,24 @@ static void gpio_unbind_with_consumers(struct kunit *test)
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);
/*
* This test deliberately keeps the consumer bound while the provider
* is unregistered. fw_devlink would force-unbind the consumer before
* the provider so use the FWNODE_FLAG_LINKS_ADDED flag to opt out of
* it as a workaround.
*/
cons = kunit_platform_device_alloc(test, GPIO_UNBIND_TEST_CONSUMER,
PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons);
ret = device_create_managed_software_node(&cons->dev, properties, NULL);
KUNIT_ASSERT_EQ(test, ret, 0);
fwnode_set_flag(dev_fwnode(&cons->dev), FWNODE_FLAG_LINKS_ADDED);
ret = kunit_platform_device_add(test, cons);
KUNIT_ASSERT_EQ(test, ret, 0);
wait_for_device_probe();
scoped_guard(device, &cons->dev)
bound = device_is_bound(&cons->dev);
@@ -346,11 +682,196 @@ static struct kunit_case gpio_unbind_with_consumers_tests[] = {
static struct kunit_suite gpio_unbind_with_consumers_test_suite = {
.name = "gpio-unbind-with-consumers",
.test_cases = gpio_unbind_with_consumers_tests,
/* We need this here too to clean any left over links. */
.init = gpio_swnode_probe_order_test_init,
};
/*
* GPIO line hogs are described by child software nodes of the provider
* carrying the "gpio-hog" property. They are picked up automatically when the
* gpiochip is registered. Each hog below sits on a distinct line of the
* provider.
*/
#define GPIO_HOG_OUTPUT_HIGH_OFFSET 0
#define GPIO_HOG_OUTPUT_LOW_OFFSET 1
#define GPIO_HOG_INPUT_OFFSET 2
static const u32 gpio_hog_output_high_gpios[] = {
GPIO_HOG_OUTPUT_HIGH_OFFSET, GPIO_ACTIVE_HIGH,
};
static const struct property_entry gpio_hog_output_high_properties[] = {
PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_output_high_gpios),
PROPERTY_ENTRY_STRING("line-name", "hog-output-high"),
PROPERTY_ENTRY_BOOL("output-high"),
PROPERTY_ENTRY_BOOL("gpio-hog"),
{ }
};
static const struct software_node gpio_hog_output_high_swnode =
SOFTWARE_NODE("hog-output-high", gpio_hog_output_high_properties,
&gpio_test_provider_swnode);
static const u32 gpio_hog_output_low_gpios[] = {
GPIO_HOG_OUTPUT_LOW_OFFSET, GPIO_ACTIVE_HIGH,
};
static const struct property_entry gpio_hog_output_low_properties[] = {
PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_output_low_gpios),
PROPERTY_ENTRY_STRING("line-name", "hog-output-low"),
PROPERTY_ENTRY_BOOL("output-low"),
PROPERTY_ENTRY_BOOL("gpio-hog"),
{ }
};
static const struct software_node gpio_hog_output_low_swnode =
SOFTWARE_NODE("hog-output-low", gpio_hog_output_low_properties,
&gpio_test_provider_swnode);
static const u32 gpio_hog_input_gpios[] = {
GPIO_HOG_INPUT_OFFSET, GPIO_ACTIVE_HIGH,
};
static const struct property_entry gpio_hog_input_properties[] = {
PROPERTY_ENTRY_U32_ARRAY("gpios", gpio_hog_input_gpios),
PROPERTY_ENTRY_STRING("line-name", "hog-input"),
PROPERTY_ENTRY_BOOL("input"),
PROPERTY_ENTRY_BOOL("gpio-hog"),
{ }
};
static const struct software_node gpio_hog_input_swnode =
SOFTWARE_NODE("hog-input", gpio_hog_input_properties,
&gpio_test_provider_swnode);
static const struct software_node *const gpio_hog_swnodes[] = {
&gpio_test_provider_swnode,
&gpio_hog_output_high_swnode,
&gpio_hog_output_low_swnode,
&gpio_hog_input_swnode,
NULL
};
/*
* Bring up the provider with a single hog child registered and verify both
* that the line was configured with the expected direction and that it is now
* exclusively owned (a consumer asking for the same line fails to bind).
*
* The provider node is referenced by the device through its fwnode rather than
* being handed to .swnode, so the device takes no software node reference of
* its own. Both the provider and the hog child are therefore test-managed and
* torn down (child first) once the test case completes.
*/
static void gpio_hog_assert(struct kunit *test, unsigned int offset,
int expected_direction)
{
struct gpio_swnode_consumer_pdata *pdata;
struct platform_device_info pdevinfo;
struct property_entry properties[2];
struct platform_device *pdev;
struct fwnode_handle *fwnode;
struct gpio_desc *desc;
bool bound = true;
int ret;
fwnode = software_node_fwnode(&gpio_test_provider_swnode);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode);
pdevinfo = (struct platform_device_info){
.name = GPIO_TEST_PROVIDER,
.id = PLATFORM_DEVID_NONE,
.fwnode = fwnode,
};
pdev = kunit_platform_device_register_full(test, &pdevinfo);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
wait_for_device_probe();
/* The hog must have configured the line with the expected direction. */
struct gpio_device *gdev __free(gpio_device_put) =
gpio_device_find_by_label(GPIO_CONSUMER_NAME);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gdev);
desc = gpio_device_get_desc(gdev, offset);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, desc);
ret = gpiod_get_direction(desc);
KUNIT_ASSERT_EQ(test, ret, expected_direction);
/* A hogged line is owned exclusively, so a consumer must fail to bind. */
properties[0] = PROPERTY_ENTRY_GPIO("foo-gpios",
&gpio_test_provider_swnode,
offset, 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_FALSE(test, bound);
pdata = dev_get_platdata(&pdev->dev);
KUNIT_ASSERT_FALSE(test, pdata->gpio_ok);
KUNIT_ASSERT_EQ(test, pdata->errno, -EBUSY);
}
static void gpio_hog_output_high(struct kunit *test)
{
gpio_hog_assert(test, GPIO_HOG_OUTPUT_HIGH_OFFSET, GPIO_LINE_DIRECTION_OUT);
}
static void gpio_hog_output_low(struct kunit *test)
{
gpio_hog_assert(test, GPIO_HOG_OUTPUT_LOW_OFFSET, GPIO_LINE_DIRECTION_OUT);
}
static void gpio_hog_input(struct kunit *test)
{
gpio_hog_assert(test, GPIO_HOG_INPUT_OFFSET, GPIO_LINE_DIRECTION_IN);
}
static int gpio_hog_suite_init(struct kunit_suite *suite)
{
return software_node_register_node_group(gpio_hog_swnodes);
}
static void gpio_hog_suite_exit(struct kunit_suite *suite)
{
software_node_unregister_node_group(gpio_hog_swnodes);
}
static struct kunit_case gpio_swnode_hog_tests[] = {
KUNIT_CASE(gpio_hog_output_high),
KUNIT_CASE(gpio_hog_output_low),
KUNIT_CASE(gpio_hog_input),
{ }
};
static struct kunit_suite gpio_swnode_hog_test_suite = {
.name = "gpio-swnode-hog",
.test_cases = gpio_swnode_hog_tests,
.suite_init = gpio_hog_suite_init,
.suite_exit = gpio_hog_suite_exit,
.init = gpio_swnode_register_drivers,
};
kunit_test_suites(
&gpio_swnode_lookup_test_suite,
&gpio_swnode_probe_order_test_suite,
&gpio_unbind_with_consumers_test_suite,
&gpio_swnode_hog_test_suite,
);
MODULE_DESCRIPTION("Test module for the GPIO subsystem");
+1 -22
View File
@@ -26,7 +26,6 @@
static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
{
const struct software_node *gdev_node;
struct gpio_device *gdev;
gdev_node = to_software_node(fwnode);
if (!gdev_node)
@@ -41,27 +40,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
return ERR_PTR(-ENOENT);
fwnode_lookup:
gdev = gpio_device_find_by_fwnode(fwnode);
if (!gdev && gdev_node && gdev_node->name)
/*
* FIXME: We shouldn't need to compare the GPIO controller's
* label against the software node that is supposedly attached
* to it. However there are currently GPIO users that - knowing
* the expected label of the GPIO chip whose pins they want to
* control - set up dummy software nodes named after those GPIO
* controllers, which aren't actually attached to them. In this
* case gpio_device_find_by_fwnode() will fail as no device on
* the GPIO bus is actually associated with the fwnode we're
* looking for.
*
* As a fallback: continue checking the label if we have no
* match. However, the situation described above is an abuse
* of the software node API and should be phased out and the
* following line - eventually removed.
*/
gdev = gpio_device_find_by_label(gdev_node->name);
return gdev ?: ERR_PTR(-EPROBE_DEFER);
return gpio_device_find_by_fwnode(fwnode) ?: ERR_PTR(-EPROBE_DEFER);
}
static int swnode_gpio_get_reference(const struct fwnode_handle *fwnode,
+4 -4
View File
@@ -560,8 +560,8 @@ out:
return ret;
}
static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
static int ad7173_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
*mask = AD7173_GPO_DATA(offset);
@@ -569,8 +569,8 @@ static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
return 0;
}
static int ad4111_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
static int ad4111_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_operation op,
unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
*mask = AD4111_GPO01_DATA(offset);
+3 -3
View File
@@ -349,9 +349,9 @@ static const struct iio_chan_spec stx104_channels_diff[] = {
STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
};
static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base,
unsigned int offset, unsigned int *const reg,
unsigned int *const mask)
static int stx104_reg_mask_xlate(struct gpio_regmap *const gpio, enum gpio_regmap_operation op,
const unsigned int base, unsigned int offset,
unsigned int *const reg, unsigned int *const mask)
{
/* Output lines are located at same register bit offsets as input lines */
if (offset >= 4)
+4 -3
View File
@@ -23,8 +23,8 @@
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/legacy.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
@@ -528,6 +528,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
*/
bdata->gpiod = NULL;
}
#ifdef CONFIG_GPIOLIB_LEGACY
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number, so request the GPIO here and
@@ -546,6 +547,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
gpiod_toggle_active_low(bdata->gpiod);
#endif
}
if (bdata->gpiod) {
@@ -583,8 +585,7 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
if (irq < 0) {
error = irq;
dev_err_probe(dev, error,
"Unable to get irq number for GPIO %d\n",
button->gpio);
"Unable to get irq number for GPIO\n");
return error;
}
bdata->irq = irq;
+3 -1
View File
@@ -18,8 +18,8 @@
#include <linux/input.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/legacy.h>
#include <linux/gpio_keys.h>
#include <linux/property.h>
@@ -301,6 +301,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(bdata->gpiod),
"failed to get gpio\n");
}
#ifdef CONFIG_GPIOLIB_LEGACY
} else if (gpio_is_valid(button->gpio)) {
/*
* Legacy GPIO number so request the GPIO here and
@@ -323,6 +324,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
if (button->active_low ^ gpiod_is_active_low(bdata->gpiod))
gpiod_toggle_active_low(bdata->gpiod);
#endif
}
bdata->last_state = -1;
+3
View File
@@ -904,6 +904,9 @@ config INPUT_IDEAPAD_SLIDEBAR
config INPUT_SOC_BUTTON_ARRAY
tristate "Windows-compatible SoC Button Array"
depends on KEYBOARD_GPIO && ACPI
depends on X86
depends on GPIOLIB
select GPIOLIB_LEGACY
help
Say Y here if you have a SoC-based tablet that originally runs
Windows 8 or a Microsoft Surface Book 2, Pro 5, Laptop 1 or later.
-1
View File
@@ -15,7 +15,6 @@
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
static bool use_low_level_irq;
+1 -1
View File
@@ -13,7 +13,7 @@
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/mfd/core.h>
#include <linux/mfd/aat2870.h>
#include <linux/regulator/machine.h>
+1 -1
View File
@@ -8,7 +8,7 @@
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
+1 -1
View File
@@ -28,7 +28,7 @@
*/
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/lp3943.h>
+1 -1
View File
@@ -35,7 +35,7 @@ struct sm501_device {
struct sm501_gpio;
#ifdef CONFIG_MFD_SM501_GPIO
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
struct sm501_gpio_chip {
struct gpio_chip gpio;
+1 -1
View File
@@ -16,7 +16,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/err.h>
+1 -1
View File
@@ -14,7 +14,7 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/tps65910.h>
#define COMP1 0
+1 -1
View File
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
+1 -1
View File
@@ -14,7 +14,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_io.h>
+1 -1
View File
@@ -23,7 +23,7 @@
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/module.h>
+1 -1
View File
@@ -11,7 +11,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <mach/hardware.h>
#include <asm/irq.h>
+1 -1
View File
@@ -32,8 +32,8 @@
#include <linux/cpufreq.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/legacy.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+1 -1
View File
@@ -7,7 +7,7 @@
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/legacy.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
@@ -3,7 +3,6 @@
#include <linux/delay.h>
#include <linux/extcon-provider.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+1 -1
View File
@@ -11,7 +11,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mutex.h>
-1
View File
@@ -11,7 +11,6 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
+1
View File
@@ -19,6 +19,7 @@
#define BCM63XX_DATA_REG 0x0c
static int bcm63xx_reg_mask_xlate(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
+1
View File
@@ -346,6 +346,7 @@ static struct tps6594_pinctrl tps6594_template_pinctrl = {
};
static int tps6594_gpio_regmap_xlate(struct gpio_regmap *gpio,
enum gpio_regmap_operation op,
unsigned int base, unsigned int offset,
unsigned int *reg, unsigned int *mask)
{
+29
View File
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* KUnit resource management helpers for firmware nodes.
*
* Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries
*/
#ifndef _KUNIT_FWNODE_H
#define _KUNIT_FWNODE_H
struct device;
struct fwnode_handle;
struct kunit;
struct property_entry;
struct software_node;
struct fwnode_handle *
kunit_fwnode_create_software_node(struct kunit *test,
const struct property_entry *properties,
const struct fwnode_handle *parent);
struct fwnode_handle *
kunit_software_node_register(struct kunit *test,
const struct software_node *node);
int kunit_software_node_register_node_group(struct kunit *test,
const struct software_node *const *nodes);
int kunit_device_add_software_node(struct kunit *test, struct device *dev,
const struct software_node *node);
#endif /* _KUNIT_FWNODE_H */

Some files were not shown because too many files have changed in this diff Show More