Commit b3c0eccb authored by Linus Torvalds's avatar Linus Torvalds

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

Pull gpio updates from Bartosz Golaszewski:
 "The majority of added lines are two new modules: the GPIO virtual
  consumer module that improves our ability to add automated tests for
  the kernel API and the "sloppy" logic analyzer module that uses the
  GPIO API to implement a coarse-grained debugging tool for useful for
  remote development.

  Other than that we have the usual assortment of various driver
  extensions, improvements to the core GPIO code, DT-bindings and other
  documentation updates as well as an extension to the interrupt
  simulator:

  GPIOLIB core:
   - rework kfifo handling rework in the character device code
   - improve the labeling of GPIOs requested as interrupts and show more
     info on interrupt-only GPIOs in debugfs
   - remove unused APIs
   - unexport interfaces that are only used from the core GPIO code
   - drop the return value from gpiochip_set_desc_names() as it cannot
     fail
   - move a string array definition out of a header and into a specific
     compilation unit
   - convert the last user of gpiochip_get_desc() other than GPIO core
     to using a safer alternative
   - use array_index_nospec() where applicable

  New drivers:
   - add a "virtual GPIO consumer" module that allows requesting GPIOs
     from actual hardware and driving tests of the in-kernel GPIO API
     from user-space over debugfs
   - add a GPIO-based "sloppy" logic analyzer module useful for "first
     glance" debugging on remote boards

  Driver improvements:
   - add support for a new model to gpio-pca953x
   - lock GPIOs as interrupts in gpio-sim when the lines are requested
     as irqs via the simulator domain + some other minor improvements
   - improve error reporting in gpio-syscon
   - convert gpio-ath79 to using dynamic GPIO base and range
   - use pcibios_err_to_errno() for converting PCIBIOS error codes to
     errno vaues in gpio-amd8111 and gpio-rdc321x
   - allow building gpio-brcmstb for the BCM2835 architecture

  DT bindings:
   - convert DT bindings for lsi,zevio, mpc8xxx, and atmel to DT schema
   - document new properties for aspeed,gpio, fsl,qoriq-gpio and
     gpio-vf610
   - document new compatibles for pca953x and fsl,qoriq-gpio

  Documentation:
   - document stricter behavior of the GPIO character device uAPI with
     regards to reconfiguring requested line without direction set
   - clarify the effect of the active-low flag on line values and edges
   - remove documentation for the legacy GPIO API in order to stop
     tempting people to use it
   - document the preference for using pread() for reading edge events
     in the sysfs API

  Other:
   - add an extended initializer to the interrupt simulator allowing to
     specify a number of callbacks callers can use to be notified about
     irqs being requested and released"

* tag 'gpio-updates-for-v6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (41 commits)
  gpio: mc33880: Convert comma to semicolon
  gpio: virtuser: actually use the "trimmed" local variable
  dt-bindings: gpio: convert Atmel GPIO to json-schema
  gpio: virtuser: new virtual testing driver for the GPIO API
  dt-bindings: gpio: vf610: Allow gpio-line-names to be set
  gpio: sim: lock GPIOs as interrupts when they are requested
  genirq/irq_sim: add an extended irq_sim initializer
  dt-bindings: gpio: fsl,qoriq-gpio: Add compatible string fsl,ls1046a-gpio
  gpiolib: unexport gpiochip_get_desc()
  gpio: add sloppy logic analyzer using polling
  Documentation: gpio: Reconfiguration with unset direction (uAPI v2)
  Documentation: gpio: Reconfiguration with unset direction (uAPI v1)
  dt-bindings: gpio: fsl,qoriq-gpio: add common property gpio-line-names
  gpio: ath79: convert to dynamic GPIO base allocation
  pinctrl: da9062: replace gpiochip_get_desc() with gpio_device_get_desc()
  gpiolib: put gpio_suffixes in a single compilation unit
  Documentation: gpio: Clarify effect of active low flag on line edges
  Documentation: gpio: Clarify effect of active low flag on line values
  gpiolib: Remove data-less gpiochip_add() function
  gpio: sim: use devm_mutex_init()
  ...
parents 3f32ab14 dfda97e3
.. SPDX-License-Identifier: GPL-2.0-only
Virtual GPIO Consumer
=====================
The virtual GPIO Consumer module allows users to instantiate virtual devices
that request GPIOs and then control their behavior over debugfs. Virtual
consumer devices can be instantiated from device-tree or over configfs.
A virtual consumer uses the driver-facing GPIO APIs and allows to cover it with
automated tests driven by user-space. The GPIOs are requested using
``gpiod_get_array()`` and so we support multiple GPIOs per connector ID.
Creating GPIO consumers
-----------------------
The gpio-consumer module registers a configfs subsystem called
``'gpio-virtuser'``. For details of the configfs filesystem, please refer to
the configfs documentation.
The user can create a hierarchy of configfs groups and items as well as modify
values of exposed attributes. Once the consumer is instantiated, this hierarchy
will be translated to appropriate device properties. The general structure is:
**Group:** ``/config/gpio-virtuser``
This is the top directory of the gpio-consumer configfs tree.
**Group:** ``/config/gpio-consumer/example-name``
**Attribute:** ``/config/gpio-consumer/example-name/live``
**Attribute:** ``/config/gpio-consumer/example-name/dev_name``
This is a directory representing a GPIO consumer device.
The read-only ``dev_name`` attribute exposes the name of the device as it will
appear in the system on the platform bus. This is useful for locating the
associated debugfs directory under
``/sys/kernel/debug/gpio-virtuser/$dev_name``.
The ``'live'`` attribute allows to trigger the actual creation of the device
once it's fully configured. The accepted values are: ``'1'`` to enable the
virtual device and ``'0'`` to disable and tear it down.
Creating GPIO lookup tables
---------------------------
Users can create a number of configfs groups under the device group:
**Group:** ``/config/gpio-consumer/example-name/con_id``
The ``'con_id'`` directory represents a single GPIO lookup and its value maps
to the ``'con_id'`` argument of the ``gpiod_get()`` function. For example:
``con_id`` == ``'reset'`` maps to the ``reset-gpios`` device property.
Users can assign a number of GPIOs to each lookup. Each GPIO is a sub-directory
with a user-defined name under the ``'con_id'`` group.
**Attribute:** ``/config/gpio-consumer/example-name/con_id/0/key``
**Attribute:** ``/config/gpio-consumer/example-name/con_id/0/offset``
**Attribute:** ``/config/gpio-consumer/example-name/con_id/0/drive``
**Attribute:** ``/config/gpio-consumer/example-name/con_id/0/pull``
**Attribute:** ``/config/gpio-consumer/example-name/con_id/0/active_low``
**Attribute:** ``/config/gpio-consumer/example-name/con_id/0/transitory``
This is a group describing a single GPIO in the ``con_id-gpios`` property.
For virtual consumers created using configfs we use machine lookup tables so
this group can be considered as a mapping between the filesystem and the fields
of a single entry in ``'struct gpiod_lookup'``.
The ``'key'`` attribute represents either the name of the chip this GPIO
belongs to or the GPIO line name. This depends on the value of the ``'offset'``
attribute: if its value is >= 0, then ``'key'`` represents the label of the
chip to lookup while ``'offset'`` represents the offset of the line in that
chip. If ``'offset'`` is < 0, then ``'key'`` represents the name of the line.
The remaining attributes map to the ``'flags'`` field of the GPIO lookup
struct. The first two take string values as arguments:
**``'drive'``:** ``'push-pull'``, ``'open-drain'``, ``'open-source'``
**``'pull'``:** ``'pull-up'``, ``'pull-down'``, ``'pull-disabled'``, ``'as-is'``
``'active_low'`` and ``'transitory'`` are boolean attributes.
Activating GPIO consumers
-------------------------
Once the confiuration is complete, the ``'live'`` attribute must be set to 1 in
order to instantiate the consumer. It can be set back to 0 to destroy the
virtual device. The module will synchronously wait for the new simulated device
to be successfully probed and if this doesn't happen, writing to ``'live'`` will
result in an error.
Device-tree
-----------
Virtual GPIO consumers can also be defined in device-tree. The compatible string
must be: ``"gpio-virtuser"`` with at least one property following the
standardized GPIO pattern.
An example device-tree code defining a virtual GPIO consumer:
.. code-block :: none
gpio-virt-consumer {
compatible = "gpio-virtuser";
foo-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>, <&gpio1 2 0>;
bar-gpios = <&gpio0 6 0>;
};
Controlling virtual GPIO consumers
----------------------------------
Once active, the device will export debugfs attributes for controlling GPIO
arrays as well as each requested GPIO line separately. Let's consider the
following device property: ``foo-gpios = <&gpio0 0 0>, <&gpio0 4 0>;``.
The following debugfs attribute groups will be created:
**Group:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo/``
This is the group that will contain the attributes for the entire GPIO array.
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo/values``
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo/values_atomic``
Both attributes allow to read and set arrays of GPIO values. User must pass
exactly the number of values that the array contains in the form of a string
containing zeroes and ones representing inactive and active GPIO states
respectively. In this example: ``echo 11 > values``.
The ``values_atomic`` attribute works the same as ``values`` but the kernel
will execute the GPIO driver callbacks in interrupt context.
**Group:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/``
This is a group that represents a single GPIO with ``$index`` being its offset
in the array.
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/consumer``
Allows to set and read the consumer label of the GPIO line.
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/debounce``
Allows to set and read the debounce period of the GPIO line.
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/direction``
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/direction_atomic``
These two attributes allow to set the direction of the GPIO line. They accept
"input" and "output" as values. The atomic variant executes the driver callback
in interrupt context.
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/interrupts``
If the line is requested in input mode, writing ``1`` to this attribute will
make the module listen for edge interrupts on the GPIO. Writing ``0`` disables
the monitoring. Reading this attribute returns the current number of registered
interrupts (both edges).
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/value``
**Attribute:** ``/sys/kernel/debug/gpio-virtuser/$dev_name/gpiod:foo:$index/value_atomic``
Both attributes allow to read and set values of individual requested GPIO lines.
They accept the following values: ``1`` and ``0``.
......@@ -10,6 +10,7 @@ GPIO
Character Device Userspace API <../../userspace-api/gpio/chardev>
gpio-aggregator
gpio-sim
gpio-virtuser
Obsolete APIs <obsolete>
.. only:: subproject and html
......
.. SPDX-License-Identifier: GPL-2.0
=============================================
Linux Kernel GPIO based sloppy logic analyzer
=============================================
:Author: Wolfram Sang
Introduction
============
This document briefly describes how to run the GPIO based in-kernel sloppy
logic analyzer running on an isolated CPU.
The sloppy logic analyzer will utilize a few GPIO lines in input mode on a
system to rapidly sample these digital lines, which will, if the Nyquist
criteria is met, result in a time series log with approximate waveforms as they
appeared on these lines. One way to use it is to analyze external traffic
connected to these GPIO lines with wires (i.e. digital probes), acting as a
common logic analyzer.
Another feature is to snoop on on-chip peripherals if the I/O cells of these
peripherals can be used in GPIO input mode at the same time as they are being
used as inputs or outputs for the peripheral. That means you could e.g. snoop
I2C traffic without any wiring (if your hardware supports it). In the pin
control subsystem such pin controllers are called "non-strict": a certain pin
can be used with a certain peripheral and as a GPIO input line at the same
time.
Note that this is a last resort analyzer which can be affected by latencies,
non-deterministic code paths and non-maskable interrupts. It is called 'sloppy'
for a reason. However, for e.g. remote development, it may be useful to get a
first view and aid further debugging.
Setup
=====
Your kernel must have CONFIG_DEBUG_FS and CONFIG_CPUSETS enabled. Ideally, your
runtime environment does not utilize cpusets otherwise, then isolation of a CPU
core is easiest. If you do need cpusets, check that helper script for the
sloppy logic analyzer does not interfere with your other settings.
Tell the kernel which GPIOs are used as probes. For a Device Tree based system,
you need to use the following bindings. Because these bindings are only for
debugging, there is no official schema::
i2c-analyzer {
compatible = "gpio-sloppy-logic-analyzer";
probe-gpios = <&gpio6 21 GPIO_OPEN_DRAIN>, <&gpio6 4 GPIO_OPEN_DRAIN>;
probe-names = "SCL", "SDA";
};
Note that you must provide a name for every GPIO specified. Currently a
maximum of 8 probes are supported. 32 are likely possible but are not
implemented yet.
Usage
=====
The logic analyzer is configurable via files in debugfs. However, it is
strongly recommended to not use them directly, but to use the script
``tools/gpio/gpio-sloppy-logic-analyzer``. Besides checking parameters more
extensively, it will isolate the CPU core so you will have the least
disturbance while measuring.
The script has a help option explaining the parameters. For the above DT
snippet which analyzes an I2C bus at 400kHz on a Renesas Salvator-XS board, the
following settings are used: The isolated CPU shall be CPU1 because it is a big
core in a big.LITTLE setup. Because CPU1 is the default, we don't need a
parameter. The bus speed is 400kHz. So, the sampling theorem says we need to
sample at least at 800kHz. However, falling edges of both signals in an I2C
start condition happen faster, so we need a higher sampling frequency, e.g.
``-s 1500000`` for 1.5MHz. Also, we don't want to sample right away but wait
for a start condition on an idle bus. So, we need to set a trigger to a falling
edge on SDA while SCL stays high, i.e. ``-t 1H+2F``. Last is the duration, let
us assume 15ms here which results in the parameter ``-d 15000``. So,
altogether::
gpio-sloppy-logic-analyzer -s 1500000 -t 1H+2F -d 15000
Note that the process will return you back to the prompt but a sub-process is
still sampling in the background. Unless this has finished, you will not find a
result file in the current or specified directory. For the above example, we
will then need to trigger I2C communication::
i2cdetect -y -r <your bus number>
Result is a .sr file to be consumed with PulseView or sigrok-cli from the free
`sigrok`_ project. It is a zip file which also contains the binary sample data
which may be consumed by other software. The filename is the logic analyzer
instance name plus a since-epoch timestamp.
.. _sigrok: https://sigrok.org/
......@@ -32,6 +32,7 @@ Documentation/dev-tools/testing-overview.rst
kunit/index
ktap
checkuapi
gpio-sloppy-logic-analyzer
.. only:: subproject and html
......
......@@ -33,6 +33,11 @@ properties:
gpio-controller: true
# Each SGPIO is represented as a pair of input and output GPIOs
gpio-line-names:
minItems: 160
maxItems: 256
'#gpio-cells':
const: 2
......@@ -41,6 +46,9 @@ properties:
interrupt-controller: true
'#interrupt-cells':
const: 2
clocks:
maxItems: 1
......@@ -55,6 +63,7 @@ required:
- '#gpio-cells'
- interrupts
- interrupt-controller
- '#interrupt-cells'
- ngpios
- clocks
- bus-frequency
......@@ -72,6 +81,7 @@ examples:
reg = <0x1e780200 0x0100>;
clocks = <&syscon ASPEED_CLK_APB>;
interrupt-controller;
#interrupt-cells = <2>;
ngpios = <80>;
bus-frequency = <12000000>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/atmel,at91rm9200-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip GPIO controller (PIO)
maintainers:
- Manikandan Muralidharan <manikandan.m@microchip.com>
properties:
compatible:
oneOf:
- items:
- enum:
- atmel,at91sam9x5-gpio
- microchip,sam9x60-gpio
- const: atmel,at91rm9200-gpio
- items:
- enum:
- microchip,sam9x7-gpio
- const: microchip,sam9x60-gpio
- const: atmel,at91rm9200-gpio
- items:
- const: atmel,at91rm9200-gpio
reg:
maxItems: 1
interrupts:
maxItems: 1
interrupt-controller: true
"#interrupt-cells":
const: 2
gpio-controller: true
gpio-line-names: true
"#gpio-cells":
const: 2
clocks:
maxItems: 1
"#gpio-lines":
description:
Number of gpio, 32 by default if absent
maxItems: 1
default: 32
required:
- compatible
- reg
- interrupts
- interrupt-controller
- "#interrupt-cells"
- gpio-controller
- "#gpio-cells"
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
gpio@fffff400 {
compatible = "atmel,at91rm9200-gpio";
reg = <0xfffff400 0x200>;
interrupts = <2 IRQ_TYPE_LEVEL_HIGH 1>;
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
#interrupt-cells = <2>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 2>;
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/fsl,qoriq-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale MPC512x/MPC8xxx/QorIQ/Layerscape GPIO controller
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
oneOf:
- enum:
- fsl,mpc5121-gpio
- fsl,mpc5125-gpio
- fsl,mpc8349-gpio
- fsl,mpc8572-gpio
- fsl,mpc8610-gpio
- fsl,pq3-gpio
- items:
- enum:
- fsl,ls1021a-gpio
- fsl,ls1028a-gpio
- fsl,ls1043a-gpio
- fsl,ls1046a-gpio
- fsl,ls1088a-gpio
- fsl,ls2080a-gpio
- const: fsl,qoriq-gpio
reg:
maxItems: 1
interrupts:
maxItems: 1
"#gpio-cells":
const: 2
gpio-controller: true
interrupt-controller: true
"#interrupt-cells":
const: 2
gpio-line-names:
minItems: 1
maxItems: 32
little-endian:
$ref: /schemas/types.yaml#/definitions/flag
description:
GPIO registers are used as little endian. If not
present registers are used as big endian by default.
required:
- compatible
- reg
- interrupts
- "#gpio-cells"
additionalProperties: false
examples:
- |
gpio@1100 {
compatible = "fsl,mpc5125-gpio";
reg = <0x1100 0x080>;
interrupts = <78 0x8>;
gpio-controller;
#gpio-cells = <2>;
};
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
gpio@2300000 {
compatible = "fsl,ls2080a-gpio", "fsl,qoriq-gpio";
reg = <0x2300000 0x10000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
little-endian;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
* Freescale MPC512x/MPC8xxx/QorIQ/Layerscape GPIO controller
Required properties:
- compatible : Should be "fsl,<soc>-gpio"
The following <soc>s are known to be supported:
mpc5121, mpc5125, mpc8349, mpc8572, mpc8610, pq3, qoriq,
ls1021a, ls1043a, ls2080a, ls1028a, ls1088a.
- reg : Address and length of the register set for the device
- interrupts : Should be the port interrupt shared by all 32 pins.
- #gpio-cells : Should be two. The first cell is the pin number and
the second cell is used to specify the gpio polarity:
0 = active high
1 = active low
Optional properties:
- little-endian : GPIO registers are used as little endian. If not
present registers are used as big endian by default.
Example of gpio-controller node for a mpc5125 SoC:
gpio0: gpio@1100 {
compatible = "fsl,mpc5125-gpio";
#gpio-cells = <2>;
reg = <0x1100 0x080>;
interrupts = <78 0x8>;
};
Example of gpio-controller node for a ls2080a SoC:
gpio0: gpio@2300000 {
compatible = "fsl,ls2080a-gpio", "fsl,qoriq-gpio";
reg = <0x0 0x2300000 0x0 0x10000>;
interrupts = <0 36 0x4>; /* Level high type */
gpio-controller;
little-endian;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
Example of gpio-controller node for a ls1028a/ls1088a SoC:
gpio1: gpio@2300000 {
compatible = "fsl,ls1028a-gpio", "fsl,ls1088a-gpio", "fsl,qoriq-gpio";
reg = <0x0 0x2300000 0x0 0x10000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
little-endian;
};
......@@ -66,6 +66,7 @@ properties:
- ti,tca6408
- ti,tca6416
- ti,tca6424
- ti,tca9535
- ti,tca9538
- ti,tca9539
- ti,tca9554
......
......@@ -51,6 +51,10 @@ properties:
gpio-controller: true
gpio-line-names:
minItems: 1
maxItems: 32
clocks:
items:
- description: SoC GPIO clock
......
Zevio GPIO controller
Required properties:
- compatible: Should be "lsi,zevio-gpio"
- reg: Address and length of the register set for the device
- #gpio-cells: Should be two. The first cell is the pin number and the
second cell is used to specify optional parameters (currently unused).
- gpio-controller: Marks the device node as a GPIO controller.
Example:
gpio: gpio@90000000 {
compatible = "lsi,zevio-gpio";
reg = <0x90000000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};
* Atmel GPIO controller (PIO)
Required properties:
- compatible: "atmel,<chip>-gpio", where <chip> is at91rm9200 or at91sam9x5.
- reg: Should contain GPIO controller registers location and length
- interrupts: Should be the port interrupt shared by all the pins.
- #gpio-cells: Should be two. The first cell is the pin number and
the second cell is used to specify optional parameters to declare if the GPIO
is active high or low. See gpio.txt.
- gpio-controller: Marks the device node as a GPIO controller.
- interrupt-controller: Marks the device node as an interrupt controller.
- #interrupt-cells: Should be two. The first cell is the pin number and the
second cell is used to specify irq type flags, see the two cell description
in interrupt-controller/interrupts.txt for details.
optional properties:
- #gpio-lines: Number of gpio if absent 32.
Example:
pioA: gpio@fffff200 {
compatible = "atmel,at91rm9200-gpio";
reg = <0xfffff200 0x100>;
interrupts = <2 4>;
#gpio-cells = <2>;
gpio-controller;
#gpio-lines = <19>;
interrupt-controller;
#interrupt-cells = <2>;
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/lsi,zevio-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Zevio GPIO controller
maintainers:
- Pratik Farkase <pratikfarkase94@gmail.com>
properties:
compatible:
items:
- const: lsi,zevio-gpio
reg:
maxItems: 1
interrupts:
maxItems: 1
"#gpio-cells":
const: 2
gpio-controller: true
required:
- compatible
- reg
- "#gpio-cells"
- gpio-controller
unevaluatedProperties: false
examples:
- |
gpio@90000000 {
compatible = "lsi,zevio-gpio";
reg = <0x90000000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};
......@@ -4,12 +4,6 @@ GPIO Mappings
This document explains how GPIOs can be assigned to given devices and functions.
Note that it only applies to the new descriptor-based interface. For a
description of the deprecated integer-based GPIO interface please refer to
legacy.rst (actually, there is no real mapping possible with the old
interface; you just fetch an integer from somewhere and request the
corresponding GPIO).
All platforms can enable the GPIO library, but if the platform strictly
requires GPIO functionality to be present, it needs to select GPIOLIB from its
Kconfig. Then, how GPIOs are mapped depends on what the platform uses to
......
......@@ -2,9 +2,7 @@
GPIO Descriptor Consumer Interface
==================================
This document describes the consumer interface of the GPIO framework. Note that
it describes the new descriptor-based interface. For a description of the
deprecated integer-based GPIO interface please refer to legacy.rst.
This document describes the consumer interface of the GPIO framework.
Guidelines for GPIOs consumers
......
......@@ -69,9 +69,8 @@ driver code:
The code implementing a gpio_chip should support multiple instances of the
controller, preferably using the driver model. That code will configure each
gpio_chip and issue gpiochip_add(), gpiochip_add_data(), or
devm_gpiochip_add_data(). Removing a GPIO controller should be rare; use
gpiochip_remove() when it is unavoidable.
gpio_chip and issue gpiochip_add_data() or devm_gpiochip_add_data(). Removing
a GPIO controller should be rare; use gpiochip_remove() when it is unavoidable.
Often a gpio_chip is part of an instance-specific structure with states not
exposed by the GPIO interfaces, such as addressing, power management, and more.
......
......@@ -13,7 +13,6 @@ Contents:
consumer
board
drivers-on-gpio
legacy
bt8xxgpio
Core
......
......@@ -10,18 +10,6 @@ The documents in this directory give detailed instructions on how to access
GPIOs in drivers, and how to write a driver for a device that provides GPIOs
itself.
Due to the history of GPIO interfaces in the kernel, there are two different
ways to obtain and use GPIOs:
- The descriptor-based interface is the preferred way to manipulate GPIOs,
and is described by all the files in this directory excepted legacy.rst.
- The legacy integer-based interface which is considered deprecated (but still
usable for compatibility reasons) is documented in legacy.rst.
The remainder of this document applies to the new descriptor-based interface.
legacy.rst contains the same information applied to the legacy
integer-based interface.
What is a GPIO?
===============
......
This diff is collapsed.
......@@ -18,8 +18,6 @@
:caption: 目录
:maxdepth: 2
legacy
Todolist:
* intro
......
This diff is collapsed.
......@@ -36,6 +36,13 @@ Description
Get the values of all requested lines.
The values returned are logical, indicating if the line is active or inactive.
The ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` flag controls the mapping between physical
values (high/low) and logical values (active/inactive).
If ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` is not set then high is active and
low is inactive. If ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` is set then low is active
and high is inactive.
The values of both input and output lines may be read.
For output lines, the value returned is driver and configuration dependent and
......
......@@ -43,7 +43,10 @@ The configuration applies to all requested lines.
The same :ref:`gpio-get-linehandle-config-rules` and
:ref:`gpio-get-linehandle-config-support` that apply when requesting the
lines also apply when updating the line configuration.
lines also apply when updating the line configuration, with the additional
restriction that a direction flag must be set. Requesting an invalid
configuration, including without a direction flag set, is an error
(**EINVAL**).
The motivating use case for this command is changing direction of
bi-directional lines between input and output, but it may be used more
......
......@@ -36,6 +36,13 @@ Description
Set the values of all requested output lines.
The values set are logical, indicating if the line is to be active or inactive.
The ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` flag controls the mapping between logical
values (active/inactive) and physical values (high/low).
If ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` is not set then active is high and
inactive is low. If ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` is set then active is low
and inactive is high.
Only the values of output lines may be set.
Attempting to set the value of input lines is an error (**EPERM**).
......
......@@ -44,6 +44,11 @@ Edge detection must be enabled for the input line using either
both. Edge events are then generated whenever edge interrupts are detected on
the input line.
Edges are defined in terms of changes to the logical line value, so an inactive
to active transition is a rising edge. If ``GPIOHANDLE_REQUEST_ACTIVE_LOW`` is
set then logical polarity is the opposite of physical polarity, and
``GPIOEVENT_REQUEST_RISING_EDGE`` then corresponds to a falling physical edge.
The kernel captures and timestamps edge events as close as possible to their
occurrence and stores them in a buffer from where they can be read by
userspace at its convenience using `read()`.
......
......@@ -40,6 +40,11 @@ Edge detection must be enabled for the input line using either
both. Edge events are then generated whenever edge interrupts are detected on
the input line.
Edges are defined in terms of changes to the logical line value, so an inactive
to active transition is a rising edge. If ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` is
set then logical polarity is the opposite of physical polarity, and
``GPIO_V2_LINE_FLAG_EDGE_RISING`` then corresponds to a falling physical edge.
The kernel captures and timestamps edge events as close as possible to their
occurrence and stores them in a buffer from where they can be read by
userspace at its convenience using `read()`.
......
......@@ -34,6 +34,13 @@ Description
Get the values of requested lines.
The values returned are logical, indicating if the line is active or inactive.
The ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` flag controls the mapping between physical
values (high/low) and logical values (active/inactive).
If ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` is not set then high is active and low is
inactive. If ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` is set then low is active and
high is inactive.
The values of both input and output lines may be read.
For output lines, the value returned is driver and configuration dependent and
......
......@@ -35,11 +35,14 @@ Description
Update the configuration of previously requested lines, without releasing the
line or introducing potential glitches.
The new configuration must specify the configuration of all requested lines.
The new configuration must specify a configuration for all requested lines.
The same :ref:`gpio-v2-get-line-config-rules` and
:ref:`gpio-v2-get-line-config-support` that apply when requesting the lines
also apply when updating the line configuration.
also apply when updating the line configuration, with the additional
restriction that a direction flag must be set to enable reconfiguration.
If no direction flag is set in the configuration for a given line then the
configuration for that line is left unchanged.
The motivating use case for this command is changing direction of
bi-directional lines between input and output, but it may also be used to
......
......@@ -35,6 +35,13 @@ Description
Set the values of requested output lines.
The values set are logical, indicating if the line is to be active or inactive.
The ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` flag controls the mapping between logical
values (active/inactive) and physical values (high/low).
If ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` is not set then active is high and inactive
is low. If ``GPIO_V2_LINE_FLAG_ACTIVE_LOW`` is set then active is low and
inactive is high.
Only the values of output lines may be set.
Attempting to set the value of an input line is an error (**EPERM**).
......
......@@ -97,9 +97,10 @@ and have the following read/write attributes:
poll(2) will return whenever the interrupt was triggered. If
you use poll(2), set the events POLLPRI and POLLERR. If you
use select(2), set the file descriptor in exceptfds. After
poll(2) returns, either lseek(2) to the beginning of the sysfs
file and read the new value or close the file and re-open it
to read the value.
poll(2) returns, use pread(2) to read the value at offset
zero. Alternatively, either lseek(2) to the beginning of the
sysfs file and read the new value or close the file and
re-open it to read the value.
"edge" ...
reads as either "none", "rising", "falling", or
......
......@@ -219,7 +219,7 @@ config GPIO_BCM_XGS_IPROC
config GPIO_BRCMSTB
tristate "BRCMSTB GPIO support"
default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST)
depends on OF_GPIO && (ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST)
select GPIO_GENERIC
select IRQ_DOMAIN
help
......@@ -1891,4 +1891,35 @@ config GPIO_SIM
endmenu
menu "GPIO Debugging utilities"
config GPIO_SLOPPY_LOGIC_ANALYZER
tristate "Sloppy GPIO logic analyzer"
depends on (GPIOLIB || COMPILE_TEST) && CPUSETS && DEBUG_FS && EXPERT
help
This option enables support for a sloppy logic analyzer using polled
GPIOs. Use the 'tools/gpio/gpio-sloppy-logic-analyzer' script with
this driver. The script will make it easier to use and will also
isolate a CPU for the polling task. Note that this is a last resort
analyzer which can be affected by latencies, non-deterministic code
paths, or NMIs. However, for e.g. remote development, it may be useful
to get a first view and aid further debugging.
If this driver is built as a module it will be called
'gpio-sloppy-logic-analyzer'.
config GPIO_VIRTUSER
tristate "GPIO Virtual User Testing Module"
select DEBUG_FS
select CONFIGFS_FS
select IRQ_WORK
help
Say Y here to enable the configurable, configfs-based virtual GPIO
consumer testing driver.
This driver is aimed as a helper in spotting any regressions in
hot-unplug handling in GPIOLIB.
endmenu
endif
......@@ -150,6 +150,7 @@ obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
obj-$(CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER) += gpio-sloppy-logic-analyzer.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
......@@ -181,6 +182,7 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o
obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
......
......@@ -195,8 +195,10 @@ static int __init amd_gpio_init(void)
found:
err = pci_read_config_dword(pdev, 0x58, &gp.pmbase);
if (err)
if (err) {
err = pcibios_err_to_errno(err);
goto out;
}
err = -EIO;
gp.pmbase &= 0x0000FF00;
if (gp.pmbase == 0)
......
......@@ -273,8 +273,6 @@ static int ath79_gpio_probe(struct platform_device *pdev)
dev_err(dev, "bgpio_init failed\n");
return err;
}
/* Use base 0 to stay compatible with legacy platforms */
ctrl->gc.base = 0;
/* Optional interrupt setup */
if (!np || of_property_read_bool(np, "interrupt-controller")) {
......
......@@ -99,7 +99,7 @@ static int mc33880_probe(struct spi_device *spi)
mc->spi = spi;
mc->chip.label = DRIVER_NAME,
mc->chip.label = DRIVER_NAME;
mc->chip.set = mc33880_set;
mc->chip.base = pdata->base;
mc->chip.ngpio = PIN_NUMBER;
......
......@@ -1315,6 +1315,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{ .compatible = "ti,tca6408", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
{ .compatible = "ti,tca9535", .data = OF_953X(16, PCA_INT), },
{ .compatible = "ti,tca9538", .data = OF_953X( 8, PCA_INT), },
{ .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
......
......@@ -102,7 +102,7 @@ static int rdc_gpio_config(struct gpio_chip *chip,
unlock:
spin_unlock(&gpch->lock);
return err;
return pcibios_err_to_errno(err);
}
/* configure GPIO pin as input */
......@@ -170,13 +170,13 @@ static int rdc321x_gpio_probe(struct platform_device *pdev)
rdc321x_gpio_dev->reg1_data_base,
&rdc321x_gpio_dev->data_reg[0]);
if (err)
return err;
return pcibios_err_to_errno(err);
err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
rdc321x_gpio_dev->reg2_data_base,
&rdc321x_gpio_dev->data_reg[1]);
if (err)
return err;
return pcibios_err_to_errno(err);
dev_info(&pdev->dev, "registering %d GPIOs\n",
rdc321x_gpio_dev->chip.ngpio);
......
......@@ -7,6 +7,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/array_size.h>
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
......@@ -20,7 +21,6 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irq_sim.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/minmax.h>
......@@ -227,6 +227,27 @@ static void gpio_sim_free(struct gpio_chip *gc, unsigned int offset)
}
}
static int gpio_sim_irq_requested(struct irq_domain *domain,
irq_hw_number_t hwirq, void *data)
{
struct gpio_sim_chip *chip = data;
return gpiochip_lock_as_irq(&chip->gc, hwirq);
}
static void gpio_sim_irq_released(struct irq_domain *domain,
irq_hw_number_t hwirq, void *data)
{
struct gpio_sim_chip *chip = data;
gpiochip_unlock_as_irq(&chip->gc, hwirq);
}
static const struct irq_sim_ops gpio_sim_irq_sim_ops = {
.irq_sim_irq_requested = gpio_sim_irq_requested,
.irq_sim_irq_released = gpio_sim_irq_released,
};
static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc)
{
struct gpio_sim_chip *chip = gpiochip_get_data(gc);
......@@ -308,13 +329,6 @@ static ssize_t gpio_sim_sysfs_pull_store(struct device *dev,
return len;
}
static void gpio_sim_mutex_destroy(void *data)
{
struct mutex *lock = data;
mutex_destroy(lock);
}
static void gpio_sim_put_device(void *data)
{
struct device *dev = data;
......@@ -450,7 +464,9 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
if (!chip->pull_map)
return -ENOMEM;
chip->irq_sim = devm_irq_domain_create_sim(dev, swnode, num_lines);
chip->irq_sim = devm_irq_domain_create_sim_full(dev, swnode, num_lines,
&gpio_sim_irq_sim_ops,
chip);
if (IS_ERR(chip->irq_sim))
return PTR_ERR(chip->irq_sim);
......@@ -458,9 +474,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev)
if (ret)
return ret;
mutex_init(&chip->lock);
ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy,
&chip->lock);
ret = devm_mutex_init(dev, &chip->lock);
if (ret)
return ret;
......@@ -581,19 +595,19 @@ static int gpio_sim_bus_notifier_call(struct notifier_block *nb,
snprintf(devname, sizeof(devname), "gpio-sim.%u", simdev->id);
if (strcmp(dev_name(dev), devname) == 0) {
if (action == BUS_NOTIFY_BOUND_DRIVER)
simdev->driver_bound = true;
else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND)
simdev->driver_bound = false;
else
return NOTIFY_DONE;
if (!device_match_name(dev, devname))
return NOTIFY_DONE;
complete(&simdev->probe_completion);
return NOTIFY_OK;
}
if (action == BUS_NOTIFY_BOUND_DRIVER)
simdev->driver_bound = true;
else if (action == BUS_NOTIFY_DRIVER_NOT_BOUND)
simdev->driver_bound = false;
else
return NOTIFY_DONE;
complete(&simdev->probe_completion);
return NOTIFY_DONE;
return NOTIFY_OK;
}
static struct gpio_sim_device *to_gpio_sim_device(struct config_item *item)
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Sloppy logic analyzer using GPIOs (to be run on an isolated CPU)
*
* Use the 'gpio-sloppy-logic-analyzer' script in the 'tools/gpio' folder for
* easier usage and further documentation. Note that this is a last resort
* analyzer which can be affected by latencies and non-deterministic code
* paths. However, for e.g. remote development, it may be useful to get a first
* view and aid further debugging.
*
* Copyright (C) Wolfram Sang <wsa@sang-engineering.com>
* Copyright (C) Renesas Electronics Corporation
*/
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/ktime.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/sizes.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#define GPIO_LA_NAME "gpio-sloppy-logic-analyzer"
#define GPIO_LA_DEFAULT_BUF_SIZE SZ_256K
/* can be increased but then we need to extend the u8 buffers */
#define GPIO_LA_MAX_PROBES 8
#define GPIO_LA_NUM_TESTS 1024
struct gpio_la_poll_priv {
struct mutex blob_lock; /* serialize access to the blob (data) */
u32 buf_idx;
struct gpio_descs *descs;
unsigned long delay_ns;
unsigned long acq_delay;
struct debugfs_blob_wrapper blob;
struct dentry *debug_dir;
struct dentry *blob_dent;
struct debugfs_blob_wrapper meta;
struct device *dev;
unsigned int trig_len;
u8 *trig_data;
};
static struct dentry *gpio_la_poll_debug_dir;
static __always_inline int gpio_la_get_array(struct gpio_descs *d, unsigned long *sptr)
{
int ret;
ret = gpiod_get_array_value(d->ndescs, d->desc, d->info, sptr);
if (ret == 0 && fatal_signal_pending(current))
ret = -EINTR;
return ret;
}
static int fops_capture_set(void *data, u64 val)
{
struct gpio_la_poll_priv *priv = data;
u8 *la_buf = priv->blob.data;
unsigned long state = 0; /* zeroed because GPIO arrays are bitfields */
unsigned long delay;
ktime_t start_time;
unsigned int i;
int ret;
if (!val)
return 0;
if (!la_buf)
return -ENOMEM;
if (!priv->delay_ns)
return -EINVAL;
mutex_lock(&priv->blob_lock);
if (priv->blob_dent) {
debugfs_remove(priv->blob_dent);
priv->blob_dent = NULL;
}
priv->buf_idx = 0;
local_irq_disable();
preempt_disable_notrace();
/* Measure delay of reading GPIOs */
start_time = ktime_get();
for (i = 0; i < GPIO_LA_NUM_TESTS; i++) {
ret = gpio_la_get_array(priv->descs, &state);
if (ret)
goto out;
}
priv->acq_delay = ktime_sub(ktime_get(), start_time) / GPIO_LA_NUM_TESTS;
if (priv->delay_ns < priv->acq_delay) {
ret = -ERANGE;
goto out;
}
delay = priv->delay_ns - priv->acq_delay;
/* Wait for triggers */
for (i = 0; i < priv->trig_len; i += 2) {
do {
ret = gpio_la_get_array(priv->descs, &state);
if (ret)
goto out;
ndelay(delay);
} while ((state & priv->trig_data[i]) != priv->trig_data[i + 1]);
}
/* With triggers, final state is also the first sample */
if (priv->trig_len)
la_buf[priv->buf_idx++] = state;
/* Sample */
while (priv->buf_idx < priv->blob.size) {
ret = gpio_la_get_array(priv->descs, &state);
if (ret)
goto out;
la_buf[priv->buf_idx++] = state;
ndelay(delay);
}
out:
preempt_enable_notrace();
local_irq_enable();
if (ret)
dev_err(priv->dev, "couldn't read GPIOs: %d\n", ret);
kfree(priv->trig_data);
priv->trig_data = NULL;
priv->trig_len = 0;
priv->blob_dent = debugfs_create_blob("sample_data", 0400, priv->debug_dir, &priv->blob);
mutex_unlock(&priv->blob_lock);
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_capture, NULL, fops_capture_set, "%llu\n");
static int fops_buf_size_get(void *data, u64 *val)
{
struct gpio_la_poll_priv *priv = data;
*val = priv->blob.size;
return 0;
}
static int fops_buf_size_set(void *data, u64 val)
{
struct gpio_la_poll_priv *priv = data;
int ret = 0;
void *p;
if (!val)
return -EINVAL;
mutex_lock(&priv->blob_lock);
vfree(priv->blob.data);
p = vzalloc(val);
if (!p) {
val = 0;
ret = -ENOMEM;
}
priv->blob.data = p;
priv->blob.size = val;
mutex_unlock(&priv->blob_lock);
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(fops_buf_size, fops_buf_size_get, fops_buf_size_set, "%llu\n");
static int trigger_open(struct inode *inode, struct file *file)
{
return single_open(file, NULL, inode->i_private);
}
static ssize_t trigger_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *offset)
{
struct seq_file *m = file->private_data;
struct gpio_la_poll_priv *priv = m->private;
char *buf;
/* upper limit is arbitrary but should be less than PAGE_SIZE */
if (count > 2048 || count & 1)
return -EINVAL;
buf = memdup_user(ubuf, count);
if (IS_ERR(buf))
return PTR_ERR(buf);
priv->trig_data = buf;
priv->trig_len = count;
return count;
}
static const struct file_operations fops_trigger = {
.owner = THIS_MODULE,
.open = trigger_open,
.write = trigger_write,
.llseek = no_llseek,
.release = single_release,
};
static int gpio_la_poll_probe(struct platform_device *pdev)
{
struct gpio_la_poll_priv *priv;
struct device *dev = &pdev->dev;
const char *devname = dev_name(dev);
const char *gpio_names[GPIO_LA_MAX_PROBES];
char *meta = NULL;
unsigned int i, meta_len = 0;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
devm_mutex_init(dev, &priv->blob_lock);
fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE);
priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN);
if (IS_ERR(priv->descs))
return PTR_ERR(priv->descs);
/* artificial limit to keep 1 byte per sample for now */
if (priv->descs->ndescs > GPIO_LA_MAX_PROBES)
return -EFBIG;
ret = device_property_read_string_array(dev, "probe-names", gpio_names,
priv->descs->ndescs);
if (ret >= 0 && ret != priv->descs->ndescs)
ret = -EBADR;
if (ret < 0)
return dev_err_probe(dev, ret, "error naming the GPIOs");
for (i = 0; i < priv->descs->ndescs; i++) {
unsigned int add_len;
char *new_meta, *consumer_name;
if (gpiod_cansleep(priv->descs->desc[i]))
return -EREMOTE;
consumer_name = kasprintf(GFP_KERNEL, "%s: %s", devname, gpio_names[i]);
if (!consumer_name)
return -ENOMEM;
gpiod_set_consumer_name(priv->descs->desc[i], consumer_name);
kfree(consumer_name);
/* '10' is length of 'probe00=\n\0' */
add_len = strlen(gpio_names[i]) + 10;
new_meta = devm_krealloc(dev, meta, meta_len + add_len, GFP_KERNEL);
if (!new_meta)
return -ENOMEM;
meta = new_meta;
meta_len += snprintf(meta + meta_len, add_len, "probe%02u=%s\n",
i + 1, gpio_names[i]);
}
platform_set_drvdata(pdev, priv);
priv->dev = dev;
priv->meta.data = meta;
priv->meta.size = meta_len;
priv->debug_dir = debugfs_create_dir(devname, gpio_la_poll_debug_dir);
debugfs_create_blob("meta_data", 0400, priv->debug_dir, &priv->meta);
debugfs_create_ulong("delay_ns", 0600, priv->debug_dir, &priv->delay_ns);
debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay);
debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size);
debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture);
debugfs_create_file_unsafe("trigger", 0200, priv->debug_dir, priv, &fops_trigger);
return 0;
}
static void gpio_la_poll_remove(struct platform_device *pdev)
{
struct gpio_la_poll_priv *priv = platform_get_drvdata(pdev);
mutex_lock(&priv->blob_lock);
debugfs_remove_recursive(priv->debug_dir);
mutex_unlock(&priv->blob_lock);
}
static const struct of_device_id gpio_la_poll_of_match[] = {
{ .compatible = GPIO_LA_NAME },
{ }
};
MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match);
static struct platform_driver gpio_la_poll_device_driver = {
.probe = gpio_la_poll_probe,
.remove_new = gpio_la_poll_remove,
.driver = {
.name = GPIO_LA_NAME,
.of_match_table = gpio_la_poll_of_match,
}
};
static int __init gpio_la_poll_init(void)
{
gpio_la_poll_debug_dir = debugfs_create_dir(GPIO_LA_NAME, NULL);
return platform_driver_register(&gpio_la_poll_device_driver);
}
/*
* Non-strict pin controllers can read GPIOs while being muxed to something else.
* To support that, we need to claim GPIOs before further pinmuxing happens. So,
* we probe early using 'late_initcall'
*/
late_initcall(gpio_la_poll_init);
static void __exit gpio_la_poll_exit(void)
{
platform_driver_unregister(&gpio_la_poll_device_driver);
debugfs_remove_recursive(gpio_la_poll_debug_dir);
}
module_exit(gpio_la_poll_exit);
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
MODULE_DESCRIPTION("Sloppy logic analyzer using GPIOs");
MODULE_LICENSE("GPL");
......@@ -208,6 +208,7 @@ static int syscon_gpio_probe(struct platform_device *pdev)
struct syscon_gpio_priv *priv;
struct device_node *np = dev->of_node;
int ret;
bool use_parent_regmap = false;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
......@@ -216,24 +217,28 @@ static int syscon_gpio_probe(struct platform_device *pdev)
priv->data = of_device_get_match_data(dev);
priv->syscon = syscon_regmap_lookup_by_phandle(np, "gpio,syscon-dev");
if (IS_ERR(priv->syscon) && np->parent)
if (IS_ERR(priv->syscon) && np->parent) {
priv->syscon = syscon_node_to_regmap(np->parent);
use_parent_regmap = true;
}
if (IS_ERR(priv->syscon))
return PTR_ERR(priv->syscon);
ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
&priv->dreg_offset);
if (ret)
dev_err(dev, "can't read the data register offset!\n");
if (!use_parent_regmap) {
ret = of_property_read_u32_index(np, "gpio,syscon-dev", 1,
&priv->dreg_offset);
if (ret)
dev_err(dev, "can't read the data register offset!\n");
priv->dreg_offset <<= 3;
priv->dreg_offset <<= 3;
ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
&priv->dir_reg_offset);
if (ret)
dev_dbg(dev, "can't read the dir register offset!\n");
ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
&priv->dir_reg_offset);
if (ret)
dev_dbg(dev, "can't read the dir register offset!\n");
priv->dir_reg_offset <<= 3;
priv->dir_reg_offset <<= 3;
}
priv->chip.parent = dev;
priv->chip.owner = THIS_MODULE;
......
This diff is collapsed.
......@@ -976,7 +976,7 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int
int i;
/* Try first from _DSD */
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
for (i = 0; i < gpio_suffix_count; i++) {
if (con_id) {
snprintf(propname, sizeof(propname), "%s-%s",
con_id, gpio_suffixes[i]);
......@@ -1453,7 +1453,7 @@ int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
unsigned int i;
/* Try first from _DSD */
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
for (i = 0; i < gpio_suffix_count; i++) {
if (con_id)
snprintf(propname, sizeof(propname), "%s-%s",
con_id, gpio_suffixes[i]);
......
......@@ -1132,6 +1132,14 @@ static void edge_detector_stop(struct line *line)
/* do not change line->level - see comment in debounced_value() */
}
static int edge_detector_fifo_init(struct linereq *req)
{
if (kfifo_initialized(&req->events))
return 0;
return kfifo_alloc(&req->events, req->event_buffer_size, GFP_KERNEL);
}
static int edge_detector_setup(struct line *line,
struct gpio_v2_line_config *lc,
unsigned int line_idx, u64 edflags)
......@@ -1143,9 +1151,8 @@ static int edge_detector_setup(struct line *line,
char *label;
eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
if (eflags && !kfifo_initialized(&line->req->events)) {
ret = kfifo_alloc(&line->req->events,
line->req->event_buffer_size, GFP_KERNEL);
if (eflags) {
ret = edge_detector_fifo_init(line->req);
if (ret)
return ret;
}
......@@ -1197,8 +1204,6 @@ static int edge_detector_update(struct line *line,
struct gpio_v2_line_config *lc,
unsigned int line_idx, u64 edflags)
{
u64 eflags;
int ret;
u64 active_edflags = READ_ONCE(line->edflags);
unsigned int debounce_period_us =
gpio_v2_line_config_debounce_period(lc, line_idx);
......@@ -1214,14 +1219,9 @@ static int edge_detector_update(struct line *line,
* ensure event fifo is initialised if edge detection
* is now enabled.
*/
eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
if (eflags && !kfifo_initialized(&line->req->events)) {
ret = kfifo_alloc(&line->req->events,
line->req->event_buffer_size,
GFP_KERNEL);
if (ret)
return ret;
}
if (edflags & GPIO_V2_LINE_EDGE_FLAGS)
return edge_detector_fifo_init(line->req);
return 0;
}
......@@ -1648,16 +1648,15 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
return ret;
}
ret = kfifo_out(&lr->events, &le, 1);
}
if (ret != 1) {
/*
* This should never happen - we were holding the
* lock from the moment we learned the fifo is no
* longer empty until now.
*/
ret = -EIO;
break;
if (kfifo_out(&lr->events, &le, 1) != 1) {
/*
* This should never happen - we hold the
* lock from the moment we learned the fifo
* is no longer empty until now.
*/
WARN(1, "failed to read from non-empty kfifo");
return -EIO;
}
}
if (copy_to_user(buf + bytes_read, &le, sizeof(le)))
......@@ -1780,6 +1779,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
mutex_init(&lr->config_mutex);
init_waitqueue_head(&lr->wait);
INIT_KFIFO(lr->events);
lr->event_buffer_size = ulr.event_buffer_size;
if (lr->event_buffer_size == 0)
lr->event_buffer_size = ulr.num_lines * 16;
......@@ -2000,16 +2000,15 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
return ret;
}
ret = kfifo_out(&le->events, &ge, 1);
}
if (ret != 1) {
/*
* This should never happen - we were holding the lock
* from the moment we learned the fifo is no longer
* empty until now.
*/
ret = -EIO;
break;
if (kfifo_out(&le->events, &ge, 1) != 1) {
/*
* This should never happen - we hold the
* lock from the moment we learned the fifo
* is no longer empty until now.
*/
WARN(1, "failed to read from non-empty kfifo");
return -EIO;
}
}
if (copy_to_user(buf + bytes_read, &ge, ge_size))
......@@ -2712,12 +2711,15 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
if (count < event_size)
return -EINVAL;
#endif
ret = kfifo_out(&cdev->events, &event, 1);
}
if (ret != 1) {
ret = -EIO;
break;
/* We should never get here. See lineevent_read(). */
if (kfifo_out(&cdev->events, &event, 1) != 1) {
/*
* This should never happen - we hold the
* lock from the moment we learned the fifo
* is no longer empty until now.
*/
WARN(1, "failed to read from non-empty kfifo");
return -EIO;
}
}
#ifdef CONFIG_GPIO_CDEV_V1
......
......@@ -103,7 +103,7 @@ int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
if (ret > 0)
return ret;
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
for (i = 0; i < gpio_suffix_count; i++) {
if (con_id)
snprintf(propname, sizeof(propname), "%s-%s",
con_id, gpio_suffixes[i]);
......@@ -694,7 +694,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id,
unsigned int i;
/* Try GPIO property "foo-gpios" and "foo-gpio" */
for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
for (i = 0; i < gpio_suffix_count; i++) {
if (con_id)
snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id,
gpio_suffixes[i]);
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/compat.h>
......@@ -17,6 +18,7 @@
#include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/nospec.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/seq_file.h>
......@@ -88,6 +90,9 @@ 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" };
const size_t gpio_suffix_count = ARRAY_SIZE(gpio_suffixes);
static void gpiochip_free_hogs(struct gpio_chip *gc);
static int gpiochip_add_irqchip(struct gpio_chip *gc,
struct lock_class_key *lock_key,
......@@ -105,16 +110,16 @@ const char *gpiod_get_label(struct gpio_desc *desc)
unsigned long flags;
flags = READ_ONCE(desc->flags);
if (test_bit(FLAG_USED_AS_IRQ, &flags) &&
!test_bit(FLAG_REQUESTED, &flags))
return "interrupt";
if (!test_bit(FLAG_REQUESTED, &flags))
return NULL;
label = srcu_dereference_check(desc->label, &desc->gdev->desc_srcu,
srcu_read_lock_held(&desc->gdev->desc_srcu));
if (test_bit(FLAG_USED_AS_IRQ, &flags))
return label->str ?: "interrupt";
if (!test_bit(FLAG_REQUESTED, &flags))
return NULL;
return label->str;
}
......@@ -174,7 +179,6 @@ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc,
{
return gpio_device_get_desc(gc->gpiodev, hwnum);
}
EXPORT_SYMBOL_GPL(gpiochip_get_desc);
/**
* gpio_device_get_desc() - get the GPIO descriptor corresponding to the given
......@@ -198,7 +202,7 @@ gpio_device_get_desc(struct gpio_device *gdev, unsigned int hwnum)
if (hwnum >= gdev->ngpio)
return ERR_PTR(-EINVAL);
return &gdev->descs[hwnum];
return &gdev->descs[array_index_nospec(hwnum, gdev->ngpio)];
}
EXPORT_SYMBOL_GPL(gpio_device_get_desc);
......@@ -485,7 +489,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
* 1. Non-unique names are still accepted,
* 2. Name collisions within the same GPIO chip are not reported.
*/
static int gpiochip_set_desc_names(struct gpio_chip *gc)
static void gpiochip_set_desc_names(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
int i;
......@@ -504,8 +508,6 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
/* Then add all names to the GPIO descriptors */
for (i = 0; i != gc->ngpio; ++i)
gdev->descs[i].name = gc->names[i];
return 0;
}
/*
......@@ -999,11 +1001,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
if (gc->names) {
ret = gpiochip_set_desc_names(gc);
if (ret)
goto err_cleanup_desc_srcu;
}
if (gc->names)
gpiochip_set_desc_names(gc);
ret = gpiochip_set_names(gc);
if (ret)
goto err_cleanup_desc_srcu;
......@@ -4798,11 +4798,11 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
for_each_gpio_desc(gc, desc) {
guard(srcu)(&desc->gdev->desc_srcu);
if (test_bit(FLAG_REQUESTED, &desc->flags)) {
is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
if (is_irq || test_bit(FLAG_REQUESTED, &desc->flags)) {
gpiod_get_direction(desc);
is_out = test_bit(FLAG_IS_OUT, &desc->flags);
value = gpio_chip_get_value(gc, desc);
is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags);
active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags);
seq_printf(s, " gpio-%-3u (%-20.20s|%-20.20s) %s %s %s%s\n",
gpio, desc->name ?: "", gpiod_get_label(desc),
......
......@@ -90,7 +90,8 @@ static inline struct gpio_device *to_gpio_device(struct device *dev)
}
/* gpio suffixes used for ACPI and device tree lookup */
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
extern const char *const gpio_suffixes[];
extern const size_t gpio_suffix_count;
/**
* struct gpio_array - Opaque descriptor for a structure of GPIO array attributes
......@@ -242,6 +243,7 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce);
int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags);
int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
const char *gpiod_get_label(struct gpio_desc *desc);
/*
......
......@@ -139,7 +139,7 @@ static int da9062_gpio_direction_input(struct gpio_chip *gc,
{
struct da9062_pctl *pctl = gpiochip_get_data(gc);
struct regmap *regmap = pctl->da9062->regmap;
struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
struct gpio_desc *desc = gpio_device_get_desc(gc->gpiodev, offset);
unsigned int gpi_type;
int ret;
......
......@@ -579,7 +579,7 @@ static int gb_gpio_probe(struct gbphy_device *gbphy_dev,
if (ret)
goto exit_line_free;
ret = gpiochip_add(gpio);
ret = gpiochip_add_data(gpio, NULL);
if (ret) {
dev_err(&gbphy_dev->dev, "failed to add gpio chip: %d\n", ret);
goto exit_line_free;
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* <linux/gpio.h>
* NOTE: This header *must not* be included.
*
* This is the LEGACY GPIO bulk include file, including legacy APIs. It is
* used for GPIO drivers still referencing the global GPIO numberspace,
......@@ -16,8 +16,6 @@
struct device;
/* see Documentation/driver-api/gpio/legacy.rst */
/* make these flag values available regardless of GPIO kconfig options */
#define GPIOF_DIR_OUT (0 << 0)
#define GPIOF_DIR_IN (1 << 0)
......@@ -121,8 +119,6 @@ static inline int gpio_to_irq(unsigned gpio)
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
/* CONFIG_GPIOLIB: bindings for managed devices that want to request gpios */
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);
......
......@@ -632,10 +632,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
devm_gpiochip_add_data_with_key(dev, gc, data, NULL, NULL)
#endif /* CONFIG_LOCKDEP */
static inline int gpiochip_add(struct gpio_chip *gc)
{
return gpiochip_add_data(gc, NULL);
}
void gpiochip_remove(struct gpio_chip *gc);
int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc,
void *data, struct lock_class_key *lock_key,
......@@ -791,7 +787,6 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
enum gpiod_flags dflags);
void gpiochip_free_own_desc(struct gpio_desc *desc);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
struct gpio_desc *
gpio_device_get_desc(struct gpio_device *gdev, unsigned int hwnum);
......
......@@ -16,11 +16,28 @@
* requested like normal irqs and enqueued from process context.
*/
struct irq_sim_ops {
int (*irq_sim_irq_requested)(struct irq_domain *domain,
irq_hw_number_t hwirq, void *data);
void (*irq_sim_irq_released)(struct irq_domain *domain,
irq_hw_number_t hwirq, void *data);
};
struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
unsigned int num_irqs);
struct irq_domain *devm_irq_domain_create_sim(struct device *dev,
struct fwnode_handle *fwnode,
unsigned int num_irqs);
struct irq_domain *irq_domain_create_sim_full(struct fwnode_handle *fwnode,
unsigned int num_irqs,
const struct irq_sim_ops *ops,
void *data);
struct irq_domain *
devm_irq_domain_create_sim_full(struct device *dev,
struct fwnode_handle *fwnode,
unsigned int num_irqs,
const struct irq_sim_ops *ops,
void *data);
void irq_domain_remove_sim(struct irq_domain *domain);
#endif /* _LINUX_IRQ_SIM_H */
......@@ -17,6 +17,8 @@ struct irq_sim_work_ctx {
unsigned int irq_count;
unsigned long *pending;
struct irq_domain *domain;
struct irq_sim_ops ops;
void *user_data;
};
struct irq_sim_irq_ctx {
......@@ -88,6 +90,31 @@ static int irq_sim_set_irqchip_state(struct irq_data *data,
return 0;
}
static int irq_sim_request_resources(struct irq_data *data)
{
struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data);
struct irq_sim_work_ctx *work_ctx = irq_ctx->work_ctx;
irq_hw_number_t hwirq = irqd_to_hwirq(data);
if (work_ctx->ops.irq_sim_irq_requested)
return work_ctx->ops.irq_sim_irq_requested(work_ctx->domain,
hwirq,
work_ctx->user_data);
return 0;
}
static void irq_sim_release_resources(struct irq_data *data)
{
struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data);
struct irq_sim_work_ctx *work_ctx = irq_ctx->work_ctx;
irq_hw_number_t hwirq = irqd_to_hwirq(data);
if (work_ctx->ops.irq_sim_irq_released)
work_ctx->ops.irq_sim_irq_released(work_ctx->domain, hwirq,
work_ctx->user_data);
}
static struct irq_chip irq_sim_irqchip = {
.name = "irq_sim",
.irq_mask = irq_sim_irqmask,
......@@ -95,6 +122,8 @@ static struct irq_chip irq_sim_irqchip = {
.irq_set_type = irq_sim_set_type,
.irq_get_irqchip_state = irq_sim_get_irqchip_state,
.irq_set_irqchip_state = irq_sim_set_irqchip_state,
.irq_request_resources = irq_sim_request_resources,
.irq_release_resources = irq_sim_release_resources,
};
static void irq_sim_handle_irq(struct irq_work *work)
......@@ -163,6 +192,15 @@ static const struct irq_domain_ops irq_sim_domain_ops = {
*/
struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
unsigned int num_irqs)
{
return irq_domain_create_sim_full(fwnode, num_irqs, NULL, NULL);
}
EXPORT_SYMBOL_GPL(irq_domain_create_sim);
struct irq_domain *irq_domain_create_sim_full(struct fwnode_handle *fwnode,
unsigned int num_irqs,
const struct irq_sim_ops *ops,
void *data)
{
struct irq_sim_work_ctx *work_ctx __free(kfree) =
kmalloc(sizeof(*work_ctx), GFP_KERNEL);
......@@ -183,10 +221,14 @@ struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
work_ctx->irq_count = num_irqs;
work_ctx->work = IRQ_WORK_INIT_HARD(irq_sim_handle_irq);
work_ctx->pending = no_free_ptr(pending);
work_ctx->user_data = data;
if (ops)
memcpy(&work_ctx->ops, ops, sizeof(*ops));
return no_free_ptr(work_ctx)->domain;
}
EXPORT_SYMBOL_GPL(irq_domain_create_sim);
EXPORT_SYMBOL_GPL(irq_domain_create_sim_full);
/**
* irq_domain_remove_sim - Deinitialize the interrupt simulator domain: free
......@@ -227,11 +269,23 @@ static void devm_irq_domain_remove_sim(void *data)
struct irq_domain *devm_irq_domain_create_sim(struct device *dev,
struct fwnode_handle *fwnode,
unsigned int num_irqs)
{
return devm_irq_domain_create_sim_full(dev, fwnode, num_irqs,
NULL, NULL);
}
EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim);
struct irq_domain *
devm_irq_domain_create_sim_full(struct device *dev,
struct fwnode_handle *fwnode,
unsigned int num_irqs,
const struct irq_sim_ops *ops,
void *data)
{
struct irq_domain *domain;
int ret;
domain = irq_domain_create_sim(fwnode, num_irqs);
domain = irq_domain_create_sim_full(fwnode, num_irqs, ops, data);
if (IS_ERR(domain))
return domain;
......@@ -241,4 +295,4 @@ struct irq_domain *devm_irq_domain_create_sim(struct device *dev,
return domain;
}
EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim);
EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim_full);
#!/bin/sh -eu
# SPDX-License-Identifier: GPL-2.0
#
# Helper script for the Linux Kernel GPIO sloppy logic analyzer
#
# Copyright (C) Wolfram Sang <wsa@sang-engineering.com>
# Copyright (C) Renesas Electronics Corporation
samplefreq=1000000
numsamples=250000
cpusetdefaultdir='/sys/fs/cgroup'
cpusetprefix='cpuset.'
debugdir='/sys/kernel/debug'
ladirname='gpio-sloppy-logic-analyzer'
outputdir="$PWD"
neededcmds='taskset zip'
max_chans=8
duration=
initcpu=
listinstances=0
lainstance=
lasysfsdir=
triggerdat=
trigger_bindat=
progname="${0##*/}"
print_help()
{
cat << EOF
$progname - helper script for the Linux Kernel Sloppy GPIO Logic Analyzer
Available options:
-c|--cpu <n>: which CPU to isolate for sampling. Only needed once. Default <1>.
Remember that a more powerful CPU gives you higher sampling speeds.
Also CPU0 is not recommended as it usually does extra bookkeeping.
-d|--duration-us <SI-n>: number of microseconds to sample. Overrides -n, no default value.
-h|--help: print this help
-i|--instance <str>: name of the logic analyzer in case you have multiple instances. Default
to first instance found
-k|--kernel-debug-dir <str>: path to the kernel debugfs mountpoint. Default: <$debugdir>
-l|--list-instances: list all available instances
-n|--num_samples <SI-n>: number of samples to acquire. Default <$numsamples>
-o|--output-dir <str>: directory to put the result files. Default: current dir
-s|--sample_freq <SI-n>: desired sampling frequency. Might be capped if too large.
Default: <1000000>
-t|--trigger <str>: pattern to use as trigger. <str> consists of two-char pairs. First
char is channel number starting at "1". Second char is trigger level:
"L" - low; "H" - high; "R" - rising; "F" - falling
These pairs can be combined with "+", so "1H+2F" triggers when probe 1
is high while probe 2 has a falling edge. You can have multiple triggers
combined with ",". So, "1H+2F,1H+2R" is like the example before but it
waits for a rising edge on probe 2 while probe 1 is still high after the
first trigger has been met.
Trigger data will only be used for the next capture and then be erased.
<SI-n> is an integer value where SI units "T", "G", "M", "K" are recognized, e.g. '1M500K' is 1500000.
Examples:
Samples $numsamples values at 1MHz with an already prepared CPU or automatically prepares CPU1 if needed,
use the first logic analyzer instance found:
'$progname'
Samples 50us at 2MHz waiting for a falling edge on channel 2. CPU and instance as above:
'$progname -d 50 -s 2M -t "2F"'
Note that the process exits after checking all parameters but a sub-process still works in
the background. The result is only available once the sub-process finishes.
Result is a .sr file to be consumed with PulseView from the free Sigrok project. It is
a zip file which also contains the binary sample data which may be consumed by others.
The filename is the logic analyzer instance name plus a since-epoch timestamp.
EOF
}
fail()
{
echo "$1"
exit 1
}
parse_si()
{
conv_si="$(printf $1 | sed 's/[tT]+\?/*1000G+/g; s/[gG]+\?/*1000M+/g; s/[mM]+\?/*1000K+/g; s/[kK]+\?/*1000+/g; s/+$//')"
si_val="$((conv_si))"
}
set_newmask()
{
for f in $(find "$1" -iname "$2"); do echo "$newmask" > "$f" 2>/dev/null || true; done
}
init_cpu()
{
isol_cpu="$1"
[ -d "$lacpusetdir" ] || mkdir "$lacpusetdir"
cur_cpu=$(cat "${lacpusetfile}cpus")
[ "$cur_cpu" = "$isol_cpu" ] && return
[ -z "$cur_cpu" ] || fail "CPU$isol_cpu requested but CPU$cur_cpu already isolated"
echo "$isol_cpu" > "${lacpusetfile}cpus" || fail "Could not isolate CPU$isol_cpu. Does it exist?"
echo 1 > "${lacpusetfile}cpu_exclusive"
echo 0 > "${lacpusetfile}mems"
oldmask=$(cat /proc/irq/default_smp_affinity)
newmask=$(printf "%x" $((0x$oldmask & ~(1 << isol_cpu))))
set_newmask '/proc/irq' '*smp_affinity'
set_newmask '/sys/devices/virtual/workqueue/' 'cpumask'
# Move tasks away from isolated CPU
for p in $(ps -o pid | tail -n +2); do
mask=$(taskset -p "$p") || continue
# Ignore tasks with a custom mask, i.e. not equal $oldmask
[ "${mask##*: }" = "$oldmask" ] || continue
taskset -p "$newmask" "$p" || continue
done 2>/dev/null >/dev/null
# Big hammer! Working with 'rcu_momentary_dyntick_idle()' for a more fine-grained solution
# still printed warnings. Same for re-enabling the stall detector after sampling.
echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
cpufreqgov="/sys/devices/system/cpu/cpu$isol_cpu/cpufreq/scaling_governor"
[ -w "$cpufreqgov" ] && echo 'performance' > "$cpufreqgov" || true
}
parse_triggerdat()
{
oldifs="$IFS"
IFS=','; for trig in $1; do
mask=0; val1=0; val2=0
IFS='+'; for elem in $trig; do
chan=${elem%[lhfrLHFR]}
mode=${elem#$chan}
# Check if we could parse something and the channel number fits
[ "$chan" != "$elem" ] && [ "$chan" -le $max_chans ] || fail "Trigger syntax error: $elem"
bit=$((1 << (chan - 1)))
mask=$((mask | bit))
case $mode in
[hH]) val1=$((val1 | bit)); val2=$((val2 | bit));;
[fF]) val1=$((val1 | bit));;
[rR]) val2=$((val2 | bit));;
esac
done
trigger_bindat="$trigger_bindat$(printf '\\%o\\%o' $mask $val1)"
[ $val1 -ne $val2 ] && trigger_bindat="$trigger_bindat$(printf '\\%o\\%o' $mask $val2)"
done
IFS="$oldifs"
}
do_capture()
{
taskset "$1" echo 1 > "$lasysfsdir"/capture || fail "Capture error! Check kernel log"
srtmp=$(mktemp -d)
echo 1 > "$srtmp"/version
cp "$lasysfsdir"/sample_data "$srtmp"/logic-1-1
cat > "$srtmp"/metadata << EOF
[global]
sigrok version=0.2.0
[device 1]
capturefile=logic-1
total probes=$(wc -l < "$lasysfsdir"/meta_data)
samplerate=${samplefreq}Hz
unitsize=1
EOF
cat "$lasysfsdir"/meta_data >> "$srtmp"/metadata
zipname="$outputdir/${lasysfsdir##*/}-$(date +%s).sr"
zip -jq "$zipname" "$srtmp"/*
rm -rf "$srtmp"
delay_ack=$(cat "$lasysfsdir"/delay_ns_acquisition)
[ "$delay_ack" -eq 0 ] && delay_ack=1
echo "Logic analyzer done. Saved '$zipname'"
echo "Max sample frequency this time: $((1000000000 / delay_ack))Hz."
}
rep=$(getopt -a -l cpu:,duration-us:,help,instance:,list-instances,kernel-debug-dir:,num_samples:,output-dir:,sample_freq:,trigger: -o c:d:hi:k:ln:o:s:t: -- "$@") || exit 1
eval set -- "$rep"
while true; do
case "$1" in
-c|--cpu) initcpu="$2"; shift;;
-d|--duration-us) parse_si $2; duration=$si_val; shift;;
-h|--help) print_help; exit 0;;
-i|--instance) lainstance="$2"; shift;;
-k|--kernel-debug-dir) debugdir="$2"; shift;;
-l|--list-instances) listinstances=1;;
-n|--num_samples) parse_si $2; numsamples=$si_val; shift;;
-o|--output-dir) outputdir="$2"; shift;;
-s|--sample_freq) parse_si $2; samplefreq=$si_val; shift;;
-t|--trigger) triggerdat="$2"; shift;;
--) break;;
*) fail "error parsing command line: $*";;
esac
shift
done
for f in $neededcmds; do
command -v "$f" >/dev/null || fail "Command '$f' not found"
done
# print cpuset mountpoint if any, errorcode > 0 if noprefix option was found
cpusetdir=$(awk '$3 == "cgroup" && $4 ~ /cpuset/ { print $2; exit (match($4, /noprefix/) > 0) }' /proc/self/mounts) || cpusetprefix=''
if [ -z "$cpusetdir" ]; then
cpusetdir="$cpusetdefaultdir"
[ -d $cpusetdir ] || mkdir $cpusetdir
mount -t cgroup -o cpuset none $cpusetdir || fail "Couldn't mount cpusets. Not in kernel or already in use?"
fi
lacpusetdir="$cpusetdir/$ladirname"
lacpusetfile="$lacpusetdir/$cpusetprefix"
sysfsdir="$debugdir/$ladirname"
[ "$samplefreq" -ne 0 ] || fail "Invalid sample frequency"
[ -d "$sysfsdir" ] || fail "Could not find logic analyzer root dir '$sysfsdir'. Module loaded?"
[ -x "$sysfsdir" ] || fail "Could not access logic analyzer root dir '$sysfsdir'. Need root?"
[ $listinstances -gt 0 ] && find "$sysfsdir" -mindepth 1 -type d | sed 's|.*/||' && exit 0
if [ -n "$lainstance" ]; then
lasysfsdir="$sysfsdir/$lainstance"
else
lasysfsdir=$(find "$sysfsdir" -mindepth 1 -type d -print -quit)
fi
[ -d "$lasysfsdir" ] || fail "Logic analyzer directory '$lasysfsdir' not found!"
[ -d "$outputdir" ] || fail "Output directory '$outputdir' not found!"
[ -n "$initcpu" ] && init_cpu "$initcpu"
[ -d "$lacpusetdir" ] || { echo "Auto-Isolating CPU1"; init_cpu 1; }
ndelay=$((1000000000 / samplefreq))
echo "$ndelay" > "$lasysfsdir"/delay_ns
[ -n "$duration" ] && numsamples=$((samplefreq * duration / 1000000))
echo $numsamples > "$lasysfsdir"/buf_size
if [ -n "$triggerdat" ]; then
parse_triggerdat "$triggerdat"
printf "$trigger_bindat" > "$lasysfsdir"/trigger 2>/dev/null || fail "Trigger data '$triggerdat' rejected"
fi
workcpu=$(cat "${lacpusetfile}effective_cpus")
[ -n "$workcpu" ] || fail "No isolated CPU found"
cpumask=$(printf '%x' $((1 << workcpu)))
instance=${lasysfsdir##*/}
echo "Setting up '$instance': $numsamples samples at ${samplefreq}Hz with ${triggerdat:-no} trigger using CPU$workcpu"
do_capture "$cpumask" &
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment