aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2009-08-13 06:17:48 -0400
committerThomas Gleixner <tglx@linutronix.de>2009-08-17 04:54:05 -0400
commit70aedd24d20e75198f5a0b11750faabbb56924e2 (patch)
tree8492641c61aa3af6f4dea421b8f628efe6fc92bd
parentb25c340c195447afb1860da580fe2a85a6b652c5 (diff)
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>
-rw-r--r--include/linux/irq.h6
-rw-r--r--kernel/irq/chip.c2
-rw-r--r--kernel/irq/internals.h13
-rw-r--r--kernel/irq/manage.c19
4 files changed, 39 insertions, 1 deletions
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 5e7c6ee8c35c..ce8171bc6fac 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -101,6 +101,9 @@ struct msi_desc;
101 * @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ 101 * @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
102 * @set_wake: enable/disable power-management wake-on of an IRQ 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 * @release: release function solely used by UML 107 * @release: release function solely used by UML
105 * @typename: obsoleted by name, kept as migration helper 108 * @typename: obsoleted by name, kept as migration helper
106 */ 109 */
@@ -124,6 +127,9 @@ struct irq_chip {
124 int (*set_type)(unsigned int irq, unsigned int flow_type); 127 int (*set_type)(unsigned int irq, unsigned int flow_type);
125 int (*set_wake)(unsigned int irq, unsigned int on); 128 int (*set_wake)(unsigned int irq, unsigned int on);
126 129
130 void (*bus_lock)(unsigned int irq);
131 void (*bus_sync_unlock)(unsigned int irq);
132
127 /* Currently used only by UML, might disappear one day.*/ 133 /* Currently used only by UML, might disappear one day.*/
128#ifdef CONFIG_IRQ_RELEASE_METHOD 134#ifdef CONFIG_IRQ_RELEASE_METHOD
129 void (*release)(unsigned int irq, void *dev_id); 135 void (*release)(unsigned int irq, void *dev_id);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index b08c0d24f202..f856330e684a 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -580,6 +580,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
580 desc->chip = &dummy_irq_chip; 580 desc->chip = &dummy_irq_chip;
581 } 581 }
582 582
583 chip_bus_lock(irq, desc);
583 spin_lock_irqsave(&desc->lock, flags); 584 spin_lock_irqsave(&desc->lock, flags);
584 585
585 /* Uninstall? */ 586 /* Uninstall? */
@@ -599,6 +600,7 @@ __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
599 desc->chip->startup(irq); 600 desc->chip->startup(irq);
600 } 601 }
601 spin_unlock_irqrestore(&desc->lock, flags); 602 spin_unlock_irqrestore(&desc->lock, flags);
603 chip_bus_sync_unlock(irq, desc);
602} 604}
603EXPORT_SYMBOL_GPL(__set_irq_handler); 605EXPORT_SYMBOL_GPL(__set_irq_handler);
604 606
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index e70ed5592eb9..1b5d742c6a77 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -44,6 +44,19 @@ extern int irq_select_affinity_usr(unsigned int irq);
44 44
45extern void irq_set_thread_affinity(struct irq_desc *desc); 45extern void irq_set_thread_affinity(struct irq_desc *desc);
46 46
47/* Inline functions for support of irq chips on slow busses */
48static 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
54static 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 * Debugging printout: 61 * Debugging printout:
49 */ 62 */
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index d7f7b5fd2476..0a3fd5b524c9 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -230,9 +230,11 @@ void disable_irq_nosync(unsigned int irq)
230 if (!desc) 230 if (!desc)
231 return; 231 return;
232 232
233 chip_bus_lock(irq, desc);
233 spin_lock_irqsave(&desc->lock, flags); 234 spin_lock_irqsave(&desc->lock, flags);
234 __disable_irq(desc, irq, false); 235 __disable_irq(desc, irq, false);
235 spin_unlock_irqrestore(&desc->lock, flags); 236 spin_unlock_irqrestore(&desc->lock, flags);
237 chip_bus_sync_unlock(irq, desc);
236} 238}
237EXPORT_SYMBOL(disable_irq_nosync); 239EXPORT_SYMBOL(disable_irq_nosync);
238 240
@@ -294,7 +296,8 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
294 * matches the last disable, processing of interrupts on this 296 * matches the last disable, processing of interrupts on this
295 * IRQ line is re-enabled. 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 */
299void enable_irq(unsigned int irq) 302void enable_irq(unsigned int irq)
300{ 303{
@@ -304,9 +307,11 @@ void enable_irq(unsigned int irq)
304 if (!desc) 307 if (!desc)
305 return; 308 return;
306 309
310 chip_bus_lock(irq, desc);
307 spin_lock_irqsave(&desc->lock, flags); 311 spin_lock_irqsave(&desc->lock, flags);
308 __enable_irq(desc, irq, false); 312 __enable_irq(desc, irq, false);
309 spin_unlock_irqrestore(&desc->lock, flags); 313 spin_unlock_irqrestore(&desc->lock, flags);
314 chip_bus_sync_unlock(irq, desc);
310} 315}
311EXPORT_SYMBOL(enable_irq); 316EXPORT_SYMBOL(enable_irq);
312 317
@@ -468,12 +473,14 @@ static int irq_wait_for_interrupt(struct irqaction *action)
468 */ 473 */
469static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc) 474static void irq_finalize_oneshot(unsigned int irq, struct irq_desc *desc)
470{ 475{
476 chip_bus_lock(irq, desc);
471 spin_lock_irq(&desc->lock); 477 spin_lock_irq(&desc->lock);
472 if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) { 478 if (!(desc->status & IRQ_DISABLED) && (desc->status & IRQ_MASKED)) {
473 desc->status &= ~IRQ_MASKED; 479 desc->status &= ~IRQ_MASKED;
474 desc->chip->unmask(irq); 480 desc->chip->unmask(irq);
475 } 481 }
476 spin_unlock_irq(&desc->lock); 482 spin_unlock_irq(&desc->lock);
483 chip_bus_sync_unlock(irq, desc);
477} 484}
478 485
479#ifdef CONFIG_SMP 486#ifdef CONFIG_SMP
@@ -904,7 +911,14 @@ EXPORT_SYMBOL_GPL(remove_irq);
904 */ 911 */
905void free_irq(unsigned int irq, void *dev_id) 912void 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 kfree(__free_irq(irq, dev_id)); 920 kfree(__free_irq(irq, dev_id));
921 chip_bus_sync_unlock(irq, desc);
908} 922}
909EXPORT_SYMBOL(free_irq); 923EXPORT_SYMBOL(free_irq);
910 924
@@ -1011,7 +1025,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1011 action->name = devname; 1025 action->name = devname;
1012 action->dev_id = dev_id; 1026 action->dev_id = dev_id;
1013 1027
1028 chip_bus_lock(irq, desc);
1014 retval = __setup_irq(irq, desc, action); 1029 retval = __setup_irq(irq, desc, action);
1030 chip_bus_sync_unlock(irq, desc);
1031
1015 if (retval) 1032 if (retval)
1016 kfree(action); 1033 kfree(action);
1017 1034