Commit 70aedd24d20e75198f5a0b11750faabbb56924e2

Authored by Thomas Gleixner
1 parent b25c340c19

genirq: Add buslock support

Some interrupt chips are connected to a "slow" bus (i2c, spi ...). The
bus access needs to sleep and therefor cannot be called in atomic
contexts.

Some of the generic interrupt management functions like disable_irq(),
enable_irq() ... call interrupt chip functions with the irq_desc->lock
held and interrupts disabled. This does not work for such devices.

Provide a separate synchronization mechanism for such interrupt
chips. The irq_chip structure is extended by two optional functions
(bus_lock and bus_sync_and_unlock).

The idea is to serialize the bus access for those operations in the
core code so that drivers which are behind that bus operated interrupt
controller do not have to worry about it and just can use the normal
interfaces. To achieve this we add two function pointers to the
irq_chip: bus_lock and bus_sync_unlock.

bus_lock() is called to serialize access to the interrupt controller
bus.

Now the core code can issue chip->mask/unmask ... commands without
changing the fast path code at all. The chip implementation merily
stores that information in a chip private data structure and
returns. No bus interaction as these functions are called from atomic
context.

After that bus_sync_unlock() is called outside the atomic context. Now
the chip implementation issues the bus commands, waits for completion
and unlocks the interrupt controller bus.

The irq_chip implementation as pseudo code:

struct irq_chip_data {
       struct mutex   mutex;
       unsigned int   irq_offset;
       unsigned long  mask;
       unsigned long  mask_status;
}

static void bus_lock(unsigned int irq)
{
        struct irq_chip_data *data = get_irq_desc_chip_data(irq);

        mutex_lock(&data->mutex);
}

static void mask(unsigned int irq)
{
        struct irq_chip_data *data = get_irq_desc_chip_data(irq);

        irq -= data->irq_offset;
        data->mask |= (1 << irq);
}

static void unmask(unsigned int irq)
{
        struct irq_chip_data *data = get_irq_desc_chip_data(irq);

        irq -= data->irq_offset;
        data->mask &= ~(1 << irq);
}

static void bus_sync_unlock(unsigned int irq)
{
        struct irq_chip_data *data = get_irq_desc_chip_data(irq);

        if (data->mask != data->mask_status) {
                do_bus_magic_to_set_mask(data->mask);
                data->mask_status = data->mask;
        }
        mutex_unlock(&data->mutex);
}

The device drivers can use request_threaded_irq, free_irq, disable_irq
and enable_irq as usual with the only restriction that the calls need
to come from non atomic context.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Trilok Soni <soni.trilok@gmail.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Brian Swetland <swetland@google.com>
Cc: Joonyoung Shim <jy0922.shim@samsung.com>
Cc: m.szyprowski@samsung.com
Cc: t.fujak@samsung.com
Cc: kyungmin.park@samsung.com,
Cc: David Brownell <david-b@pacbell.net>
Cc: Daniel Ribeiro <drwyrm@gmail.com>
Cc: arve@android.com
Cc: Barry Song <21cnbao@gmail.com>

Showing 4 changed files with 39 additions and 1 deletions Side-by-side Diff

... ... @@ -101,6 +101,9 @@
101 101 * @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
102 102 * @set_wake: enable/disable power-management wake-on of an IRQ
103 103 *
  104 + * @bus_lock: function to lock access to slow bus (i2c) chips
  105 + * @bus_sync_unlock: function to sync and unlock slow bus (i2c) chips
  106 + *
104 107 * @release: release function solely used by UML
105 108 * @typename: obsoleted by name, kept as migration helper
106 109 */
... ... @@ -123,6 +126,9 @@
123 126 int (*retrigger)(unsigned int irq);
124 127 int (*set_type)(unsigned int irq, unsigned int flow_type);
125 128 int (*set_wake)(unsigned int irq, unsigned int on);
  129 +
  130 + void (*bus_lock)(unsigned int irq);
  131 + void (*bus_sync_unlock)(unsigned int irq);
126 132  
127 133 /* Currently used only by UML, might disappear one day.*/
128 134 #ifdef CONFIG_IRQ_RELEASE_METHOD
... ... @@ -580,6 +580,7 @@
580 580 desc->chip = &dummy_irq_chip;
581 581 }
582 582  
  583 + chip_bus_lock(irq, desc);
583 584 spin_lock_irqsave(&desc->lock, flags);
584 585  
585 586 /* Uninstall? */
... ... @@ -599,6 +600,7 @@
599 600 desc->chip->startup(irq);
600 601 }
601 602 spin_unlock_irqrestore(&desc->lock, flags);
  603 + chip_bus_sync_unlock(irq, desc);
602 604 }
603 605 EXPORT_SYMBOL_GPL(__set_irq_handler);
604 606  
kernel/irq/internals.h
... ... @@ -44,6 +44,19 @@
44 44  
45 45 extern void irq_set_thread_affinity(struct irq_desc *desc);
46 46  
  47 +/* Inline functions for support of irq chips on slow busses */
  48 +static inline void chip_bus_lock(unsigned int irq, struct irq_desc *desc)
  49 +{
  50 + if (unlikely(desc->chip->bus_lock))
  51 + desc->chip->bus_lock(irq);
  52 +}
  53 +
  54 +static inline void chip_bus_sync_unlock(unsigned int irq, struct irq_desc *desc)
  55 +{
  56 + if (unlikely(desc->chip->bus_sync_unlock))
  57 + desc->chip->bus_sync_unlock(irq);
  58 +}
  59 +
47 60 /*
48 61 * Debugging printout:
49 62 */
... ... @@ -230,9 +230,11 @@
230 230 if (!desc)
231 231 return;
232 232  
  233 + chip_bus_lock(irq, desc);
233 234 spin_lock_irqsave(&desc->lock, flags);
234 235 __disable_irq(desc, irq, false);
235 236 spin_unlock_irqrestore(&desc->lock, flags);
  237 + chip_bus_sync_unlock(irq, desc);
236 238 }
237 239 EXPORT_SYMBOL(disable_irq_nosync);
238 240  
... ... @@ -294,7 +296,8 @@
294 296 * matches the last disable, processing of interrupts on this
295 297 * IRQ line is re-enabled.
296 298 *
297   - * This function may be called from IRQ context.
  299 + * This function may be called from IRQ context only when
  300 + * desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
298 301 */
299 302 void enable_irq(unsigned int irq)
300 303 {
301 304  
... ... @@ -304,9 +307,11 @@
304 307 if (!desc)
305 308 return;
306 309  
  310 + chip_bus_lock(irq, desc);
307 311 spin_lock_irqsave(&desc->lock, flags);
308 312 __enable_irq(desc, irq, false);
309 313 spin_unlock_irqrestore(&desc->lock, flags);
  314 + chip_bus_sync_unlock(irq, desc);
310 315 }
311 316 EXPORT_SYMBOL(enable_irq);
312 317  
313 318  
... ... @@ -468,12 +473,14 @@
468 473 */
469 474 static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
470 475 {
  476 + chip_bus_lock(irq, desc);
471 477 spin_lock_irq(&desc->lock);
472 478 if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
473 479 desc->status &= ~IRQ_MASKED;
474 480 desc->chip->unmask(irq);
475 481 }
476 482 spin_unlock_irq(&desc->lock);
  483 + chip_bus_sync_unlock(irq, desc);
477 484 }
478 485  
479 486 #ifdef CONFIG_SMP
480 487  
... ... @@ -904,7 +911,14 @@
904 911 */
905 912 void free_irq(unsigned int irq, void *dev_id)
906 913 {
  914 + struct irq_desc *desc = irq_to_desc(irq);
  915 +
  916 + if (!desc)
  917 + return;
  918 +
  919 + chip_bus_lock(irq, desc);
907 920 kfree(__free_irq(irq, dev_id));
  921 + chip_bus_sync_unlock(irq, desc);
908 922 }
909 923 EXPORT_SYMBOL(free_irq);
910 924  
911 925  
... ... @@ -1011,7 +1025,10 @@
1011 1025 action->name = devname;
1012 1026 action->dev_id = dev_id;
1013 1027  
  1028 + chip_bus_lock(irq, desc);
1014 1029 retval = __setup_irq(irq, desc, action);
  1030 + chip_bus_sync_unlock(irq, desc);
  1031 +
1015 1032 if (retval)
1016 1033 kfree(action);
1017 1034