mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-20 08:38:24 +08:00
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>
This commit is contained in:
parent
b25c340c19
commit
70aedd24d2
@ -101,6 +101,9 @@ struct msi_desc;
|
||||
* @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
|
||||
* @set_wake: enable/disable power-management wake-on of an IRQ
|
||||
*
|
||||
* @bus_lock: function to lock access to slow bus (i2c) chips
|
||||
* @bus_sync_unlock: function to sync and unlock slow bus (i2c) chips
|
||||
*
|
||||
* @release: release function solely used by UML
|
||||
* @typename: obsoleted by name, kept as migration helper
|
||||
*/
|
||||
@ -124,6 +127,9 @@ struct irq_chip {
|
||||
int (*set_type)(unsigned int irq, unsigned int flow_type);
|
||||
int (*set_wake)(unsigned int irq, unsigned int on);
|
||||
|
||||
void (*bus_lock)(unsigned int irq);
|
||||
void (*bus_sync_unlock)(unsigned int irq);
|
||||
|
||||
/* Currently used only by UML, might disappear one day.*/
|
||||
#ifdef CONFIG_IRQ_RELEASE_METHOD
|
||||
void (*release)(unsigned int irq, void *dev_id);
|
||||
|
@ -580,6 +580,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
desc->chip = &dummy_irq_chip;
|
||||
}
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
/* Uninstall? */
|
||||
@ -599,6 +600,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
desc->chip->startup(irq);
|
||||
}
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__set_irq_handler);
|
||||
|
||||
|
@ -44,6 +44,19 @@ extern int irq_select_affinity_usr(unsigned int irq);
|
||||
|
||||
extern void irq_set_thread_affinity(struct irq_desc *desc);
|
||||
|
||||
/* Inline functions for support of irq chips on slow busses */
|
||||
static inline void chip_bus_lock(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
if (unlikely(desc->chip->bus_lock))
|
||||
desc->chip->bus_lock(irq);
|
||||
}
|
||||
|
||||
static inline void chip_bus_sync_unlock(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
if (unlikely(desc->chip->bus_sync_unlock))
|
||||
desc->chip->bus_sync_unlock(irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging printout:
|
||||
*/
|
||||
|
@ -230,9 +230,11 @@ void disable_irq_nosync(unsigned int irq)
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
__disable_irq(desc, irq, false);
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL(disable_irq_nosync);
|
||||
|
||||
@ -294,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
|
||||
* matches the last disable, processing of interrupts on this
|
||||
* IRQ line is re-enabled.
|
||||
*
|
||||
* This function may be called from IRQ context.
|
||||
* This function may be called from IRQ context only when
|
||||
* desc->chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
|
||||
*/
|
||||
void enable_irq(unsigned int irq)
|
||||
{
|
||||
@ -304,9 +307,11 @@ void enable_irq(unsigned int irq)
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irqsave(&desc->lock, flags);
|
||||
__enable_irq(desc, irq, false);
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL(enable_irq);
|
||||
|
||||
@ -468,12 +473,14 @@ static int irq_wait_for_interrupt(struct irqaction *action)
|
||||
*/
|
||||
static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
chip_bus_lock(irq, desc);
|
||||
spin_lock_irq(&desc->lock);
|
||||
if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
|
||||
desc->status &= ~IRQ_MASKED;
|
||||
desc->chip->unmask(irq);
|
||||
}
|
||||
spin_unlock_irq(&desc->lock);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@ -904,7 +911,14 @@ EXPORT_SYMBOL_GPL(remove_irq);
|
||||
*/
|
||||
void free_irq(unsigned int irq, void *dev_id)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
kfree(__free_irq(irq, dev_id));
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
}
|
||||
EXPORT_SYMBOL(free_irq);
|
||||
|
||||
@ -1011,7 +1025,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
|
||||
action->name = devname;
|
||||
action->dev_id = dev_id;
|
||||
|
||||
chip_bus_lock(irq, desc);
|
||||
retval = __setup_irq(irq, desc, action);
|
||||
chip_bus_sync_unlock(irq, desc);
|
||||
|
||||
if (retval)
|
||||
kfree(action);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user