diff mbox

[RFC,v3,1/9] arm: fiq: arbitrary mappings from IRQ to FIQ virqs

Message ID 1401961994-18033-2-git-send-email-daniel.thompson@linaro.org
State New
Headers show

Commit Message

Daniel Thompson June 5, 2014, 9:53 a.m. UTC
Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
virq into a FIQ virq. This is too inflexible for multi-platform kernels
and makes runtime error checking impossible.

We solve this by introducing a flexible mapping that allows interrupt
controllers that support FIQ to register those mappings. This, in turn,
makes it much possible for drivers in DT kernels to gain access to
FIQ virqs.

Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Fabio Estevam <festevam@gmail.com>
Cc: Nicolas Pitre <nico@linaro.org>
---
 arch/arm/include/asm/fiq.h |  2 ++
 arch/arm/kernel/fiq.c      | 57 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 53 insertions(+), 6 deletions(-)

Comments

Russell King - ARM Linux June 5, 2014, 11:51 a.m. UTC | #1
On Thu, Jun 05, 2014 at 10:53:06AM +0100, Daniel Thompson wrote:
>  static int fiq_def_op(void *ref, int relinquish)
>  {
> -	if (!relinquish)
> +	if (!relinquish) {
> +		unsigned offset = FIQ_OFFSET;
> +		no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
>  		set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
> +	}
...
>  void __init init_FIQ(int start)
>  {
> -	unsigned offset = FIQ_OFFSET;
> -	no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
>  	fiq_start = start;
>  }

This is wrong - when the default handler is "reinstalled", this change has
the effect that we read the first instruction of the existing handler, and
then write that same instruction back, rather than replacing the first
instruction with the value that was there at boot.
Daniel Thompson June 5, 2014, 1:08 p.m. UTC | #2
On 05/06/14 12:51, Russell King - ARM Linux wrote:
> On Thu, Jun 05, 2014 at 10:53:06AM +0100, Daniel Thompson wrote:
>>  static int fiq_def_op(void *ref, int relinquish)
>>  {
>> -	if (!relinquish)
>> +	if (!relinquish) {
>> +		unsigned offset = FIQ_OFFSET;
>> +		no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
>>  		set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
>> +	}
> ...
>>  void __init init_FIQ(int start)
>>  {
>> -	unsigned offset = FIQ_OFFSET;
>> -	no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
>>  	fiq_start = start;
>>  }
> 
> This is wrong - when the default handler is "reinstalled", this change has
> the effect that we read the first instruction of the existing handler, and
> then write that same instruction back, rather than replacing the first
> instruction with the value that was there at boot.

Thanks. I'll fix this.
Linus Walleij June 12, 2014, 8:37 a.m. UTC | #3
On Thu, Jun 5, 2014 at 11:53 AM, Daniel Thompson
<daniel.thompson@linaro.org> wrote:

> Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
> virq into a FIQ virq. This is too inflexible for multi-platform kernels
> and makes runtime error checking impossible.
>
> We solve this by introducing a flexible mapping that allows interrupt
> controllers that support FIQ to register those mappings. This, in turn,
> makes it much possible for drivers in DT kernels to gain access to
> FIQ virqs.

I always had a big problem with the term "virq" which I take to mean
"virtual IRQ".

The distinction is:

- Hardware IRQ offsets/numbers - a number encoded in HW
  specs which we usually call hwirqs

- Linux IRQ numbers - just some number

Sometimes, just sometimes, the Linux IRQ number matches the
HW numbers, and then I guess the IRQ is regarded "non-virtual".
The only real effect being that the numbers shown in
/proc/interrupts are the same in both columns... the leftmost
column with the Linux IRQ number and the one right next to
the IRQ controller name which is the hwirq local offset.

But all Linux IRQ numbers are virtual by definition. It's just some
number that the kernel use as a cookie to look up the irq_desc.
And one day we may get rid of IRQ numbers altogether.

I would prefer just to s/virq/irq/g on this patch or if that is
disturbing something more to the point like s/virq/linux_irq/g.

Maybe this is a pointless battle, but still...

Yours,
Linus Walleij
Daniel Thompson June 12, 2014, 9:54 a.m. UTC | #4
On 12/06/14 09:37, Linus Walleij wrote:
> On Thu, Jun 5, 2014 at 11:53 AM, Daniel Thompson
> <daniel.thompson@linaro.org> wrote:
> 
>> Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
>> virq into a FIQ virq. This is too inflexible for multi-platform kernels
>> and makes runtime error checking impossible.
>>
>> We solve this by introducing a flexible mapping that allows interrupt
>> controllers that support FIQ to register those mappings. This, in turn,
>> makes it much possible for drivers in DT kernels to gain access to
>> FIQ virqs.
> 
> I always had a big problem with the term "virq" which I take to mean
> "virtual IRQ".

So do I...

That said, I think of it as the virtual as in virtual memory rather the
virtual contained within virtualization.


> The distinction is:
> 
> - Hardware IRQ offsets/numbers - a number encoded in HW
>   specs which we usually call hwirqs
> 
> - Linux IRQ numbers - just some number
> 
> Sometimes, just sometimes, the Linux IRQ number matches the
> HW numbers, and then I guess the IRQ is regarded "non-virtual".
> The only real effect being that the numbers shown in
> /proc/interrupts are the same in both columns... the leftmost
> column with the Linux IRQ number and the one right next to
> the IRQ controller name which is the hwirq local offset.
> 
> But all Linux IRQ numbers are virtual by definition. It's just some
> number that the kernel use as a cookie to look up the irq_desc.
> And one day we may get rid of IRQ numbers altogether.
> 
> I would prefer just to s/virq/irq/g on this patch or if that is
> disturbing something more to the point like s/virq/linux_irq/g.
> 
> Maybe this is a pointless battle, but still...

I chose the term not because I have a personal attachement but because
that is how they are spelt in the C symbols within irqdomain.[ch] which
my patch relies upon heavily. The english documentation and comments is
(very nearly) strict about using "Linux IRQ" so I'll happily change all
the English text.

However...

I don't fancy s/virq/irq/ since this risks confusion between Linux irq
and ARM IRQ and I don't fancy s/virq/linux_irq/ because in the kernel
today virq is ~750x more frequenctly used than 'linux_irq' (git grep and
wc suggests its 1507 versus 2).

How strongly do you feel about this?
Rob Herring June 13, 2014, 2:29 p.m. UTC | #5
On Thu, Jun 5, 2014 at 4:53 AM, Daniel Thompson
<daniel.thompson@linaro.org> wrote:
> Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
> virq into a FIQ virq. This is too inflexible for multi-platform kernels
> and makes runtime error checking impossible.
>
> We solve this by introducing a flexible mapping that allows interrupt
> controllers that support FIQ to register those mappings. This, in turn,
> makes it much possible for drivers in DT kernels to gain access to
> FIQ virqs.

I don't get why you need a separate linux irq numbers for FIQ. Isn't
enabling FIQ simply a property of an irq like edge vs. level trigger?
Also, given the constraints on FIQ, we can't really have more that 1
IRQ assigned to FIQ.

Rob
Daniel Thompson June 18, 2014, 11:24 a.m. UTC | #6
On 13/06/14 15:29, Rob Herring wrote:
> On Thu, Jun 5, 2014 at 4:53 AM, Daniel Thompson
> <daniel.thompson@linaro.org> wrote:
>> Currently enable_fiq/disable_fiq use a simple offset to convert an IRQ
>> virq into a FIQ virq. This is too inflexible for multi-platform kernels
>> and makes runtime error checking impossible.
>>
>> We solve this by introducing a flexible mapping that allows interrupt
>> controllers that support FIQ to register those mappings. This, in turn,
>> makes it much possible for drivers in DT kernels to gain access to
>> FIQ virqs.
> 
> I don't get why you need a separate linux irq numbers for FIQ. Isn't
> enabling FIQ simply a property of an irq like edge vs. level trigger?
> Also, given the constraints on FIQ, we can't really have more that 1
> IRQ assigned to FIQ.

No particular reason. I mostly went that way because it mimics the
effect of fiq_start on enable_fiq/disable_fiq whilst supporting
multi-platform.

I'm tempted to keep the radix tree in the FIQ infrastructure but rather
than messing about with shadow virqs use it to lookup a fiq_chip
structure. I think this would keep a clean separation between the ARM
centric (and slightly weird) FIQ from the generic irq code.


Daniel.
diff mbox

Patch

diff --git a/arch/arm/include/asm/fiq.h b/arch/arm/include/asm/fiq.h
index d493d0b..75d98c6 100644
--- a/arch/arm/include/asm/fiq.h
+++ b/arch/arm/include/asm/fiq.h
@@ -36,8 +36,10 @@  struct fiq_handler {
 extern int claim_fiq(struct fiq_handler *f);
 extern void release_fiq(struct fiq_handler *f);
 extern void set_fiq_handler(void *start, unsigned int length);
+extern struct irq_data *lookup_fiq_irq_data(int fiq);
 extern void enable_fiq(int fiq);
 extern void disable_fiq(int fiq);
+extern void fiq_add_mapping(int irq, int fiq_virq, unsigned int length);
 
 /* helpers defined in fiqasm.S: */
 extern void __set_fiq_regs(unsigned long const *regs);
diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c
index 918875d..177939c 100644
--- a/arch/arm/kernel/fiq.c
+++ b/arch/arm/kernel/fiq.c
@@ -40,6 +40,8 @@ 
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/radix-tree.h>
 
 #include <asm/cacheflush.h>
 #include <asm/cp15.h>
@@ -53,6 +55,9 @@ 
 	})
 
 static unsigned long no_fiq_insn;
+static int fiq_start = -1;
+static RADIX_TREE(fiq_virq_tree, GFP_KERNEL);
+static DEFINE_MUTEX(fiq_virq_mutex);
 
 /* Default reacquire function
  * - we always relinquish FIQ control
@@ -60,8 +65,11 @@  static unsigned long no_fiq_insn;
  */
 static int fiq_def_op(void *ref, int relinquish)
 {
-	if (!relinquish)
+	if (!relinquish) {
+		unsigned offset = FIQ_OFFSET;
+		no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
 		set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
+	}
 
 	return 0;
 }
@@ -127,16 +135,33 @@  void release_fiq(struct fiq_handler *f)
 	while (current_fiq->fiq_op(current_fiq->dev_id, 0));
 }
 
-static int fiq_start;
+struct irq_data *lookup_fiq_irq_data(int fiq)
+{
+	struct irq_data *d;
+
+	if (fiq_start != -1)
+		return irq_get_irq_data(fiq + fiq_start);
+
+	rcu_read_lock();
+	d = radix_tree_lookup(&fiq_virq_tree, fiq);
+	rcu_read_unlock();
+	return d;
+}
 
 void enable_fiq(int fiq)
 {
-	enable_irq(fiq + fiq_start);
+	struct irq_data *d = lookup_fiq_irq_data(fiq);
+	if (WARN_ON(!d))
+		return;
+	enable_irq(d->irq);
 }
 
 void disable_fiq(int fiq)
 {
-	disable_irq(fiq + fiq_start);
+	struct irq_data *d = lookup_fiq_irq_data(fiq);
+	if (WARN_ON(!d))
+		return;
+	disable_irq(d->irq);
 }
 
 EXPORT_SYMBOL(set_fiq_handler);
@@ -144,12 +169,32 @@  EXPORT_SYMBOL(__set_fiq_regs);	/* defined in fiqasm.S */
 EXPORT_SYMBOL(__get_fiq_regs);	/* defined in fiqasm.S */
 EXPORT_SYMBOL(claim_fiq);
 EXPORT_SYMBOL(release_fiq);
+EXPORT_SYMBOL(lookup_fiq_irq_data);
 EXPORT_SYMBOL(enable_fiq);
 EXPORT_SYMBOL(disable_fiq);
 
+/*
+ * Add a mapping between a normal IRQ and a FIQ shadow.
+ */
+void fiq_add_mapping(int irq, int fiq_virq, unsigned int length)
+{
+	int i;
+
+	/* fiq_add_mapping can't be mixed with init_FIQ */
+	BUG_ON(fiq_start != -1);
+
+	mutex_lock(&fiq_virq_mutex);
+	for (i = 0; i < length; i++) {
+		int err = radix_tree_insert(&fiq_virq_tree, irq + i,
+					    irq_get_irq_data(fiq_virq + i));
+		if (err)
+			pr_err("fiq: Cannot map %d to %d\n", irq + i,
+			       fiq_virq + i);
+	}
+	mutex_unlock(&fiq_virq_mutex);
+}
+
 void __init init_FIQ(int start)
 {
-	unsigned offset = FIQ_OFFSET;
-	no_fiq_insn = *(unsigned long *)(0xffff0000 + offset);
 	fiq_start = start;
 }