요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=====================
GPIO Driver Interface
=====================
This document serves as a guide for writers of GPIO chip drivers.
Each GPIO controller driver needs to include the following header, which defines
the structures used to define a GPIO driver::
#include <linux/gpio/driver.h>
Internal Representation of GPIOs
================================
A GPIO chip handles one or more GPIO lines. To be considered a GPIO chip, the
lines must conform to the definition: General Purpose Input/Output. If the
line is not general purpose, it is not GPIO and should not be handled by a
GPIO chip. The use case is the indicative: certain lines in a system may be
called GPIO but serve a very particular purpose thus not meeting the criteria
of a general purpose I/O. On the other hand a LED driver line may be used as a
GPIO and should therefore still be handled by a GPIO chip driver.
Inside a GPIO driver, individual GPIO lines are identified by their hardware
number, sometime also referred to as ``offset``, which is a unique number
between 0 and n-1, n being the number of GPIOs managed by the chip.
The hardware GPIO number should be something intuitive to the hardware, for
example if a system uses a memory-mapped set of I/O-registers where 32 GPIO
lines are handled by one bit per line in a 32-bit register, it makes sense to
use hardware offsets 0..31 for these, corresponding to bits 0..31 in the
register.
This number is purely internal: the hardware number of a particular GPIO
line is never made visible outside of the driver.
On top of this internal number, each GPIO line also needs to have a global
number in the integer GPIO namespace so that it can be used with the legacy GPIO
interface. Each chip must thus have a "base" number (which can be automatically
assigned), and for each GPIO line the global number will be (base + hardware
number). Although the integer representation is considered deprecated, it still
has many users and thus needs to be maintained.
So for example one platform could use global numbers 32-159 for GPIOs, with a
controller defining 128 GPIOs at a "base" of 32 ; while another platform uses
global numbers 0..63 with one set of GPIO controllers, 64-79 with another type
of GPIO controller, and on one particular board 80-95 with an FPGA. The legacy
numbers need not be contiguous; either of those platforms could also use numbers
2000-2063 to identify GPIO lines in a bank of I2C GPIO expanders.
Controller Drivers: gpio_chip
=============================
In the gpiolib framework each GPIO controller is packaged as a "struct
gpio_chip" (see <linux/gpio/driver.h> for its complete definition) with members
common to each controller of that type, these should be assigned by the
driver code:
- methods to establish GPIO line direction
- methods used to access GPIO line values
- method to set electrical configuration for a given GPIO line
- method to return the IRQ number associated to a given GPIO line
- flag saying whether calls to its methods may sleep
- optional line names array to identify lines
- optional debugfs dump method (showing extra state information)
- optional base number (will be automatically assigned if omitted)
- optional label for diagnostics and GPIO chip mapping using platform data
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_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.
Chips such as audio codecs will have complex non-GPIO states.
Any debugfs dump method should normally ignore lines which haven't been
requested. They can use gpiochip_is_requested(), which returns either
NULL or the label associated with that GPIO line when it was requested.
Realtime considerations: the GPIO driver should not use spinlock_t or any
sleepable APIs (like PM runtime) in its gpio_chip implementation (.get/.set
and direction control callbacks) if it is expected to call GPIO APIs from
atomic context on realtime kernels (inside hard IRQ handlers and similar
contexts). Normally this should not be required.
GPIO electrical configuration
-----------------------------
GPIO lines can be configured for several electrical modes of operation by using
the .set_config() callback. Currently this API supports setting:
- Debouncing
- Single-ended modes (open drain/open source)
- Pull up and pull down resistor enablement
These settings are described below.
The .set_config() callback uses the same enumerators and configuration
semantics as the generic pin control drivers. This is not a coincidence: it is
possible to assign the .set_config() to the function gpiochip_generic_config()
which will result in pinctrl_gpio_set_config() being called and eventually
ending up in the pin control back-end "behind" the GPIO controller, usually
closer to the actual pins. This way the pin controller can manage the below
listed GPIO configurations.
If a pin controller back-end is used, the GPIO controller or hardware
description needs to provide "GPIO ranges" mapping the GPIO line offsets to pin
numbers on the pin controller so they can properly cross-reference each other.
GPIO lines with debounce support
--------------------------------
Debouncing is a configuration set to a pin indicating that it is connected to
a mechanical switch or button, or similar that may bounce. Bouncing means the
line is pulled high/low quickly at very short intervals for mechanical
reasons. This can result in the value being unstable or irqs firing repeatedly
unless the line is debounced.
Debouncing in practice involves setting up a timer when something happens on
the line, wait a little while and then sample the line again, so see if it
still has the same value (low or high). This could also be repeated by a clever
state machine, waiting for a line to become stable. In either case, it sets
a certain number of milliseconds for debouncing, or just "on/off" if that time
is not configurable.
GPIO lines with open drain/source support
-----------------------------------------
Open drain (CMOS) or open collector (TTL) means the line is not actively driven
high: instead you provide the drain/collector as output, so when the transistor
is not open, it will present a high-impedance (tristate) to the external rail::
CMOS CONFIGURATION TTL CONFIGURATION
||--- out +--- out
in ----|| |/
||--+ in ----|
| |\
GND GND
This configuration is normally used as a way to achieve one of two things:
- Level-shifting: to reach a logical level higher than that of the silicon
where the output resides.
- Inverse wire-OR on an I/O line, for example a GPIO line, making it possible
for any driving stage on the line to drive it low even if any other output
to the same line is simultaneously driving it high. A special case of this
is driving the SCL and SDA lines of an I2C bus, which is by definition a
wire-OR bus.
Both use cases require that the line be equipped with a pull-up resistor. This
resistor will make the line tend to high level unless one of the transistors on
the rail actively pulls it down.
The level on the line will go as high as the VDD on the pull-up resistor, which
may be higher than the level supported by the transistor, achieving a
level-shift to the higher VDD.
Integrated electronics often have an output driver stage in the form of a CMOS
"totem-pole" with one N-MOS and one P-MOS transistor where one of them drives
the line high and one of them drives the line low. This is called a push-pull
output. The "totem-pole" looks like so::
VDD
|
OD ||--+
+--/ ---o|| P-MOS-FET
| ||--+
IN --+ +----- out
| ||--+
+--/ ----|| N-MOS-FET
OS ||--+
|
GND
The desired output signal (e.g. coming directly from some GPIO output register)
arrives at IN. The switches named "OD" and "OS" are normally closed, creating
a push-pull circuit.
Consider the little "switches" named "OD" and "OS" that enable/disable the
P-MOS or N-MOS transistor right after the split of the input. As you can see,
either transistor will go totally numb if this switch is open. The totem-pole
is then halved and give high impedance instead of actively driving the line
high or low respectively. That is usually how software-controlled open
drain/source works.
Some GPIO hardware come in open drain / open source configuration. Some are
hard-wired lines that will only support open drain or open source no matter
what: there is only one transistor there. Some are software-configurable:
by flipping a bit in a register the output can be configured as open drain
or open source, in practice by flicking open the switches labeled "OD" and "OS"
in the drawing above.
By disabling the P-MOS transistor, the output can be driven between GND and
high impedance (open drain), and by disabling the N-MOS transistor, the output
can be driven between VDD and high impedance (open source). In the first case,
a pull-up resistor is needed on the outgoing rail to complete the circuit, and
in the second case, a pull-down resistor is needed on the rail.
Hardware that supports open drain or open source or both, can implement a
special callback in the gpio_chip: .set_config() that takes a generic
pinconf packed value telling whether to configure the line as open drain,
open source or push-pull. This will happen in response to the
GPIO_OPEN_DRAIN or GPIO_OPEN_SOURCE flag set in the machine file, or coming
from other hardware descriptions.
If this state can not be configured in hardware, i.e. if the GPIO hardware does
not support open drain/open source in hardware, the GPIO library will instead
use a trick: when a line is set as output, if the line is flagged as open
drain, and the IN output value is low, it will be driven low as usual. But
if the IN output value is set to high, it will instead *NOT* be driven high,
instead it will be switched to input, as input mode is an equivalent to
high impedance, thus achieving an "open drain emulation" of sorts: electrically
the behaviour will be identical, with the exception of possible hardware glitches
when switching the mode of the line.
For open source configuration the same principle is used, just that instead
of actively driving the line low, it is set to input.
GPIO lines with pull up/down resistor support
---------------------------------------------
A GPIO line can support pull-up/down using the .set_config() callback. This
means that a pull up or pull-down resistor is available on the output of the
GPIO line, and this resistor is software controlled.
In discrete designs, a pull-up or pull-down resistor is simply soldered on
the circuit board. This is not something we deal with or model in software. The
most you will think about these lines is that they will very likely be
configured as open drain or open source (see the section above).
The .set_config() callback can only turn pull up or down on and off, and will
no have any semantic knowledge about the resistance used. It will only say
switch a bit in a register enabling or disabling pull-up or pull-down.
If the GPIO line supports shunting in different resistance values for the
pull-up or pull-down resistor, the GPIO chip callback .set_config() will not
suffice. For these complex use cases, a combined GPIO chip and pin controller
need to be implemented, as the pin config interface of a pin controller
supports more versatile control over electrical properties and can handle
different pull-up or pull-down resistance values.
GPIO drivers providing IRQs
===========================
It is custom that GPIO drivers (GPIO chips) are also providing interrupts,
most often cascaded off a parent interrupt controller, and in some special
cases the GPIO logic is melded with a SoC's primary interrupt controller.
The IRQ portions of the GPIO block are implemented using an irq_chip, using
the header <linux/irq.h>. So this combined driver is utilizing two sub-
systems simultaneously: gpio and irq.
It is legal for any IRQ consumer to request an IRQ from any irqchip even if it
is a combined GPIO+IRQ driver. The basic premise is that gpio_chip and
irq_chip are orthogonal, and offering their services independent of each
other.
gpiod_to_irq() is just a convenience function to figure out the IRQ for a
certain GPIO line and should not be relied upon to have been called before
the IRQ is used.
Always prepare the hardware and make it ready for action in respective
callbacks from the GPIO and irq_chip APIs. Do not rely on gpiod_to_irq() having
been called first.
We can divide GPIO irqchips in two broad categories:
- CASCADED INTERRUPT CHIPS: this means that the GPIO chip has one common
interrupt output line, which is triggered by any enabled GPIO line on that
chip. The interrupt output line will then be routed to an parent interrupt
controller one level up, in the most simple case the systems primary
interrupt controller. This is modeled by an irqchip that will inspect bits
inside the GPIO controller to figure out which line fired it. The irqchip
part of the driver needs to inspect registers to figure this out and it
will likely also need to acknowledge that it is handling the interrupt
by clearing some bit (sometime implicitly, by just reading a status
register) and it will often need to set up the configuration such as
edge sensitivity (rising or falling edge, or high/low level interrupt for
example).
- HIERARCHICAL INTERRUPT CHIPS: this means that each GPIO line has a dedicated
irq line to a parent interrupt controller one level up. There is no need
to inquire the GPIO hardware to figure out which line has fired, but it
may still be necessary to acknowledge the interrupt and set up configuration
such as edge sensitivity.
Realtime considerations: a realtime compliant GPIO driver should not use
spinlock_t or any sleepable APIs (like PM runtime) as part of its irqchip
implementation.
- spinlock_t should be replaced with raw_spinlock_t.[1]
- If sleepable APIs have to be used, these can be done from the .irq_bus_lock()
and .irq_bus_unlock() callbacks, as these are the only slowpath callbacks
on an irqchip. Create the callbacks if needed.[2]
Cascaded GPIO irqchips
----------------------
Cascaded GPIO irqchips usually fall in one of three categories:
- CHAINED CASCADED GPIO IRQCHIPS: these are usually the type that is embedded on
an SoC. This means that there is a fast IRQ flow handler for the GPIOs that
gets called in a chain from the parent IRQ handler, most typically the
system interrupt controller. This means that the GPIO irqchip handler will
be called immediately from the parent irqchip, while holding the IRQs
disabled. The GPIO irqchip will then end up calling something like this
sequence in its interrupt handler::
static irqreturn_t foo_gpio_irq(int irq, void *data)
chained_irq_enter(...);
generic_handle_irq(...);
chained_irq_exit(...);
Chained GPIO irqchips typically can NOT set the .can_sleep flag on
struct gpio_chip, as everything happens directly in the callbacks: no
slow bus traffic like I2C can be used.
Realtime considerations: Note that chained IRQ handlers will not be forced
threaded on -RT. As a result, spinlock_t or any sleepable APIs (like PM
runtime) can't be used in a chained IRQ handler.
If required (and if it can't be converted to the nested threaded GPIO irqchip,
see below) a chained IRQ handler can be converted to generic irq handler and
this way it will become a threaded IRQ handler on -RT and a hard IRQ handler
on non-RT (for example, see [3]).
The generic_handle_irq() is expected to be called with IRQ disabled,
so the IRQ core will complain if it is called from an IRQ handler which is
forced to a thread. The "fake?" raw lock can be used to work around this
problem::
raw_spinlock_t wa_lock;
static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
unsigned long wa_lock_flags;
raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
generic_handle_irq(irq_find_mapping(bank->chip.irq.domain, bit));
raw_spin_unlock_irqrestore(&bank->wa_lock, wa_lock_flags);
- GENERIC CHAINED GPIO IRQCHIPS: these are the same as "CHAINED GPIO irqchips",
but chained IRQ handlers are not used. Instead GPIO IRQs dispatching is
performed by generic IRQ handler which is configured using request_irq().
The GPIO irqchip will then end up calling something like this sequence in
its interrupt handler::
static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
for each detected GPIO IRQ
generic_handle_irq(...);
Realtime considerations: this kind of handlers will be forced threaded on -RT,
and as result the IRQ core will complain that generic_handle_irq() is called
with IRQ enabled and the same work-around as for "CHAINED GPIO irqchips" can
be applied.
- NESTED THREADED GPIO IRQCHIPS: these are off-chip GPIO expanders and any
other GPIO irqchip residing on the other side of a sleeping bus such as I2C
or SPI.
Of course such drivers that need slow bus traffic to read out IRQ status and
similar, traffic which may in turn incur other IRQs to happen, cannot be
handled in a quick IRQ handler with IRQs disabled. Instead they need to spawn
a thread and then mask the parent IRQ line until the interrupt is handled
by the driver. The hallmark of this driver is to call something like
this in its interrupt handler::
static irqreturn_t foo_gpio_irq(int irq, void *data)
...
handle_nested_irq(irq);
The hallmark of threaded GPIO irqchips is that they set the .can_sleep
flag on struct gpio_chip to true, indicating that this chip may sleep
when accessing the GPIOs.
These kinds of irqchips are inherently realtime tolerant as they are
already set up to handle sleeping contexts.
Infrastructure helpers for GPIO irqchips
----------------------------------------
To help out in handling the set-up and management of GPIO irqchips and the
associated irqdomain and resource allocation callbacks. These are activated
by selecting the Kconfig symbol GPIOLIB_IRQCHIP. If the symbol
IRQ_DOMAIN_HIERARCHY is also selected, hierarchical helpers will also be
provided. A big portion of overhead code will be managed by gpiolib,
under the assumption that your interrupts are 1-to-1-mapped to the
GPIO line index:
.. csv-table::
:header: GPIO line offset, Hardware IRQ
0,0
1,1
2,2
...,...
ngpio-1, ngpio-1
If some GPIO lines do not have corresponding IRQs, the bitmask valid_mask
and the flag need_valid_mask in gpio_irq_chip can be used to mask off some
lines as invalid for associating with IRQs.
The preferred way to set up the helpers is to fill in the
struct gpio_irq_chip inside struct gpio_chip before adding the gpio_chip.
If you do this, the additional irq_chip will be set up by gpiolib at the
same time as setting up the rest of the GPIO functionality. The following
is a typical example of a chained cascaded interrupt handler using
the gpio_irq_chip. Note how the mask/unmask (or disable/enable) functions
call into the core gpiolib code:
.. code-block:: c
/* Typical state container */
struct my_gpio {
struct gpio_chip gc;
};
static void my_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
/*
* Perform any necessary action to mask the interrupt,
* and then call into the core code to synchronise the
* state.
*/
gpiochip_disable_irq(gc, hwirq);
}
static void my_gpio_unmask_irq(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);
/*
* Perform any necessary action to unmask the interrupt,
* after having called into the core code to synchronise
* the state.
*/
}
/*
* Statically populate the irqchip. Note that it is made const
* (further indicated by the IRQCHIP_IMMUTABLE flag), and that
* the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra
* callbacks to the structure.
*/
static const struct irq_chip my_gpio_irq_chip = {
.name = "my_gpio_irq",
.irq_ack = my_gpio_ack_irq,
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_set_type = my_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
/* Provide the gpio resource callbacks */
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
int irq; /* from platform etc */
struct my_gpio *g;
struct gpio_irq_chip *girq;
/* Get a pointer to the gpio_irq_chip */
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip);
girq->parent_handler = ftgpio_gpio_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->parents[0] = irq;
return devm_gpiochip_add_data(dev, &g->gc, g);
The helper supports using threaded interrupts as well. Then you just request
the interrupt separately and go with it:
.. code-block:: c
/* Typical state container */
struct my_gpio {
struct gpio_chip gc;
};
static void my_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
/*
* Perform any necessary action to mask the interrupt,
* and then call into the core code to synchronise the
* state.
*/
gpiochip_disable_irq(gc, hwirq);
}
static void my_gpio_unmask_irq(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);
/*
* Perform any necessary action to unmask the interrupt,
* after having called into the core code to synchronise
* the state.
*/
}
/*
* Statically populate the irqchip. Note that it is made const
* (further indicated by the IRQCHIP_IMMUTABLE flag), and that
* the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra
* callbacks to the structure.
*/
static const struct irq_chip my_gpio_irq_chip = {
.name = "my_gpio_irq",
.irq_ack = my_gpio_ack_irq,
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_set_type = my_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
/* Provide the gpio resource callbacks */
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
int irq; /* from platform etc */
struct my_gpio *g;
struct gpio_irq_chip *girq;
ret = devm_request_threaded_irq(dev, irq, NULL, irq_thread_fn,
IRQF_ONESHOT, "my-chip", g);
if (ret < 0)
return ret;
/* Get a pointer to the gpio_irq_chip */
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
return devm_gpiochip_add_data(dev, &g->gc, g);
The helper supports using hierarchical interrupt controllers as well.
In this case the typical set-up will look like this:
.. code-block:: c
/* Typical state container with dynamic irqchip */
struct my_gpio {
struct gpio_chip gc;
struct fwnode_handle *fwnode;
};
static void my_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
/*
* Perform any necessary action to mask the interrupt,
* and then call into the core code to synchronise the
* state.
*/
gpiochip_disable_irq(gc, hwirq);
irq_mask_mask_parent(d);
}
static void my_gpio_unmask_irq(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);
/*
* Perform any necessary action to unmask the interrupt,
* after having called into the core code to synchronise
* the state.
*/
irq_mask_unmask_parent(d);
}
/*
* Statically populate the irqchip. Note that it is made const
* (further indicated by the IRQCHIP_IMMUTABLE flag), and that
* the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra
* callbacks to the structure.
*/
static const struct irq_chip my_gpio_irq_chip = {
.name = "my_gpio_irq",
.irq_ack = my_gpio_ack_irq,
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_set_type = my_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
/* Provide the gpio resource callbacks */
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
struct my_gpio *g;
struct gpio_irq_chip *girq;
/* Get a pointer to the gpio_irq_chip */
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip);
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->fwnode = g->fwnode;
girq->parent_domain = parent;
girq->child_to_parent_hwirq = my_gpio_child_to_parent_hwirq;
return devm_gpiochip_add_data(dev, &g->gc, g);
As you can see pretty similar, but you do not supply a parent handler for
the IRQ, instead a parent irqdomain, an fwnode for the hardware and
a function .child_to_parent_hwirq() that has the purpose of looking up
the parent hardware irq from a child (i.e. this gpio chip) hardware irq.
As always it is good to look at examples in the kernel tree for advice
on how to find the required pieces.
If there is a need to exclude certain GPIO lines from the IRQ domain handled by
these helpers, we can set .irq.need_valid_mask of the gpiochip before
devm_gpiochip_add_data() or gpiochip_add_data() is called. This allocates an
.irq.valid_mask with as many bits set as there are GPIO lines in the chip, each
bit representing line 0..n-1. Drivers can exclude GPIO lines by clearing bits
from this mask. The mask can be filled in the init_valid_mask() callback
that is part of the struct gpio_irq_chip.
To use the helpers please keep the following in mind:
- Make sure to assign all relevant members of the struct gpio_chip so that
the irqchip can initialize. E.g. .dev and .can_sleep shall be set up
properly.
- Nominally set gpio_irq_chip.handler to handle_bad_irq. Then, if your irqchip
is cascaded, set the handler to handle_level_irq() and/or handle_edge_irq()
in the irqchip .set_type() callback depending on what your controller
supports and what is requested by the consumer.
Locking IRQ usage
-----------------
Since GPIO and irq_chip are orthogonal, we can get conflicts between different
use cases. For example a GPIO line used for IRQs should be an input line,
it does not make sense to fire interrupts on an output GPIO.
If there is competition inside the subsystem which side is using the
resource (a certain GPIO line and register for example) it needs to deny
certain operations and keep track of usage inside of the gpiolib subsystem.
Input GPIOs can be used as IRQ signals. When this happens, a driver is requested
to mark the GPIO as being used as an IRQ::
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
This will prevent the use of non-irq related GPIO APIs until the GPIO IRQ lock
is released::
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
When implementing an irqchip inside a GPIO driver, these two functions should
typically be called in the .startup() and .shutdown() callbacks from the
irqchip.
When using the gpiolib irqchip helpers, these callbacks are automatically
assigned.
Disabling and enabling IRQs
---------------------------
In some (fringe) use cases, a driver may be using a GPIO line as input for IRQs,
but occasionally switch that line over to drive output and then back to being
an input with interrupts again. This happens on things like CEC (Consumer
Electronics Control).
When a GPIO is used as an IRQ signal, then gpiolib also needs to know if
the IRQ is enabled or disabled. In order to inform gpiolib about this,
the irqchip driver should call::
void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset)
This allows drivers to drive the GPIO as an output while the IRQ is
disabled. When the IRQ is enabled again, a driver should call::
void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset)
When implementing an irqchip inside a GPIO driver, these two functions should
typically be called in the .irq_disable() and .irq_enable() callbacks from the
irqchip.
When IRQCHIP_IMMUTABLE is not advertised by the irqchip, these callbacks
are automatically assigned. This behaviour is deprecated and on its way
to be removed from the kernel.
Real-Time compliance for GPIO IRQ chips
---------------------------------------
Any provider of irqchips needs to be carefully tailored to support Real-Time
preemption. It is desirable that all irqchips in the GPIO subsystem keep this
in mind and do the proper testing to assure they are real time-enabled.
So, pay attention on above realtime considerations in the documentation.
The following is a checklist to follow when preparing a driver for real-time
compliance:
- ensure spinlock_t is not used as part irq_chip implementation
- ensure that sleepable APIs are not used as part irq_chip implementation
If sleepable APIs have to be used, these can be done from the .irq_bus_lock()
and .irq_bus_unlock() callbacks
- Chained GPIO irqchips: ensure spinlock_t or any sleepable APIs are not used
from the chained IRQ handler
- Generic chained GPIO irqchips: take care about generic_handle_irq() calls and
apply corresponding work-around
- Chained GPIO irqchips: get rid of the chained IRQ handler and use generic irq
handler if possible
- regmap_mmio: it is possible to disable internal locking in regmap by setting
.disable_locking and handling the locking in the GPIO driver
- Test your driver with the appropriate in-kernel real-time test cases for both
level and edge IRQs
* [1] https://lore.kernel.org/r/1437496011-11486-1-git-send-email-bigeasy@linutronix.de/
* [2] https://lore.kernel.org/r/1443209283-20781-2-git-send-email-grygorii.strashko@ti.com
* [3] https://lore.kernel.org/r/1443209283-20781-3-git-send-email-grygorii.strashko@ti.com
Requesting self-owned GPIO pins
===============================
Sometimes it is useful to allow a GPIO chip driver to request its own GPIO
descriptors through the gpiolib API. A GPIO driver can use the following
functions to request and free descriptors::
struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc,
u16 hwnum,
const char *label,
enum gpiod_flags flags)
void gpiochip_free_own_desc(struct gpio_desc *desc)
Descriptors requested with gpiochip_request_own_desc() must be released with
gpiochip_free_own_desc().
These functions must be used with care since they do not affect module use
count. Do not use the functions to request gpio descriptors not owned by the
calling driver.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
GPIO driver와 내부 번호 표현
1-51문서 제목은 `GPIO Driver Interface`이며 GPIO chip driver 작성자를 위한 지침입니다. 모든 GPIO controller driver는 GPIO driver structure가 선언된 header를 include해야 합니다.
#include <linux/gpio/driver.h>
GPIO chip은 하나 이상의 GPIO line을 다룹니다. Line이 General Purpose Input/Output 정의에 맞아야 GPIO입니다. 특정 용도로만 쓰이는 line은 이름에 GPIO가 들어가더라도 GPIO chip이 관리하면 안 됩니다. 반대로 LED driver line이라도 범용 GPIO로 사용할 수 있다면 GPIO chip driver가 관리해야 합니다.
GPIO driver 내부에서 각 line은 hardware number, 즉 `offset`으로 식별합니다. 이 값은 chip이 관리하는 GPIO 수를 n이라고 할 때 0부터 n-1 사이의 고유 번호입니다.
Hardware GPIO number는 hardware와 직관적으로 대응해야 합니다. 예를 들어 32-bit memory-mapped I/O register의 각 bit가 32개 GPIO line 하나씩을 담당한다면 register bit 0..31에 맞춰 hardware offset 0..31을 쓰는 것이 자연스럽습니다.
이 번호는 완전히 내부용이며 특정 GPIO line의 hardware number는 driver 밖에 노출되지 않습니다.
Legacy GPIO interface를 위해 각 line에는 integer GPIO namespace의 global number도 필요합니다. 각 chip은 자동 할당할 수 있는 `base` number를 가지며 line의 global number는 `base + hardware number`입니다. Integer 표현은 deprecated이지만 사용자가 많아 유지해야 합니다.
예를 들어 한 platform은 base 32에서 시작하는 128-line controller로 global GPIO 32-159를 쓸 수 있습니다. 다른 platform은 controller별로 0..63, 64-79를 쓰고 특정 board의 FPGA에 80-95를 줄 수 있습니다. Legacy number는 연속일 필요가 없으며 I2C GPIO expander bank에 2000-2063을 배정할 수도 있습니다.
Driver 내부 offset과 deprecated global integer 번호의 관계입니다.
Controller driver와 struct gpio_chip
52-89gpiolib framework는 각 GPIO controller를 `<linux/gpio/driver.h>`에 완전한 정의가 있는 `struct gpio_chip`으로 포장합니다. Driver는 controller type에 공통인 member를 지정해야 합니다.
- GPIO line direction을 설정하는 method
- GPIO line value를 읽고 쓰는 method
- 특정 line의 electrical configuration을 설정하는 method
- 특정 line과 연결된 IRQ number를 반환하는 method
- Method 호출이 sleep할 수 있는지를 나타내는 flag
- Line 식별용 optional line-name array
- 추가 state를 표시하는 optional debugfs dump method
- 생략 시 자동 할당되는 optional base number
- 진단과 platform-data GPIO chip mapping용 optional label
`gpio_chip` 구현은 driver model을 사용해 controller의 여러 instance를 지원하는 것이 좋습니다. 각 `gpio_chip`을 구성한 뒤 `gpiochip_add_data()` 또는 `devm_gpiochip_add_data()`를 호출합니다. GPIO controller 제거는 드물어야 하며 피할 수 없을 때만 `gpiochip_remove()`를 사용합니다.
`gpio_chip`은 흔히 address, power management처럼 GPIO interface에 노출되지 않는 instance별 state를 가진 structure의 일부입니다. Audio codec 같은 chip에는 복잡한 non-GPIO state도 함께 존재합니다.
Debugfs dump method는 보통 요청되지 않은 line을 무시해야 합니다. `gpiochip_is_requested()`는 `NULL` 또는 요청 당시 line과 연결된 label을 반환하므로 이를 사용할 수 있습니다.
실시간 kernel에서 hard IRQ handler 같은 atomic context가 GPIO API를 호출할 수 있어야 한다면 `gpio_chip`의 `.get`, `.set`, direction callback에서 `spinlock_t`나 PM runtime 같은 sleep 가능 API를 사용하면 안 됩니다. 보통은 이런 요구가 없어야 합니다.
필수 동작과 optional metadata를 구분합니다.
전기 구성과 debounce
90-131GPIO line은 `.set_config()` callback으로 여러 electrical operation mode를 구성할 수 있습니다. 현재 API는 debounce, open drain/open source 같은 single-ended mode, pull-up·pull-down resistor enable을 지원합니다.
`.set_config()`는 generic pin control driver와 같은 enumerator와 configuration semantics를 사용합니다. `.set_config()`에 `gpiochip_generic_config()`를 지정하면 `pinctrl_gpio_set_config()`가 호출되고 결국 실제 pin에 가까운 GPIO controller 뒤쪽의 pin-control backend로 전달됩니다. 이 방식으로 pin controller가 GPIO electrical configuration을 관리합니다.
Pin-controller backend를 사용하면 GPIO controller 또는 hardware description에 GPIO line offset과 pin-controller pin number를 연결하는 `GPIO ranges`가 있어야 서로를 정확히 참조할 수 있습니다.
Debounce는 mechanical switch나 button처럼 접점 튐이 발생할 수 있는 pin에 설정합니다. Bouncing은 기계적 이유로 line이 매우 짧은 간격에 high와 low를 오가며, debounce하지 않으면 값이 불안정하거나 IRQ가 반복해서 발생할 수 있습니다.
실제 debounce는 line에 event가 생기면 timer를 시작하고 잠시 기다린 뒤 같은 low 또는 high인지 다시 sampling합니다. 더 정교한 state machine이 안정될 때까지 반복할 수도 있습니다. Configuration은 debounce millisecond 수를 지정하거나, 시간을 조정할 수 없는 hardware에서는 단순 on/off를 지정합니다.
gpio_chip callback에서 실제 pin-control backend까지의 경로입니다.
기계 접점의 일시적 진동을 안정된 값으로 판별합니다.
Open drain·open source와 push-pull
132-228Open drain(CMOS) 또는 open collector(TTL)는 line을 high로 적극 구동하지 않습니다. Drain 또는 collector를 output으로 제공하되 transistor가 열려 있지 않을 때 external rail에는 high-impedance, 즉 tristate가 나타납니다.
CMOS CONFIGURATION TTL CONFIGURATION
||--- out +--- out
in ----|| |/
||--+ in ----|
| |\
GND GND
이 구성은 두 가지 목적으로 사용합니다. 첫째는 output silicon보다 높은 logical level에 도달하는 level shifting입니다. 둘째는 I/O line의 inverse wire-OR입니다. 같은 line의 다른 output이 high를 구동 중이어도 어느 한 driving stage가 low로 끌어내릴 수 있습니다. I2C의 SCL과 SDA가 대표적인 wire-OR bus입니다.
두 경우 모두 pull-up resistor가 필요합니다. Rail의 transistor가 적극적으로 low로 끌어내리지 않는 한 resistor가 line을 high 쪽으로 끌어올립니다. Line level은 pull-up resistor의 VDD까지 올라가며 transistor가 지원하는 level보다 높을 수 있어 더 높은 VDD로 level shift됩니다.
Integrated electronics의 output driver stage는 흔히 N-MOS와 P-MOS transistor 하나씩으로 만든 CMOS `totem-pole`입니다. 하나는 line을 high로, 다른 하나는 low로 구동하며 이를 push-pull output이라고 합니다.
VDD
|
OD ||--+
+--/ ---o|| P-MOS-FET
| ||--+
IN --+ +----- out
| ||--+
+--/ ----|| N-MOS-FET
OS ||--+
|
GND
GPIO output register 등에서 온 원하는 output signal은 `IN`으로 들어옵니다. 입력 분기 직후 P-MOS와 N-MOS를 enable·disable하는 `OD`, `OS` switch는 보통 닫혀 있어 push-pull circuit을 만듭니다.
이 switch를 열면 해당 transistor가 완전히 비활성화됩니다. Totem-pole의 한쪽이 사라져 line을 high 또는 low로 적극 구동하는 대신 high impedance가 됩니다. Software-controlled open drain/source는 보통 이런 방식으로 구현됩니다.
일부 GPIO hardware는 open drain 또는 open source로 고정되어 transistor가 하나뿐입니다. 다른 hardware는 register bit를 바꿔 그림의 `OD` 또는 `OS` switch를 여는 방식으로 mode를 software에서 선택할 수 있습니다.
P-MOS를 disable하면 output은 GND와 high impedance 사이를 오가므로 open drain이 되고 outgoing rail에는 pull-up resistor가 필요합니다. N-MOS를 disable하면 VDD와 high impedance 사이를 오가므로 open source가 되고 pull-down resistor가 필요합니다.
Open drain·open source를 hardware에서 지원하면 `gpio_chip`의 `.set_config()` callback이 generic pinconf packed value를 받아 open drain, open source 또는 push-pull로 설정할 수 있습니다. Machine file이나 다른 hardware description의 `GPIO_OPEN_DRAIN`, `GPIO_OPEN_SOURCE` flag에 응답해 호출됩니다.
Hardware가 이 mode를 지원하지 않으면 GPIO library가 흉내 냅니다. Open-drain line에 low를 출력하면 그대로 low로 구동하지만 high를 출력하면 high를 적극 구동하지 않고 input으로 바꿉니다. Input mode는 high impedance와 같으므로 electrical behavior는 같지만 direction 전환 중 hardware glitch가 생길 수 있습니다.
Open-source emulation도 같은 원리이며, 차이는 low를 적극 구동해야 할 때 output 대신 input으로 전환한다는 점입니다.
원문의 transistor 회로를 동작 상태로 구조화했습니다.
Hardware mode가 없을 때 direction 전환으로 high impedance를 만듭니다.
Pull-up·pull-down resistor 지원
229-252GPIO line은 `.set_config()` callback을 통해 software-controlled pull-up 또는 pull-down resistor를 지원할 수 있습니다.
Discrete design에서는 pull-up이나 pull-down resistor를 circuit board에 직접 납땜합니다. Software는 이를 다루거나 model하지 않으며, 이런 line이 open drain 또는 open source로 구성될 가능성만 고려합니다.
`.set_config()` callback은 pull-up·pull-down을 켜거나 끌 뿐 resistance 값의 의미를 알지 못합니다. Register의 enable bit를 바꾸라고 지시하는 수준입니다.
여러 resistance 값을 선택할 수 있는 GPIO line에는 GPIO chip의 `.set_config()`만으로 부족합니다. GPIO chip과 pin controller를 결합해 구현해야 하며, pin controller의 pin-config interface는 더 다양한 electrical property와 여러 pull resistance 값을 처리할 수 있습니다.
Board 부품, GPIO callback, pin controller가 담당하는 범위입니다.
GPIO driver가 제공하는 IRQ
253-307GPIO chip이 interrupt도 제공하는 경우가 흔합니다. 대부분 parent interrupt controller에 cascaded되며, 특별한 경우에는 GPIO logic이 SoC primary interrupt controller와 합쳐져 있습니다.
GPIO block의 IRQ 부분은 `<linux/irq.h>`의 `irq_chip`으로 구현하므로 결합 driver는 GPIO와 IRQ 두 subsystem을 동시에 사용합니다.
GPIO+IRQ 결합 driver라도 모든 IRQ consumer는 어느 irqchip에서든 IRQ를 요청할 수 있습니다. 기본 전제는 `gpio_chip`과 `irq_chip`이 직교하며 서로 독립적으로 service를 제공한다는 것입니다.
`gpiod_to_irq()`는 특정 GPIO line의 IRQ를 찾는 편의 function일 뿐입니다. IRQ를 사용하기 전에 이 function이 호출됐다고 의존하면 안 됩니다. GPIO와 `irq_chip` API 각각의 callback에서 hardware를 항상 준비해 즉시 동작 가능한 상태로 만들어야 합니다.
GPIO irqchip은 크게 두 범주입니다. Cascaded interrupt chip은 chip의 enabled GPIO line 중 하나가 발생시키는 공통 interrupt output 하나를 parent controller로 전달합니다. Driver의 irqchip은 GPIO controller register bit를 조사해 발생 line을 찾고, status bit clear 또는 status register read로 acknowledge하며 edge·level sensitivity를 설정합니다.
Hierarchical interrupt chip은 각 GPIO line이 한 단계 위 parent interrupt controller에 전용 IRQ line을 가집니다. 어느 GPIO가 발생했는지 GPIO hardware에 물을 필요는 없지만 acknowledge와 edge sensitivity 같은 구성은 여전히 필요할 수 있습니다.
Real-time compliant GPIO driver는 irqchip 구현에서 `spinlock_t`나 PM runtime 같은 sleep 가능 API를 사용하면 안 됩니다. `spinlock_t`는 `raw_spinlock_t`로 바꿉니다. Sleep 가능 API가 꼭 필요하면 irqchip의 유일한 slowpath인 `.irq_bus_lock()`과 `.irq_bus_unlock()` callback에서 사용하고 필요하면 callback을 추가합니다.
Parent interrupt controller에 연결되는 두 topology입니다.
gpiod_to_irq() 호출에 hardware 준비를 의존하지 않는 원칙입니다.
Cascaded GPIO irqchip 세 유형
308-388Cascaded GPIO irqchip은 보통 세 범주로 나뉩니다.
`CHAINED CASCADED GPIO IRQCHIPS`는 대개 SoC에 내장됩니다. Parent IRQ handler, 보통 system interrupt controller에서 GPIO용 fast IRQ flow handler가 chain으로 즉시 호출되며 IRQ가 disabled된 상태입니다. Handler는 대략 다음 순서를 실행합니다.
static irqreturn_t foo_gpio_irq(int irq, void *data)
chained_irq_enter(...);
generic_handle_irq(...);
chained_irq_exit(...);
Chained GPIO irqchip은 callback에서 모든 작업이 바로 일어나므로 보통 `struct gpio_chip`의 `.can_sleep`을 설정할 수 없고 I2C 같은 느린 bus traffic도 사용할 수 없습니다.
-RT에서도 chained IRQ handler는 강제로 threaded되지 않습니다. 따라서 chained handler에서 `spinlock_t`, PM runtime 같은 sleep 가능 API를 사용할 수 없습니다.
Nested threaded GPIO irqchip으로 바꿀 수 없다면 chained IRQ handler를 generic IRQ handler로 변환할 수 있습니다. 그러면 -RT에서는 threaded handler가 되고 non-RT에서는 hard IRQ handler가 됩니다.
`generic_handle_irq()`는 IRQ disabled 상태에서 호출되어야 합니다. 강제로 thread가 된 IRQ handler가 IRQ enabled 상태로 이를 호출하면 IRQ core가 경고하므로 raw lock을 이용한 workaround를 적용할 수 있습니다.
raw_spinlock_t wa_lock;
static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
unsigned long wa_lock_flags;
raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
generic_handle_irq(irq_find_mapping(bank->chip.irq.domain, bit));
raw_spin_unlock_irqrestore(&bank->wa_lock, wa_lock_flags);
`GENERIC CHAINED GPIO IRQCHIPS`는 chained handler를 쓰지 않는다는 점만 다릅니다. `request_irq()`로 구성한 generic IRQ handler가 GPIO IRQ를 dispatch합니다.
static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id)
for each detected GPIO IRQ
generic_handle_irq(...);
이 handler는 -RT에서 강제로 threaded되므로 IRQ enabled 상태에서 `generic_handle_irq()`를 호출한다는 IRQ core 경고가 생길 수 있습니다. Chained GPIO irqchip과 같은 raw-lock workaround를 적용합니다.
`NESTED THREADED GPIO IRQCHIPS`는 I2C나 SPI처럼 sleep하는 bus 반대편의 off-chip GPIO expander 등에 사용합니다. IRQ status를 읽는 느린 traffic 자체가 다른 IRQ를 일으킬 수 있어 IRQ disabled 상태의 빠른 handler에서 처리할 수 없습니다. 대신 thread를 만들고 driver가 interrupt를 처리할 때까지 parent IRQ line을 mask합니다.
static irqreturn_t foo_gpio_irq(int irq, void *data)
...
handle_nested_irq(irq);
Threaded GPIO irqchip은 `struct gpio_chip`의 `.can_sleep`을 true로 설정해 GPIO access가 sleep할 수 있음을 나타냅니다. 이미 sleeping context를 처리하도록 구성되므로 본질적으로 real-time tolerant합니다.
실행 context, bus 제약, dispatch primitive를 비교합니다.
Sleep bus 뒤의 GPIO expander가 interrupt를 처리하는 흐름입니다.
GPIO irqchip infrastructure helper
389-421GPIO irqchip과 연결된 irqdomain·resource allocation callback의 설정과 관리를 돕는 helper가 있습니다. Kconfig `GPIOLIB_IRQCHIP`을 선택하면 활성화되고 `IRQ_DOMAIN_HIERARCHY`도 선택하면 hierarchical helper까지 제공됩니다.
Interrupt가 GPIO line index와 1:1로 mapping된다는 가정 아래 gpiolib이 상당한 boilerplate를 관리합니다.
.. csv-table::
:header: GPIO line offset, Hardware IRQ
0,0
1,1
2,2
...,...
ngpio-1, ngpio-1
일부 GPIO line에 대응 IRQ가 없다면 `gpio_irq_chip`의 `valid_mask` bitmask와 `need_valid_mask` flag로 IRQ 연결이 invalid한 line을 mask할 수 있습니다.
권장 설정 방식은 `gpio_chip`을 추가하기 전에 내부의 `struct gpio_irq_chip`을 채우는 것입니다. 그러면 gpiolib이 GPIO 기능을 설정할 때 추가 `irq_chip`도 함께 구성합니다. 이어지는 chained cascaded example에서는 mask/unmask 또는 disable/enable function이 core gpiolib code를 호출해 state를 동기화합니다.
원문의 CSV 표를 구조화했습니다.
GPIO chip 등록과 동시에 IRQ infrastructure를 만드는 순서입니다.
Chained cascaded helper 예제
422-495다음 C 예제는 typical state container, immutable `irq_chip`, GPIO core와 mask state를 동기화하는 callback, parent IRQ 한 개를 가진 chained setup을 모두 보여 줍니다. 원문 코드는 식별자와 주석을 포함해 그대로 보존합니다.
.. code-block:: c
/* Typical state container */
struct my_gpio {
struct gpio_chip gc;
};
static void my_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
/*
* Perform any necessary action to mask the interrupt,
* and then call into the core code to synchronise the
* state.
*/
gpiochip_disable_irq(gc, hwirq);
}
static void my_gpio_unmask_irq(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);
/*
* Perform any necessary action to unmask the interrupt,
* after having called into the core code to synchronise
* the state.
*/
}
/*
* Statically populate the irqchip. Note that it is made const
* (further indicated by the IRQCHIP_IMMUTABLE flag), and that
* the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra
* callbacks to the structure.
*/
static const struct irq_chip my_gpio_irq_chip = {
.name = "my_gpio_irq",
.irq_ack = my_gpio_ack_irq,
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_set_type = my_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
/* Provide the gpio resource callbacks */
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
int irq; /* from platform etc */
struct my_gpio *g;
struct gpio_irq_chip *girq;
/* Get a pointer to the gpio_irq_chip */
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip);
girq->parent_handler = ftgpio_gpio_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->parents[0] = irq;
return devm_gpiochip_add_data(dev, &g->gc, g);
`my_gpio_mask_irq()`는 hardware interrupt를 mask하는 동작 뒤 `gpiochip_disable_irq()`로 core state를 동기화합니다. `my_gpio_unmask_irq()`는 먼저 `gpiochip_enable_irq()`를 호출한 뒤 hardware를 unmask합니다.
`IRQCHIP_IMMUTABLE`과 `GPIOCHIP_IRQ_RESOURCE_HELPERS`가 있는 static const `irq_chip`을 `gpio_irq_chip_set_chip()`으로 연결합니다. Parent handler와 parent IRQ array, default type, `handle_bad_irq`를 지정한 뒤 `devm_gpiochip_add_data()`로 등록합니다.
Helper는 threaded interrupt도 지원하므로 다음 절처럼 interrupt를 별도로 요청할 수 있습니다.
예제 코드의 parent IRQ 연결과 GPIO chip 등록 흐름입니다.
Threaded helper 예제
496-571Threaded 예제도 mask/unmask callback과 immutable `irq_chip` 정의는 chained 예제와 같습니다. 차이는 parent interrupt를 `devm_request_threaded_irq()`로 driver가 직접 요청한다는 점입니다.
.. code-block:: c
/* Typical state container */
struct my_gpio {
struct gpio_chip gc;
};
static void my_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
/*
* Perform any necessary action to mask the interrupt,
* and then call into the core code to synchronise the
* state.
*/
gpiochip_disable_irq(gc, hwirq);
}
static void my_gpio_unmask_irq(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);
/*
* Perform any necessary action to unmask the interrupt,
* after having called into the core code to synchronise
* the state.
*/
}
/*
* Statically populate the irqchip. Note that it is made const
* (further indicated by the IRQCHIP_IMMUTABLE flag), and that
* the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra
* callbacks to the structure.
*/
static const struct irq_chip my_gpio_irq_chip = {
.name = "my_gpio_irq",
.irq_ack = my_gpio_ack_irq,
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_set_type = my_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
/* Provide the gpio resource callbacks */
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
int irq; /* from platform etc */
struct my_gpio *g;
struct gpio_irq_chip *girq;
ret = devm_request_threaded_irq(dev, irq, NULL, irq_thread_fn,
IRQF_ONESHOT, "my-chip", g);
if (ret < 0)
return ret;
/* Get a pointer to the gpio_irq_chip */
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip);
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
return devm_gpiochip_add_data(dev, &g->gc, g);
Threaded parent IRQ는 `IRQF_ONESHOT`으로 요청합니다. Driver가 parent IRQ를 직접 처리하도록 `girq->parent_handler = NULL`, `num_parents = 0`, `parents = NULL`로 설정하고 나머지 default type과 child handler를 지정한 뒤 GPIO chip을 등록합니다.
Helper는 hierarchical interrupt controller도 지원하며 다음 절의 전형적인 설정을 사용할 수 있습니다.
Parent handler를 gpiolib에 넘기지 않고 driver thread가 처리합니다.
Hierarchical helper와 valid mask
572-668Hierarchical interrupt controller 예제는 dynamic irqchip state container에 `fwnode`를 보관하고 child mask/unmask와 parent mask/unmask를 함께 수행합니다.
.. code-block:: c
/* Typical state container with dynamic irqchip */
struct my_gpio {
struct gpio_chip gc;
struct fwnode_handle *fwnode;
};
static void my_gpio_mask_irq(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
/*
* Perform any necessary action to mask the interrupt,
* and then call into the core code to synchronise the
* state.
*/
gpiochip_disable_irq(gc, hwirq);
irq_mask_mask_parent(d);
}
static void my_gpio_unmask_irq(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);
/*
* Perform any necessary action to unmask the interrupt,
* after having called into the core code to synchronise
* the state.
*/
irq_mask_unmask_parent(d);
}
/*
* Statically populate the irqchip. Note that it is made const
* (further indicated by the IRQCHIP_IMMUTABLE flag), and that
* the GPIOCHIP_IRQ_RESOURCE_HELPER macro adds some extra
* callbacks to the structure.
*/
static const struct irq_chip my_gpio_irq_chip = {
.name = "my_gpio_irq",
.irq_ack = my_gpio_ack_irq,
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_set_type = my_gpio_set_irq_type,
.flags = IRQCHIP_IMMUTABLE,
/* Provide the gpio resource callbacks */
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
struct my_gpio *g;
struct gpio_irq_chip *girq;
/* Get a pointer to the gpio_irq_chip */
girq = &g->gc.irq;
gpio_irq_chip_set_chip(girq, &my_gpio_irq_chip);
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->fwnode = g->fwnode;
girq->parent_domain = parent;
girq->child_to_parent_hwirq = my_gpio_child_to_parent_hwirq;
return devm_gpiochip_add_data(dev, &g->gc, g);
구조는 앞선 예제와 비슷하지만 parent IRQ handler를 제공하지 않습니다. 대신 parent `irqdomain`, hardware용 `fwnode`, child hardware IRQ에서 parent hardware IRQ를 찾는 `.child_to_parent_hwirq()` function을 제공합니다. 필요한 구성 요소를 찾을 때는 kernel tree의 실제 driver 예제를 참고하는 것이 좋습니다.
Helper가 관리하는 IRQ domain에서 특정 GPIO line을 제외하려면 `devm_gpiochip_add_data()` 또는 `gpiochip_add_data()` 전에 gpiochip의 `.irq.need_valid_mask`를 설정합니다. 그러면 chip의 line 수만큼 bit가 설정된 `.irq.valid_mask`가 할당되며 0..n-1 line마다 bit 하나가 대응합니다. Driver는 bit를 clear해 line을 제외하거나 `struct gpio_irq_chip`의 `init_valid_mask()` callback에서 mask를 채울 수 있습니다.
Helper 사용 시 `irqchip`이 초기화되도록 `struct gpio_chip`의 관련 member를 모두 지정해야 합니다. 예를 들어 `.dev`와 `.can_sleep`을 올바르게 설정합니다.
보통 `gpio_irq_chip.handler`는 `handle_bad_irq`로 시작합니다. Cascaded irqchip이라면 controller 지원과 consumer 요청에 따라 irqchip `.set_type()` callback에서 `handle_level_irq()` 또는 `handle_edge_irq()`로 바꿉니다.
Child GPIO hwirq를 parent irqdomain으로 연결합니다.
IRQ를 지원하지 않는 GPIO line을 domain에서 제외합니다.
IRQ 사용으로 GPIO line 잠그기
669-697GPIO와 `irq_chip`은 직교하므로 use case가 충돌할 수 있습니다. IRQ에 쓰는 GPIO line은 input이어야 하며 output GPIO에서 interrupt를 발생시키는 것은 의미가 없습니다.
Subsystem 내부에서 특정 GPIO line과 register 같은 resource 사용 주체가 경쟁하면 일부 operation을 거부하고 gpiolib subsystem 내부에서 사용 상태를 추적해야 합니다.
Input GPIO를 IRQ signal로 사용할 때 driver는 다음 function으로 IRQ 사용 상태를 표시합니다.
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
이 lock이 유지되는 동안 non-IRQ GPIO API 사용은 막힙니다. 다음 function으로 GPIO IRQ lock을 해제합니다.
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
GPIO driver 안에 irqchip을 구현할 때 두 function은 보통 irqchip의 `.startup()`과 `.shutdown()` callback에서 호출합니다. Gpiolib irqchip helper를 사용하면 callback이 자동 할당됩니다.
Input line을 IRQ resource로 잠그고 다시 해제합니다.
IRQ disable·enable 상태 동기화
698-725드문 use case에서는 GPIO line을 IRQ input으로 사용하다가 일시적으로 output을 구동한 뒤 다시 interrupt input으로 되돌립니다. CEC(Consumer Electronics Control)가 예입니다.
GPIO가 IRQ signal이면 gpiolib도 IRQ의 enabled·disabled 상태를 알아야 합니다. Driver는 다음 function으로 disabled 상태를 알립니다.
void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset)
IRQ가 disabled된 동안에는 GPIO를 output으로 구동할 수 있습니다. IRQ를 다시 enable할 때는 다음 function을 호출합니다.
void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset)
GPIO driver 안에 irqchip을 구현할 때 이 두 function은 보통 `.irq_disable()`과 `.irq_enable()` callback에서 호출합니다.
irqchip이 `IRQCHIP_IMMUTABLE`을 광고하지 않으면 callback이 자동 할당됩니다. 이 동작은 deprecated이며 kernel에서 제거될 예정입니다.
CEC 같은 경우 gpiolib 상태와 direction을 맞춥니다.
GPIO IRQ chip의 Real-Time 준수
726-757모든 irqchip provider는 Real-Time preemption을 지원하도록 세심하게 설계해야 합니다. GPIO subsystem의 irqchip도 이를 고려하고 적절히 test해 real-time enabled임을 확인하는 것이 바람직합니다.
앞 절의 real-time 고려 사항에 주의하며 다음 checklist를 따릅니다.
- `irq_chip` 구현에서 `spinlock_t`를 사용하지 않음
- Sleep 가능 API를 사용하지 않음. 꼭 필요하면 `.irq_bus_lock()`·`.irq_bus_unlock()`에서 사용
- Chained handler에서 `spinlock_t`나 sleep 가능 API를 사용하지 않음
- Generic chained의 `generic_handle_irq()` 호출에 맞는 workaround 적용
- 가능하면 chained handler를 제거하고 generic IRQ handler 사용
- `regmap_mmio`는 `.disable_locking`으로 internal lock을 끄고 GPIO driver가 관리 가능
- Level·edge IRQ 모두 적절한 in-kernel real-time test로 검증
원문은 `raw_spinlock_t` 전환, irqchip slowpath callback, chained handler를 generic handler로 바꾸는 사례를 설명하는 lore.kernel.org reference 세 개를 제공합니다.
* [1] https://lore.kernel.org/r/1437496011-11486-1-git-send-email-bigeasy@linutronix.de/
* [2] https://lore.kernel.org/r/1443209283-20781-2-git-send-email-grygorii.strashko@ti.com
* [3] https://lore.kernel.org/r/1443209283-20781-3-git-send-email-grygorii.strashko@ti.com
Lock, sleep, handler context, test 항목을 정리했습니다.
Driver 자체 소유 GPIO descriptor 요청
758-777GPIO chip driver가 gpiolib API를 통해 자신의 GPIO descriptor를 요청해야 할 때가 있습니다. 다음 function으로 자체 descriptor를 요청하고 해제할 수 있습니다.
struct gpio_desc *gpiochip_request_own_desc(struct gpio_desc *desc,
u16 hwnum,
const char *label,
enum gpiod_flags flags)
void gpiochip_free_own_desc(struct gpio_desc *desc)
`gpiochip_request_own_desc()`로 요청한 descriptor는 반드시 `gpiochip_free_own_desc()`로 해제해야 합니다.
이 function은 module use count에 영향을 주지 않으므로 주의해서 사용해야 합니다. 호출 driver가 소유하지 않은 GPIO descriptor를 요청하는 데 사용하면 안 됩니다.
GPIO chip driver가 자신의 line을 요청하고 해제하는 규칙입니다.
요약과 해설
driver.rst:1-777GPIO provider driver는 hardware offset을 중심으로 `gpio_chip`을 구현하고, 필요하면 pin control과 `irq_chip`을 결합합니다. Electrical mode와 IRQ topology는 hardware capability와 실행 context를 정확히 반영해야 합니다.
GPIO와 IRQ service는 독립적이므로 `gpiod_to_irq()` 호출에 초기화를 의존해서는 안 됩니다. -RT에서는 chained·generic·threaded handler의 context 차이를 고려하고 lock과 sleep 가능 API를 엄격히 배치해야 합니다.