Linux Sysfs Gpio: What Re-Arms Interrupt

How is sysfs updated when a GPIO changes state?

How is this file updated by the OS? I think that an interrupt mechanism is required.

It does not require an interrupt mechanism unless it supports polling (man poll) or alternate asynchronous notifications. At least with most version, the /sys/class/gpio/ only does a read of the GPIO level when someone reads the file.

sysfs, debugfs, configfs, procfs, etc are virtual file systems. When you access the file, code within the Linux kernel runs to provide the value. sysfs only provides a file like interface; that doesn't mean it is backed with actual state. The state is the GPIO level which can be read at any time.

gpio_value_show() appears to be the current implementation. What you describe with interrupts is possible. It can be done through the sysfs_set_active_low() function or the sysfs file /sys/class/gpio/gpioN/edge. Writing to the file may return an error if the GPIO doesn't support interrupts. See gpio.txt for more (especially for your particular version of Linux).

What is the advantage of using GPIO as IRQ.?

What is the advantage of using GPIO as IRQ?

If I get your question, you are asking why even bother having a GPIO? The other answers show that someone may not even want the IRQ feature of an interrupt. Typical GPIO controllers can configure an I/O as either an input or an output.

Many GPIO pads have the flexibility to be open drain. With an open drain configuration, you may have a bi-direction 'BUS' and data can be both sent and received. Here you need to change from an input to an output. You can imagine this if you bit-bash I2C communications. This type of use maybe fine if the I2C is only used to initialize some other interface at boot.

Even if the interface is not bi-directional, you might wish to capture on each edge. Various peripherals use zero crossing and a timer to decode a signal. For example a laser bar code reader, a magnetic stripe reader, or a bit-bashed UART might look at the time between zero crossings. Is the time double a bit width? Is the line high or low; then shift previous value and add two bits. In these cases you have to look at the signal to see whether the line is high or low. This can happen even if polarity shouldn't matter as short noise pulses can cause confusion.

So even for the case where you have only the input as an interrupt, the current level of the signal is often very useful. If this GPIO interrupt happens to be connected to an Ethernet controller and active high means data is ready, then you don't need to have the 'I/O' feature. However, this case is using the GPIO interrupt feature as glue logic. Often this signalling will be integrated into a dedicated module. The case where you only need the interrupt is typically some custom hardware to detect a signal (case open, power disconnect, etc) which is not industry standard.

The ARM SOC vendor has no idea which case above the OEM might use. The SOC vendor gives lots of flexibility as the transistors on the die are cheap compared to the wire bond/pins on the package. It means that you, who only use the interrupt feature, gets economies of scale (and a cheaper part) because other might be using these features and the ARM SOC vendor gets to distribute the NRE cost between more people.

In a perfect world, there is maybe no need for this. Not so long ago when tranistors where more expensive, some lines did only behave as interrupts (some M68k CPUs have this). Historically the ARM only has a single interrupt line with one common routine (the Cortex-M are different). So the interrupt source has to be determined by reading another register. As the hardware needs to capture the state of the line on the ARM, it is almost free to add the 'input controller' portion.

Also, for this reason, all of the ARM Linux GPIO drivers have a macro to convert from a GPIO pin to an interrupt number as they are usually one-to-one mapped. There is usually a single 'GIC' interrupt for the GPIO controller. There is a 'GPIO' interrupt controller which forms a tree of interrupt controllers with the GIC as the root. Typically, the GPIO irq numbers are Max GIC IRQ + port *32 + pin; so the GPIO irq numbers are just appended to the 'GIC' irq numbers.

Port B GPIO ep93xx/gpio.c interrupt issue

There are a lot of question on this issue (and ARM irq OR interrupt). Please look at them.

We can see many changes by looking at more recent Linux 3.0 gpio.c change logs versus the 2.6.34 logs and the current version. You should be able to get the current Linux stable tree and extract these patches and back port them to your kernel. For instance, there is a bug where port C and F are swapped; I don't know if this is in your ts7200_matt variant.

Some important change sets to look at,

  1. arm: Fold irq_set_chip/irq_set_handler
  2. arm: Cleanup the irq namespace
  3. arm: ep93xx: Use proper irq accessor functions
  4. arm: ep93xx: Add basic interrupt info
  5. ARM: ep93xx: irq_data conversion.
  6. ARM: 5954/1: ep93xx: move gpio interrupt support to gpio.c
  7. [ARM] 5243/1: ep93xx: bugfix, GPIO ports C and F are swapped

You may have #6, but it is worth looking at as it is basically the interrupt implementation for your controller. After about linux-3.0, your SOC's GPIO controller was moved to drivers/gpio/gpio-ep93xx.c. You may wish to look at these changes, but none seem to be related to your issue. You should be aware of structural changes to Linux. Ie, overall changes to interrupt handling and/or the generic GPIO infrastructure. A good guess is that Thomas Gleixner or Russell King will make these changes.

The patches can be extracted from a particular Linux stable tree with git format-patch b685004.. b0ec5cf1 gpio.c. This will create several patch files. Move them to your tree and apply with either git am or patch -p1. You may have to massage these files to get them to apply cleanly to your tree; if you take them all, even though they are not related to interrupt handling, you will have better luck doing this automatically. You can also look at the patch set and try to manually patch the file with a text editor.

None of this addresses your specific questions. However, it gives a path to merge changes from the latest Linux versions. Also, the previous stack overflow questions give details on the structure of the GPIO interrupt handling. Coupled with your data sheet, the Linux GPIO document, and the given change sets, you should be able to fix your own problem. Otherwise, you need someone familiar with the EP93xx and the question is fairly localized.

Note: The stack trace indicates that a GPIO is being used without a corresponding gpio_request()
. This is either a bug in the machine file or in the EP93xx GPIO interrupt handling code.

How to request GPIO interrupt from i2c io expander linux (fails with -EINVAL)

At least one reason for the failure is that the parent interrupt controller uses a threaded interrupt handler and so the child interrupt handlers also need to be threaded (these will be "nested" threaded interrupt handlers).

The incompatibility is tested in __setup_irq in "kernel/irq/manage.c":

    /*
* Check whether the interrupt nests into another interrupt
* thread.
*/
nested = irq_settings_is_nested_thread(desc);
if (nested) {
if (!new->thread_fn) {
ret = -EINVAL;
goto out_mput;
}
/*
* Replace the primary handler which was provided from
* the driver for non nested interrupt handling by the
* dummy function which warns when called.
*/
new->handler = irq_nested_primary_handler;

(new->thread_fn is NULL when requesting a non-threaded IRQ handler. The above code results in a return value of -EINVAL from request_irq(). The new->handler = irq_nested_primary_handler; part is described as a footnote to the discussion of request_threaded_irq below.)


The request_irq call in the original code can be changed to the following to use a threaded IRQ handler:

                err = request_threaded_irq(
irq_ids[i],
NULL,
mymodule_irq_handler,
IRQF_TRIGGER_HIGH,
"mymodule_irq_handler",
NULL
);

The main difference is the "hard-IRQ" handler has been set to NULL because it is not used for a purely threaded IRQ handler (including nested threaded IRQ handlers).1 (I also removed the type cast operator from (irq_handler_t) mymodule_irq_handler because that only serves to hide problems if mymodule_irq_handler has not been declared correctly.)

As an alternative to using request_threaded_irq, the request_any_context_irq function could be used instead as follows:

                err = request_any_context_irq(
irq_ids[i],
mymodule_irq_handler,
IRQF_TRIGGER_HIGH,
"mymodule_irq_handler",
NULL
);

That will select a hard-IRQ handler or a threaded IRQ handler, automatically for those IRQ requests that do not need both a hard and a threaded part.

[You can stop reading here if you want.]



1 If a "hard-IRQ" handler function is specified, it won't be called anyway because the threaded interrupt handler is going to be "nested" into the parent's threaded interrupt handler. The specified "hard-IRQ" handler will be replaced with irq_nested_primary_handler as shown in the code from __setup_irq above, but that replacement handler should not get called either. All it does is log a warning:

/*
* Primary handler for nested threaded interrupts. Should never be
* called.
*/
static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
{
WARN(1, "Primary handler called for nested irq %d\n", irq);
return IRQ_NONE;
}

The interrupt controller is configured as a threaded interrupt controller in mcp23s08_probe_one in "drivers/pinctrl/pinctrl-mcp23s08.c":

    if (mcp->irq && mcp->irq_controller) {
struct gpio_irq_chip *girq = &mcp->chip.irq;

girq->chip = &mcp->irq_chip;
/* This will let us handle the parent IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->parents = NULL;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_simple_irq;
girq->threaded = true;
}

(girq->threaded = true; is the relevant line.)

Each child IRQ is mapped by gpiochip_irq_map in "drivers/gpio/gpiolib.c" and marked as using nested thread handlers:

    /* Chips that use nested thread handlers have them marked */
if (gc->irq.threaded)
irq_set_nested_thread(irq, 1);

The parent interrupt handler is itself set up as a threaded IRQ handler by mcp23s08_irq_setup in "drivers/pinctrl/pinctrl-mcp23s08.c":

static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
{
struct gpio_chip *chip = &mcp->chip;
int err;
unsigned long irqflags = IRQF_ONESHOT | IRQF_SHARED;

if (mcp->irq_active_high)
irqflags |= IRQF_TRIGGER_HIGH;
else
irqflags |= IRQF_TRIGGER_LOW;

err = devm_request_threaded_irq(chip->parent, mcp->irq, NULL,
mcp23s08_irq,
irqflags, dev_name(chip->parent), mcp);
if (err != 0) {
dev_err(chip->parent, "unable to request IRQ#%d: %d\n",
mcp->irq, err);
return err;
}

return 0;
}

The parent interrupt handler mcp23s08_irq in "drivers/pinctrl/pinctrl-mcp23s08.c" calls handle_nested_irq to handle the child interrupt handlers:

            child_irq = irq_find_mapping(mcp->chip.irq.domain, i);
handle_nested_irq(child_irq);

handle_nested_irq in "kernel/irq/chip.c" calls the child nested interrupt handler(s) for the child IRQ:

    for_each_action_of_desc(desc, action)
action_ret |= action->thread_fn(action->irq, action->dev_id);


Related Topics



Leave a reply



Submit