Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1/*
  2 *  Copyright (C) 2009 Imagination Technologies
  3 *
  4 * This file is subject to the terms and conditions of the GNU General Public
  5 * License.  See the file COPYING in the main directory of this archive
  6 * for more details.
  7 *
  8 * The Meta KICK interrupt mechanism is generally a useful feature, so
  9 * we provide an interface for registering multiple interrupt
 10 * handlers. All the registered interrupt handlers are "chained". When
 11 * a KICK interrupt is received the first function in the list is
 12 * called. If that interrupt handler cannot handle the KICK the next
 13 * one is called, then the next until someone handles it (or we run
 14 * out of functions). As soon as one function handles the interrupt no
 15 * other handlers are called.
 16 *
 17 * The only downside of chaining interrupt handlers is that each
 18 * handler must be able to detect whether the KICK was intended for it
 19 * or not.  For example, when the IPI handler runs and it sees that
 20 * there are no IPI messages it must not signal that the KICK was
 21 * handled, thereby giving the other handlers a chance to run.
 22 *
 23 * The reason that we provide our own interface for calling KICK
 24 * handlers instead of using the generic kernel infrastructure is that
 25 * the KICK handlers require access to a CPU's pTBI structure. So we
 26 * pass it as an argument.
 27 */
 28#include <linux/export.h>
 29#include <linux/hardirq.h>
 30#include <linux/irq.h>
 31#include <linux/kernel.h>
 32#include <linux/mm.h>
 33#include <linux/types.h>
 34
 35#include <asm/traps.h>
 36
 37/*
 38 * All accesses/manipulations of kick_handlers_list should be
 39 * performed while holding kick_handlers_lock.
 40 */
 41static DEFINE_SPINLOCK(kick_handlers_lock);
 42static LIST_HEAD(kick_handlers_list);
 43
 44void kick_register_func(struct kick_irq_handler *kh)
 45{
 46	unsigned long flags;
 47
 48	spin_lock_irqsave(&kick_handlers_lock, flags);
 49
 50	list_add_tail(&kh->list, &kick_handlers_list);
 51
 52	spin_unlock_irqrestore(&kick_handlers_lock, flags);
 53}
 54EXPORT_SYMBOL(kick_register_func);
 55
 56void kick_unregister_func(struct kick_irq_handler *kh)
 57{
 58	unsigned long flags;
 59
 60	spin_lock_irqsave(&kick_handlers_lock, flags);
 61
 62	list_del(&kh->list);
 63
 64	spin_unlock_irqrestore(&kick_handlers_lock, flags);
 65}
 66EXPORT_SYMBOL(kick_unregister_func);
 67
 68TBIRES
 69kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI)
 70{
 71	struct pt_regs *old_regs;
 72	struct kick_irq_handler *kh;
 73	struct list_head *lh;
 74	int handled = 0;
 75	TBIRES ret;
 76
 77	head_end(State, ~INTS_OFF_MASK);
 78
 79	/* If we interrupted user code handle any critical sections. */
 80	if (State.Sig.SaveMask & TBICTX_PRIV_BIT)
 81		restart_critical_section(State);
 82
 83	trace_hardirqs_off();
 84
 85	old_regs = set_irq_regs((struct pt_regs *)State.Sig.pCtx);
 86	irq_enter();
 87
 88	/*
 89	 * There is no need to disable interrupts here because we
 90	 * can't nest KICK interrupts in a KICK interrupt handler.
 91	 */
 92	spin_lock(&kick_handlers_lock);
 93
 94	list_for_each(lh, &kick_handlers_list) {
 95		kh = list_entry(lh, struct kick_irq_handler, list);
 96
 97		ret = kh->func(State, SigNum, Triggers, Inst, pTBI, &handled);
 98		if (handled)
 99			break;
100	}
101
102	spin_unlock(&kick_handlers_lock);
103
104	WARN_ON(!handled);
105
106	irq_exit();
107	set_irq_regs(old_regs);
108
109	return tail_end(ret);
110}