mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Merge tag 'gpio-updates-for-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski:
"For this merge window we have two new drivers: support for
GPIO-signalled ACPI events on Intel platforms and a generic
GPIO-over-pinctrl driver using the ARM SCMI protocol for
controlling pins.
Several things have been reworked in GPIO core: we unduplicated GPIO
hog handling, reduced the number of SRCU locks and dereferences,
improved support for software-node-based lookup and removed more
legacy code after converting remaining users to modern alternatives.
There's also a number of driver reworks and refactoring, documentation
updates, some bug-fixes and new tests.
GPIO core:
- defer probe on software node lookups when the remote software node
exists but has not been registered as a firmware node yet
- unify GPIO hog handling by moving code duplicated in OF and ACPI
modules into GPIO core and allow setting up hogs with software
nodes
- allow matching GPIO controllers by secondary firmware node if
matching by primary does not succeed
- demote deferral warnings to debug level as they are quite normal
when using software nodes which don't support fw_devlink yet
- disable the legacy GPIO character device uAPI v1 supprt in Kconfig
by default
- rework several core functions in preparation for the upcoming
Revocable helper library for protecting resources against sudden
removal, this reduces the number of SRCU dereferences in GPIO core
- simplify file descriptor logic in GPIO character device code by
using FD_PREPARE()
- introduce a header defining symbols used by both GPIO consumers and
providers to avoid having to include provider-specific headers from
drivers which only consume GPIOs
- replace snprintf() with strscpy() where formatting is not required
New drivers:
- add the gpio-by-pinctrl generic driver using the ARM SCMI protocol
to control GPIOs (along with SCMI changes pulled from the pinctrl
tree)
- add a driver providing support for handling of platform events via
GPIO-signalled ACPI events (used on Intel Nova Lake and later
platforms)
Driver changes:
- extend the gpio-kempld driver with support for more recent models,
interrupts and setting/getting multiple values at once
- improve interrupt handling in gpio-brcmstb
- add support for multi-SoC systems in gpio-tegra186
- make sure we return correct values from the .get() callbacks in
several GPIO drivers by normalizing any values other than 0, 1 or
negative error numbers
- use flexible arrays in several drivers to reduce the number of
required memory allocations
- simplify synchronous waiting for virtual drivers to probe and
remove the dedicated, a bit overengineered helper library
dev-sync-probe
- remove unneeded Kconfig dependencies on OF_GPIO in several drivers
and subsystems
- convert the two remaining users of of_get_named_gpio() to using
GPIO descriptors and remove the (no longer used) function along
with the header that declares it
- add missing includes in gpio-mmio
- shrink and simplify code in gpio-max732x by using guard(mutex)
- remove duplicated code handling the 'ngpios' property from
gpio-ts4800, it's already handled in GPIO core
- use correct variable type in gpio-aspeed
- add support for a new model in gpio-realtek-otto
- allow to specify the active-low setting of simulated hogs over the
configfs interface (in addition to existing devicetree support) in
gpio-sim
Bug fixes:
- clear the OF_POPULATED flag on hog nodes in GPIO chip remove path
on OF systems
- fix resource leaks in error path in gpiochip_add_data_with_key()
- drop redundant device reference in gpio-mpsse
Tests:
- add selftests for use-after-free cases in GPIO character device
code
DT bindings:
- add a DT binding document for SCMI based, gpio-over-pinctrl devices
- fix interrupt description in microchip,mpfs-gpio
- add new compatible for gpio-realtek-otto
- describe the resets of the mpfs-gpio controller
- fix maintainer's email in gpio-delay bindings
- remove the binding document for cavium,thunder-8890 as the
corresponding device is bound over PCI and not firmware nodes
Documentation:
- update the recommended way of converting legacy boards to using
software nodes for GPIO description
- describe GPIO line value semantics
- misc updates to kerneldocs
Misc:
- convert OMAP1 ams-delta board to using GPIO hogs described with
software nodes"
* tag 'gpio-updates-for-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (79 commits)
gpio: swnode: defer probe on references to unregistered software nodes
dt-bindings: gpio: cavium,thunder-8890: Remove DT binding
Documentation: gpio: update the preferred method for using software node lookup
gpio: gpio-by-pinctrl: s/used to do/is used to do/
gpio: aspeed: fix unsigned long int declaration
gpio: rockchip: convert to dynamic GPIO base allocation
gpio: remove dev-sync-probe
gpio: virtuser: stop using dev-sync-probe
gpio: aggregator: stop using dev-sync-probe
gpio: sim: stop using dev-sync-probe
gpio: Add Intel Nova Lake ACPI GPIO events driver
gpiolib: Make deferral warnings debug messages
gpiolib: fix hogs with multiple lines
gpio: fix up CONFIG_OF dependencies
gpio: gpio-by-pinctrl: add pinctrl based generic GPIO driver
gpio: dt-bindings: Add GPIO on top of generic pin control
firmware: arm_scmi: Allow PINCTRL_REQUEST to return EOPNOTSUPP
pinctrl: scmi: ignore PIN_CONFIG_PERSIST_STATE
pinctrl: scmi: Delete PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS support
pinctrl: scmi: Add SCMI_PIN_INPUT_VALUE
...
This commit is contained in:
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: GPIO delay controller
|
||||
|
||||
maintainers:
|
||||
- Alexander Stein <linux@ew.tq-group.com>
|
||||
- Alexander Stein <alexander.stein@ew.tq-group.com>
|
||||
|
||||
description: |
|
||||
This binding describes an electrical setup where setting an GPIO output
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
Cavium ThunderX/OCTEON-TX GPIO controller bindings
|
||||
|
||||
Required Properties:
|
||||
- reg: The controller bus address.
|
||||
- gpio-controller: Marks the device node as a GPIO controller.
|
||||
- #gpio-cells: Must be 2.
|
||||
- First cell is the GPIO pin number relative to the controller.
|
||||
- Second cell is a standard generic flag bitfield as described in gpio.txt.
|
||||
|
||||
Optional Properties:
|
||||
- compatible: "cavium,thunder-8890-gpio", unused as PCI driver binding is used.
|
||||
- interrupt-controller: Marks the device node as an interrupt controller.
|
||||
- #interrupt-cells: Must be present and have value of 2 if
|
||||
"interrupt-controller" is present.
|
||||
- First cell is the GPIO pin number relative to the controller.
|
||||
- Second cell is triggering flags as defined in interrupts.txt.
|
||||
|
||||
Example:
|
||||
|
||||
gpio_6_0: gpio@6,0 {
|
||||
compatible = "cavium,thunder-8890-gpio";
|
||||
reg = <0x3000 0 0 0 0>; /* DEVFN = 0x30 (6:0) */
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
};
|
||||
@@ -33,6 +33,9 @@ properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
@@ -62,6 +65,11 @@ allOf:
|
||||
contains:
|
||||
const: microchip,mpfs-gpio
|
||||
then:
|
||||
properties:
|
||||
ngpios:
|
||||
enum: [14, 24, 32]
|
||||
interrupts:
|
||||
minItems: 14
|
||||
required:
|
||||
- interrupts
|
||||
- "#interrupt-cells"
|
||||
@@ -82,18 +90,19 @@ examples:
|
||||
compatible = "microchip,mpfs-gpio";
|
||||
reg = <0x20122000 0x1000>;
|
||||
clocks = <&clkcfg 25>;
|
||||
interrupt-parent = <&plic>;
|
||||
interrupt-parent = <&irqmux>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <32>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>,
|
||||
<53>, <53>, <53>, <53>;
|
||||
interrupts = <64>, <65>, <66>, <67>,
|
||||
<68>, <69>, <70>, <71>,
|
||||
<72>, <73>, <74>, <75>,
|
||||
<76>, <77>, <78>, <79>,
|
||||
<80>, <81>, <82>, <83>,
|
||||
<84>, <85>, <86>, <87>,
|
||||
<88>, <89>, <90>, <91>,
|
||||
<92>, <93>, <94>, <95>;
|
||||
};
|
||||
...
|
||||
|
||||
59
Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml
Normal file
59
Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/gpio/pin-control-gpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Pin control based generic GPIO controller
|
||||
|
||||
description:
|
||||
The pin control-based GPIO will facilitate a pin controller's ability
|
||||
to drive electric lines high/low and other generic properties of a
|
||||
pin controller to perform general-purpose one-bit binary I/O.
|
||||
|
||||
maintainers:
|
||||
- Dan Carpenter <dan.carpenter@linaro.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: scmi-pinctrl-gpio
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
gpio-line-names: true
|
||||
|
||||
gpio-ranges: true
|
||||
|
||||
ngpios: true
|
||||
|
||||
patternProperties:
|
||||
"^.+-hog(-[0-9]+)?$":
|
||||
type: object
|
||||
|
||||
required:
|
||||
- gpio-hog
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- gpio-controller
|
||||
- "#gpio-cells"
|
||||
- gpio-ranges
|
||||
- ngpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
gpio {
|
||||
compatible = "scmi-pinctrl-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
ngpios = <4>;
|
||||
gpio-line-names = "gpio_5_17", "gpio_5_20", "gpio_5_22", "gpio_2_1";
|
||||
gpio-ranges = <&scmi_pinctrl 0 30 4>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&keys_pins>;
|
||||
};
|
||||
@@ -30,6 +30,7 @@ properties:
|
||||
- realtek,rtl8390-gpio
|
||||
- realtek,rtl9300-gpio
|
||||
- realtek,rtl9310-gpio
|
||||
- realtek,rtl9607-gpio
|
||||
- const: realtek,otto-gpio
|
||||
|
||||
reg: true
|
||||
|
||||
@@ -108,9 +108,8 @@ macro, which ties a software node representing the GPIO controller with
|
||||
consumer device. It allows consumers to use regular gpiolib APIs, such as
|
||||
gpiod_get(), gpiod_get_optional().
|
||||
|
||||
The software node representing a GPIO controller need not be attached to the
|
||||
GPIO controller device. The only requirement is that the node must be
|
||||
registered and its name must match the GPIO controller's label.
|
||||
The software node representing a GPIO controller must be attached to the
|
||||
GPIO controller device - either as the primary or the secondary firmware node.
|
||||
|
||||
For example, here is how to describe a single GPIO-connected LED. This is an
|
||||
alternative to using platform_data on legacy systems.
|
||||
@@ -122,8 +121,7 @@ alternative to using platform_data on legacy systems.
|
||||
#include <linux/gpio/property.h>
|
||||
|
||||
/*
|
||||
* 1. Define a node for the GPIO controller. Its .name must match the
|
||||
* controller's label.
|
||||
* 1. Define a node for the GPIO controller.
|
||||
*/
|
||||
static const struct software_node gpio_controller_node = {
|
||||
.name = "gpio-foo",
|
||||
@@ -153,6 +151,21 @@ alternative to using platform_data on legacy systems.
|
||||
};
|
||||
software_node_register_node_group(swnodes);
|
||||
|
||||
/*
|
||||
* 5. Attach the GPIO controller's software node to the device and
|
||||
* register it.
|
||||
*/
|
||||
static void gpio_foo_register(void)
|
||||
{
|
||||
struct platform_device_info pdev_info = {
|
||||
.name = "gpio-foo",
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
.swnode = &gpio_controller_node
|
||||
};
|
||||
|
||||
platform_device_register_full(&pdev_info);
|
||||
}
|
||||
|
||||
// Then register a platform_device for "leds-gpio" and associate
|
||||
// it with &led_device_swnode via .fwnode.
|
||||
|
||||
@@ -239,22 +252,6 @@ mapping and is thus transparent to GPIO consumers.
|
||||
A set of functions such as gpiod_set_value() is available to work with
|
||||
the new descriptor-oriented interface.
|
||||
|
||||
Boards using platform data can also hog GPIO lines by defining GPIO hog tables.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct gpiod_hog gpio_hog_table[] = {
|
||||
GPIO_HOG("gpio.0", 10, "foo", GPIO_ACTIVE_LOW, GPIOD_OUT_HIGH),
|
||||
{ }
|
||||
};
|
||||
|
||||
And the table can be added to the board code as follows::
|
||||
|
||||
gpiod_add_hogs(gpio_hog_table);
|
||||
|
||||
The line will be hogged as soon as the gpiochip is created or - in case the
|
||||
chip was created earlier - when the hog table is registered.
|
||||
|
||||
Arrays of pins
|
||||
--------------
|
||||
In addition to requesting pins belonging to a function one by one, a device may
|
||||
|
||||
@@ -87,6 +87,33 @@ atomic context on realtime kernels (inside hard IRQ handlers and similar
|
||||
contexts). Normally this should not be required.
|
||||
|
||||
|
||||
GPIO level semantics
|
||||
--------------------
|
||||
|
||||
The gpip_chip .get/set[_multiple]() line values are clamped to the boolean
|
||||
space [0, 1], low level or high level.
|
||||
|
||||
Low and high values are defined as physical low on the line in/out to the
|
||||
connector such as a physical pad, pin or rail.
|
||||
|
||||
The GPIO library has internal logic to handle lines that are active low, such
|
||||
as indicated by overstrike or #name in a schematic, and the driver should not
|
||||
try to second-guess the logic value of a line.
|
||||
|
||||
The way GPIO values are handled by the consumers is that the library present
|
||||
the *logical* value to the consumer. A line is *asserted* if its *logical*
|
||||
value is 1, and *de-asserted* if its logical value is 0. If inversion is
|
||||
required, this is handled by gpiolib and configured using hardware descriptions
|
||||
such as device tree or ACPI that can clearly indicate if a line is active
|
||||
high or low.
|
||||
|
||||
Since electronics commonly insert inverters as driving stages or protection
|
||||
buffers in front of a GPIO line it is necessary that this semantic is part
|
||||
of the hardware description, so that consumers such as kernel drivers need
|
||||
not worry about this, and can for example assert a RESET line tied to a GPIO
|
||||
pin by setting it to logic 1 even if it is physically active low.
|
||||
|
||||
|
||||
GPIO electrical configuration
|
||||
-----------------------------
|
||||
|
||||
|
||||
@@ -36,12 +36,10 @@ Requirements for GPIO Properties
|
||||
When using software nodes to describe GPIO connections, the following
|
||||
requirements must be met for the GPIO core to correctly resolve the reference:
|
||||
|
||||
1. **The GPIO controller's software node "name" must match the controller's
|
||||
"label".** The gpiolib core uses this name to find the corresponding
|
||||
struct gpio_chip at runtime.
|
||||
This software node has to be registered, but need not be attached to the
|
||||
device representing the GPIO controller that is providing the GPIO in
|
||||
question. It may be left as a "free floating" node.
|
||||
1. **The GPIO controller's software node must be registered and attached to
|
||||
the controller's ``struct device`` either as its primary or secondary
|
||||
firmware node.** The gpiolib core uses the address of the firmware node to
|
||||
find the corresponding ``struct gpio_chip`` at runtime.
|
||||
|
||||
2. **The GPIO property must be a reference.** The ``PROPERTY_ENTRY_GPIO()``
|
||||
macro handles this as it is an alias for ``PROPERTY_ENTRY_REF()``.
|
||||
@@ -121,13 +119,21 @@ A typical legacy board file might look like this:
|
||||
/* Device registration */
|
||||
static int __init myboard_init(void)
|
||||
{
|
||||
struct platform_device_info pdev_info = {
|
||||
.name = MYBOARD_GPIO_CONTROLLER,
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
.swnode = &gpio_controller_node
|
||||
};
|
||||
|
||||
gpiod_add_lookup_table(&myboard_leds_gpios);
|
||||
gpiod_add_lookup_table(&myboard_buttons_gpios);
|
||||
|
||||
platform_device_register_full(&pdev_info);
|
||||
platform_device_register_data(NULL, "leds-gpio", -1,
|
||||
&myboard_leds_pdata, sizeof(myboard_leds_pdata));
|
||||
platform_device_register_data(NULL, "gpio-keys", -1,
|
||||
&myboard_buttons_pdata, sizeof(myboard_buttons_pdata));
|
||||
&myboard_buttons_pdata,
|
||||
sizeof(myboard_buttons_pdata));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -141,8 +147,7 @@ Step 1: Define the GPIO Controller Node
|
||||
***************************************
|
||||
|
||||
First, define a software node that represents the GPIO controller that the
|
||||
LEDs and buttons are connected to. The ``name`` of this node must match the
|
||||
name of the driver for the GPIO controller (e.g., "gpio-foo").
|
||||
LEDs and buttons are connected to. The ``name`` of this node is optional.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -257,6 +262,16 @@ software nodes using the ``fwnode`` field in struct platform_device_info.
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
memset(&pdev_info, 0, sizeof(pdev_info));
|
||||
pdev_info.name = MYBOARD_GPIO_CONTROLLER;
|
||||
pdev_info.id = PLATFORM_DEVID_NONE;
|
||||
pdev_info.swnode = &myboard_gpio_controller_node;
|
||||
gpio_pdev = platform_device_register_full(&pdev_info);
|
||||
if (IS_ERR(gpio_pdev)) {
|
||||
error = PTR_ERR(gpio_pdev);
|
||||
goto err_unregister_nodes;
|
||||
}
|
||||
|
||||
memset(&pdev_info, 0, sizeof(pdev_info));
|
||||
pdev_info.name = "leds-gpio";
|
||||
pdev_info.id = PLATFORM_DEVID_NONE;
|
||||
@@ -264,6 +279,7 @@ software nodes using the ``fwnode`` field in struct platform_device_info.
|
||||
leds_pdev = platform_device_register_full(&pdev_info);
|
||||
if (IS_ERR(leds_pdev)) {
|
||||
error = PTR_ERR(leds_pdev);
|
||||
platform_device_unregister(gpio_pdev);
|
||||
goto err_unregister_nodes;
|
||||
}
|
||||
|
||||
@@ -274,6 +290,7 @@ software nodes using the ``fwnode`` field in struct platform_device_info.
|
||||
keys_pdev = platform_device_register_full(&pdev_info);
|
||||
if (IS_ERR(keys_pdev)) {
|
||||
error = PTR_ERR(keys_pdev);
|
||||
platform_device_unregister(gpio_pdev);
|
||||
platform_device_unregister(leds_pdev);
|
||||
goto err_unregister_nodes;
|
||||
}
|
||||
@@ -289,6 +306,7 @@ software nodes using the ``fwnode`` field in struct platform_device_info.
|
||||
{
|
||||
platform_device_unregister(keys_pdev);
|
||||
platform_device_unregister(leds_pdev);
|
||||
platform_device_unregister(gpio_pdev);
|
||||
software_node_unregister_node_group(myboard_swnodes);
|
||||
}
|
||||
|
||||
|
||||
@@ -10982,7 +10982,6 @@ F: drivers/gpio/
|
||||
F: include/dt-bindings/gpio/
|
||||
F: include/linux/gpio.h
|
||||
F: include/linux/gpio/
|
||||
F: include/linux/of_gpio.h
|
||||
K: (devm_)?gpio_(request|free|direction|get|set)
|
||||
K: GPIOD_FLAGS_BIT_NONEXCLUSIVE
|
||||
K: devm_gpiod_unhinge
|
||||
@@ -12872,6 +12871,13 @@ F: drivers/gpio/gpio-sodaville.c
|
||||
F: drivers/gpio/gpio-tangier.c
|
||||
F: drivers/gpio/gpio-tangier.h
|
||||
|
||||
INTEL GPIO GPE DRIVER
|
||||
M: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
|
||||
R: Mika Westerberg <westeri@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/gpio/gpio-novalake-events.c
|
||||
|
||||
INTEL GVT-g DRIVERS (Intel GPU Virtualization)
|
||||
R: Zhenyu Wang <zhenyuw.linux@gmail.com>
|
||||
R: Zhi Wang <zhi.wang.linux@gmail.com>
|
||||
|
||||
@@ -7,7 +7,6 @@ menuconfig ARC_PLAT_AXS10X
|
||||
bool "Synopsys ARC AXS10x Software Development Platforms"
|
||||
select DW_APB_ICTL
|
||||
select GPIO_DWAPB
|
||||
select OF_GPIO
|
||||
select HAVE_PCI
|
||||
select GENERIC_IRQ_CHIP
|
||||
select GPIOLIB
|
||||
|
||||
@@ -556,10 +556,30 @@ static struct gpiod_lookup_table *ams_delta_gpio_tables[] __initdata = {
|
||||
&ams_delta_nand_gpio_table,
|
||||
};
|
||||
|
||||
static struct gpiod_hog ams_delta_gpio_hogs[] = {
|
||||
GPIO_HOG(LATCH2_LABEL, LATCH2_PIN_KEYBRD_DATAOUT, "keybrd_dataout",
|
||||
GPIO_ACTIVE_HIGH, GPIOD_OUT_LOW),
|
||||
{},
|
||||
static const struct software_node latch2_gpio_swnode = {
|
||||
.name = LATCH2_LABEL,
|
||||
};
|
||||
|
||||
static const u32 latch2_hog_gpios[] = { LATCH2_PIN_KEYBRD_DATAOUT, 0 };
|
||||
|
||||
static const struct property_entry latch2_gpio_hog_props[] = {
|
||||
PROPERTY_ENTRY_BOOL("gpio-hog"),
|
||||
PROPERTY_ENTRY_U32_ARRAY("gpios", latch2_hog_gpios),
|
||||
PROPERTY_ENTRY_STRING("line-name", "keybrd_dataout"),
|
||||
PROPERTY_ENTRY_BOOL("output-low"),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node latch2_gpio_hog_swnode = {
|
||||
.parent = &latch2_gpio_swnode,
|
||||
.name = "latch2-hog",
|
||||
.properties = latch2_gpio_hog_props,
|
||||
};
|
||||
|
||||
static const struct software_node *const latch2_gpio_swnodes[] = {
|
||||
&latch2_gpio_swnode,
|
||||
&latch2_gpio_hog_swnode,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct plat_serial8250_port ams_delta_modem_ports[];
|
||||
@@ -684,7 +704,6 @@ static void __init ams_delta_init(void)
|
||||
|
||||
omap_gpio_deps_init();
|
||||
ams_delta_latch2_init();
|
||||
gpiod_add_hogs(ams_delta_gpio_hogs);
|
||||
|
||||
omap_serial_init();
|
||||
omap_register_i2c_bus(1, 100, NULL, 0);
|
||||
@@ -693,6 +712,9 @@ static void __init ams_delta_init(void)
|
||||
platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices));
|
||||
|
||||
platform_device_register_full(&latch1_gpio_devinfo);
|
||||
|
||||
software_node_register_node_group(latch2_gpio_swnodes);
|
||||
latch2_gpio_devinfo.fwnode = software_node_fwnode(&latch2_gpio_swnode);
|
||||
platform_device_register_full(&latch2_gpio_devinfo);
|
||||
|
||||
/*
|
||||
|
||||
@@ -217,7 +217,6 @@ config GE_IMP3A
|
||||
config SGY_CTS1000
|
||||
tristate "Servergy CTS-1000 support"
|
||||
select GPIOLIB
|
||||
select OF_GPIO
|
||||
depends on CORENET_GENERIC
|
||||
help
|
||||
Enable this to support functionality in Servergy's CTS-1000 systems.
|
||||
|
||||
@@ -578,6 +578,8 @@ static int scmi_pinctrl_request_free(const struct scmi_protocol_handle *ph,
|
||||
tx->flags = cpu_to_le32(type);
|
||||
|
||||
ret = ph->xops->do_xfer(ph, t);
|
||||
if (ret == -EOPNOTSUPP)
|
||||
ret = 0;
|
||||
ph->xops->xfer_put(ph, t);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -95,7 +95,6 @@ config GPIO_CDEV
|
||||
|
||||
config GPIO_CDEV_V1
|
||||
bool "Support GPIO ABI Version 1"
|
||||
default y
|
||||
depends on GPIO_CDEV
|
||||
help
|
||||
Say Y here to support version 1 of the GPIO CDEV ABI.
|
||||
@@ -103,8 +102,6 @@ config GPIO_CDEV_V1
|
||||
This ABI version is deprecated.
|
||||
Please use the latest ABI for new developments.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config GPIO_GENERIC
|
||||
depends on HAS_IOMEM # Only for IOMEM drivers
|
||||
tristate
|
||||
@@ -145,7 +142,7 @@ menu "Memory mapped GPIO drivers"
|
||||
|
||||
config GPIO_74XX_MMIO
|
||||
tristate "GPIO driver for 74xx-ICs with MMIO access"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
help
|
||||
Say yes here to support GPIO functionality for 74xx-compatible ICs
|
||||
@@ -175,14 +172,14 @@ config GPIO_AMDPT
|
||||
|
||||
config GPIO_ASPEED
|
||||
tristate "Aspeed GPIO support"
|
||||
depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers.
|
||||
|
||||
config GPIO_ASPEED_SGPIO
|
||||
bool "Aspeed SGPIO support"
|
||||
depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -201,7 +198,6 @@ config GPIO_ATH79
|
||||
config GPIO_RASPBERRYPI_EXP
|
||||
tristate "Raspberry Pi 3 GPIO Expander"
|
||||
default RASPBERRYPI_FIRMWARE
|
||||
depends on OF_GPIO
|
||||
# Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only
|
||||
# happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE.
|
||||
depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
|
||||
@@ -218,7 +214,7 @@ config GPIO_BCM_KONA
|
||||
|
||||
config GPIO_BCM_XGS_IPROC
|
||||
tristate "BRCM XGS iProc GPIO support"
|
||||
depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST)
|
||||
depends on ARCH_BCM_IPROC || COMPILE_TEST
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
default ARCH_BCM_IPROC
|
||||
@@ -229,7 +225,6 @@ config GPIO_BLZP1600
|
||||
tristate "Blaize BLZP1600 GPIO support"
|
||||
default y if ARCH_BLAIZE
|
||||
depends on ARCH_BLAIZE || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -240,15 +235,29 @@ config GPIO_BLZP1600
|
||||
config GPIO_BRCMSTB
|
||||
tristate "BRCMSTB GPIO support"
|
||||
default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
|
||||
depends on OF_GPIO && (ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST)
|
||||
depends on ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
|
||||
|
||||
config GPIO_BY_PINCTRL
|
||||
tristate "GPIO support based on a pure pin control backend"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Support for generic GPIO handling based on top of pin control.
|
||||
Traditionally, firmware creates a GPIO interface or a pin
|
||||
controller interface and we have a driver to support it. But
|
||||
in SCMI, the pin control interface is generic and we can
|
||||
create a simple GPIO device based on the pin control interface
|
||||
without doing anything custom.
|
||||
|
||||
This driver is used to access GPIOs over the ARM SCMI protocol.
|
||||
|
||||
config GPIO_CADENCE
|
||||
tristate "Cadence GPIO support"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -280,14 +289,13 @@ config GPIO_DWAPB
|
||||
config GPIO_EIC_SPRD
|
||||
tristate "Spreadtrum EIC support"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support Spreadtrum EIC device.
|
||||
|
||||
config GPIO_EM
|
||||
tristate "Emma Mobile GPIO"
|
||||
depends on (ARCH_EMEV2 || COMPILE_TEST) && OF_GPIO
|
||||
depends on ARCH_EMEV2 || COMPILE_TEST
|
||||
help
|
||||
Say yes here to support GPIO on Renesas Emma Mobile SoCs.
|
||||
|
||||
@@ -329,7 +337,7 @@ config GPIO_GE_FPGA
|
||||
|
||||
config GPIO_FTGPIO010
|
||||
bool "Faraday FTGPIO010 GPIO"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
default (ARCH_GEMINI || ARCH_MOXART)
|
||||
@@ -383,7 +391,7 @@ config GPIO_HISI
|
||||
|
||||
config GPIO_HLWD
|
||||
tristate "Nintendo Wii (Hollywood) GPIO"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -434,7 +442,6 @@ config GPIO_LOONGSON
|
||||
config GPIO_LOONGSON_64BIT
|
||||
tristate "Loongson 64 bit GPIO support"
|
||||
depends on LOONGARCH || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -447,7 +454,7 @@ config GPIO_LOONGSON_64BIT
|
||||
config GPIO_LPC18XX
|
||||
tristate "NXP LPC18XX/43XX GPIO support"
|
||||
default y if ARCH_LPC18XX
|
||||
depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -456,7 +463,8 @@ config GPIO_LPC18XX
|
||||
|
||||
config GPIO_LPC32XX
|
||||
tristate "NXP LPC32XX GPIO support"
|
||||
depends on OF_GPIO && (ARCH_LPC32XX || COMPILE_TEST)
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
depends on OF
|
||||
help
|
||||
Select this option to enable GPIO driver for
|
||||
NXP LPC32XX devices.
|
||||
@@ -499,7 +507,7 @@ config GPIO_MPC8XXX
|
||||
config GPIO_MT7621
|
||||
bool "Mediatek MT7621 GPIO Support"
|
||||
depends on SOC_MT7620 || SOC_MT7621 || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -592,7 +600,6 @@ config GPIO_RCAR
|
||||
config GPIO_RDA
|
||||
bool "RDA Micro GPIO controller support"
|
||||
depends on ARCH_RDA || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -641,8 +648,8 @@ config GPIO_RTD
|
||||
|
||||
config GPIO_SAMA5D2_PIOBU
|
||||
tristate "SAMA5D2 PIOBU GPIO support"
|
||||
depends on OF
|
||||
depends on MFD_SYSCON
|
||||
depends on OF_GPIO
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
select GPIO_SYSCON
|
||||
help
|
||||
@@ -654,7 +661,7 @@ config GPIO_SAMA5D2_PIOBU
|
||||
|
||||
config GPIO_SIFIVE
|
||||
tristate "SiFive GPIO support"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
@@ -673,7 +680,6 @@ config GPIO_SIOX
|
||||
config GPIO_SNPS_CREG
|
||||
bool "Synopsys GPIO via CREG (Control REGisters) driver"
|
||||
depends on ARC || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
help
|
||||
This driver supports GPIOs via CREG on various Synopsys SoCs.
|
||||
This is a single-register MMIO GPIO driver for complex cases
|
||||
@@ -683,7 +689,7 @@ config GPIO_SNPS_CREG
|
||||
config GPIO_SPACEMIT_K1
|
||||
tristate "SPACEMIT K1 GPIO support"
|
||||
depends on ARCH_SPACEMIT || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIO_GENERIC
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
@@ -699,7 +705,6 @@ config GPIO_SPEAR_SPICS
|
||||
config GPIO_SPRD
|
||||
tristate "Spreadtrum GPIO support"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support Spreadtrum GPIO device.
|
||||
@@ -707,7 +712,6 @@ config GPIO_SPRD
|
||||
config GPIO_STP_XWAY
|
||||
bool "XWAY STP GPIOs"
|
||||
depends on SOC_XWAY || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
help
|
||||
This enables support for the Serial To Parallel (STP) unit found on
|
||||
XWAY SoC. The STP allows the SoC to drive a shift registers cascade,
|
||||
@@ -742,7 +746,6 @@ config GPIO_TEGRA
|
||||
tristate "NVIDIA Tegra GPIO support"
|
||||
default ARCH_TEGRA
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
@@ -752,7 +755,7 @@ config GPIO_TEGRA186
|
||||
tristate "NVIDIA Tegra186 GPIO support"
|
||||
default ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC
|
||||
depends on ARCH_TEGRA_186_SOC || ARCH_TEGRA_194_SOC || ARCH_TEGRA_234_SOC || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIOLIB_IRQCHIP
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
@@ -760,7 +763,6 @@ config GPIO_TEGRA186
|
||||
|
||||
config GPIO_TS4800
|
||||
tristate "TS-4800 DIO blocks and compatibles"
|
||||
depends on OF_GPIO
|
||||
depends on SOC_IMX51 || COMPILE_TEST
|
||||
select GPIO_GENERIC
|
||||
help
|
||||
@@ -780,7 +782,6 @@ config GPIO_THUNDERX
|
||||
config GPIO_UNIPHIER
|
||||
tristate "UniPhier GPIO support"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
help
|
||||
Say yes here to support UniPhier GPIOs.
|
||||
@@ -797,7 +798,6 @@ config GPIO_VF610
|
||||
config GPIO_VISCONTI
|
||||
tristate "Toshiba Visconti GPIO support"
|
||||
depends on ARCH_VISCONTI || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
select GPIO_GENERIC
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
@@ -806,14 +806,14 @@ config GPIO_VISCONTI
|
||||
|
||||
config GPIO_WCD934X
|
||||
tristate "Qualcomm Technologies Inc WCD9340/WCD9341 GPIO controller driver"
|
||||
depends on MFD_WCD934X && OF_GPIO
|
||||
depends on MFD_WCD934X
|
||||
help
|
||||
This driver is to support GPIO block found on the Qualcomm Technologies
|
||||
Inc WCD9340/WCD9341 Audio Codec.
|
||||
|
||||
config GPIO_XGENE
|
||||
bool "APM X-Gene GPIO controller support"
|
||||
depends on ARM64 && OF_GPIO
|
||||
depends on ARM64
|
||||
help
|
||||
This driver is to support the GPIO block within the APM X-Gene SoC
|
||||
platform's generic flash controller. The GPIO pins are muxed with
|
||||
@@ -1057,6 +1057,32 @@ config GPIO_SCH
|
||||
The Intel Quark X1000 SoC has 2 GPIOs powered by the core
|
||||
power well and 6 from the suspend power well.
|
||||
|
||||
config GPIO_NOVALAKE
|
||||
tristate "Intel Nova Lake GPIO-signaled ACPI events support"
|
||||
depends on (X86 || COMPILE_TEST) && ACPI
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Select this to enable GPIO-signaled ACPI events support on platforms
|
||||
with the following SoCs:
|
||||
|
||||
- Intel Nova Lake
|
||||
|
||||
This driver adds support for new mode of handling platform events,
|
||||
through the use of GPIO-signaled ACPI events. Main purpose is to
|
||||
handle platform IRQs that originate in PCH components, for example
|
||||
interrupt triggered by Power Management Event (PME).
|
||||
|
||||
This driver, at this time, is not required to handle platform events.
|
||||
Listed platform(s) will stay in legacy mode, handling ACPI events as
|
||||
in previous generations. However, future platforms will eventually
|
||||
switch to new handling mode, requiring this driver to run events
|
||||
properly.
|
||||
|
||||
Driver supports up to 128 GPIO pins per GPE block.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called gpio-novalake-events.
|
||||
|
||||
config GPIO_SCH311X
|
||||
tristate "SMSC SCH311x SuperI/O GPIO"
|
||||
help
|
||||
@@ -1111,7 +1137,7 @@ menu "I2C GPIO expanders"
|
||||
|
||||
config GPIO_ADNP
|
||||
tristate "Avionic Design N-bit GPIO expander"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
This option enables support for N GPIOs found on Avionic Design
|
||||
@@ -1144,7 +1170,7 @@ config GPIO_DS4520
|
||||
|
||||
config GPIO_GW_PLD
|
||||
tristate "Gateworks PLD GPIO Expander"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to provide access to the Gateworks I2C PLD GPIO
|
||||
Expander. This is used at least on the Cambria GW2358-4.
|
||||
@@ -1440,6 +1466,7 @@ config GPIO_JANZ_TTL
|
||||
config GPIO_KEMPLD
|
||||
tristate "Kontron ETX / COMexpress GPIO"
|
||||
depends on MFD_KEMPLD
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
This enables support for the PLD GPIO interface on some Kontron ETX
|
||||
and COMexpress (ETXexpress) modules.
|
||||
@@ -1567,7 +1594,6 @@ config GPIO_PALMAS
|
||||
config GPIO_PMIC_EIC_SPRD
|
||||
tristate "Spreadtrum PMIC EIC support"
|
||||
depends on MFD_SC27XX_PMIC || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support Spreadtrum PMIC EIC device.
|
||||
@@ -1606,7 +1632,6 @@ config GPIO_SL28CPLD
|
||||
config GPIO_STMPE
|
||||
tristate "STMPE GPIOs"
|
||||
depends on MFD_STMPE
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
This enables support for the GPIOs found on the STMPE I/O
|
||||
@@ -1615,7 +1640,6 @@ config GPIO_STMPE
|
||||
config GPIO_TC3589X
|
||||
bool "TC3589X GPIOs"
|
||||
depends on MFD_TC3589X
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
This enables support for the GPIOs found on the TC3589X
|
||||
@@ -1986,7 +2010,6 @@ menu "Virtual GPIO drivers"
|
||||
config GPIO_AGGREGATOR
|
||||
tristate "GPIO Aggregator"
|
||||
select CONFIGFS_FS
|
||||
select DEV_SYNC_PROBE
|
||||
help
|
||||
Say yes here to enable the GPIO Aggregator, which provides a way to
|
||||
aggregate existing GPIO lines into a new virtual GPIO chip.
|
||||
@@ -2005,7 +2028,7 @@ config GPIO_LATCH
|
||||
|
||||
config GPIO_LINE_MUX
|
||||
tristate "GPIO line mux driver"
|
||||
depends on OF_GPIO
|
||||
depends on OF
|
||||
select MULTIPLEXER
|
||||
help
|
||||
Say Y here to support the GPIO line mux, which can provide virtual
|
||||
@@ -2038,7 +2061,6 @@ config GPIO_SIM
|
||||
tristate "GPIO Simulator Module"
|
||||
select IRQ_SIM
|
||||
select CONFIGFS_FS
|
||||
select DEV_SYNC_PROBE
|
||||
help
|
||||
This enables the GPIO simulator - a configfs-based GPIO testing
|
||||
driver.
|
||||
@@ -2076,7 +2098,6 @@ config GPIO_VIRTUSER
|
||||
select DEBUG_FS
|
||||
select CONFIGFS_FS
|
||||
select IRQ_WORK
|
||||
select DEV_SYNC_PROBE
|
||||
help
|
||||
Say Y here to enable the configurable, configfs-based virtual GPIO
|
||||
consumer testing driver.
|
||||
@@ -2087,6 +2108,3 @@ config GPIO_VIRTUSER
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
||||
config DEV_SYNC_PROBE
|
||||
tristate
|
||||
|
||||
@@ -21,9 +21,6 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o
|
||||
# directly supported by gpio-generic
|
||||
gpio-generic-$(CONFIG_GPIO_GENERIC) += gpio-mmio.o
|
||||
|
||||
# Utilities for drivers that need synchronous fake device creation
|
||||
obj-$(CONFIG_DEV_SYNC_PROBE) += dev-sync-probe.o
|
||||
|
||||
obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o
|
||||
obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o
|
||||
obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o
|
||||
@@ -51,6 +48,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
|
||||
obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o
|
||||
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
|
||||
obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o
|
||||
obj-$(CONFIG_GPIO_BY_PINCTRL) += gpio-by-pinctrl.o
|
||||
obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o
|
||||
obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o
|
||||
obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o
|
||||
@@ -134,6 +132,7 @@ obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
|
||||
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
|
||||
obj-$(CONFIG_GPIO_NCT6694) += gpio-nct6694.o
|
||||
obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o
|
||||
obj-$(CONFIG_GPIO_NOVALAKE) += gpio-novalake-events.o
|
||||
obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o
|
||||
obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
|
||||
obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o
|
||||
|
||||
@@ -58,34 +58,6 @@ Work items:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Get rid of <linux/of_gpio.h>
|
||||
|
||||
This header and helpers appeared at one point when there was no proper
|
||||
driver infrastructure for doing simpler MMIO GPIO devices and there was
|
||||
no core support for parsing device tree GPIOs from the core library with
|
||||
the [devm_]gpiod_get() calls we have today that will implicitly go into
|
||||
the device tree back-end. It is legacy and should not be used in new code.
|
||||
|
||||
Work items:
|
||||
|
||||
- Change all consumer drivers that #include <linux/of_gpio.h> to
|
||||
#include <linux/gpio/consumer.h> and stop doing custom parsing of the
|
||||
GPIO lines from the device tree. This can be tricky and often involves
|
||||
changing board files, etc.
|
||||
|
||||
- Pull semantics for legacy device tree (OF) GPIO lookups into
|
||||
gpiolib-of.c: in some cases subsystems are doing custom flags and
|
||||
lookups for polarity inversion, open drain and what not. As we now
|
||||
handle this with generic OF bindings, pull all legacy handling into
|
||||
gpiolib so the library API becomes narrow and deep and handle all
|
||||
legacy bindings internally. (See e.g. commits 6953c57ab172,
|
||||
6a537d48461d etc)
|
||||
|
||||
- Delete <linux/of_gpio.h> when all the above is complete and everything
|
||||
uses <linux/gpio/consumer.h> or <linux/gpio/driver.h> instead.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Collect drivers
|
||||
|
||||
Collect GPIO drivers from arch/* and other places that should be placed
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Common code for drivers creating fake platform devices.
|
||||
*
|
||||
* Provides synchronous device creation: waits for probe completion and
|
||||
* returns the probe success or error status to the device creator.
|
||||
*
|
||||
* Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
* Copyright (C) 2025 Koichiro Den <koichiro.den@canonical.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dev-sync-probe.h"
|
||||
|
||||
static int dev_sync_probe_notifier_call(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct dev_sync_probe_data *pdata;
|
||||
struct device *dev = data;
|
||||
|
||||
pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier);
|
||||
if (!device_match_name(dev, pdata->name))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_BOUND_DRIVER:
|
||||
pdata->driver_bound = true;
|
||||
break;
|
||||
case BUS_NOTIFY_DRIVER_NOT_BOUND:
|
||||
pdata->driver_bound = false;
|
||||
break;
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
complete(&pdata->probe_completion);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
void dev_sync_probe_init(struct dev_sync_probe_data *data)
|
||||
{
|
||||
memset(data, 0, sizeof(*data));
|
||||
init_completion(&data->probe_completion);
|
||||
data->bus_notifier.notifier_call = dev_sync_probe_notifier_call;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_sync_probe_init);
|
||||
|
||||
int dev_sync_probe_register(struct dev_sync_probe_data *data,
|
||||
struct platform_device_info *pdevinfo)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
char *name;
|
||||
|
||||
name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
data->driver_bound = false;
|
||||
data->name = name;
|
||||
reinit_completion(&data->probe_completion);
|
||||
bus_register_notifier(&platform_bus_type, &data->bus_notifier);
|
||||
|
||||
pdev = platform_device_register_full(pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);
|
||||
kfree(data->name);
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
wait_for_completion(&data->probe_completion);
|
||||
bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);
|
||||
|
||||
if (!data->driver_bound) {
|
||||
platform_device_unregister(pdev);
|
||||
kfree(data->name);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
data->pdev = pdev;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_sync_probe_register);
|
||||
|
||||
void dev_sync_probe_unregister(struct dev_sync_probe_data *data)
|
||||
{
|
||||
platform_device_unregister(data->pdev);
|
||||
kfree(data->name);
|
||||
data->pdev = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_sync_probe_unregister);
|
||||
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
|
||||
MODULE_AUTHOR("Koichiro Den <koichiro.den@canonical.com>");
|
||||
MODULE_DESCRIPTION("Utilities for synchronous fake device creation");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -1,25 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef DEV_SYNC_PROBE_H
|
||||
#define DEV_SYNC_PROBE_H
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct dev_sync_probe_data {
|
||||
struct platform_device *pdev;
|
||||
const char *name;
|
||||
|
||||
/* Synchronize with probe */
|
||||
struct notifier_block bus_notifier;
|
||||
struct completion probe_completion;
|
||||
bool driver_bound;
|
||||
};
|
||||
|
||||
void dev_sync_probe_init(struct dev_sync_probe_data *data);
|
||||
int dev_sync_probe_register(struct dev_sync_probe_data *data,
|
||||
struct platform_device_info *pdevinfo);
|
||||
void dev_sync_probe_unregister(struct dev_sync_probe_data *data);
|
||||
|
||||
#endif /* DEV_SYNC_PROBE_H */
|
||||
@@ -32,8 +32,6 @@
|
||||
#include <linux/gpio/forwarder.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
|
||||
#include "dev-sync-probe.h"
|
||||
|
||||
#define AGGREGATOR_MAX_GPIOS 512
|
||||
#define AGGREGATOR_LEGACY_PREFIX "_sysfs"
|
||||
|
||||
@@ -42,7 +40,7 @@
|
||||
*/
|
||||
|
||||
struct gpio_aggregator {
|
||||
struct dev_sync_probe_data probe_data;
|
||||
struct platform_device *pdev;
|
||||
struct config_group group;
|
||||
struct gpiod_lookup_table *lookups;
|
||||
struct mutex lock;
|
||||
@@ -135,7 +133,7 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr)
|
||||
{
|
||||
lockdep_assert_held(&aggr->lock);
|
||||
|
||||
return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev);
|
||||
return aggr->pdev && platform_get_drvdata(aggr->pdev);
|
||||
}
|
||||
|
||||
/* Only aggregators created via legacy sysfs can be "activating". */
|
||||
@@ -143,7 +141,7 @@ static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr)
|
||||
{
|
||||
lockdep_assert_held(&aggr->lock);
|
||||
|
||||
return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev);
|
||||
return aggr->pdev && !platform_get_drvdata(aggr->pdev);
|
||||
}
|
||||
|
||||
static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr)
|
||||
@@ -909,6 +907,7 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr)
|
||||
{
|
||||
struct platform_device_info pdevinfo;
|
||||
struct gpio_aggregator_line *line;
|
||||
struct platform_device *pdev;
|
||||
struct fwnode_handle *swnode;
|
||||
unsigned int n = 0;
|
||||
int ret = 0;
|
||||
@@ -962,12 +961,23 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr)
|
||||
|
||||
gpiod_add_lookup_table(aggr->lookups);
|
||||
|
||||
ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo);
|
||||
if (ret)
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
ret = PTR_ERR(pdev);
|
||||
goto err_remove_lookup_table;
|
||||
}
|
||||
|
||||
wait_for_device_probe();
|
||||
if (!device_is_bound(&pdev->dev)) {
|
||||
ret = -ENXIO;
|
||||
goto err_unregister_pdev;
|
||||
}
|
||||
|
||||
aggr->pdev = pdev;
|
||||
return 0;
|
||||
|
||||
err_unregister_pdev:
|
||||
platform_device_unregister(pdev);
|
||||
err_remove_lookup_table:
|
||||
kfree(aggr->lookups->dev_id);
|
||||
gpiod_remove_lookup_table(aggr->lookups);
|
||||
@@ -981,7 +991,8 @@ err_remove_lookups:
|
||||
|
||||
static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr)
|
||||
{
|
||||
dev_sync_probe_unregister(&aggr->probe_data);
|
||||
platform_device_unregister(aggr->pdev);
|
||||
aggr->pdev = NULL;
|
||||
gpiod_remove_lookup_table(aggr->lookups);
|
||||
kfree(aggr->lookups->dev_id);
|
||||
kfree(aggr->lookups);
|
||||
@@ -1145,7 +1156,7 @@ gpio_aggregator_device_dev_name_show(struct config_item *item, char *page)
|
||||
|
||||
guard(mutex)(&aggr->lock);
|
||||
|
||||
pdev = aggr->probe_data.pdev;
|
||||
pdev = aggr->pdev;
|
||||
if (pdev)
|
||||
return sysfs_emit(page, "%s\n", dev_name(&pdev->dev));
|
||||
|
||||
@@ -1322,7 +1333,6 @@ gpio_aggregator_make_group(struct config_group *group, const char *name)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type);
|
||||
dev_sync_probe_init(&aggr->probe_data);
|
||||
|
||||
return &aggr->group;
|
||||
}
|
||||
@@ -1471,12 +1481,6 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver,
|
||||
scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id);
|
||||
config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type);
|
||||
|
||||
/*
|
||||
* Since the device created by sysfs might be toggled via configfs
|
||||
* 'live' attribute later, this initialization is needed.
|
||||
*/
|
||||
dev_sync_probe_init(&aggr->probe_data);
|
||||
|
||||
/* Expose to configfs */
|
||||
res = configfs_register_group(&gpio_aggregator_subsys.su_group,
|
||||
&aggr->group);
|
||||
@@ -1495,7 +1499,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver,
|
||||
goto remove_table;
|
||||
}
|
||||
|
||||
aggr->probe_data.pdev = pdev;
|
||||
aggr->pdev = pdev;
|
||||
module_put(THIS_MODULE);
|
||||
return count;
|
||||
|
||||
|
||||
@@ -655,7 +655,7 @@ static void aspeed_init_irq_valid_mask(struct gpio_chip *gc,
|
||||
|
||||
while (!is_bank_props_sentinel(props)) {
|
||||
unsigned int offset;
|
||||
const unsigned long int input = props->input;
|
||||
const unsigned long input = props->input;
|
||||
|
||||
/* Pretty crummy approach, but similar to GPIO core */
|
||||
for_each_clear_bit(offset, &input, 32) {
|
||||
|
||||
@@ -58,15 +58,6 @@
|
||||
#define LOCK_CODE 0xffffffff
|
||||
#define UNLOCK_CODE 0x00000000
|
||||
|
||||
struct bcm_kona_gpio {
|
||||
void __iomem *reg_base;
|
||||
int num_bank;
|
||||
raw_spinlock_t lock;
|
||||
struct gpio_chip gpio_chip;
|
||||
struct irq_domain *irq_domain;
|
||||
struct bcm_kona_gpio_bank *banks;
|
||||
};
|
||||
|
||||
struct bcm_kona_gpio_bank {
|
||||
int id;
|
||||
int irq;
|
||||
@@ -90,6 +81,15 @@ struct bcm_kona_gpio_bank {
|
||||
struct bcm_kona_gpio *kona_gpio;
|
||||
};
|
||||
|
||||
struct bcm_kona_gpio {
|
||||
void __iomem *reg_base;
|
||||
int num_bank;
|
||||
raw_spinlock_t lock;
|
||||
struct gpio_chip gpio_chip;
|
||||
struct irq_domain *irq_domain;
|
||||
struct bcm_kona_gpio_bank banks[] __counted_by(num_bank);
|
||||
};
|
||||
|
||||
static inline void bcm_kona_gpio_write_lock_regs(void __iomem *reg_base,
|
||||
int bank_id, u32 lockcode)
|
||||
{
|
||||
@@ -584,12 +584,6 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
kona_gpio = devm_kzalloc(dev, sizeof(*kona_gpio), GFP_KERNEL);
|
||||
if (!kona_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
kona_gpio->gpio_chip = template_chip;
|
||||
chip = &kona_gpio->gpio_chip;
|
||||
ret = platform_irq_count(pdev);
|
||||
if (!ret) {
|
||||
dev_err(dev, "Couldn't determine # GPIO banks\n");
|
||||
@@ -597,6 +591,11 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
|
||||
} else if (ret < 0) {
|
||||
return dev_err_probe(dev, ret, "Couldn't determine GPIO banks\n");
|
||||
}
|
||||
|
||||
kona_gpio = devm_kzalloc(dev, struct_size(kona_gpio, banks, ret), GFP_KERNEL);
|
||||
if (!kona_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
kona_gpio->num_bank = ret;
|
||||
|
||||
if (kona_gpio->num_bank > GPIO_MAX_BANK_NUM) {
|
||||
@@ -604,13 +603,9 @@ static int bcm_kona_gpio_probe(struct platform_device *pdev)
|
||||
GPIO_MAX_BANK_NUM);
|
||||
return -ENXIO;
|
||||
}
|
||||
kona_gpio->banks = devm_kcalloc(dev,
|
||||
kona_gpio->num_bank,
|
||||
sizeof(*kona_gpio->banks),
|
||||
GFP_KERNEL);
|
||||
if (!kona_gpio->banks)
|
||||
return -ENOMEM;
|
||||
|
||||
kona_gpio->gpio_chip = template_chip;
|
||||
chip = &kona_gpio->gpio_chip;
|
||||
chip->parent = dev;
|
||||
chip->ngpio = kona_gpio->num_bank * GPIO_PER_BANK;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ static int bd9571mwv_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val & BIT(offset);
|
||||
return !!(val & BIT(offset));
|
||||
}
|
||||
|
||||
static int bd9571mwv_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
// Copyright (C) 2015-2017 Broadcom
|
||||
// Copyright (C) 2015-2017, 2026 Broadcom
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
@@ -54,6 +54,7 @@ struct brcmstb_gpio_priv {
|
||||
int parent_irq;
|
||||
int num_gpios;
|
||||
int parent_wake_irq;
|
||||
bool suspended;
|
||||
};
|
||||
|
||||
#define MAX_GPIO_PER_BANK 32
|
||||
@@ -95,15 +96,13 @@ static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq,
|
||||
return hwirq - bank->chip.gc.offset;
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
|
||||
unsigned int hwirq, bool enable)
|
||||
static void __brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
|
||||
irq_hw_number_t hwirq, bool enable)
|
||||
{
|
||||
struct brcmstb_gpio_priv *priv = bank->parent_priv;
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank));
|
||||
u32 imask;
|
||||
|
||||
guard(gpio_generic_lock_irqsave)(&bank->chip);
|
||||
|
||||
imask = gpio_generic_read_reg(&bank->chip,
|
||||
priv->reg_base + GIO_MASK(bank->id));
|
||||
if (enable)
|
||||
@@ -114,6 +113,13 @@ static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
|
||||
priv->reg_base + GIO_MASK(bank->id), imask);
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
|
||||
irq_hw_number_t hwirq, bool enable)
|
||||
{
|
||||
guard(gpio_generic_lock_irqsave)(&bank->chip);
|
||||
__brcmstb_gpio_set_imask(bank, hwirq, enable);
|
||||
}
|
||||
|
||||
static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
|
||||
@@ -132,7 +138,21 @@ static void brcmstb_gpio_irq_mask(struct irq_data *d)
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
|
||||
|
||||
brcmstb_gpio_set_imask(bank, d->hwirq, false);
|
||||
brcmstb_gpio_set_imask(bank, irqd_to_hwirq(d), false);
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_irq_mask_ack(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
|
||||
struct brcmstb_gpio_priv *priv = bank->parent_priv;
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank));
|
||||
|
||||
guard(gpio_generic_lock_irqsave)(&bank->chip);
|
||||
__brcmstb_gpio_set_imask(bank, hwirq, false);
|
||||
gpio_generic_write_reg(&bank->chip,
|
||||
priv->reg_base + GIO_STAT(bank->id), mask);
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_irq_unmask(struct irq_data *d)
|
||||
@@ -140,7 +160,7 @@ static void brcmstb_gpio_irq_unmask(struct irq_data *d)
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
|
||||
|
||||
brcmstb_gpio_set_imask(bank, d->hwirq, true);
|
||||
brcmstb_gpio_set_imask(bank, irqd_to_hwirq(d), true);
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_irq_ack(struct irq_data *d)
|
||||
@@ -148,7 +168,7 @@ static void brcmstb_gpio_irq_ack(struct irq_data *d)
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
|
||||
struct brcmstb_gpio_priv *priv = bank->parent_priv;
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank));
|
||||
|
||||
gpio_generic_write_reg(&bank->chip,
|
||||
priv->reg_base + GIO_STAT(bank->id), mask);
|
||||
@@ -159,7 +179,7 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
|
||||
struct brcmstb_gpio_priv *priv = bank->parent_priv;
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank));
|
||||
u32 edge_insensitive, iedge_insensitive;
|
||||
u32 edge_config, iedge_config;
|
||||
u32 level, ilevel;
|
||||
@@ -221,6 +241,9 @@ static int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv,
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (priv->parent_wake_irq == priv->parent_irq)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
ret = enable_irq_wake(priv->parent_wake_irq);
|
||||
else
|
||||
@@ -236,7 +259,7 @@ static int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
|
||||
struct brcmstb_gpio_priv *priv = bank->parent_priv;
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank));
|
||||
u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(irqd_to_hwirq(d), bank));
|
||||
|
||||
/*
|
||||
* Do not do anything specific for now, suspend/resume callbacks will
|
||||
@@ -271,6 +294,11 @@ static void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank)
|
||||
while ((status = brcmstb_gpio_get_active_irqs(bank))) {
|
||||
unsigned int offset;
|
||||
|
||||
if (priv->suspended && bank->wake_active & status) {
|
||||
priv->suspended = false;
|
||||
pm_wakeup_event(&priv->pdev->dev, 0);
|
||||
}
|
||||
|
||||
for_each_set_bit(offset, &status, 32) {
|
||||
if (offset >= bank->width)
|
||||
dev_warn(&priv->pdev->dev,
|
||||
@@ -444,18 +472,18 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "wakeup-source")) {
|
||||
/*
|
||||
* Set wakeup capability so we can process boot-time
|
||||
* "wakeups" (e.g., from S5 cold boot).
|
||||
*/
|
||||
device_set_wakeup_capable(dev, true);
|
||||
device_wakeup_enable(dev);
|
||||
priv->parent_wake_irq = platform_get_irq(pdev, 1);
|
||||
if (priv->parent_wake_irq < 0) {
|
||||
priv->parent_wake_irq = 0;
|
||||
priv->parent_wake_irq = priv->parent_irq;
|
||||
dev_warn(dev,
|
||||
"Couldn't get wake IRQ - GPIOs will not be able to wake from sleep");
|
||||
} else {
|
||||
/*
|
||||
* Set wakeup capability so we can process boot-time
|
||||
* "wakeups" (e.g., from S5 cold boot)
|
||||
*/
|
||||
device_set_wakeup_capable(dev, true);
|
||||
device_wakeup_enable(dev);
|
||||
err = devm_request_irq(dev, priv->parent_wake_irq,
|
||||
brcmstb_gpio_wake_irq_handler,
|
||||
IRQF_SHARED,
|
||||
@@ -466,18 +494,17 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,
|
||||
goto out_free_domain;
|
||||
}
|
||||
}
|
||||
priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake;
|
||||
}
|
||||
|
||||
priv->irq_chip.name = dev_name(dev);
|
||||
priv->irq_chip.irq_disable = brcmstb_gpio_irq_mask;
|
||||
priv->irq_chip.irq_mask = brcmstb_gpio_irq_mask;
|
||||
priv->irq_chip.irq_mask_ack = brcmstb_gpio_irq_mask_ack;
|
||||
priv->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask;
|
||||
priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack;
|
||||
priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type;
|
||||
|
||||
if (priv->parent_wake_irq)
|
||||
priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake;
|
||||
|
||||
irq_set_chained_handler_and_data(priv->parent_irq,
|
||||
brcmstb_gpio_irq_handler, priv);
|
||||
irq_set_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY);
|
||||
@@ -500,16 +527,11 @@ static void brcmstb_gpio_bank_save(struct brcmstb_gpio_priv *priv,
|
||||
priv->reg_base + GIO_BANK_OFF(bank->id, i));
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_quiesce(struct device *dev, bool save)
|
||||
static void brcmstb_gpio_quiesce(struct brcmstb_gpio_priv *priv, bool save)
|
||||
{
|
||||
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
|
||||
struct brcmstb_gpio_bank *bank;
|
||||
u32 imask;
|
||||
|
||||
/* disable non-wake interrupt */
|
||||
if (priv->parent_irq >= 0)
|
||||
disable_irq(priv->parent_irq);
|
||||
|
||||
list_for_each_entry(bank, &priv->bank_list, node) {
|
||||
if (save)
|
||||
brcmstb_gpio_bank_save(priv, bank);
|
||||
@@ -527,8 +549,13 @@ static void brcmstb_gpio_quiesce(struct device *dev, bool save)
|
||||
|
||||
static void brcmstb_gpio_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct brcmstb_gpio_priv *priv = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (priv->parent_irq > 0)
|
||||
disable_irq(priv->parent_irq);
|
||||
|
||||
/* Enable GPIO for S5 cold boot */
|
||||
brcmstb_gpio_quiesce(&pdev->dev, false);
|
||||
brcmstb_gpio_quiesce(priv, false);
|
||||
}
|
||||
|
||||
static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
|
||||
@@ -544,7 +571,30 @@ static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
|
||||
|
||||
static int brcmstb_gpio_suspend(struct device *dev)
|
||||
{
|
||||
brcmstb_gpio_quiesce(dev, true);
|
||||
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->parent_irq > 0)
|
||||
priv->suspended = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brcmstb_gpio_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
/* Catch any wakeup sources occurring between suspend and noirq */
|
||||
if (!priv->suspended)
|
||||
return -EBUSY;
|
||||
|
||||
if (priv->parent_irq > 0)
|
||||
disable_irq(priv->parent_irq);
|
||||
|
||||
brcmstb_gpio_quiesce(priv, true);
|
||||
|
||||
if (priv->parent_wake_irq)
|
||||
enable_irq(priv->parent_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -552,25 +602,24 @@ static int brcmstb_gpio_resume(struct device *dev)
|
||||
{
|
||||
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
|
||||
struct brcmstb_gpio_bank *bank;
|
||||
bool need_wakeup_event = false;
|
||||
|
||||
list_for_each_entry(bank, &priv->bank_list, node) {
|
||||
need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
|
||||
if (priv->parent_wake_irq)
|
||||
disable_irq(priv->parent_irq);
|
||||
|
||||
priv->suspended = false;
|
||||
|
||||
list_for_each_entry(bank, &priv->bank_list, node)
|
||||
brcmstb_gpio_bank_restore(priv, bank);
|
||||
}
|
||||
|
||||
if (priv->parent_wake_irq && need_wakeup_event)
|
||||
pm_wakeup_event(dev, 0);
|
||||
|
||||
/* enable non-wake interrupt */
|
||||
if (priv->parent_irq >= 0)
|
||||
if (priv->parent_irq > 0)
|
||||
enable_irq(priv->parent_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
|
||||
.suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend),
|
||||
.suspend = pm_sleep_ptr(brcmstb_gpio_suspend),
|
||||
.suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend_noirq),
|
||||
.resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume),
|
||||
};
|
||||
|
||||
|
||||
101
drivers/gpio/gpio-by-pinctrl.c
Normal file
101
drivers/gpio/gpio-by-pinctrl.c
Normal file
@@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (C) 2026 Linaro Inc.
|
||||
// Author: AKASHI takahiro <takahiro.akashi@linaro.org>
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
unsigned long config;
|
||||
int ret;
|
||||
|
||||
config = PIN_CONFIG_OUTPUT_ENABLE;
|
||||
ret = pinctrl_gpio_get_config(gc, offset, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (config)
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
}
|
||||
|
||||
static int pin_control_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int val)
|
||||
{
|
||||
return pinctrl_gpio_direction_output(chip, offset);
|
||||
}
|
||||
|
||||
static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
unsigned long config;
|
||||
int ret;
|
||||
|
||||
config = PIN_CONFIG_LEVEL;
|
||||
ret = pinctrl_gpio_get_config(chip, offset, &config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!config;
|
||||
}
|
||||
|
||||
static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int val)
|
||||
{
|
||||
unsigned long config;
|
||||
|
||||
config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, val);
|
||||
return pinctrl_gpio_set_config(chip, offset, config);
|
||||
}
|
||||
|
||||
static int pin_control_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct gpio_chip *chip;
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->label = dev_name(dev);
|
||||
chip->parent = dev;
|
||||
chip->base = -1;
|
||||
|
||||
chip->request = gpiochip_generic_request;
|
||||
chip->free = gpiochip_generic_free;
|
||||
chip->get_direction = pin_control_gpio_get_direction;
|
||||
chip->direction_input = pinctrl_gpio_direction_input;
|
||||
chip->direction_output = pin_control_gpio_direction_output;
|
||||
chip->get = pin_control_gpio_get;
|
||||
chip->set = pin_control_gpio_set;
|
||||
chip->set_config = gpiochip_generic_config;
|
||||
|
||||
return devm_gpiochip_add_data(dev, chip, NULL);
|
||||
}
|
||||
|
||||
static const struct of_device_id pin_control_gpio_match[] = {
|
||||
{ .compatible = "scmi-pinctrl-gpio" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pin_control_gpio_match);
|
||||
|
||||
static struct platform_driver pin_control_gpio_driver = {
|
||||
.probe = pin_control_gpio_probe,
|
||||
.driver = {
|
||||
.name = "pin-control-gpio",
|
||||
.of_match_table = pin_control_gpio_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(pin_control_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("AKASHI Takahiro <takahiro.akashi@linaro.org>");
|
||||
MODULE_DESCRIPTION("Pinctrl based GPIO driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -47,8 +47,8 @@ static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
else
|
||||
return (int)(val & (u8)BIT(offset));
|
||||
|
||||
return !!(val & BIT(offset));
|
||||
}
|
||||
|
||||
static int __cgbc_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
|
||||
@@ -39,10 +39,6 @@ static ulong mask = GPIO_DEFAULT_MASK;
|
||||
module_param_named(mask, mask, ulong, 0444);
|
||||
MODULE_PARM_DESC(mask, "GPIO channel mask.");
|
||||
|
||||
/*
|
||||
* FIXME: convert this singleton driver to use the state container
|
||||
* design pattern, see Documentation/driver-api/driver-model/design-patterns.rst
|
||||
*/
|
||||
static struct cs5535_gpio_chip {
|
||||
struct gpio_chip chip;
|
||||
resource_size_t base;
|
||||
@@ -285,30 +281,29 @@ static const char * const cs5535_gpio_names[] = {
|
||||
"GPIO28", NULL, NULL, NULL,
|
||||
};
|
||||
|
||||
static struct cs5535_gpio_chip cs5535_gpio_chip = {
|
||||
.chip = {
|
||||
.owner = THIS_MODULE,
|
||||
.label = DRV_NAME,
|
||||
|
||||
.base = 0,
|
||||
.ngpio = 32,
|
||||
.names = cs5535_gpio_names,
|
||||
.request = chip_gpio_request,
|
||||
|
||||
.get = chip_gpio_get,
|
||||
.set = chip_gpio_set,
|
||||
|
||||
.direction_input = chip_direction_input,
|
||||
.direction_output = chip_direction_output,
|
||||
},
|
||||
};
|
||||
|
||||
static int cs5535_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cs5535_gpio_chip *priv;
|
||||
struct gpio_chip *gc;
|
||||
struct resource *res;
|
||||
int err = -EIO;
|
||||
ulong mask_orig = mask;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
gc = &priv->chip;
|
||||
gc->owner = THIS_MODULE;
|
||||
gc->label = DRV_NAME;
|
||||
gc->ngpio = 32;
|
||||
gc->names = cs5535_gpio_names;
|
||||
gc->request = chip_gpio_request;
|
||||
gc->get = chip_gpio_get;
|
||||
gc->set = chip_gpio_set;
|
||||
gc->direction_input = chip_direction_input;
|
||||
gc->direction_output = chip_direction_output;
|
||||
|
||||
/* There are two ways to get the GPIO base address; one is by
|
||||
* fetching it from MSR_LBAR_GPIO, the other is by reading the
|
||||
* PCI BAR info. The latter method is easier (especially across
|
||||
@@ -329,9 +324,9 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* set up the driver-specific struct */
|
||||
cs5535_gpio_chip.base = res->start;
|
||||
cs5535_gpio_chip.pdev = pdev;
|
||||
spin_lock_init(&cs5535_gpio_chip.lock);
|
||||
priv->base = res->start;
|
||||
priv->pdev = pdev;
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
dev_info(&pdev->dev, "reserved resource region %pR\n", res);
|
||||
|
||||
@@ -347,8 +342,7 @@ static int cs5535_gpio_probe(struct platform_device *pdev)
|
||||
mask_orig, mask);
|
||||
|
||||
/* finally, register with the generic GPIO API */
|
||||
return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip,
|
||||
&cs5535_gpio_chip);
|
||||
return devm_gpiochip_add_data(&pdev->dev, gc, priv);
|
||||
}
|
||||
|
||||
static struct platform_driver cs5535_gpio_driver = {
|
||||
|
||||
@@ -55,7 +55,7 @@ static int da9055_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret & (1 << offset);
|
||||
return !!(ret & (1 << offset));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ struct dwapb_port_property {
|
||||
};
|
||||
|
||||
struct dwapb_platform_data {
|
||||
struct dwapb_port_property *properties;
|
||||
unsigned int nports;
|
||||
struct dwapb_port_property properties[] __counted_by(nports);
|
||||
};
|
||||
|
||||
/* Store GPIO context across system-wide suspend/resume transitions */
|
||||
@@ -114,11 +114,11 @@ static inline struct dwapb_gpio *to_dwapb_gpio(struct gpio_chip *gc)
|
||||
struct dwapb_gpio {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct dwapb_gpio_port *ports;
|
||||
unsigned int nr_ports;
|
||||
unsigned int flags;
|
||||
struct reset_control *rst;
|
||||
struct clk_bulk_data clks[DWAPB_NR_CLOCKS];
|
||||
struct dwapb_gpio_port ports[] __counted_by(nr_ports);
|
||||
};
|
||||
|
||||
static inline u32 gpio_reg_v2_convert(unsigned int offset)
|
||||
@@ -585,14 +585,10 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev)
|
||||
if (nports == 0)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
pdata = devm_kzalloc(dev, struct_size(pdata, properties, nports), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL);
|
||||
if (!pdata->properties)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->nports = nports;
|
||||
|
||||
i = 0;
|
||||
@@ -714,22 +710,17 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
|
||||
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||
gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, ports, pdata->nports), GFP_KERNEL);
|
||||
if (!gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio->dev = &pdev->dev;
|
||||
gpio->nr_ports = pdata->nports;
|
||||
gpio->dev = &pdev->dev;
|
||||
|
||||
err = dwapb_get_reset(gpio);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
|
||||
sizeof(*gpio->ports), GFP_KERNEL);
|
||||
if (!gpio->ports)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(gpio->regs))
|
||||
return PTR_ERR(gpio->regs);
|
||||
|
||||
@@ -46,8 +46,8 @@ struct egpio_info {
|
||||
uint chained_irq;
|
||||
|
||||
/* egpio info */
|
||||
struct egpio_chip *chip;
|
||||
int nchips;
|
||||
struct egpio_chip chip[] __counted_by(nchips);
|
||||
};
|
||||
|
||||
static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg)
|
||||
@@ -270,10 +270,12 @@ static int __init egpio_probe(struct platform_device *pdev)
|
||||
int i;
|
||||
|
||||
/* Initialize ei data structure. */
|
||||
ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
|
||||
ei = devm_kzalloc(&pdev->dev, struct_size(ei, chip, pdata->num_chips), GFP_KERNEL);
|
||||
if (!ei)
|
||||
return -ENOMEM;
|
||||
|
||||
ei->nchips = pdata->num_chips;
|
||||
|
||||
spin_lock_init(&ei->lock);
|
||||
|
||||
/* Find chained irq */
|
||||
@@ -302,13 +304,6 @@ static int __init egpio_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, ei);
|
||||
|
||||
ei->nchips = pdata->num_chips;
|
||||
ei->chip = devm_kcalloc(&pdev->dev,
|
||||
ei->nchips, sizeof(struct egpio_chip),
|
||||
GFP_KERNEL);
|
||||
if (!ei->chip)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ei->nchips; i++) {
|
||||
ei->chip[i].reg_start = pdata->chip[i].reg_start;
|
||||
ei->chip[i].cached_values = pdata->chip[i].initial_values;
|
||||
|
||||
@@ -11,20 +11,35 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/kempld.h>
|
||||
|
||||
#define KEMPLD_GPIO_MAX_NUM 16
|
||||
#define KEMPLD_GPIO_MASK(x) (BIT((x) % 8))
|
||||
#define KEMPLD_GPIO_DIR_NUM(x) (0x40 + (x) / 8)
|
||||
#define KEMPLD_GPIO_LVL_NUM(x) (0x42 + (x) / 8)
|
||||
#define KEMPLD_GPIO_DIR 0x40
|
||||
#define KEMPLD_GPIO_LVL 0x42
|
||||
#define KEMPLD_GPIO_STS 0x44
|
||||
#define KEMPLD_GPIO_EVT_LVL_EDGE 0x46
|
||||
#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48
|
||||
#define KEMPLD_GPIO_IEN 0x4A
|
||||
#define KEMPLD_GPIO_OUT_LVL 0x4E
|
||||
|
||||
/* The IRQ to use if none was configured in the BIOS */
|
||||
static unsigned int gpio_irq;
|
||||
module_param_hw(gpio_irq, uint, irq, 0444);
|
||||
MODULE_PARM_DESC(gpio_irq, "Set legacy GPIO IRQ (1-15)");
|
||||
|
||||
struct kempld_gpio_data {
|
||||
struct gpio_chip chip;
|
||||
struct kempld_device_data *pld;
|
||||
u8 out_lvl_reg;
|
||||
|
||||
struct mutex irq_lock;
|
||||
u16 ien;
|
||||
u16 evt_low_high;
|
||||
u16 evt_lvl_edge;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -32,24 +47,25 @@ struct kempld_gpio_data {
|
||||
* kempld_get_mutex must be called prior to calling this function.
|
||||
*/
|
||||
static void kempld_gpio_bitop(struct kempld_device_data *pld,
|
||||
u8 reg, u8 bit, u8 val)
|
||||
u8 reg, unsigned int bit, bool val)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
status = kempld_read8(pld, reg);
|
||||
status = kempld_read8(pld, reg + (bit / 8));
|
||||
if (val)
|
||||
status |= KEMPLD_GPIO_MASK(bit);
|
||||
else
|
||||
status &= ~KEMPLD_GPIO_MASK(bit);
|
||||
kempld_write8(pld, reg, status);
|
||||
kempld_write8(pld, reg + (bit / 8), status);
|
||||
}
|
||||
|
||||
static int kempld_gpio_get_bit(struct kempld_device_data *pld, u8 reg, u8 bit)
|
||||
static int kempld_gpio_get_bit(struct kempld_device_data *pld,
|
||||
u8 reg, unsigned int bit)
|
||||
{
|
||||
u8 status;
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
status = kempld_read8(pld, reg);
|
||||
status = kempld_read8(pld, reg + (bit / 8));
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
return !!(status & KEMPLD_GPIO_MASK(bit));
|
||||
@@ -60,7 +76,34 @@ static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
|
||||
return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset);
|
||||
return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL, offset);
|
||||
}
|
||||
|
||||
static int kempld_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
u8 reg = KEMPLD_GPIO_LVL;
|
||||
unsigned int shift;
|
||||
|
||||
bits[0] &= ~mask[0];
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
|
||||
/* Try to reduce to a single 8 bits access if possible */
|
||||
for (shift = 0; shift < gpio->chip.ngpio; shift += 8, reg++) {
|
||||
unsigned long msk = (mask[0] >> shift) & 0xff;
|
||||
|
||||
if (!msk)
|
||||
continue;
|
||||
|
||||
bits[0] |= (kempld_read8(pld, reg) & msk) << shift;
|
||||
}
|
||||
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
@@ -70,7 +113,38 @@ static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
|
||||
kempld_gpio_bitop(pld, gpio->out_lvl_reg, offset, value);
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kempld_gpio_set_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
u8 reg = gpio->out_lvl_reg;
|
||||
unsigned int shift;
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
|
||||
/* Try to reduce to a single 8 bits access if possible */
|
||||
for (shift = 0; shift < gpio->chip.ngpio; shift += 8, reg++) {
|
||||
u8 val, msk = mask[0] >> shift;
|
||||
|
||||
if (!msk)
|
||||
continue;
|
||||
|
||||
if (msk != 0xFF)
|
||||
val = kempld_read8(pld, reg) & ~msk;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
val |= (bits[0] >> shift) & msk;
|
||||
kempld_write8(pld, reg, val);
|
||||
}
|
||||
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
return 0;
|
||||
@@ -82,7 +156,7 @@ static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 0);
|
||||
kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR, offset, 0);
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
return 0;
|
||||
@@ -95,8 +169,8 @@ static int kempld_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value);
|
||||
kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR_NUM(offset), offset, 1);
|
||||
kempld_gpio_bitop(pld, gpio->out_lvl_reg, offset, value);
|
||||
kempld_gpio_bitop(pld, KEMPLD_GPIO_DIR, offset, 1);
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
return 0;
|
||||
@@ -107,7 +181,7 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
|
||||
if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset))
|
||||
if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR, offset))
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
@@ -133,6 +207,180 @@ static int kempld_gpio_pincount(struct kempld_device_data *pld)
|
||||
return evt ? __ffs(evt) : 16;
|
||||
}
|
||||
|
||||
static void kempld_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
|
||||
gpio->ien &= ~BIT(irqd_to_hwirq(data));
|
||||
gpiochip_disable_irq(chip, irqd_to_hwirq(data));
|
||||
}
|
||||
|
||||
static void kempld_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
|
||||
gpiochip_enable_irq(chip, irqd_to_hwirq(data));
|
||||
gpio->ien |= BIT(irqd_to_hwirq(data));
|
||||
}
|
||||
|
||||
static int kempld_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
gpio->evt_low_high |= BIT(data->hwirq);
|
||||
gpio->evt_lvl_edge |= BIT(data->hwirq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
gpio->evt_low_high &= ~BIT(data->hwirq);
|
||||
gpio->evt_lvl_edge |= BIT(data->hwirq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
gpio->evt_low_high |= BIT(data->hwirq);
|
||||
gpio->evt_lvl_edge &= ~BIT(data->hwirq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
gpio->evt_low_high &= ~BIT(data->hwirq);
|
||||
gpio->evt_lvl_edge &= ~BIT(data->hwirq);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kempld_irq_bus_lock(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
|
||||
mutex_lock(&gpio->irq_lock);
|
||||
}
|
||||
|
||||
static void kempld_irq_bus_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct kempld_gpio_data *gpio = gpiochip_get_data(chip);
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
|
||||
kempld_get_mutex(pld);
|
||||
kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpio->evt_lvl_edge);
|
||||
kempld_write16(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpio->evt_low_high);
|
||||
kempld_write16(pld, KEMPLD_GPIO_IEN, gpio->ien);
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
mutex_unlock(&gpio->irq_lock);
|
||||
}
|
||||
|
||||
static const struct irq_chip kempld_irqchip = {
|
||||
.name = "kempld-gpio",
|
||||
.irq_mask = kempld_irq_mask,
|
||||
.irq_unmask = kempld_irq_unmask,
|
||||
.irq_set_type = kempld_irq_set_type,
|
||||
.irq_bus_lock = kempld_irq_bus_lock,
|
||||
.irq_bus_sync_unlock = kempld_irq_bus_sync_unlock,
|
||||
.flags = IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static irqreturn_t kempld_gpio_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct kempld_gpio_data *gpio = data;
|
||||
struct gpio_chip *chip = &gpio->chip;
|
||||
unsigned int pin, child_irq;
|
||||
unsigned long status;
|
||||
|
||||
kempld_get_mutex(gpio->pld);
|
||||
|
||||
status = kempld_read16(gpio->pld, KEMPLD_GPIO_STS);
|
||||
if (status)
|
||||
kempld_write16(gpio->pld, KEMPLD_GPIO_STS, status);
|
||||
|
||||
kempld_release_mutex(gpio->pld);
|
||||
|
||||
status &= gpio->ien;
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(pin, &status, chip->ngpio) {
|
||||
child_irq = irq_find_mapping(chip->irq.domain, pin);
|
||||
handle_nested_irq(child_irq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int kempld_gpio_irq_init(struct device *dev,
|
||||
struct kempld_gpio_data *gpio)
|
||||
{
|
||||
struct kempld_device_data *pld = gpio->pld;
|
||||
struct gpio_chip *chip = &gpio->chip;
|
||||
struct gpio_irq_chip *girq;
|
||||
unsigned int irq;
|
||||
int ret;
|
||||
|
||||
/* Get the IRQ configured by the BIOS in the PLD */
|
||||
kempld_get_mutex(pld);
|
||||
irq = kempld_read8(pld, KEMPLD_IRQ_GPIO);
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
if (irq == 0xff) {
|
||||
dev_info(dev, "GPIO controller has no IRQ support\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow overriding the IRQ with the module parameter */
|
||||
if (gpio_irq > 0) {
|
||||
dev_warn(dev, "Forcing IRQ to %d\n", gpio_irq);
|
||||
irq &= ~KEMPLD_IRQ_GPIO_MASK;
|
||||
irq |= gpio_irq & KEMPLD_IRQ_GPIO_MASK;
|
||||
}
|
||||
|
||||
if (!(irq & KEMPLD_IRQ_GPIO_MASK)) {
|
||||
dev_warn(dev, "No IRQ configured\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the current config, disable all child interrupts, clear them
|
||||
* and set the parent IRQ
|
||||
*/
|
||||
kempld_get_mutex(pld);
|
||||
gpio->evt_low_high = kempld_read16(pld, KEMPLD_GPIO_EVT_LOW_HIGH);
|
||||
gpio->evt_lvl_edge = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE);
|
||||
kempld_write16(pld, KEMPLD_GPIO_IEN, 0);
|
||||
kempld_write16(pld, KEMPLD_GPIO_STS, 0xFFFF);
|
||||
kempld_write16(pld, KEMPLD_IRQ_GPIO, irq);
|
||||
kempld_release_mutex(pld);
|
||||
|
||||
girq = &chip->irq;
|
||||
gpio_irq_chip_set_chip(girq, &kempld_irqchip);
|
||||
|
||||
girq->parent_handler = NULL;
|
||||
girq->num_parents = 0;
|
||||
girq->parents = NULL;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_simple_irq;
|
||||
girq->threaded = true;
|
||||
|
||||
mutex_init(&gpio->irq_lock);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq & KEMPLD_IRQ_GPIO_MASK,
|
||||
NULL, kempld_gpio_irq_handler,
|
||||
IRQF_ONESHOT, chip->label,
|
||||
gpio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kempld_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -152,6 +400,15 @@ static int kempld_gpio_probe(struct platform_device *pdev)
|
||||
if (!gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Starting with version 2.8 there is a dedicated register for the
|
||||
* output state, earlier versions share the register used to read
|
||||
* the line level.
|
||||
*/
|
||||
if (pld->info.spec_major > 2 || pld->info.spec_minor >= 8)
|
||||
gpio->out_lvl_reg = KEMPLD_GPIO_OUT_LVL;
|
||||
else
|
||||
gpio->out_lvl_reg = KEMPLD_GPIO_LVL;
|
||||
|
||||
gpio->pld = pld;
|
||||
|
||||
platform_set_drvdata(pdev, gpio);
|
||||
@@ -169,13 +426,19 @@ static int kempld_gpio_probe(struct platform_device *pdev)
|
||||
chip->direction_output = kempld_gpio_direction_output;
|
||||
chip->get_direction = kempld_gpio_get_direction;
|
||||
chip->get = kempld_gpio_get;
|
||||
chip->get_multiple = kempld_gpio_get_multiple;
|
||||
chip->set = kempld_gpio_set;
|
||||
chip->set_multiple = kempld_gpio_set_multiple;
|
||||
chip->ngpio = kempld_gpio_pincount(pld);
|
||||
if (chip->ngpio == 0) {
|
||||
dev_err(dev, "No GPIO pins detected\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = kempld_gpio_irq_init(dev, gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, chip, gpio);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not register GPIO chip\n");
|
||||
|
||||
@@ -55,7 +55,7 @@ static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val & BIT(offset * BITS_PER_GPO);
|
||||
return !!(val & BIT(offset * BITS_PER_GPO));
|
||||
}
|
||||
|
||||
static int lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
|
||||
@@ -10,14 +10,18 @@
|
||||
* Derived from drivers/gpio/pca953x.c
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_data/max732x.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/max732x.h>
|
||||
|
||||
/*
|
||||
* Each port of MAX732x (including MAX7319) falls into one of the
|
||||
@@ -207,22 +211,20 @@ static void max732x_gpio_set_mask(struct gpio_chip *gc, unsigned off, int mask,
|
||||
uint8_t reg_out;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0];
|
||||
reg_out = (reg_out & ~mask) | (val & mask);
|
||||
|
||||
ret = max732x_writeb(chip, is_group_a(chip, off), reg_out);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
/* update the shadow register then */
|
||||
if (off > 7)
|
||||
chip->reg_out[1] = reg_out;
|
||||
else
|
||||
chip->reg_out[0] = reg_out;
|
||||
out:
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
static int max732x_gpio_set_value(struct gpio_chip *gc, unsigned int off,
|
||||
@@ -329,7 +331,7 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
|
||||
if (chip->irq_features == INT_NO_MASK)
|
||||
return;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
guard(mutex)(&chip->lock);
|
||||
|
||||
switch (chip->irq_features) {
|
||||
case INT_INDEP_MASK:
|
||||
@@ -342,8 +344,6 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
|
||||
max732x_writeb(chip, 1, (uint8_t)msg);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
static void max732x_irq_mask(struct irq_data *d)
|
||||
|
||||
@@ -42,18 +42,16 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
||||
@@ -548,13 +548,6 @@ static void gpio_mpsse_ida_remove(void *data)
|
||||
ida_free(&gpio_mpsse_ida, priv->id);
|
||||
}
|
||||
|
||||
static void gpio_mpsse_usb_put_dev(void *data)
|
||||
{
|
||||
struct mpsse_priv *priv = data;
|
||||
|
||||
usb_put_dev(priv->udev);
|
||||
}
|
||||
|
||||
static int mpsse_init_valid_mask(struct gpio_chip *chip,
|
||||
unsigned long *valid_mask,
|
||||
unsigned int ngpios)
|
||||
@@ -598,11 +591,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
|
||||
|
||||
INIT_LIST_HEAD(&priv->workers);
|
||||
|
||||
priv->udev = usb_get_dev(interface_to_usbdev(interface));
|
||||
err = devm_add_action_or_reset(dev, gpio_mpsse_usb_put_dev, priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
priv->udev = interface_to_usbdev(interface);
|
||||
priv->intf = interface;
|
||||
priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
|
||||
|
||||
323
drivers/gpio/gpio-novalake-events.c
Normal file
323
drivers/gpio/gpio-novalake-events.c
Normal file
@@ -0,0 +1,323 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Intel Nova Lake GPIO-signaled ACPI events driver
|
||||
*
|
||||
* Copyright (c) 2026, Intel Corporation.
|
||||
*
|
||||
* Author: Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>
|
||||
*
|
||||
* Intel client platforms released in 2026 and later (starting with Intel Nova
|
||||
* Lake) support two modes of handling ACPI General Purpose Events (GPE):
|
||||
* exposed GPIO interrupt mode and legacy mode.
|
||||
*
|
||||
* By default, the platform uses legacy mode, handling GPEs as usual. If this
|
||||
* driver is installed, it signals to the platform (on every boot) that exposed
|
||||
* GPIO interrupt mode is supported. The platform then switches to exposed
|
||||
* mode, which takes effect on next boot. From the user perspective, this
|
||||
* change is transparent.
|
||||
*
|
||||
* However, if driver is uninstalled while in exposed interrupt mode, GPEs will
|
||||
* _not_ be handled until platform falls back to legacy mode. This means that
|
||||
* USB keyboard, mouse might not function properly for the fallback duration.
|
||||
* Fallback requires two reboots to take effect: on first reboot, platform no
|
||||
* longer receives signal from this driver and switches to legacy mode, which
|
||||
* takes effect on second boot.
|
||||
*
|
||||
* Example ACPI event: Power Management Event coming from motherboard PCH,
|
||||
* waking system from sleep following USB mouse hotplug.
|
||||
*
|
||||
* This driver supports up to 128 GPIO pins in each GPE block, per ACPI
|
||||
* specification v6.6 section 5.6.4.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gfp_types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
/*
|
||||
* GPE block has two registers, each register takes half the block size.
|
||||
* Convert size to bits to get total GPIO pin count.
|
||||
*/
|
||||
#define GPE_BLK_REG_SIZE(block_size) ((block_size) / 2)
|
||||
#define GPE_REG_PIN_COUNT(block_size) BYTES_TO_BITS(GPE_BLK_REG_SIZE(block_size))
|
||||
#define GPE_STS_REG_OFFSET 0
|
||||
#define GPE_EN_REG_OFFSET(block_size) GPE_BLK_REG_SIZE(block_size)
|
||||
|
||||
/**
|
||||
* struct nvl_gpio - Intel Nova Lake GPIO driver state
|
||||
* @gc: GPIO controller interface
|
||||
* @reg_base: Base address of the GPE registers
|
||||
* @lock: Guard register access
|
||||
* @blk_size: GPE block length
|
||||
*/
|
||||
struct nvl_gpio {
|
||||
struct gpio_chip gc;
|
||||
void __iomem *reg_base;
|
||||
raw_spinlock_t lock;
|
||||
size_t blk_size;
|
||||
};
|
||||
|
||||
static void __iomem *nvl_gpio_get_byte_addr(struct nvl_gpio *priv,
|
||||
unsigned int reg_offset,
|
||||
unsigned long gpio)
|
||||
{
|
||||
return priv->reg_base + reg_offset + gpio;
|
||||
}
|
||||
|
||||
static int nvl_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct nvl_gpio *priv = gpiochip_get_data(gc);
|
||||
unsigned int byte_idx = gpio / BITS_PER_BYTE;
|
||||
unsigned int bit_idx = gpio % BITS_PER_BYTE;
|
||||
void __iomem *addr;
|
||||
u8 reg;
|
||||
|
||||
addr = nvl_gpio_get_byte_addr(priv, GPE_STS_REG_OFFSET, byte_idx);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
reg = ioread8(addr);
|
||||
|
||||
return !!(reg & BIT(bit_idx));
|
||||
}
|
||||
|
||||
static const struct gpio_chip nvl_gpio_chip = {
|
||||
.owner = THIS_MODULE,
|
||||
.get = nvl_gpio_get,
|
||||
};
|
||||
|
||||
static int nvl_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
if (type & IRQ_TYPE_EDGE_BOTH)
|
||||
irq_set_handler_locked(d, handle_edge_irq);
|
||||
else if (type & IRQ_TYPE_LEVEL_MASK)
|
||||
irq_set_handler_locked(d, handle_level_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nvl_gpio_irq_mask_unmask(struct gpio_chip *gc, unsigned long hwirq,
|
||||
bool mask)
|
||||
{
|
||||
struct nvl_gpio *priv = gpiochip_get_data(gc);
|
||||
unsigned int byte_idx = hwirq / BITS_PER_BYTE;
|
||||
unsigned int bit_idx = hwirq % BITS_PER_BYTE;
|
||||
void __iomem *addr;
|
||||
u8 reg;
|
||||
|
||||
addr = nvl_gpio_get_byte_addr(priv, GPE_EN_REG_OFFSET(priv->blk_size), byte_idx);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
reg = ioread8(addr);
|
||||
if (mask)
|
||||
reg &= ~BIT(bit_idx);
|
||||
else
|
||||
reg |= BIT(bit_idx);
|
||||
iowrite8(reg, addr);
|
||||
}
|
||||
|
||||
static void nvl_gpio_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
gpiochip_enable_irq(gc, hwirq);
|
||||
nvl_gpio_irq_mask_unmask(gc, hwirq, false);
|
||||
}
|
||||
|
||||
static void nvl_gpio_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
|
||||
nvl_gpio_irq_mask_unmask(gc, hwirq, true);
|
||||
gpiochip_disable_irq(gc, hwirq);
|
||||
}
|
||||
|
||||
static void nvl_gpio_irq_ack(struct irq_data *d)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct nvl_gpio *priv = gpiochip_get_data(gc);
|
||||
irq_hw_number_t hwirq = irqd_to_hwirq(d);
|
||||
unsigned int byte_idx = hwirq / BITS_PER_BYTE;
|
||||
unsigned int bit_idx = hwirq % BITS_PER_BYTE;
|
||||
void __iomem *addr;
|
||||
u8 reg;
|
||||
|
||||
addr = nvl_gpio_get_byte_addr(priv, GPE_STS_REG_OFFSET, byte_idx);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&priv->lock);
|
||||
|
||||
reg = ioread8(addr);
|
||||
reg |= BIT(bit_idx);
|
||||
iowrite8(reg, addr);
|
||||
}
|
||||
|
||||
static const struct irq_chip nvl_gpio_irq_chip = {
|
||||
.name = "gpio-novalake",
|
||||
.irq_ack = nvl_gpio_irq_ack,
|
||||
.irq_mask = nvl_gpio_irq_mask,
|
||||
.irq_unmask = nvl_gpio_irq_unmask,
|
||||
.irq_set_type = nvl_gpio_irq_set_type,
|
||||
.flags = IRQCHIP_IMMUTABLE,
|
||||
GPIOCHIP_IRQ_RESOURCE_HELPERS,
|
||||
};
|
||||
|
||||
static irqreturn_t nvl_gpio_irq(int irq, void *data)
|
||||
{
|
||||
struct nvl_gpio *priv = data;
|
||||
const size_t block_size = priv->blk_size;
|
||||
unsigned int handled = 0;
|
||||
|
||||
for (unsigned int i = 0; i < block_size; i++) {
|
||||
const void __iomem *reg = priv->reg_base + i;
|
||||
unsigned long pending;
|
||||
unsigned long enabled;
|
||||
unsigned int bit_idx;
|
||||
|
||||
scoped_guard(raw_spinlock, &priv->lock) {
|
||||
pending = ioread8(reg + GPE_STS_REG_OFFSET);
|
||||
enabled = ioread8(reg + GPE_EN_REG_OFFSET(block_size));
|
||||
}
|
||||
pending &= enabled;
|
||||
|
||||
for_each_set_bit(bit_idx, &pending, BITS_PER_BYTE) {
|
||||
unsigned int hwirq = i * BITS_PER_BYTE + bit_idx;
|
||||
|
||||
generic_handle_domain_irq(priv->gc.irq.domain, hwirq);
|
||||
}
|
||||
|
||||
handled += pending ? 1 : 0;
|
||||
}
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
/* UUID for GPE device _DSM: 079406e6-bdea-49cf-8563-03e2811901cb */
|
||||
static const guid_t nvl_gpe_dsm_guid =
|
||||
GUID_INIT(0x079406e6, 0xbdea, 0x49cf,
|
||||
0x85, 0x63, 0x03, 0xe2, 0x81, 0x19, 0x01, 0xcb);
|
||||
|
||||
#define DSM_GPE_MODE_REV 1
|
||||
#define DSM_GPE_MODE_FN_INDEX 1
|
||||
#define DSM_ENABLE_GPE_MODE 1
|
||||
|
||||
static int nvl_acpi_enable_gpe_mode(struct device *dev)
|
||||
{
|
||||
union acpi_object argv4[2];
|
||||
union acpi_object *obj;
|
||||
|
||||
argv4[0].type = ACPI_TYPE_PACKAGE;
|
||||
argv4[0].package.count = 1;
|
||||
argv4[0].package.elements = &argv4[1];
|
||||
argv4[1].integer.type = ACPI_TYPE_INTEGER;
|
||||
argv4[1].integer.value = DSM_ENABLE_GPE_MODE;
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(dev), &nvl_gpe_dsm_guid,
|
||||
DSM_GPE_MODE_REV, DSM_GPE_MODE_FN_INDEX,
|
||||
argv4, ACPI_TYPE_BUFFER);
|
||||
if (!obj)
|
||||
return -EIO;
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvl_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
resource_size_t ioresource_size;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct nvl_gpio *priv;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret, irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* GPE block length should be non-negative multiple of two and allow up
|
||||
* to 128 pins. ACPI v6.6 section 5.2.9 and 5.6.4.
|
||||
*/
|
||||
ioresource_size = resource_size(res);
|
||||
if (!ioresource_size || ioresource_size % 2 || ioresource_size > 0x20)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"invalid GPE block length, resource: %pR\n",
|
||||
res);
|
||||
|
||||
regs = devm_ioport_map(dev, res->start, ioresource_size);
|
||||
if (!regs)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
|
||||
priv->reg_base = regs;
|
||||
priv->blk_size = ioresource_size;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(dev, irq, nvl_gpio_irq, IRQF_SHARED, dev_name(dev), priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->gc = nvl_gpio_chip;
|
||||
priv->gc.label = dev_name(dev);
|
||||
priv->gc.parent = dev;
|
||||
priv->gc.ngpio = GPE_REG_PIN_COUNT(priv->blk_size);
|
||||
priv->gc.base = -1;
|
||||
|
||||
girq = &priv->gc.irq;
|
||||
gpio_irq_chip_set_chip(girq, &nvl_gpio_irq_chip);
|
||||
girq->parent_handler = NULL;
|
||||
girq->num_parents = 0;
|
||||
girq->parents = NULL;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_bad_irq;
|
||||
|
||||
ret = devm_gpiochip_add_data(dev, &priv->gc, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nvl_acpi_enable_gpe_mode(dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id nvl_gpio_acpi_match[] = {
|
||||
{ "INTC1114" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, nvl_gpio_acpi_match);
|
||||
|
||||
static struct platform_driver nvl_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-novalake-events",
|
||||
.acpi_match_table = nvl_gpio_acpi_match,
|
||||
},
|
||||
.probe = nvl_gpio_probe,
|
||||
};
|
||||
module_platform_driver(nvl_gpio_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alan Borzeszkowski <alan.borzeszkowski@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Nova Lake ACPI GPIO events driver");
|
||||
@@ -351,6 +351,10 @@ static const struct of_device_id realtek_gpio_of_match[] = {
|
||||
{
|
||||
.compatible = "realtek,rtl9310-gpio",
|
||||
},
|
||||
{
|
||||
.compatible = "realtek,rtl9607-gpio",
|
||||
.data = (void *)GPIO_PORTS_REVERSED,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, realtek_gpio_of_match);
|
||||
|
||||
@@ -582,7 +582,7 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
|
||||
bank->gpio_chip = rockchip_gpiolib_chip;
|
||||
|
||||
gc = &bank->gpio_chip;
|
||||
gc->base = bank->pin_base;
|
||||
gc->base = -1;
|
||||
gc->ngpio = bank->nr_pins;
|
||||
gc->label = bank->name;
|
||||
gc->parent = bank->dev;
|
||||
|
||||
@@ -36,10 +36,9 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "dev-sync-probe.h"
|
||||
|
||||
#define GPIO_SIM_NGPIO_MAX 1024
|
||||
#define GPIO_SIM_PROP_MAX 5 /* Max 4 properties + sentinel. */
|
||||
#define GPIO_SIM_HOG_PROP_MAX 5
|
||||
#define GPIO_SIM_NUM_ATTRS 3 /* value, pull and sentinel */
|
||||
|
||||
static DEFINE_IDA(gpio_sim_ida);
|
||||
@@ -545,7 +544,7 @@ static struct platform_driver gpio_sim_driver = {
|
||||
};
|
||||
|
||||
struct gpio_sim_device {
|
||||
struct dev_sync_probe_data probe_data;
|
||||
struct platform_device *pdev;
|
||||
struct config_group group;
|
||||
|
||||
int id;
|
||||
@@ -561,8 +560,6 @@ struct gpio_sim_device {
|
||||
*/
|
||||
struct mutex lock;
|
||||
|
||||
struct gpiod_hog *hogs;
|
||||
|
||||
struct list_head bank_list;
|
||||
};
|
||||
|
||||
@@ -655,6 +652,7 @@ struct gpio_sim_hog {
|
||||
|
||||
char *name;
|
||||
int dir;
|
||||
bool active_low;
|
||||
};
|
||||
|
||||
static struct gpio_sim_hog *to_gpio_sim_hog(struct config_item *item)
|
||||
@@ -673,7 +671,7 @@ static bool gpio_sim_device_is_live(struct gpio_sim_device *dev)
|
||||
{
|
||||
lockdep_assert_held(&dev->lock);
|
||||
|
||||
return !!dev->probe_data.pdev;
|
||||
return !!dev->pdev;
|
||||
}
|
||||
|
||||
static char *gpio_sim_strdup_trimmed(const char *str, size_t count)
|
||||
@@ -695,7 +693,7 @@ static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
|
||||
|
||||
guard(mutex)(&dev->lock);
|
||||
|
||||
pdev = dev->probe_data.pdev;
|
||||
pdev = dev->pdev;
|
||||
if (pdev)
|
||||
return sprintf(page, "%s\n", dev_name(&pdev->dev));
|
||||
|
||||
@@ -774,102 +772,6 @@ static void gpio_sim_set_reserved_ranges(struct gpio_sim_bank *bank,
|
||||
}
|
||||
}
|
||||
|
||||
static void gpio_sim_remove_hogs(struct gpio_sim_device *dev)
|
||||
{
|
||||
struct gpiod_hog *hog;
|
||||
|
||||
if (!dev->hogs)
|
||||
return;
|
||||
|
||||
gpiod_remove_hogs(dev->hogs);
|
||||
|
||||
for (hog = dev->hogs; hog->chip_label; hog++) {
|
||||
kfree(hog->chip_label);
|
||||
kfree(hog->line_name);
|
||||
}
|
||||
|
||||
kfree(dev->hogs);
|
||||
dev->hogs = NULL;
|
||||
}
|
||||
|
||||
static int gpio_sim_add_hogs(struct gpio_sim_device *dev)
|
||||
{
|
||||
unsigned int num_hogs = 0, idx = 0;
|
||||
struct gpio_sim_bank *bank;
|
||||
struct gpio_sim_line *line;
|
||||
struct gpiod_hog *hog;
|
||||
|
||||
list_for_each_entry(bank, &dev->bank_list, siblings) {
|
||||
list_for_each_entry(line, &bank->line_list, siblings) {
|
||||
if (line->offset >= bank->num_lines)
|
||||
continue;
|
||||
|
||||
if (line->hog)
|
||||
num_hogs++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!num_hogs)
|
||||
return 0;
|
||||
|
||||
/* Allocate one more for the sentinel. */
|
||||
dev->hogs = kzalloc_objs(*dev->hogs, num_hogs + 1);
|
||||
if (!dev->hogs)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry(bank, &dev->bank_list, siblings) {
|
||||
list_for_each_entry(line, &bank->line_list, siblings) {
|
||||
if (line->offset >= bank->num_lines)
|
||||
continue;
|
||||
|
||||
if (!line->hog)
|
||||
continue;
|
||||
|
||||
hog = &dev->hogs[idx++];
|
||||
|
||||
/*
|
||||
* We need to make this string manually because at this
|
||||
* point the device doesn't exist yet and so dev_name()
|
||||
* is not available.
|
||||
*/
|
||||
if (gpio_sim_bank_has_label(bank))
|
||||
hog->chip_label = kstrdup(bank->label,
|
||||
GFP_KERNEL);
|
||||
else
|
||||
hog->chip_label = kasprintf(GFP_KERNEL,
|
||||
"gpio-sim.%u:%pfwP",
|
||||
dev->id,
|
||||
bank->swnode);
|
||||
if (!hog->chip_label) {
|
||||
gpio_sim_remove_hogs(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to duplicate this because the hog config
|
||||
* item can be removed at any time (and we can't block
|
||||
* it) and gpiolib doesn't make a deep copy of the hog
|
||||
* data.
|
||||
*/
|
||||
if (line->hog->name) {
|
||||
hog->line_name = kstrdup(line->hog->name,
|
||||
GFP_KERNEL);
|
||||
if (!hog->line_name) {
|
||||
gpio_sim_remove_hogs(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
hog->chip_hwnum = line->offset;
|
||||
hog->dflags = line->hog->dir;
|
||||
}
|
||||
}
|
||||
|
||||
gpiod_add_hogs(dev->hogs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fwnode_handle *
|
||||
gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
|
||||
struct fwnode_handle *parent)
|
||||
@@ -917,12 +819,61 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank,
|
||||
return fwnode_create_software_node(properties, parent);
|
||||
}
|
||||
|
||||
static int gpio_sim_bank_add_hogs(struct gpio_sim_bank *bank)
|
||||
{
|
||||
struct property_entry properties[GPIO_SIM_HOG_PROP_MAX];
|
||||
struct fwnode_handle *swnode;
|
||||
struct gpio_sim_line *line;
|
||||
struct gpio_sim_hog *hog;
|
||||
unsigned int idx;
|
||||
u32 gpios[2];
|
||||
|
||||
list_for_each_entry(line, &bank->line_list, siblings) {
|
||||
if (!line->hog)
|
||||
continue;
|
||||
|
||||
hog = line->hog;
|
||||
|
||||
gpios[0] = line->offset;
|
||||
gpios[1] = hog->active_low ? 1 : 0;
|
||||
|
||||
memset(properties, 0, sizeof(properties));
|
||||
|
||||
idx = 0;
|
||||
properties[idx++] = PROPERTY_ENTRY_BOOL("gpio-hog");
|
||||
properties[idx++] = PROPERTY_ENTRY_U32_ARRAY("gpios", gpios);
|
||||
properties[idx++] = PROPERTY_ENTRY_STRING("line-name", hog->name);
|
||||
|
||||
switch (hog->dir) {
|
||||
case GPIOD_IN:
|
||||
properties[idx++] = PROPERTY_ENTRY_BOOL("input");
|
||||
break;
|
||||
case GPIOD_OUT_HIGH:
|
||||
properties[idx++] = PROPERTY_ENTRY_BOOL("output-high");
|
||||
break;
|
||||
case GPIOD_OUT_LOW:
|
||||
properties[idx++] = PROPERTY_ENTRY_BOOL("output-low");
|
||||
break;
|
||||
default:
|
||||
/* Would have been validated at configfs store. */
|
||||
WARN(1, "Unexpected hog direction value: %d", hog->dir);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
swnode = fwnode_create_software_node(properties, bank->swnode);
|
||||
if (IS_ERR(swnode))
|
||||
return PTR_ERR(swnode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
|
||||
fwnode_for_each_child_node(swnode, child)
|
||||
fwnode_remove_software_node(child);
|
||||
gpio_sim_remove_swnode_recursive(child);
|
||||
|
||||
fwnode_remove_software_node(swnode);
|
||||
}
|
||||
@@ -947,6 +898,7 @@ static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev)
|
||||
static int gpio_sim_device_activate(struct gpio_sim_device *dev)
|
||||
{
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device *pdev;
|
||||
struct fwnode_handle *swnode;
|
||||
struct gpio_sim_bank *bank;
|
||||
int ret;
|
||||
@@ -974,29 +926,39 @@ static int gpio_sim_device_activate(struct gpio_sim_device *dev)
|
||||
bank->swnode = gpio_sim_make_bank_swnode(bank, swnode);
|
||||
if (IS_ERR(bank->swnode)) {
|
||||
ret = PTR_ERR(bank->swnode);
|
||||
gpio_sim_remove_swnode_recursive(swnode);
|
||||
return ret;
|
||||
goto err_remove_swnode;
|
||||
}
|
||||
}
|
||||
|
||||
ret = gpio_sim_add_hogs(dev);
|
||||
if (ret) {
|
||||
gpio_sim_remove_swnode_recursive(swnode);
|
||||
return ret;
|
||||
ret = gpio_sim_bank_add_hogs(bank);
|
||||
if (ret)
|
||||
goto err_remove_swnode;
|
||||
}
|
||||
|
||||
pdevinfo.name = "gpio-sim";
|
||||
pdevinfo.fwnode = swnode;
|
||||
pdevinfo.id = dev->id;
|
||||
|
||||
ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo);
|
||||
if (ret) {
|
||||
gpio_sim_remove_hogs(dev);
|
||||
gpio_sim_remove_swnode_recursive(swnode);
|
||||
return ret;
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
ret = PTR_ERR(pdev);
|
||||
goto err_remove_swnode;
|
||||
}
|
||||
|
||||
wait_for_device_probe();
|
||||
if (!device_is_bound(&pdev->dev)) {
|
||||
ret = -ENXIO;
|
||||
goto err_unregister_pdev;
|
||||
}
|
||||
|
||||
dev->pdev = pdev;
|
||||
return 0;
|
||||
|
||||
err_unregister_pdev:
|
||||
platform_device_unregister(pdev);
|
||||
err_remove_swnode:
|
||||
gpio_sim_remove_swnode_recursive(swnode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)
|
||||
@@ -1005,9 +967,9 @@ static void gpio_sim_device_deactivate(struct gpio_sim_device *dev)
|
||||
|
||||
lockdep_assert_held(&dev->lock);
|
||||
|
||||
swnode = dev_fwnode(&dev->probe_data.pdev->dev);
|
||||
dev_sync_probe_unregister(&dev->probe_data);
|
||||
gpio_sim_remove_hogs(dev);
|
||||
swnode = dev_fwnode(&dev->pdev->dev);
|
||||
platform_device_unregister(dev->pdev);
|
||||
dev->pdev = NULL;
|
||||
gpio_sim_remove_swnode_recursive(swnode);
|
||||
}
|
||||
|
||||
@@ -1109,7 +1071,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item,
|
||||
guard(mutex)(&dev->lock);
|
||||
|
||||
if (gpio_sim_device_is_live(dev))
|
||||
return device_for_each_child(&dev->probe_data.pdev->dev, &ctx,
|
||||
return device_for_each_child(&dev->pdev->dev, &ctx,
|
||||
gpio_sim_emit_chip_name);
|
||||
|
||||
return sprintf(page, "none\n");
|
||||
@@ -1365,9 +1327,46 @@ gpio_sim_hog_config_direction_store(struct config_item *item,
|
||||
|
||||
CONFIGFS_ATTR(gpio_sim_hog_config_, direction);
|
||||
|
||||
static ssize_t gpio_sim_hog_config_active_low_show(struct config_item *item,
|
||||
char *page)
|
||||
{
|
||||
struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
|
||||
struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
|
||||
|
||||
guard(mutex)(&dev->lock);
|
||||
|
||||
return sprintf(page, "%c\n", hog->active_low ? '1' : '0');
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
gpio_sim_hog_config_active_low_store(struct config_item *item,
|
||||
const char *page, size_t count)
|
||||
{
|
||||
struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
|
||||
struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
|
||||
bool active_low;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&dev->lock);
|
||||
|
||||
if (gpio_sim_device_is_live(dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = kstrtobool(page, &active_low);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hog->active_low = active_low;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR(gpio_sim_hog_config_, active_low);
|
||||
|
||||
static struct configfs_attribute *gpio_sim_hog_config_attrs[] = {
|
||||
&gpio_sim_hog_config_attr_name,
|
||||
&gpio_sim_hog_config_attr_direction,
|
||||
&gpio_sim_hog_config_attr_active_low,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -1583,8 +1582,6 @@ gpio_sim_config_make_device_group(struct config_group *group, const char *name)
|
||||
mutex_init(&dev->lock);
|
||||
INIT_LIST_HEAD(&dev->bank_list);
|
||||
|
||||
dev_sync_probe_init(&dev->probe_data);
|
||||
|
||||
return &no_free_ptr(dev)->group;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ static int xway_stp_get(struct gpio_chip *gc, unsigned int gpio)
|
||||
{
|
||||
struct xway_stp *chip = gpiochip_get_data(gc);
|
||||
|
||||
return (xway_stp_r32(chip->virt, XWAY_STP_CPU0) & BIT(gpio));
|
||||
return !!(xway_stp_r32(chip->virt, XWAY_STP_CPU0) & BIT(gpio));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -125,7 +125,6 @@ struct tegra_gpio_soc {
|
||||
struct tegra_gpio {
|
||||
struct gpio_chip gpio;
|
||||
unsigned int num_irq;
|
||||
unsigned int *irq;
|
||||
|
||||
const struct tegra_gpio_soc *soc;
|
||||
unsigned int num_irqs_per_bank;
|
||||
@@ -133,6 +132,8 @@ struct tegra_gpio {
|
||||
|
||||
void __iomem *secure;
|
||||
void __iomem *base;
|
||||
|
||||
unsigned int irq[] __counted_by(num_irq);
|
||||
};
|
||||
|
||||
static const struct tegra_gpio_port *
|
||||
@@ -857,12 +858,18 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
struct device_node *np;
|
||||
struct resource *res;
|
||||
char **names;
|
||||
int err;
|
||||
int node, err;
|
||||
|
||||
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
|
||||
err = platform_irq_count(pdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
gpio = devm_kzalloc(&pdev->dev, struct_size(gpio, irq, err), GFP_KERNEL);
|
||||
if (!gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio->num_irq = err;
|
||||
|
||||
gpio->soc = device_get_match_data(&pdev->dev);
|
||||
gpio->gpio.label = gpio->soc->name;
|
||||
gpio->gpio.parent = &pdev->dev;
|
||||
@@ -889,21 +896,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(gpio->base))
|
||||
return PTR_ERR(gpio->base);
|
||||
|
||||
err = platform_irq_count(pdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
gpio->num_irq = err;
|
||||
|
||||
err = tegra186_gpio_irqs_per_bank(gpio);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
gpio->irq = devm_kcalloc(&pdev->dev, gpio->num_irq, sizeof(*gpio->irq),
|
||||
GFP_KERNEL);
|
||||
if (!gpio->irq)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < gpio->num_irq; i++) {
|
||||
err = platform_get_irq(pdev, i);
|
||||
if (err < 0)
|
||||
@@ -937,16 +933,22 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
if (!names)
|
||||
return -ENOMEM;
|
||||
|
||||
node = dev_to_node(&pdev->dev);
|
||||
|
||||
for (i = 0, offset = 0; i < gpio->soc->num_ports; i++) {
|
||||
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
|
||||
char *name;
|
||||
|
||||
for (j = 0; j < port->pins; j++) {
|
||||
if (gpio->soc->prefix)
|
||||
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "%s-P%s.%02x",
|
||||
gpio->soc->prefix, port->name, j);
|
||||
if (node >= 0)
|
||||
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL,
|
||||
"%d-%sP%s.%02x", node,
|
||||
gpio->soc->prefix ?: "",
|
||||
port->name, j);
|
||||
else
|
||||
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "P%s.%02x",
|
||||
name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL,
|
||||
"%sP%s.%02x",
|
||||
gpio->soc->prefix ?: "",
|
||||
port->name, j);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
@@ -1373,6 +1375,9 @@ static const struct tegra_gpio_soc tegra256_main_soc = {
|
||||
.has_vm_support = true,
|
||||
};
|
||||
|
||||
/* Macro to define GPIO name prefix with separator */
|
||||
#define TEGRA_GPIO_PREFIX(_x) _x "-"
|
||||
|
||||
#define TEGRA410_COMPUTE_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
TEGRA_GPIO_PORT(TEGRA410_COMPUTE, _name, _bank, _port, _pins)
|
||||
|
||||
@@ -1388,7 +1393,7 @@ static const struct tegra_gpio_soc tegra410_compute_soc = {
|
||||
.num_ports = ARRAY_SIZE(tegra410_compute_ports),
|
||||
.ports = tegra410_compute_ports,
|
||||
.name = "tegra410-gpio-compute",
|
||||
.prefix = "COMPUTE",
|
||||
.prefix = TEGRA_GPIO_PREFIX("COMPUTE"),
|
||||
.num_irqs_per_bank = 8,
|
||||
.instance = 0,
|
||||
};
|
||||
@@ -1418,7 +1423,7 @@ static const struct tegra_gpio_soc tegra410_system_soc = {
|
||||
.num_ports = ARRAY_SIZE(tegra410_system_ports),
|
||||
.ports = tegra410_system_ports,
|
||||
.name = "tegra410-gpio-system",
|
||||
.prefix = "SYSTEM",
|
||||
.prefix = TEGRA_GPIO_PREFIX("SYSTEM"),
|
||||
.num_irqs_per_bank = 8,
|
||||
.instance = 0,
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ static int tps65086_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val & BIT(4 + offset);
|
||||
return !!(val & BIT(4 + offset));
|
||||
}
|
||||
|
||||
static int tps65086_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define DEFAULT_PIN_NUMBER 16
|
||||
#define INPUT_REG_OFFSET 0x00
|
||||
#define OUTPUT_REG_OFFSET 0x02
|
||||
#define DIRECTION_REG_OFFSET 0x04
|
||||
@@ -23,7 +22,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
|
||||
struct gpio_generic_chip *chip;
|
||||
void __iomem *base_addr;
|
||||
int retval;
|
||||
u32 ngpios;
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
@@ -33,12 +31,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(base_addr))
|
||||
return PTR_ERR(base_addr);
|
||||
|
||||
retval = device_property_read_u32(dev, "ngpios", &ngpios);
|
||||
if (retval == -EINVAL)
|
||||
ngpios = DEFAULT_PIN_NUMBER;
|
||||
else if (retval)
|
||||
return retval;
|
||||
|
||||
config = (struct gpio_generic_chip_config) {
|
||||
.dev = dev,
|
||||
.sz = 2,
|
||||
@@ -52,8 +44,6 @@ static int ts4800_gpio_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, retval,
|
||||
"failed to initialize the generic GPIO chip\n");
|
||||
|
||||
chip->gc.ngpio = ngpios;
|
||||
|
||||
return devm_gpiochip_add_data(dev, &chip->gc, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip,
|
||||
|
||||
/* if io is set to output, just return the saved value */
|
||||
if (gpio->gpiob_out & (1 << offset))
|
||||
return gpio->gpiob_val & (1 << offset);
|
||||
return !!(gpio->gpiob_val & (1 << offset));
|
||||
|
||||
mutex_lock(&vb->lock);
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "dev-sync-probe.h"
|
||||
|
||||
#define GPIO_VIRTUSER_NAME_BUF_LEN 32
|
||||
|
||||
static DEFINE_IDA(gpio_virtuser_ida);
|
||||
@@ -978,7 +976,7 @@ static struct platform_driver gpio_virtuser_driver = {
|
||||
};
|
||||
|
||||
struct gpio_virtuser_device {
|
||||
struct dev_sync_probe_data probe_data;
|
||||
struct platform_device *pdev;
|
||||
struct config_group group;
|
||||
|
||||
int id;
|
||||
@@ -1002,7 +1000,7 @@ gpio_virtuser_device_is_live(struct gpio_virtuser_device *dev)
|
||||
{
|
||||
lockdep_assert_held(&dev->lock);
|
||||
|
||||
return !!dev->probe_data.pdev;
|
||||
return !!dev->pdev;
|
||||
}
|
||||
|
||||
struct gpio_virtuser_lookup {
|
||||
@@ -1342,7 +1340,7 @@ gpio_virtuser_device_config_dev_name_show(struct config_item *item,
|
||||
|
||||
guard(mutex)(&dev->lock);
|
||||
|
||||
pdev = dev->probe_data.pdev;
|
||||
pdev = dev->pdev;
|
||||
if (pdev)
|
||||
return sprintf(page, "%s\n", dev_name(&pdev->dev));
|
||||
|
||||
@@ -1450,6 +1448,7 @@ static int
|
||||
gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)
|
||||
{
|
||||
struct platform_device_info pdevinfo;
|
||||
struct platform_device *pdev;
|
||||
struct fwnode_handle *swnode;
|
||||
int ret;
|
||||
|
||||
@@ -1471,12 +1470,23 @@ gpio_virtuser_device_activate(struct gpio_virtuser_device *dev)
|
||||
if (ret)
|
||||
goto err_remove_swnode;
|
||||
|
||||
ret = dev_sync_probe_register(&dev->probe_data, &pdevinfo);
|
||||
if (ret)
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev)) {
|
||||
ret = PTR_ERR(pdev);
|
||||
goto err_remove_lookup_table;
|
||||
}
|
||||
|
||||
wait_for_device_probe();
|
||||
if (!device_is_bound(&pdev->dev)) {
|
||||
ret = -ENXIO;
|
||||
goto err_unregister_pdev;
|
||||
}
|
||||
|
||||
dev->pdev = pdev;
|
||||
return 0;
|
||||
|
||||
err_unregister_pdev:
|
||||
platform_device_unregister(pdev);
|
||||
err_remove_lookup_table:
|
||||
gpio_virtuser_remove_lookup_table(dev);
|
||||
err_remove_swnode:
|
||||
@@ -1492,8 +1502,9 @@ gpio_virtuser_device_deactivate(struct gpio_virtuser_device *dev)
|
||||
|
||||
lockdep_assert_held(&dev->lock);
|
||||
|
||||
swnode = dev_fwnode(&dev->probe_data.pdev->dev);
|
||||
dev_sync_probe_unregister(&dev->probe_data);
|
||||
swnode = dev_fwnode(&dev->pdev->dev);
|
||||
platform_device_unregister(dev->pdev);
|
||||
dev->pdev = NULL;
|
||||
gpio_virtuser_remove_lookup_table(dev);
|
||||
fwnode_remove_software_node(swnode);
|
||||
}
|
||||
@@ -1723,7 +1734,6 @@ gpio_virtuser_config_make_device_group(struct config_group *group,
|
||||
&gpio_virtuser_device_config_group_type);
|
||||
mutex_init(&dev->lock);
|
||||
INIT_LIST_HEAD(&dev->lookup_list);
|
||||
dev_sync_probe_init(&dev->probe_data);
|
||||
|
||||
return &no_free_ptr(dev)->group;
|
||||
}
|
||||
|
||||
@@ -1220,75 +1220,6 @@ static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip)
|
||||
}
|
||||
}
|
||||
|
||||
static struct gpio_desc *
|
||||
acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip,
|
||||
struct fwnode_handle *fwnode,
|
||||
const char **name,
|
||||
unsigned long *lflags,
|
||||
enum gpiod_flags *dflags)
|
||||
{
|
||||
struct gpio_chip *chip = achip->chip;
|
||||
struct gpio_desc *desc;
|
||||
u32 gpios[2];
|
||||
int ret;
|
||||
|
||||
*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
||||
*dflags = GPIOD_ASIS;
|
||||
*name = NULL;
|
||||
|
||||
ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios,
|
||||
ARRAY_SIZE(gpios));
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
desc = gpiochip_get_desc(chip, gpios[0]);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
|
||||
if (gpios[1])
|
||||
*lflags |= GPIO_ACTIVE_LOW;
|
||||
|
||||
if (fwnode_property_present(fwnode, "input"))
|
||||
*dflags |= GPIOD_IN;
|
||||
else if (fwnode_property_present(fwnode, "output-low"))
|
||||
*dflags |= GPIOD_OUT_LOW;
|
||||
else if (fwnode_property_present(fwnode, "output-high"))
|
||||
*dflags |= GPIOD_OUT_HIGH;
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
fwnode_property_read_string(fwnode, "line-name", name);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip)
|
||||
{
|
||||
struct gpio_chip *chip = achip->chip;
|
||||
|
||||
device_for_each_child_node_scoped(chip->parent, fwnode) {
|
||||
unsigned long lflags;
|
||||
enum gpiod_flags dflags;
|
||||
struct gpio_desc *desc;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
if (!fwnode_property_present(fwnode, "gpio-hog"))
|
||||
continue;
|
||||
|
||||
desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name,
|
||||
&lflags, &dflags);
|
||||
if (IS_ERR(desc))
|
||||
continue;
|
||||
|
||||
ret = gpiod_hog(desc, name, lflags, dflags);
|
||||
if (ret) {
|
||||
dev_err(chip->parent, "Failed to hog GPIO\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void acpi_gpiochip_add(struct gpio_chip *chip)
|
||||
{
|
||||
struct acpi_gpio_chip *acpi_gpio;
|
||||
@@ -1321,7 +1252,6 @@ void acpi_gpiochip_add(struct gpio_chip *chip)
|
||||
}
|
||||
|
||||
acpi_gpiochip_request_regions(acpi_gpio);
|
||||
acpi_gpiochip_scan_gpios(acpi_gpio);
|
||||
acpi_dev_clear_dependencies(adev);
|
||||
}
|
||||
|
||||
|
||||
@@ -1586,15 +1586,16 @@ static const struct file_operations line_fileops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
DEFINE_FREE(linereq_free, struct linereq *, if (!IS_ERR_OR_NULL(_T)) linereq_free(_T))
|
||||
|
||||
static int linereq_create(struct gpio_device *gdev, void __user *ip)
|
||||
{
|
||||
struct gpio_v2_line_request ulr;
|
||||
struct gpio_v2_line_config *lc;
|
||||
struct linereq *lr;
|
||||
struct file *file;
|
||||
struct linereq *lr __free(linereq_free) = NULL;
|
||||
u64 flags, edflags;
|
||||
unsigned int i;
|
||||
int fd, ret;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&ulr, ip, sizeof(ulr)))
|
||||
return -EFAULT;
|
||||
@@ -1627,10 +1628,8 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
|
||||
/* label is only initialized if consumer is set */
|
||||
lr->label = kstrndup(ulr.consumer, sizeof(ulr.consumer) - 1,
|
||||
GFP_KERNEL);
|
||||
if (!lr->label) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_linereq;
|
||||
}
|
||||
if (!lr->label)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_init(&lr->config_mutex);
|
||||
@@ -1649,14 +1648,12 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
|
||||
u32 offset = ulr.offsets[i];
|
||||
struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
|
||||
|
||||
if (IS_ERR(desc)) {
|
||||
ret = PTR_ERR(desc);
|
||||
goto out_free_linereq;
|
||||
}
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
ret = gpiod_request_user(desc, lr->label);
|
||||
if (ret)
|
||||
goto out_free_linereq;
|
||||
return ret;
|
||||
|
||||
lr->lines[i].desc = desc;
|
||||
flags = gpio_v2_line_config_flags(lc, i);
|
||||
@@ -1664,7 +1661,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
|
||||
|
||||
ret = gpiod_set_transitory(desc, false);
|
||||
if (ret < 0)
|
||||
goto out_free_linereq;
|
||||
return ret;
|
||||
|
||||
edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
|
||||
/*
|
||||
@@ -1676,16 +1673,16 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
|
||||
|
||||
ret = gpiod_direction_output_nonotify(desc, val);
|
||||
if (ret)
|
||||
goto out_free_linereq;
|
||||
return ret;
|
||||
} else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
|
||||
ret = gpiod_direction_input_nonotify(desc);
|
||||
if (ret)
|
||||
goto out_free_linereq;
|
||||
return ret;
|
||||
|
||||
ret = edge_detector_setup(&lr->lines[i], lc, i,
|
||||
edflags);
|
||||
if (ret)
|
||||
goto out_free_linereq;
|
||||
return ret;
|
||||
}
|
||||
|
||||
lr->lines[i].edflags = edflags;
|
||||
@@ -1700,44 +1697,25 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
|
||||
ret = blocking_notifier_chain_register(&gdev->device_notifier,
|
||||
&lr->device_unregistered_nb);
|
||||
if (ret)
|
||||
goto out_free_linereq;
|
||||
return ret;
|
||||
|
||||
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out_free_linereq;
|
||||
}
|
||||
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
|
||||
anon_inode_getfile("gpio-line", &line_fileops, lr,
|
||||
O_RDONLY | O_CLOEXEC));
|
||||
if (fdf.err)
|
||||
return fdf.err;
|
||||
retain_and_null_ptr(lr);
|
||||
|
||||
file = anon_inode_getfile("gpio-line", &line_fileops, lr,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto out_put_unused_fd;
|
||||
}
|
||||
|
||||
ulr.fd = fd;
|
||||
if (copy_to_user(ip, &ulr, sizeof(ulr))) {
|
||||
/*
|
||||
* fput() will trigger the release() callback, so do not go onto
|
||||
* the regular error cleanup path here.
|
||||
*/
|
||||
fput(file);
|
||||
put_unused_fd(fd);
|
||||
ulr.fd = fd_prepare_fd(fdf);
|
||||
if (copy_to_user(ip, &ulr, sizeof(ulr)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fd_install(fd, file);
|
||||
fd_publish(fdf);
|
||||
|
||||
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
|
||||
lr->num_lines);
|
||||
ulr.num_lines);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put_unused_fd:
|
||||
put_unused_fd(fd);
|
||||
out_free_linereq:
|
||||
linereq_free(lr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIO_CDEV_V1
|
||||
@@ -2010,16 +1988,16 @@ static irqreturn_t lineevent_irq_handler(int irq, void *p)
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
DEFINE_FREE(lineevent_free, struct lineevent_state *, if (!IS_ERR_OR_NULL(_T)) lineevent_free(_T))
|
||||
|
||||
static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
||||
{
|
||||
struct gpioevent_request eventreq;
|
||||
struct lineevent_state *le;
|
||||
struct lineevent_state *le __free(lineevent_free) = NULL;
|
||||
struct gpio_desc *desc;
|
||||
struct file *file;
|
||||
u32 offset;
|
||||
u32 lflags;
|
||||
u32 eflags;
|
||||
int fd;
|
||||
int ret;
|
||||
int irq, irqflags = 0;
|
||||
char *label;
|
||||
@@ -2064,15 +2042,13 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
||||
le->label = kstrndup(eventreq.consumer_label,
|
||||
sizeof(eventreq.consumer_label) - 1,
|
||||
GFP_KERNEL);
|
||||
if (!le->label) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_le;
|
||||
}
|
||||
if (!le->label)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = gpiod_request_user(desc, le->label);
|
||||
if (ret)
|
||||
goto out_free_le;
|
||||
return ret;
|
||||
le->desc = desc;
|
||||
le->eflags = eflags;
|
||||
|
||||
@@ -2080,15 +2056,13 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
||||
|
||||
ret = gpiod_direction_input(desc);
|
||||
if (ret)
|
||||
goto out_free_le;
|
||||
return ret;
|
||||
|
||||
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
|
||||
|
||||
irq = gpiod_to_irq(desc);
|
||||
if (irq <= 0) {
|
||||
ret = -ENODEV;
|
||||
goto out_free_le;
|
||||
}
|
||||
if (irq <= 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (eflags & GPIOEVENT_REQUEST_RISING_EDGE)
|
||||
irqflags |= test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags) ?
|
||||
@@ -2105,13 +2079,11 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
||||
ret = blocking_notifier_chain_register(&gdev->device_notifier,
|
||||
&le->device_unregistered_nb);
|
||||
if (ret)
|
||||
goto out_free_le;
|
||||
return ret;
|
||||
|
||||
label = make_irq_label(le->label);
|
||||
if (IS_ERR(label)) {
|
||||
ret = PTR_ERR(label);
|
||||
goto out_free_le;
|
||||
}
|
||||
if (IS_ERR(label))
|
||||
return PTR_ERR(label);
|
||||
|
||||
/* Request a thread to read the events */
|
||||
ret = request_threaded_irq(irq,
|
||||
@@ -2122,46 +2094,25 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
||||
le);
|
||||
if (ret) {
|
||||
free_irq_label(label);
|
||||
goto out_free_le;
|
||||
return ret;
|
||||
}
|
||||
|
||||
le->irq = irq;
|
||||
|
||||
fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
ret = fd;
|
||||
goto out_free_le;
|
||||
}
|
||||
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
|
||||
anon_inode_getfile("gpio-event", &lineevent_fileops, le,
|
||||
O_RDONLY | O_CLOEXEC));
|
||||
if (fdf.err)
|
||||
return fdf.err;
|
||||
retain_and_null_ptr(le);
|
||||
|
||||
file = anon_inode_getfile("gpio-event",
|
||||
&lineevent_fileops,
|
||||
le,
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto out_put_unused_fd;
|
||||
}
|
||||
|
||||
eventreq.fd = fd;
|
||||
if (copy_to_user(ip, &eventreq, sizeof(eventreq))) {
|
||||
/*
|
||||
* fput() will trigger the release() callback, so do not go onto
|
||||
* the regular error cleanup path here.
|
||||
*/
|
||||
fput(file);
|
||||
put_unused_fd(fd);
|
||||
eventreq.fd = fd_prepare_fd(fdf);
|
||||
if (copy_to_user(ip, &eventreq, sizeof(eventreq)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fd_install(fd, file);
|
||||
fd_publish(fdf);
|
||||
|
||||
return 0;
|
||||
|
||||
out_put_unused_fd:
|
||||
put_unused_fd(fd);
|
||||
out_free_le:
|
||||
lineevent_free(le);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2,
|
||||
@@ -2689,12 +2640,6 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
|
||||
struct gpio_chardev_data *cdev;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
guard(srcu)(&gdev->srcu);
|
||||
|
||||
/* Fail on open if the backing gpiochip is gone */
|
||||
if (!rcu_access_pointer(gdev->chip))
|
||||
return -ENODEV;
|
||||
|
||||
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
||||
if (!cdev)
|
||||
return -ENOMEM;
|
||||
@@ -2782,9 +2727,9 @@ static const struct file_operations gpio_fileops = {
|
||||
#endif
|
||||
};
|
||||
|
||||
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
|
||||
int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt)
|
||||
{
|
||||
struct gpio_chip *gc;
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
int ret;
|
||||
|
||||
cdev_init(&gdev->chrdev, &gpio_fileops);
|
||||
@@ -2802,14 +2747,6 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
|
||||
return ret;
|
||||
}
|
||||
|
||||
guard(srcu)(&gdev->srcu);
|
||||
gc = srcu_dereference(gdev->chip, &gdev->srcu);
|
||||
if (!gc) {
|
||||
cdev_device_del(&gdev->chrdev, &gdev->dev);
|
||||
destroy_workqueue(gdev->line_state_wq);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
struct gpio_device;
|
||||
|
||||
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt);
|
||||
int gpiolib_cdev_register(struct gpio_chip *gc, dev_t devt);
|
||||
void gpiolib_cdev_unregister(struct gpio_device *gdev);
|
||||
|
||||
#endif /* GPIOLIB_CDEV_H */
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
@@ -446,32 +446,6 @@ out:
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_get_named_gpio() - Get a GPIO number to use with GPIO API
|
||||
* @np: device node to get GPIO from
|
||||
* @propname: Name of property containing gpio specifier(s)
|
||||
* @index: index of the GPIO
|
||||
*
|
||||
* **DEPRECATED** This function is deprecated and must not be used in new code.
|
||||
*
|
||||
* Returns:
|
||||
* GPIO number to use with Linux generic GPIO API, or one of the errno
|
||||
* value on the error condition.
|
||||
*/
|
||||
int of_get_named_gpio(const struct device_node *np, const char *propname,
|
||||
int index)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
|
||||
desc = of_get_named_gpiod_flags(np, propname, index, NULL);
|
||||
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
else
|
||||
return desc_to_gpio(desc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_get_named_gpio);
|
||||
|
||||
/* Converts gpio_lookup_flags into bitmask of GPIO_* values */
|
||||
static unsigned long of_convert_gpio_flags(enum of_gpio_flags flags)
|
||||
{
|
||||
@@ -542,6 +516,10 @@ static struct gpio_desc *of_find_gpio_rename(struct device_node *np,
|
||||
{ "reset", "reset-n-io", "marvell,nfc-uart" },
|
||||
{ "reset", "reset-n-io", "mrvl,nfc-uart" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_NFC_S3FWRN5_I2C)
|
||||
{ "en", "s3fwrn5,en-gpios", "samsung,s3fwrn5-i2c" },
|
||||
{ "wake", "s3fwrn5,fw-gpios", "samsung,s3fwrn5-i2c" },
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_PCI_LANTIQ)
|
||||
/* MIPS Lantiq PCI */
|
||||
{ "reset", "gpio-reset", "lantiq,pci-xway" },
|
||||
@@ -735,139 +713,26 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API
|
||||
* @np: device node to get GPIO from
|
||||
* @chip: GPIO chip whose hog is parsed
|
||||
* @idx: Index of the GPIO to parse
|
||||
* @name: GPIO line name
|
||||
* @lflags: bitmask of gpio_lookup_flags GPIO_* values - returned from
|
||||
* of_find_gpio() or of_parse_own_gpio()
|
||||
* @dflags: gpiod_flags - optional GPIO initialization flags
|
||||
*
|
||||
* Returns:
|
||||
* GPIO descriptor to use with Linux GPIO API, or one of the errno
|
||||
* value on the error condition.
|
||||
*/
|
||||
static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
||||
struct gpio_chip *chip,
|
||||
unsigned int idx, const char **name,
|
||||
unsigned long *lflags,
|
||||
enum gpiod_flags *dflags)
|
||||
int of_gpiochip_get_lflags(struct gpio_chip *chip,
|
||||
struct fwnode_reference_args *gpiospec,
|
||||
unsigned long *lflags)
|
||||
{
|
||||
struct device_node *chip_np;
|
||||
enum of_gpio_flags xlate_flags;
|
||||
struct of_phandle_args gpiospec;
|
||||
struct of_phandle_args args;
|
||||
struct gpio_desc *desc;
|
||||
unsigned int i;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
chip_np = dev_of_node(&chip->gpiodev->dev);
|
||||
if (!chip_np)
|
||||
return ERR_PTR(-EINVAL);
|
||||
args.np = to_of_node(gpiospec->fwnode);
|
||||
args.args_count = gpiospec->nargs;
|
||||
|
||||
xlate_flags = 0;
|
||||
*lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
||||
*dflags = GPIOD_ASIS;
|
||||
for (int i = 0; i < args.args_count; i++)
|
||||
args.args[i] = gpiospec->args[i];
|
||||
|
||||
ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
gpiospec.np = chip_np;
|
||||
gpiospec.args_count = tmp;
|
||||
|
||||
for (i = 0; i < tmp; i++) {
|
||||
ret = of_property_read_u32_index(np, "gpios", idx * tmp + i,
|
||||
&gpiospec.args[i]);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, &xlate_flags);
|
||||
desc = of_xlate_and_get_gpiod_flags(chip, &args, &xlate_flags);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
return PTR_ERR(desc);
|
||||
|
||||
*lflags = of_convert_gpio_flags(xlate_flags);
|
||||
|
||||
if (of_property_read_bool(np, "input"))
|
||||
*dflags |= GPIOD_IN;
|
||||
else if (of_property_read_bool(np, "output-low"))
|
||||
*dflags |= GPIOD_OUT_LOW;
|
||||
else if (of_property_read_bool(np, "output-high"))
|
||||
*dflags |= GPIOD_OUT_HIGH;
|
||||
else {
|
||||
pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n",
|
||||
desc_to_gpio(desc), np);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (name && of_property_read_string(np, "line-name", name))
|
||||
*name = np->name;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_gpiochip_add_hog - Add all hogs in a hog device node
|
||||
* @chip: gpio chip to act on
|
||||
* @hog: device node describing the hogs
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, or negative errno on failure.
|
||||
*/
|
||||
static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
|
||||
{
|
||||
enum gpiod_flags dflags;
|
||||
struct gpio_desc *desc;
|
||||
unsigned long lflags;
|
||||
const char *name;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags);
|
||||
if (IS_ERR(desc))
|
||||
break;
|
||||
|
||||
ret = gpiod_hog(desc, name, lflags, dflags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_OF_DYNAMIC
|
||||
WRITE_ONCE(desc->hog, hog);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
|
||||
* @chip: gpio chip to act on
|
||||
*
|
||||
* This is only used by of_gpiochip_add to request/set GPIO initial
|
||||
* configuration.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success, or negative errno on failure.
|
||||
*/
|
||||
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
for_each_available_child_of_node_scoped(dev_of_node(&chip->gpiodev->dev), np) {
|
||||
if (!of_property_read_bool(np, "gpio-hog"))
|
||||
continue;
|
||||
|
||||
ret = of_gpiochip_add_hog(chip, np);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
of_node_set_flag(np, OF_POPULATED);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -922,7 +787,7 @@ static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
|
||||
if (!gdev)
|
||||
return NOTIFY_DONE; /* not for us */
|
||||
|
||||
ret = of_gpiochip_add_hog(gpio_device_get_chip(gdev), rd->dn);
|
||||
ret = gpiochip_add_hog(gpio_device_get_chip(gdev), of_fwnode_handle(rd->dn));
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to add hogs for %pOF\n", __func__,
|
||||
rd->dn);
|
||||
@@ -1201,16 +1066,24 @@ int of_gpiochip_add(struct gpio_chip *chip)
|
||||
|
||||
of_node_get(np);
|
||||
|
||||
ret = of_gpiochip_scan_gpios(chip);
|
||||
if (ret)
|
||||
of_node_put(np);
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
if (of_property_read_bool(child, "gpio-hog"))
|
||||
of_node_set_flag(child, OF_POPULATED);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void of_gpiochip_remove(struct gpio_chip *chip)
|
||||
{
|
||||
of_node_put(dev_of_node(&chip->gpiodev->dev));
|
||||
struct device_node *np = dev_of_node(&chip->gpiodev->dev);
|
||||
|
||||
for_each_child_of_node_scoped(np, child) {
|
||||
if (of_property_present(child, "gpio-hog"))
|
||||
of_node_clear_flag(child, OF_POPULATED);
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
struct device_node;
|
||||
struct fwnode_handle;
|
||||
struct fwnode_reference_args;
|
||||
|
||||
struct gpio_chip;
|
||||
struct gpio_desc;
|
||||
@@ -24,6 +25,9 @@ int of_gpiochip_add(struct gpio_chip *gc);
|
||||
void of_gpiochip_remove(struct gpio_chip *gc);
|
||||
bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index);
|
||||
int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id);
|
||||
int of_gpiochip_get_lflags(struct gpio_chip *chip,
|
||||
struct fwnode_reference_args *gpiospec,
|
||||
unsigned long *lflags);
|
||||
#else
|
||||
static inline struct gpio_desc *of_find_gpio(struct device_node *np,
|
||||
const char *con_id,
|
||||
@@ -44,6 +48,12 @@ static inline int of_gpio_count(const struct fwnode_handle *fwnode,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int of_gpiochip_get_lflags(struct gpio_chip *chip,
|
||||
struct fwnode_reference_args *gpiospec,
|
||||
unsigned long *lflags)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif /* CONFIG_OF_GPIO */
|
||||
|
||||
extern struct notifier_block gpio_of_notifier;
|
||||
|
||||
@@ -93,6 +93,14 @@ struct gpio_desc *swnode_find_gpio(struct fwnode_handle *fwnode,
|
||||
ret = swnode_gpio_get_reference(fwnode, propname, idx, &args);
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret == -ENOTCONN)
|
||||
/*
|
||||
* -ENOTCONN for a software node reference lookup means
|
||||
* that a remote struct software_node exists but has
|
||||
* not yet been registered as a firmware node. Defer
|
||||
* until this happens.
|
||||
*/
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
if (ret) {
|
||||
pr_debug("%s: can't parse '%s' property of node '%pfwP[%d]'\n",
|
||||
|
||||
@@ -983,10 +983,10 @@ void gpiod_unexport(struct gpio_desc *desc)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_unexport);
|
||||
|
||||
int gpiochip_sysfs_register(struct gpio_device *gdev)
|
||||
int gpiochip_sysfs_register(struct gpio_chip *gc)
|
||||
{
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
struct gpiodev_data *data;
|
||||
struct gpio_chip *chip;
|
||||
struct device *parent;
|
||||
int err;
|
||||
|
||||
@@ -999,18 +999,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
|
||||
if (!class_is_registered(&gpio_class))
|
||||
return 0;
|
||||
|
||||
guard(srcu)(&gdev->srcu);
|
||||
|
||||
chip = srcu_dereference(gdev->chip, &gdev->srcu);
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* For sysfs backward compatibility we need to preserve this
|
||||
* preferred parenting to the gpio_chip parent field, if set.
|
||||
*/
|
||||
if (chip->parent)
|
||||
parent = chip->parent;
|
||||
if (gc->parent)
|
||||
parent = gc->parent;
|
||||
else
|
||||
parent = &gdev->dev;
|
||||
|
||||
@@ -1029,7 +1023,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
|
||||
MKDEV(0, 0), data,
|
||||
gpiochip_groups,
|
||||
GPIOCHIP_NAME "%d",
|
||||
chip->base);
|
||||
gc->base);
|
||||
if (IS_ERR(data->cdev_base)) {
|
||||
err = PTR_ERR(data->cdev_base);
|
||||
kfree(data);
|
||||
@@ -1053,11 +1047,11 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
||||
void gpiochip_sysfs_unregister(struct gpio_chip *gc)
|
||||
{
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
struct gpiodev_data *data;
|
||||
struct gpio_desc *desc;
|
||||
struct gpio_chip *chip;
|
||||
|
||||
guard(mutex)(&sysfs_lock);
|
||||
|
||||
@@ -1065,13 +1059,8 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
guard(srcu)(&gdev->srcu);
|
||||
chip = srcu_dereference(gdev->chip, &gdev->srcu);
|
||||
if (!chip)
|
||||
return;
|
||||
|
||||
/* unregister gpiod class devices owned by sysfs */
|
||||
for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) {
|
||||
for_each_gpio_desc_with_flag(gc, desc, GPIOD_FLAG_SYSFS) {
|
||||
gpiod_unexport_unlocked(desc);
|
||||
gpiod_free(desc);
|
||||
}
|
||||
@@ -1090,10 +1079,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
||||
*/
|
||||
static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
|
||||
{
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
int ret;
|
||||
|
||||
ret = gpiochip_sysfs_register(gdev);
|
||||
ret = gpiochip_sysfs_register(gc);
|
||||
if (ret)
|
||||
gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret);
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@ struct gpio_device;
|
||||
|
||||
#ifdef CONFIG_GPIO_SYSFS
|
||||
|
||||
int gpiochip_sysfs_register(struct gpio_device *gdev);
|
||||
void gpiochip_sysfs_unregister(struct gpio_device *gdev);
|
||||
int gpiochip_sysfs_register(struct gpio_chip *gc);
|
||||
void gpiochip_sysfs_unregister(struct gpio_chip *gc);
|
||||
|
||||
#else
|
||||
|
||||
static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
|
||||
static inline int gpiochip_sysfs_register(struct gpio_chip *gc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
|
||||
static inline void gpiochip_sysfs_unregister(struct gpio_chip *gc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
@@ -102,9 +103,6 @@ static DEFINE_MUTEX(gpio_devices_lock);
|
||||
/* Ensures coherence during read-only accesses to the list of GPIO devices. */
|
||||
DEFINE_STATIC_SRCU(gpio_devices_srcu);
|
||||
|
||||
static DEFINE_MUTEX(gpio_machine_hogs_mutex);
|
||||
static LIST_HEAD(gpio_machine_hogs);
|
||||
|
||||
const char *const gpio_suffixes[] = { "gpios", "gpio", NULL };
|
||||
|
||||
static void gpiochip_free_hogs(struct gpio_chip *gc);
|
||||
@@ -340,7 +338,15 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
|
||||
|
||||
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
|
||||
/**
|
||||
* gpiochip_find_base_unlocked() - Find a global GPIO number base
|
||||
* @ngpio: Number of consecutive GPIOs to number
|
||||
*
|
||||
* Finds and allocates a consecutive range of unsigned integers representing
|
||||
* the GPIOs on the system. Using this numberspace outside of gpiolibs
|
||||
* internals is STRONGLY DISCOURAGED, drivers and consumers should NOT concern
|
||||
* themselves with this numberspace.
|
||||
*/
|
||||
static int gpiochip_find_base_unlocked(u16 ngpio)
|
||||
{
|
||||
unsigned int base = GPIO_DYNAMIC_BASE;
|
||||
@@ -881,14 +887,14 @@ static const struct device_type gpio_dev_type = {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GPIO_CDEV
|
||||
#define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt))
|
||||
#define gcdev_register(gc, devt) gpiolib_cdev_register((gc), (devt))
|
||||
#define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev))
|
||||
#else
|
||||
/*
|
||||
* gpiolib_cdev_register() indirectly calls device_add(), which is still
|
||||
* required even when cdev is not selected.
|
||||
*/
|
||||
#define gcdev_register(gdev, devt) device_add(&(gdev)->dev)
|
||||
#define gcdev_register(gc, devt) device_add(&(gc)->gpiodev->dev)
|
||||
#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
|
||||
#endif
|
||||
|
||||
@@ -896,8 +902,9 @@ static const struct device_type gpio_dev_type = {
|
||||
* An initial reference count has been held in gpiochip_add_data_with_key().
|
||||
* The caller should drop the reference via gpio_device_put() on errors.
|
||||
*/
|
||||
static int gpiochip_setup_dev(struct gpio_device *gdev)
|
||||
static int gpiochip_setup_dev(struct gpio_chip *gc)
|
||||
{
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
|
||||
int ret;
|
||||
|
||||
@@ -908,11 +915,11 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
|
||||
if (fwnode && !fwnode->dev)
|
||||
fwnode_dev_initialized(fwnode, false);
|
||||
|
||||
ret = gcdev_register(gdev, gpio_devt);
|
||||
ret = gcdev_register(gc, gpio_devt);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiochip_sysfs_register(gdev);
|
||||
ret = gpiochip_sysfs_register(gc);
|
||||
if (ret)
|
||||
goto err_remove_device;
|
||||
|
||||
@@ -926,46 +933,131 @@ err_remove_device:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
|
||||
int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *gc_node = dev_fwnode(&gc->gpiodev->dev);
|
||||
struct fwnode_reference_args gpiospec;
|
||||
enum gpiod_flags dflags;
|
||||
const char *name = NULL;
|
||||
struct gpio_desc *desc;
|
||||
int rv;
|
||||
unsigned int num_hogs;
|
||||
unsigned long lflags;
|
||||
int ret, argc;
|
||||
/*
|
||||
* For devicetree-based systems, this needs to be defined in bindings
|
||||
* and there's no real default value. For other firmware descriptions
|
||||
* it makes the most sense to use 2 cells for the GPIO offset and
|
||||
* request flags.
|
||||
*/
|
||||
u32 cells = 2;
|
||||
|
||||
desc = gpiochip_get_desc(gc, hog->chip_hwnum);
|
||||
if (IS_ERR(desc)) {
|
||||
gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n",
|
||||
__func__, PTR_ERR(desc));
|
||||
return;
|
||||
lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
|
||||
dflags = GPIOD_ASIS;
|
||||
name = NULL;
|
||||
|
||||
argc = fwnode_property_count_u32(fwnode, "gpios");
|
||||
if (argc < 0)
|
||||
return argc;
|
||||
|
||||
ret = fwnode_property_read_u32(gc_node, "#gpio-cells", &cells);
|
||||
if (ret && is_of_node(fwnode))
|
||||
return ret;
|
||||
if (argc % cells)
|
||||
return -EINVAL;
|
||||
|
||||
num_hogs = argc / cells;
|
||||
|
||||
u32 *gpios __free(kfree) = kzalloc_objs(*gpios, argc);
|
||||
if (!gpios)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, argc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (fwnode_property_present(fwnode, "input"))
|
||||
dflags |= GPIOD_IN;
|
||||
else if (fwnode_property_present(fwnode, "output-low"))
|
||||
dflags |= GPIOD_OUT_LOW;
|
||||
else if (fwnode_property_present(fwnode, "output-high"))
|
||||
dflags |= GPIOD_OUT_HIGH;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
fwnode_property_read_string(fwnode, "line-name", &name);
|
||||
|
||||
for (unsigned int i = 0; i < num_hogs; i++) {
|
||||
if (is_of_node(fwnode)) {
|
||||
/*
|
||||
* OF-nodes need some additional special handling for
|
||||
* translating of devicetree flags.
|
||||
*/
|
||||
memset(&gpiospec, 0, sizeof(gpiospec));
|
||||
gpiospec.fwnode = fwnode;
|
||||
gpiospec.nargs = cells;
|
||||
|
||||
for (unsigned int j = 0; j < cells; j++)
|
||||
gpiospec.args[j] = gpios[i * cells + j];
|
||||
|
||||
ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* GPIO_ACTIVE_LOW is currently the only lookup flag
|
||||
* supported for non-OF firmware nodes.
|
||||
*/
|
||||
if (gpios[i * cells + 1])
|
||||
lflags |= GPIO_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
desc = gpiochip_get_desc(gc, gpios[i * cells]);
|
||||
if (IS_ERR(desc))
|
||||
return PTR_ERR(desc);
|
||||
|
||||
ret = gpiod_hog(desc, name, lflags, dflags);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags);
|
||||
if (rv)
|
||||
gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n",
|
||||
__func__, gc->label, hog->chip_hwnum, rv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void machine_gpiochip_add(struct gpio_chip *gc)
|
||||
static int gpiochip_hog_lines(struct gpio_chip *gc)
|
||||
{
|
||||
struct gpiod_hog *hog;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&gpio_machine_hogs_mutex);
|
||||
device_for_each_child_node_scoped(&gc->gpiodev->dev, fwnode) {
|
||||
if (!fwnode_property_present(fwnode, "gpio-hog"))
|
||||
continue;
|
||||
|
||||
list_for_each_entry(hog, &gpio_machine_hogs, list) {
|
||||
if (!strcmp(gc->label, hog->chip_label))
|
||||
gpiochip_machine_hog(gc, hog);
|
||||
ret = gpiochip_add_hog(gc, fwnode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpiochip_setup_devs(void)
|
||||
{
|
||||
struct gpio_device *gdev;
|
||||
struct gpio_chip *gc;
|
||||
int ret;
|
||||
|
||||
guard(srcu)(&gpio_devices_srcu);
|
||||
|
||||
list_for_each_entry_srcu(gdev, &gpio_devices, list,
|
||||
srcu_read_lock_held(&gpio_devices_srcu)) {
|
||||
ret = gpiochip_setup_dev(gdev);
|
||||
guard(srcu)(&gdev->srcu);
|
||||
|
||||
gc = srcu_dereference(gdev->chip, &gdev->srcu);
|
||||
if (!gc) {
|
||||
dev_err(&gdev->dev, "Underlying GPIO chip is gone\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = gpiochip_setup_dev(gc);
|
||||
if (ret) {
|
||||
gpio_device_put(gdev);
|
||||
dev_err(&gdev->dev,
|
||||
@@ -1197,7 +1289,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
||||
|
||||
acpi_gpiochip_add(gc);
|
||||
|
||||
machine_gpiochip_add(gc);
|
||||
ret = gpiochip_hog_lines(gc);
|
||||
if (ret)
|
||||
goto err_remove_of_chip;
|
||||
|
||||
ret = gpiochip_irqchip_init_valid_mask(gc);
|
||||
if (ret)
|
||||
@@ -1224,7 +1318,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
||||
* Otherwise, defer until later.
|
||||
*/
|
||||
if (gpiolib_initialized) {
|
||||
ret = gpiochip_setup_dev(gdev);
|
||||
ret = gpiochip_setup_dev(gc);
|
||||
if (ret)
|
||||
goto err_teardown_shared;
|
||||
}
|
||||
@@ -1284,7 +1378,7 @@ void gpiochip_remove(struct gpio_chip *gc)
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
|
||||
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
|
||||
gpiochip_sysfs_unregister(gdev);
|
||||
gpiochip_sysfs_unregister(gc);
|
||||
gpiochip_free_hogs(gc);
|
||||
gpiochip_free_remaining_irqs(gc);
|
||||
|
||||
@@ -1389,7 +1483,16 @@ EXPORT_SYMBOL_GPL(gpio_device_find_by_label);
|
||||
|
||||
static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, const void *fwnode)
|
||||
{
|
||||
return device_match_fwnode(&gc->gpiodev->dev, fwnode);
|
||||
struct device *dev = &gc->gpiodev->dev;
|
||||
struct fwnode_handle *node = dev_fwnode(dev);
|
||||
|
||||
if (IS_ERR(fwnode))
|
||||
return 0;
|
||||
|
||||
if (device_match_fwnode(dev, fwnode))
|
||||
return 1;
|
||||
|
||||
return node && node->secondary == fwnode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4469,42 +4572,6 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table);
|
||||
|
||||
/**
|
||||
* gpiod_add_hogs() - register a set of GPIO hogs from machine code
|
||||
* @hogs: table of gpio hog entries with a zeroed sentinel at the end
|
||||
*/
|
||||
void gpiod_add_hogs(struct gpiod_hog *hogs)
|
||||
{
|
||||
struct gpiod_hog *hog;
|
||||
|
||||
guard(mutex)(&gpio_machine_hogs_mutex);
|
||||
|
||||
for (hog = &hogs[0]; hog->chip_label; hog++) {
|
||||
list_add_tail(&hog->list, &gpio_machine_hogs);
|
||||
|
||||
/*
|
||||
* The chip may have been registered earlier, so check if it
|
||||
* exists and, if so, try to hog the line now.
|
||||
*/
|
||||
struct gpio_device *gdev __free(gpio_device_put) =
|
||||
gpio_device_find_by_label(hog->chip_label);
|
||||
if (gdev)
|
||||
gpiochip_machine_hog(gpio_device_get_chip(gdev), hog);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_add_hogs);
|
||||
|
||||
void gpiod_remove_hogs(struct gpiod_hog *hogs)
|
||||
{
|
||||
struct gpiod_hog *hog;
|
||||
|
||||
guard(mutex)(&gpio_machine_hogs_mutex);
|
||||
|
||||
for (hog = &hogs[0]; hog->chip_label; hog++)
|
||||
list_del(&hog->list);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_remove_hogs);
|
||||
|
||||
static bool gpiod_match_lookup_table(struct device *dev,
|
||||
const struct gpiod_lookup_table *table)
|
||||
{
|
||||
@@ -4557,8 +4624,8 @@ static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *c
|
||||
return desc;
|
||||
}
|
||||
|
||||
dev_warn(dev, "cannot find GPIO line %s, deferring\n",
|
||||
p->key);
|
||||
dev_dbg(dev, "cannot find GPIO line %s, deferring\n",
|
||||
p->key);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
@@ -4572,8 +4639,8 @@ static struct gpio_desc *gpio_desc_table_match(struct device *dev, const char *c
|
||||
* consumer be probed again or let the Deferred
|
||||
* Probe infrastructure handle the error.
|
||||
*/
|
||||
dev_warn(dev, "cannot find GPIO chip %s, deferring\n",
|
||||
p->key);
|
||||
dev_dbg(dev, "cannot find GPIO chip %s, deferring\n",
|
||||
p->key);
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
@@ -5317,23 +5384,14 @@ core_initcall(gpiolib_dev_init);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
|
||||
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *gc)
|
||||
{
|
||||
bool active_low, is_irq, is_out;
|
||||
struct gpio_desc *desc;
|
||||
unsigned int gpio = 0;
|
||||
struct gpio_chip *gc;
|
||||
unsigned long flags;
|
||||
int value;
|
||||
|
||||
guard(srcu)(&gdev->srcu);
|
||||
|
||||
gc = srcu_dereference(gdev->chip, &gdev->srcu);
|
||||
if (!gc) {
|
||||
seq_puts(s, "Underlying GPIO chip is gone\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_gpio_desc(gc, desc) {
|
||||
guard(srcu)(&desc->gdev->desc_srcu);
|
||||
flags = READ_ONCE(desc->flags);
|
||||
@@ -5446,7 +5504,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v)
|
||||
if (gc->dbg_show)
|
||||
gc->dbg_show(s, gc);
|
||||
else
|
||||
gpiolib_dbg_show(s, gdev);
|
||||
gpiolib_dbg_show(s, gc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,14 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/srcu.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define GPIOCHIP_NAME "gpiochip"
|
||||
|
||||
struct fwnode_handle;
|
||||
|
||||
/**
|
||||
* struct gpio_device - internal state container for GPIO devices
|
||||
* @dev: the GPIO device struct
|
||||
@@ -107,7 +110,7 @@ extern const char *const gpio_suffixes[];
|
||||
if (con_id) \
|
||||
snprintf(propname, sizeof(propname), "%s-%s", con_id, __gs); \
|
||||
else \
|
||||
snprintf(propname, sizeof(propname), "%s", __gs); \
|
||||
strscpy(propname, __gs); \
|
||||
1; \
|
||||
}); \
|
||||
__suffixes++)
|
||||
@@ -273,6 +276,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
||||
int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
|
||||
int gpiod_hog(struct gpio_desc *desc, const char *name,
|
||||
unsigned long lflags, enum gpiod_flags dflags);
|
||||
int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode);
|
||||
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
|
||||
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
|
||||
const char *gpiod_get_label(struct gpio_desc *desc);
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/nfc.h>
|
||||
#include <net/nfc/nci.h>
|
||||
#include <net/nfc/nci_core.h>
|
||||
@@ -112,13 +112,12 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
|
||||
|
||||
memcpy(&priv->config, pdata, sizeof(*pdata));
|
||||
|
||||
if (gpio_is_valid(priv->config.reset_n_io)) {
|
||||
rc = gpio_request_one(priv->config.reset_n_io,
|
||||
GPIOF_OUT_INIT_LOW,
|
||||
"nfcmrvl_reset_n");
|
||||
if (rc < 0) {
|
||||
priv->config.reset_n_io = -EINVAL;
|
||||
nfc_err(dev, "failed to request reset_n io\n");
|
||||
if (!priv->config.reset_gpio) {
|
||||
priv->config.reset_gpio =
|
||||
devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(priv->config.reset_gpio)) {
|
||||
priv->config.reset_gpio = NULL;
|
||||
nfc_err(dev, "failed to get reset gpio\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +143,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(enum nfcmrvl_phy phy,
|
||||
if (!priv->ndev) {
|
||||
nfc_err(dev, "nci_allocate_device failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto error_free_gpio;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
rc = nfcmrvl_fw_dnld_init(priv);
|
||||
@@ -171,9 +170,7 @@ error_fw_dnld_deinit:
|
||||
nfcmrvl_fw_dnld_deinit(priv);
|
||||
error_free_dev:
|
||||
nci_free_device(priv->ndev);
|
||||
error_free_gpio:
|
||||
if (gpio_is_valid(priv->config.reset_n_io))
|
||||
gpio_free(priv->config.reset_n_io);
|
||||
error_free:
|
||||
kfree(priv);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
@@ -189,9 +186,6 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
|
||||
|
||||
nfcmrvl_fw_dnld_deinit(priv);
|
||||
|
||||
if (gpio_is_valid(priv->config.reset_n_io))
|
||||
gpio_free(priv->config.reset_n_io);
|
||||
|
||||
nci_free_device(ndev);
|
||||
kfree(priv);
|
||||
}
|
||||
@@ -233,34 +227,25 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv)
|
||||
/* Reset possible fault of previous session */
|
||||
clear_bit(NFCMRVL_PHY_ERROR, &priv->flags);
|
||||
|
||||
if (gpio_is_valid(priv->config.reset_n_io)) {
|
||||
if (priv->config.reset_gpio) {
|
||||
nfc_info(priv->dev, "reset the chip\n");
|
||||
gpio_set_value(priv->config.reset_n_io, 0);
|
||||
gpiod_set_value(priv->config.reset_gpio, 1);
|
||||
usleep_range(5000, 10000);
|
||||
gpio_set_value(priv->config.reset_n_io, 1);
|
||||
gpiod_set_value(priv->config.reset_gpio, 0);
|
||||
} else
|
||||
nfc_info(priv->dev, "no reset available on this interface\n");
|
||||
}
|
||||
|
||||
void nfcmrvl_chip_halt(struct nfcmrvl_private *priv)
|
||||
{
|
||||
if (gpio_is_valid(priv->config.reset_n_io))
|
||||
gpio_set_value(priv->config.reset_n_io, 0);
|
||||
if (priv->config.reset_gpio)
|
||||
gpiod_set_value(priv->config.reset_gpio, 1);
|
||||
}
|
||||
|
||||
int nfcmrvl_parse_dt(struct device_node *node,
|
||||
struct nfcmrvl_platform_data *pdata)
|
||||
{
|
||||
int reset_n_io;
|
||||
|
||||
reset_n_io = of_get_named_gpio(node, "reset-n-io", 0);
|
||||
if (reset_n_io < 0) {
|
||||
pr_info("no reset-n-io config\n");
|
||||
} else if (!gpio_is_valid(reset_n_io)) {
|
||||
pr_err("invalid reset-n-io GPIO\n");
|
||||
return reset_n_io;
|
||||
}
|
||||
pdata->reset_n_io = reset_n_io;
|
||||
pdata->reset_gpio = NULL;
|
||||
pdata->hci_muxed = of_property_read_bool(node, "hci-muxed");
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "fw_dnld.h"
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
/* Define private flags: */
|
||||
#define NFCMRVL_NCI_RUNNING 1
|
||||
#define NFCMRVL_PHY_ERROR 2
|
||||
@@ -54,7 +56,7 @@ struct nfcmrvl_platform_data {
|
||||
*/
|
||||
|
||||
/* GPIO that is wired to RESET_N signal */
|
||||
int reset_n_io;
|
||||
struct gpio_desc *reset_gpio;
|
||||
/* Tell if transport is muxed in HCI one */
|
||||
bool hci_muxed;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/printk.h>
|
||||
@@ -20,7 +21,6 @@
|
||||
static unsigned int hci_muxed;
|
||||
static unsigned int flow_control;
|
||||
static unsigned int break_control;
|
||||
static int reset_n_io = -EINVAL;
|
||||
|
||||
/*
|
||||
* NFCMRVL NCI OPS
|
||||
@@ -62,9 +62,11 @@ static const struct nfcmrvl_if_ops uart_ops = {
|
||||
};
|
||||
|
||||
static int nfcmrvl_uart_parse_dt(struct device_node *node,
|
||||
struct nfcmrvl_platform_data *pdata)
|
||||
struct nfcmrvl_platform_data *pdata,
|
||||
struct device *dev)
|
||||
{
|
||||
struct device_node *matched_node;
|
||||
struct gpio_desc *reset_gpio;
|
||||
int ret;
|
||||
|
||||
matched_node = of_get_compatible_child(node, "marvell,nfc-uart");
|
||||
@@ -84,6 +86,16 @@ static int nfcmrvl_uart_parse_dt(struct device_node *node,
|
||||
pdata->flow_control = of_property_read_bool(matched_node, "flow-control");
|
||||
pdata->break_control = of_property_read_bool(matched_node, "break-control");
|
||||
|
||||
reset_gpio = devm_fwnode_gpiod_get_optional(dev,
|
||||
of_fwnode_handle(matched_node),
|
||||
"reset", GPIOD_OUT_HIGH,
|
||||
"nfcmrvl_reset_n");
|
||||
if (IS_ERR(reset_gpio)) {
|
||||
of_node_put(matched_node);
|
||||
return PTR_ERR(reset_gpio);
|
||||
}
|
||||
pdata->reset_gpio = reset_gpio;
|
||||
|
||||
of_node_put(matched_node);
|
||||
|
||||
return 0;
|
||||
@@ -107,13 +119,13 @@ static int nfcmrvl_nci_uart_open(struct nci_uart *nu)
|
||||
*/
|
||||
|
||||
if (dev && dev->parent && dev->parent->of_node)
|
||||
if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config) == 0)
|
||||
if (nfcmrvl_uart_parse_dt(dev->parent->of_node, &config, dev) == 0)
|
||||
pdata = &config;
|
||||
|
||||
if (!pdata) {
|
||||
pr_info("No platform data / DT -> fallback to module params\n");
|
||||
config.hci_muxed = hci_muxed;
|
||||
config.reset_n_io = reset_n_io;
|
||||
config.reset_gpio = NULL;
|
||||
config.flow_control = flow_control;
|
||||
config.break_control = break_control;
|
||||
pdata = &config;
|
||||
@@ -201,6 +213,3 @@ MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal.");
|
||||
|
||||
module_param(hci_muxed, uint, 0);
|
||||
MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one.");
|
||||
|
||||
module_param(reset_n_io, int, 0);
|
||||
MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal.");
|
||||
|
||||
@@ -294,7 +294,7 @@ static int nfcmrvl_probe(struct usb_interface *intf,
|
||||
|
||||
/* No configuration for USB */
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.reset_n_io = -EINVAL;
|
||||
config.reset_gpio = NULL;
|
||||
|
||||
nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
|
||||
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <net/nfc/nfc.h>
|
||||
@@ -146,37 +144,6 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int s3fwrn5_i2c_parse_dt(struct i2c_client *client)
|
||||
{
|
||||
struct s3fwrn5_i2c_phy *phy = i2c_get_clientdata(client);
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
|
||||
if (!gpio_is_valid(phy->common.gpio_en)) {
|
||||
/* Support also deprecated property */
|
||||
phy->common.gpio_en = of_get_named_gpio(np,
|
||||
"s3fwrn5,en-gpios",
|
||||
0);
|
||||
if (!gpio_is_valid(phy->common.gpio_en))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
|
||||
if (!gpio_is_valid(phy->common.gpio_fw_wake)) {
|
||||
/* Support also deprecated property */
|
||||
phy->common.gpio_fw_wake = of_get_named_gpio(np,
|
||||
"s3fwrn5,fw-gpios",
|
||||
0);
|
||||
if (!gpio_is_valid(phy->common.gpio_fw_wake))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3fwrn5_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct s3fwrn5_i2c_phy *phy;
|
||||
@@ -193,20 +160,13 @@ static int s3fwrn5_i2c_probe(struct i2c_client *client)
|
||||
phy->i2c_dev = client;
|
||||
i2c_set_clientdata(client, phy);
|
||||
|
||||
ret = s3fwrn5_i2c_parse_dt(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
phy->common.gpio_en = devm_gpiod_get(&client->dev, "en", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(phy->common.gpio_en))
|
||||
return PTR_ERR(phy->common.gpio_en);
|
||||
|
||||
ret = devm_gpio_request_one(&phy->i2c_dev->dev, phy->common.gpio_en,
|
||||
GPIOF_OUT_INIT_HIGH, "s3fwrn5_en");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_gpio_request_one(&phy->i2c_dev->dev,
|
||||
phy->common.gpio_fw_wake,
|
||||
GPIOF_OUT_INIT_LOW, "s3fwrn5_fw_wake");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
phy->common.gpio_fw_wake = devm_gpiod_get(&client->dev, "wake", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(phy->common.gpio_fw_wake))
|
||||
return PTR_ERR(phy->common.gpio_fw_wake);
|
||||
|
||||
/*
|
||||
* S3FWRN5 depends on a clock input ("XI" pin) to function properly.
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* Bongsu Jeon <bongsu.jeon@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
@@ -19,7 +18,7 @@ void s3fwrn5_phy_set_wake(void *phy_id, bool wake)
|
||||
struct phy_common *phy = phy_id;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
gpio_set_value(phy->gpio_fw_wake, wake);
|
||||
gpiod_set_value(phy->gpio_fw_wake, wake);
|
||||
if (wake)
|
||||
msleep(S3FWRN5_EN_WAIT_TIME);
|
||||
mutex_unlock(&phy->mutex);
|
||||
@@ -33,14 +32,14 @@ bool s3fwrn5_phy_power_ctrl(struct phy_common *phy, enum s3fwrn5_mode mode)
|
||||
|
||||
phy->mode = mode;
|
||||
|
||||
gpio_set_value(phy->gpio_en, 1);
|
||||
gpio_set_value(phy->gpio_fw_wake, 0);
|
||||
gpiod_set_value(phy->gpio_en, 1);
|
||||
gpiod_set_value(phy->gpio_fw_wake, 0);
|
||||
if (mode == S3FWRN5_MODE_FW)
|
||||
gpio_set_value(phy->gpio_fw_wake, 1);
|
||||
gpiod_set_value(phy->gpio_fw_wake, 1);
|
||||
|
||||
if (mode != S3FWRN5_MODE_COLD) {
|
||||
msleep(S3FWRN5_EN_WAIT_TIME);
|
||||
gpio_set_value(phy->gpio_en, 0);
|
||||
gpiod_set_value(phy->gpio_en, 0);
|
||||
msleep(S3FWRN5_EN_WAIT_TIME);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define __NFC_S3FWRN5_PHY_COMMON_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <net/nfc/nci_core.h>
|
||||
|
||||
#include "s3fwrn5.h"
|
||||
@@ -21,8 +22,8 @@
|
||||
struct phy_common {
|
||||
struct nci_dev *ndev;
|
||||
|
||||
int gpio_en;
|
||||
int gpio_fw_wake;
|
||||
struct gpio_desc *gpio_en;
|
||||
struct gpio_desc *gpio_fw_wake;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nfc.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include "phy_common.h"
|
||||
|
||||
@@ -92,25 +91,6 @@ static const struct of_device_id s3fwrn82_uart_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, s3fwrn82_uart_of_match);
|
||||
|
||||
static int s3fwrn82_uart_parse_dt(struct serdev_device *serdev)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy = serdev_device_get_drvdata(serdev);
|
||||
struct device_node *np = serdev->dev.of_node;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
phy->common.gpio_en = of_get_named_gpio(np, "en-gpios", 0);
|
||||
if (!gpio_is_valid(phy->common.gpio_en))
|
||||
return -ENODEV;
|
||||
|
||||
phy->common.gpio_fw_wake = of_get_named_gpio(np, "wake-gpios", 0);
|
||||
if (!gpio_is_valid(phy->common.gpio_fw_wake))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3fwrn82_uart_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct s3fwrn82_uart_phy *phy;
|
||||
@@ -144,20 +124,17 @@ static int s3fwrn82_uart_probe(struct serdev_device *serdev)
|
||||
|
||||
serdev_device_set_flow_control(serdev, false);
|
||||
|
||||
ret = s3fwrn82_uart_parse_dt(serdev);
|
||||
if (ret < 0)
|
||||
phy->common.gpio_en = devm_gpiod_get(&serdev->dev, "en", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(phy->common.gpio_en)) {
|
||||
ret = PTR_ERR(phy->common.gpio_en);
|
||||
goto err_serdev;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request_one(&phy->ser_dev->dev, phy->common.gpio_en,
|
||||
GPIOF_OUT_INIT_HIGH, "s3fwrn82_en");
|
||||
if (ret < 0)
|
||||
goto err_serdev;
|
||||
|
||||
ret = devm_gpio_request_one(&phy->ser_dev->dev,
|
||||
phy->common.gpio_fw_wake,
|
||||
GPIOF_OUT_INIT_LOW, "s3fwrn82_fw_wake");
|
||||
if (ret < 0)
|
||||
phy->common.gpio_fw_wake = devm_gpiod_get(&serdev->dev, "wake", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(phy->common.gpio_fw_wake)) {
|
||||
ret = PTR_ERR(phy->common.gpio_fw_wake);
|
||||
goto err_serdev;
|
||||
}
|
||||
|
||||
ret = s3fwrn5_probe(&phy->common.ndev, phy, &phy->ser_dev->dev,
|
||||
&uart_phy_ops);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pinctrl/devinfo.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
|
||||
#include "core.h"
|
||||
@@ -938,6 +939,36 @@ int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_gpio_set_config);
|
||||
|
||||
/**
|
||||
* pinctrl_gpio_get_config() - Get the config for a given GPIO pin
|
||||
* @gc: GPIO chip structure from the GPIO subsystem
|
||||
* @offset: hardware offset of the GPIO relative to the controller
|
||||
* @config: the configuration to query. On success it holds the result
|
||||
* Return: 0 on success, negative errno otherwise
|
||||
*/
|
||||
int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, unsigned long *config)
|
||||
{
|
||||
struct pinctrl_gpio_range *range;
|
||||
struct pinctrl_dev *pctldev;
|
||||
int ret, pin;
|
||||
|
||||
ret = pinctrl_get_device_gpio_range(gc, offset, &pctldev, &range);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&pctldev->mutex);
|
||||
pin = gpio_to_pin(range, gc, offset);
|
||||
ret = pin_config_get_for_pin(pctldev, pin, config);
|
||||
mutex_unlock(&pctldev->mutex);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*config = pinconf_to_config_argument(*config);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_gpio_get_config);
|
||||
|
||||
static struct pinctrl_state *find_state(struct pinctrl *p,
|
||||
const char *name)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,12 @@ static inline int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int p
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned int pin,
|
||||
unsigned long *config)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PINCONF) && defined(CONFIG_DEBUG_FS)
|
||||
|
||||
@@ -251,15 +251,9 @@ static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param,
|
||||
case PIN_CONFIG_MODE_LOW_POWER:
|
||||
*type = SCMI_PIN_LOW_POWER_MODE;
|
||||
break;
|
||||
case PIN_CONFIG_LEVEL:
|
||||
*type = SCMI_PIN_OUTPUT_VALUE;
|
||||
break;
|
||||
case PIN_CONFIG_OUTPUT_ENABLE:
|
||||
*type = SCMI_PIN_OUTPUT_MODE;
|
||||
break;
|
||||
case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS:
|
||||
*type = SCMI_PIN_OUTPUT_VALUE;
|
||||
break;
|
||||
case PIN_CONFIG_POWER_SOURCE:
|
||||
*type = SCMI_PIN_POWER_SOURCE;
|
||||
break;
|
||||
@@ -276,6 +270,28 @@ static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pinctrl_scmi_map_pinconf_type_get(enum pin_config_param param,
|
||||
enum scmi_pinctrl_conf_type *type)
|
||||
{
|
||||
if (param == PIN_CONFIG_LEVEL) {
|
||||
*type = SCMI_PIN_INPUT_VALUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pinctrl_scmi_map_pinconf_type(param, type);
|
||||
}
|
||||
|
||||
static int pinctrl_scmi_map_pinconf_type_set(enum pin_config_param param,
|
||||
enum scmi_pinctrl_conf_type *type)
|
||||
{
|
||||
if (param == PIN_CONFIG_LEVEL) {
|
||||
*type = SCMI_PIN_OUTPUT_VALUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pinctrl_scmi_map_pinconf_type(param, type);
|
||||
}
|
||||
|
||||
static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin, unsigned long *config)
|
||||
{
|
||||
@@ -290,7 +306,7 @@ static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev,
|
||||
|
||||
config_type = pinconf_to_config_param(*config);
|
||||
|
||||
ret = pinctrl_scmi_map_pinconf_type(config_type, &type);
|
||||
ret = pinctrl_scmi_map_pinconf_type_get(config_type, &type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -345,7 +361,7 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev,
|
||||
unsigned long *configs,
|
||||
unsigned int num_configs)
|
||||
{
|
||||
int i, ret;
|
||||
int i, cnt, ret;
|
||||
struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
|
||||
enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS];
|
||||
u32 config_value[SCMI_NUM_CONFIGS];
|
||||
@@ -361,17 +377,21 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cnt = 0;
|
||||
for (i = 0; i < num_configs; i++) {
|
||||
param = pinconf_to_config_param(configs[i]);
|
||||
ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]);
|
||||
if (param == PIN_CONFIG_PERSIST_STATE)
|
||||
continue;
|
||||
ret = pinctrl_scmi_map_pinconf_type_set(param, &p_config_type[cnt]);
|
||||
if (ret) {
|
||||
dev_err(pmx->dev, "Error map pinconf_type %d\n", ret);
|
||||
goto free_config;
|
||||
}
|
||||
p_config_value[i] = pinconf_to_config_argument(configs[i]);
|
||||
p_config_value[cnt] = pinconf_to_config_argument(configs[i]);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, num_configs,
|
||||
ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, cnt,
|
||||
p_config_type, p_config_value);
|
||||
if (ret)
|
||||
dev_err(pmx->dev, "Error parsing config %d\n", ret);
|
||||
@@ -405,7 +425,7 @@ static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev,
|
||||
|
||||
for (i = 0; i < num_configs; i++) {
|
||||
param = pinconf_to_config_param(configs[i]);
|
||||
ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]);
|
||||
ret = pinctrl_scmi_map_pinconf_type_set(param, &p_config_type[i]);
|
||||
if (ret) {
|
||||
dev_err(pmx->dev, "Error map pinconf_type %d\n", ret);
|
||||
goto free_config;
|
||||
@@ -440,7 +460,7 @@ static int pinctrl_scmi_pinconf_group_get(struct pinctrl_dev *pctldev,
|
||||
return -EINVAL;
|
||||
|
||||
config_type = pinconf_to_config_param(*config);
|
||||
ret = pinctrl_scmi_map_pinconf_type(config_type, &type);
|
||||
ret = pinctrl_scmi_map_pinconf_type_get(config_type, &type);
|
||||
if (ret) {
|
||||
dev_err(pmx->dev, "Error map pinconf_type %d\n", ret);
|
||||
return ret;
|
||||
|
||||
@@ -1232,7 +1232,6 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY
|
||||
depends on ARM || ARM64 || COMPILE_TEST
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on I2C
|
||||
depends on OF_GPIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports ATTINY regulator on the Raspberry Pi 7-inch
|
||||
@@ -1332,7 +1331,6 @@ config REGULATOR_RT5133
|
||||
depends on I2C && GPIOLIB && OF
|
||||
select REGMAP
|
||||
select CRC8
|
||||
select OF_GPIO
|
||||
help
|
||||
This driver adds support for RT5133 PMIC regulators.
|
||||
RT5133 is an integrated chip. It includes 8 LDOs and 3 GPOs that
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
struct acpi_device;
|
||||
struct device;
|
||||
struct fwnode_handle;
|
||||
|
||||
9
include/linux/gpio/defs.h
Normal file
9
include/linux/gpio/defs.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __LINUX_GPIO_DEFS_H
|
||||
#define __LINUX_GPIO_DEFS_H
|
||||
|
||||
#define GPIO_LINE_DIRECTION_IN 1
|
||||
#define GPIO_LINE_DIRECTION_OUT 0
|
||||
|
||||
#endif /* __LINUX_GPIO_DEFS_H */
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <asm/msi.h>
|
||||
#endif
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
struct device;
|
||||
struct irq_chip;
|
||||
struct irq_data;
|
||||
@@ -42,9 +44,6 @@ union gpio_irq_fwspec {
|
||||
#endif
|
||||
};
|
||||
|
||||
#define GPIO_LINE_DIRECTION_IN 1
|
||||
#define GPIO_LINE_DIRECTION_OUT 0
|
||||
|
||||
/**
|
||||
* struct gpio_irq_chip - GPIO interrupt controller
|
||||
*/
|
||||
@@ -344,11 +343,17 @@ struct gpio_irq_chip {
|
||||
* @direction_output: configures signal "offset" as output, returns 0 on
|
||||
* success or a negative error number. This can be omitted on input-only
|
||||
* or output-only gpio chips.
|
||||
* @get: returns value for signal "offset", 0=low, 1=high, or negative error
|
||||
* @get: returns value for signal "offset", 0=low, 1=high, or negative error.
|
||||
* the low and high values are defined as physical low on the line
|
||||
* in/out to the connector such as a physical pad, pin or rail. The GPIO
|
||||
* library has internal logic to handle lines that are active low, such
|
||||
* as indicated by overstrike or #name in a schematic, and the driver
|
||||
* should not try to second-guess the logic value of a line.
|
||||
* @get_multiple: reads values for multiple signals defined by "mask" and
|
||||
* stores them in "bits", returns 0 on success or negative error
|
||||
* @set: assigns output value for signal "offset", returns 0 on success or
|
||||
* negative error value
|
||||
* negative error value. The output value follows the same semantic
|
||||
* rules as for @get.
|
||||
* @set_multiple: assigns output values for multiple signals defined by
|
||||
* "mask", returns 0 on success or negative error value
|
||||
* @set_config: optional hook for all kinds of settings. Uses the same
|
||||
|
||||
@@ -3,9 +3,15 @@
|
||||
#ifndef __LINUX_GPIO_GENERIC_H
|
||||
#define __LINUX_GPIO_GENERIC_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
struct device;
|
||||
|
||||
|
||||
@@ -46,23 +46,6 @@ struct gpiod_lookup_table {
|
||||
struct gpiod_lookup table[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpiod_hog - GPIO line hog table
|
||||
* @chip_label: name of the chip the GPIO belongs to
|
||||
* @chip_hwnum: hardware number (i.e. relative to the chip) of the GPIO
|
||||
* @line_name: consumer name for the hogged line
|
||||
* @lflags: bitmask of gpio_lookup_flags GPIO_* values
|
||||
* @dflags: GPIO flags used to specify the direction and value
|
||||
*/
|
||||
struct gpiod_hog {
|
||||
struct list_head list;
|
||||
const char *chip_label;
|
||||
u16 chip_hwnum;
|
||||
const char *line_name;
|
||||
unsigned long lflags;
|
||||
int dflags;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper for lookup tables with just one single lookup for a device.
|
||||
*/
|
||||
@@ -95,24 +78,10 @@ static struct gpiod_lookup_table _name = { \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple definition of a single GPIO hog in an array.
|
||||
*/
|
||||
#define GPIO_HOG(_chip_label, _chip_hwnum, _line_name, _lflags, _dflags) \
|
||||
(struct gpiod_hog) { \
|
||||
.chip_label = _chip_label, \
|
||||
.chip_hwnum = _chip_hwnum, \
|
||||
.line_name = _line_name, \
|
||||
.lflags = _lflags, \
|
||||
.dflags = _dflags, \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
void gpiod_add_lookup_table(struct gpiod_lookup_table *table);
|
||||
void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n);
|
||||
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table);
|
||||
void gpiod_add_hogs(struct gpiod_hog *hogs);
|
||||
void gpiod_remove_hogs(struct gpiod_hog *hogs);
|
||||
#else /* ! CONFIG_GPIOLIB */
|
||||
static inline
|
||||
void gpiod_add_lookup_table(struct gpiod_lookup_table *table) {}
|
||||
@@ -120,8 +89,6 @@ static inline
|
||||
void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) {}
|
||||
static inline
|
||||
void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) {}
|
||||
static inline void gpiod_add_hogs(struct gpiod_hog *hogs) {}
|
||||
static inline void gpiod_remove_hogs(struct gpiod_hog *hogs) {}
|
||||
#endif /* CONFIG_GPIOLIB */
|
||||
|
||||
#endif /* __LINUX_GPIO_MACHINE_H */
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#define KEMPLD_SPEC_GET_MINOR(x) (x & 0x0f)
|
||||
#define KEMPLD_SPEC_GET_MAJOR(x) ((x >> 4) & 0x0f)
|
||||
#define KEMPLD_IRQ_GPIO 0x35
|
||||
#define KEMPLD_IRQ_GPIO_MASK 0x0f
|
||||
#define KEMPLD_IRQ_I2C 0x36
|
||||
#define KEMPLD_CFG 0x37
|
||||
#define KEMPLD_CFG_GPIO_I2C_MUX (1 << 0)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* OF helpers for the GPIO API
|
||||
*
|
||||
* Copyright (c) 2007-2008 MontaVista Software, Inc.
|
||||
*
|
||||
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_OF_GPIO_H
|
||||
#define __LINUX_OF_GPIO_H
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio.h> /* FIXME: Shouldn't be here */
|
||||
#include <linux/of.h>
|
||||
|
||||
struct device_node;
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
|
||||
extern int of_get_named_gpio(const struct device_node *np,
|
||||
const char *list_name, int index);
|
||||
|
||||
#else /* CONFIG_OF_GPIO */
|
||||
|
||||
#include <linux/errno.h>
|
||||
|
||||
/* Drivers may not strictly depend on the GPIO support, so let them link. */
|
||||
static inline int of_get_named_gpio(const struct device_node *np,
|
||||
const char *propname, int index)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_OF_GPIO */
|
||||
|
||||
#endif /* __LINUX_OF_GPIO_H */
|
||||
@@ -35,6 +35,8 @@ int pinctrl_gpio_direction_output(struct gpio_chip *gc,
|
||||
unsigned int offset);
|
||||
int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
||||
unsigned long config);
|
||||
int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset,
|
||||
unsigned long *config);
|
||||
|
||||
struct pinctrl * __must_check pinctrl_get(struct device *dev);
|
||||
void pinctrl_put(struct pinctrl *p);
|
||||
@@ -101,6 +103,13 @@ pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset,
|
||||
unsigned long *config)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
||||
unsigned long config)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh
|
||||
TEST_PROGS := gpio-mockup.sh gpio-sim.sh gpio-aggregator.sh gpio-cdev-uaf.sh
|
||||
TEST_FILES := gpio-mockup-sysfs.sh
|
||||
TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name
|
||||
TEST_GEN_PROGS_EXTENDED := gpio-mockup-cdev gpio-chip-info gpio-line-name \
|
||||
gpio-cdev-uaf
|
||||
CFLAGS += -O2 -g -Wall $(KHDR_INCLUDES)
|
||||
|
||||
include ../lib.mk
|
||||
|
||||
292
tools/testing/selftests/gpio/gpio-cdev-uaf.c
Normal file
292
tools/testing/selftests/gpio/gpio-cdev-uaf.c
Normal file
@@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* GPIO character device helper for UAF tests.
|
||||
*
|
||||
* Copyright 2026 Google LLC
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define CONFIGFS_DIR "/sys/kernel/config/gpio-sim"
|
||||
#define PROCFS_DIR "/proc"
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("usage:\n");
|
||||
printf(" gpio-cdev-uaf [chip|handle|event|req] [poll|read|ioctl]\n");
|
||||
}
|
||||
|
||||
static int _create_chip(const char *name, int create)
|
||||
{
|
||||
char path[64];
|
||||
|
||||
snprintf(path, sizeof(path), CONFIGFS_DIR "/%s", name);
|
||||
|
||||
if (create)
|
||||
return mkdir(path, 0755);
|
||||
else
|
||||
return rmdir(path);
|
||||
}
|
||||
|
||||
static int create_chip(const char *name)
|
||||
{
|
||||
return _create_chip(name, 1);
|
||||
}
|
||||
|
||||
static void remove_chip(const char *name)
|
||||
{
|
||||
_create_chip(name, 0);
|
||||
}
|
||||
|
||||
static int _create_bank(const char *chip_name, const char *name, int create)
|
||||
{
|
||||
char path[64];
|
||||
|
||||
snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s", chip_name, name);
|
||||
|
||||
if (create)
|
||||
return mkdir(path, 0755);
|
||||
else
|
||||
return rmdir(path);
|
||||
}
|
||||
|
||||
static int create_bank(const char *chip_name, const char *name)
|
||||
{
|
||||
return _create_bank(chip_name, name, 1);
|
||||
}
|
||||
|
||||
static void remove_bank(const char *chip_name, const char *name)
|
||||
{
|
||||
_create_bank(chip_name, name, 0);
|
||||
}
|
||||
|
||||
static int _enable_chip(const char *name, int enable)
|
||||
{
|
||||
char path[64];
|
||||
int fd, ret;
|
||||
|
||||
snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/live", name);
|
||||
|
||||
fd = open(path, O_WRONLY);
|
||||
if (fd == -1)
|
||||
return fd;
|
||||
|
||||
if (enable)
|
||||
ret = write(fd, "1", 1);
|
||||
else
|
||||
ret = write(fd, "0", 1);
|
||||
|
||||
close(fd);
|
||||
return ret == 1 ? 0 : -1;
|
||||
}
|
||||
|
||||
static int enable_chip(const char *name)
|
||||
{
|
||||
return _enable_chip(name, 1);
|
||||
}
|
||||
|
||||
static void disable_chip(const char *name)
|
||||
{
|
||||
_enable_chip(name, 0);
|
||||
}
|
||||
|
||||
static int open_chip(const char *chip_name, const char *bank_name)
|
||||
{
|
||||
char path[64], dev_name[32];
|
||||
int ret, fd;
|
||||
|
||||
ret = create_chip(chip_name);
|
||||
if (ret) {
|
||||
fprintf(stderr, "failed to create chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = create_bank(chip_name, bank_name);
|
||||
if (ret) {
|
||||
fprintf(stderr, "failed to create bank\n");
|
||||
goto err_remove_chip;
|
||||
}
|
||||
|
||||
ret = enable_chip(chip_name);
|
||||
if (ret) {
|
||||
fprintf(stderr, "failed to enable chip\n");
|
||||
goto err_remove_bank;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), CONFIGFS_DIR "/%s/%s/chip_name",
|
||||
chip_name, bank_name);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
ret = fd;
|
||||
fprintf(stderr, "failed to open %s\n", path);
|
||||
goto err_disable_chip;
|
||||
}
|
||||
|
||||
ret = read(fd, dev_name, sizeof(dev_name) - 1);
|
||||
close(fd);
|
||||
if (ret == -1) {
|
||||
fprintf(stderr, "failed to read %s\n", path);
|
||||
goto err_disable_chip;
|
||||
}
|
||||
dev_name[ret] = '\0';
|
||||
if (ret && dev_name[ret - 1] == '\n')
|
||||
dev_name[ret - 1] = '\0';
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s", dev_name);
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd == -1) {
|
||||
ret = fd;
|
||||
fprintf(stderr, "failed to open %s\n", path);
|
||||
goto err_disable_chip;
|
||||
}
|
||||
|
||||
return fd;
|
||||
err_disable_chip:
|
||||
disable_chip(chip_name);
|
||||
err_remove_bank:
|
||||
remove_bank(chip_name, bank_name);
|
||||
err_remove_chip:
|
||||
remove_chip(chip_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void close_chip(const char *chip_name, const char *bank_name)
|
||||
{
|
||||
disable_chip(chip_name);
|
||||
remove_bank(chip_name, bank_name);
|
||||
remove_chip(chip_name);
|
||||
}
|
||||
|
||||
static int test_poll(int fd)
|
||||
{
|
||||
struct pollfd pfds;
|
||||
|
||||
pfds.fd = fd;
|
||||
pfds.events = POLLIN;
|
||||
pfds.revents = 0;
|
||||
|
||||
if (poll(&pfds, 1, 0) == -1)
|
||||
return -1;
|
||||
|
||||
return (pfds.revents & ~(POLLHUP | POLLERR)) ? -1 : 0;
|
||||
}
|
||||
|
||||
static int test_read(int fd)
|
||||
{
|
||||
char data;
|
||||
|
||||
if (read(fd, &data, 1) == -1 && errno == ENODEV)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int test_ioctl(int fd)
|
||||
{
|
||||
if (ioctl(fd, 0, NULL) == -1 && errno == ENODEV)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int cfd, fd, ret;
|
||||
int (*test_func)(int);
|
||||
|
||||
if (argc != 3) {
|
||||
print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "chip") == 0 ||
|
||||
strcmp(argv[1], "event") == 0 ||
|
||||
strcmp(argv[1], "req") == 0) {
|
||||
if (strcmp(argv[2], "poll") &&
|
||||
strcmp(argv[2], "read") &&
|
||||
strcmp(argv[2], "ioctl")) {
|
||||
fprintf(stderr, "unknown command: %s\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (strcmp(argv[1], "handle") == 0) {
|
||||
if (strcmp(argv[2], "ioctl")) {
|
||||
fprintf(stderr, "unknown command: %s\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "unknown command: %s\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (strcmp(argv[2], "poll") == 0)
|
||||
test_func = test_poll;
|
||||
else if (strcmp(argv[2], "read") == 0)
|
||||
test_func = test_read;
|
||||
else /* strcmp(argv[2], "ioctl") == 0 */
|
||||
test_func = test_ioctl;
|
||||
|
||||
cfd = open_chip("chip", "bank");
|
||||
if (cfd == -1) {
|
||||
fprintf(stderr, "failed to open chip\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Step 1: Hold a FD to the test target. */
|
||||
if (strcmp(argv[1], "chip") == 0) {
|
||||
fd = cfd;
|
||||
} else if (strcmp(argv[1], "handle") == 0) {
|
||||
struct gpiohandle_request req = {0};
|
||||
|
||||
req.lines = 1;
|
||||
if (ioctl(cfd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1) {
|
||||
fprintf(stderr, "failed to get handle FD\n");
|
||||
goto err_close_chip;
|
||||
}
|
||||
|
||||
close(cfd);
|
||||
fd = req.fd;
|
||||
} else if (strcmp(argv[1], "event") == 0) {
|
||||
struct gpioevent_request req = {0};
|
||||
|
||||
if (ioctl(cfd, GPIO_GET_LINEEVENT_IOCTL, &req) == -1) {
|
||||
fprintf(stderr, "failed to get event FD\n");
|
||||
goto err_close_chip;
|
||||
}
|
||||
|
||||
close(cfd);
|
||||
fd = req.fd;
|
||||
} else { /* strcmp(argv[1], "req") == 0 */
|
||||
struct gpio_v2_line_request req = {0};
|
||||
|
||||
req.num_lines = 1;
|
||||
if (ioctl(cfd, GPIO_V2_GET_LINE_IOCTL, &req) == -1) {
|
||||
fprintf(stderr, "failed to get req FD\n");
|
||||
goto err_close_chip;
|
||||
}
|
||||
|
||||
close(cfd);
|
||||
fd = req.fd;
|
||||
}
|
||||
|
||||
/* Step 2: Free the chip. */
|
||||
close_chip("chip", "bank");
|
||||
|
||||
/* Step 3: Access the dangling FD to trigger UAF. */
|
||||
ret = test_func(fd);
|
||||
close(fd);
|
||||
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
err_close_chip:
|
||||
close(cfd);
|
||||
close_chip("chip", "bank");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
63
tools/testing/selftests/gpio/gpio-cdev-uaf.sh
Executable file
63
tools/testing/selftests/gpio/gpio-cdev-uaf.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright 2026 Google LLC
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
MODULE="gpio-cdev-uaf"
|
||||
|
||||
fail() {
|
||||
echo "$*" >&2
|
||||
echo "GPIO $MODULE test FAIL"
|
||||
exit 1
|
||||
}
|
||||
|
||||
skip() {
|
||||
echo "$*" >&2
|
||||
echo "GPIO $MODULE test SKIP"
|
||||
exit 4
|
||||
}
|
||||
|
||||
# Load the gpio-sim module. This will pull in configfs if needed too.
|
||||
modprobe gpio-sim || skip "unable to load the gpio-sim module"
|
||||
# Make sure configfs is mounted at /sys/kernel/config. Wait a bit if needed.
|
||||
for _ in `seq 5`; do
|
||||
mountpoint -q /sys/kernel/config && break
|
||||
mount -t configfs none /sys/kernel/config
|
||||
sleep 0.1
|
||||
done
|
||||
mountpoint -q /sys/kernel/config || \
|
||||
skip "configfs not mounted at /sys/kernel/config"
|
||||
|
||||
echo "1. GPIO"
|
||||
|
||||
echo "1.1. poll"
|
||||
$BASE_DIR/gpio-cdev-uaf chip poll || fail "failed to test chip poll"
|
||||
echo "1.2. read"
|
||||
$BASE_DIR/gpio-cdev-uaf chip read || fail "failed to test chip read"
|
||||
echo "1.3. ioctl"
|
||||
$BASE_DIR/gpio-cdev-uaf chip ioctl || fail "failed to test chip ioctl"
|
||||
|
||||
echo "2. linehandle"
|
||||
|
||||
echo "2.1. ioctl"
|
||||
$BASE_DIR/gpio-cdev-uaf handle ioctl || fail "failed to test handle ioctl"
|
||||
|
||||
echo "3. lineevent"
|
||||
|
||||
echo "3.1. read"
|
||||
$BASE_DIR/gpio-cdev-uaf event read || fail "failed to test event read"
|
||||
echo "3.2. poll"
|
||||
$BASE_DIR/gpio-cdev-uaf event poll || fail "failed to test event poll"
|
||||
echo "3.3. ioctl"
|
||||
$BASE_DIR/gpio-cdev-uaf event ioctl || fail "failed to test event ioctl"
|
||||
|
||||
echo "4. linereq"
|
||||
|
||||
echo "4.1. read"
|
||||
$BASE_DIR/gpio-cdev-uaf req read || fail "failed to test req read"
|
||||
echo "4.2. poll"
|
||||
$BASE_DIR/gpio-cdev-uaf req poll || fail "failed to test req poll"
|
||||
echo "4.3. ioctl"
|
||||
$BASE_DIR/gpio-cdev-uaf req ioctl || fail "failed to test req ioctl"
|
||||
|
||||
echo "GPIO $MODULE test PASS"
|
||||
Reference in New Issue
Block a user