• Thomas Gleixner's avatar
    genirq: Add buslock support · 70aedd24
    Thomas Gleixner authored
    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: default avatarThomas 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>
    70aedd24
internals.h 2.91 KB