diff mbox

[RFC] powerpc/irq: Add generic API for setting up cascaded IRQs

Message ID 20090912054410.21847.63718.stgit@localhost.localdomain (mailing list archive)
State Not Applicable
Headers show

Commit Message

Grant Likely Sept. 12, 2009, 5:46 a.m. UTC
From: Grant Likely <grant.likely@secretlab.ca>

prototype implementation.  This probably doesn't work at all right now.

Ben, I'm posting this now to get your thoughts before I go too far down
this path.

Cheers,
g.

---

 arch/powerpc/include/asm/irq.h            |    3 ++
 arch/powerpc/kernel/irq.c                 |   20 +++++++++++++++
 arch/powerpc/platforms/52xx/mpc52xx_pic.c |   39 +++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 0 deletions(-)

Comments

Benjamin Herrenschmidt Sept. 12, 2009, 6:28 a.m. UTC | #1
On Fri, 2009-09-11 at 23:46 -0600, Grant Likely wrote:
> From: Grant Likely <grant.likely@secretlab.ca>
> 
> prototype implementation.  This probably doesn't work at all right now.
> 
> Ben, I'm posting this now to get your thoughts before I go too far down
> this path.

Looks ok. I was initially thinking about putting get_irq() in irq_host,
but as we discussed on IRC, a host is not necessarily a PIC, and it's
nice for the parent to have a way to setup/init the cascade in case
it needs to do some HW tweaking there as well.

However, why cascade_setup() and not setup_cascade() which sounds
somewhat more natural ? :-)

Cheers,
Ben.

> Cheers,
> g.
> 
> ---
> 
>  arch/powerpc/include/asm/irq.h            |    3 ++
>  arch/powerpc/kernel/irq.c                 |   20 +++++++++++++++
>  arch/powerpc/platforms/52xx/mpc52xx_pic.c |   39 +++++++++++++++++++++++++++++
>  3 files changed, 62 insertions(+), 0 deletions(-)
> 
> 
> diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
> index 0a51376..014e1e8 100644
> --- a/arch/powerpc/include/asm/irq.h
> +++ b/arch/powerpc/include/asm/irq.h
> @@ -90,6 +90,9 @@ struct irq_host_ops {
>  	/* Update of such a mapping  */
>  	void (*remap)(struct irq_host *h, unsigned int virq, irq_hw_number_t hw);
>  
> +	/* Setup hook for cascade irqs */
> +	int (*cascade_setup)(int virq, int (*get_irq)(void *data), void *data);
> +
>  	/* Translate device-tree interrupt specifier from raw format coming
>  	 * from the firmware to a irq_hw_number_t (interrupt line number) and
>  	 * type (sense) that can be passed to set_irq_type(). In the absence
> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> index f7f376e..5a9cb46 100644
> --- a/arch/powerpc/kernel/irq.c
> +++ b/arch/powerpc/kernel/irq.c
> @@ -750,6 +750,26 @@ unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
>  }
>  EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
>  
> +unsigned int irq_of_cascade_setup(int virq, int (*get_irq)(void *data),
> +				  void *data)
> +{
> +	struct irq_host *host = irq_map[virq].host;
> +
> +	if (!host) {
> +		pr_err("error: no irq host found virq %i !\n", virq);
> +		return -ENODEV;
> +	}
> +
> +	/* If host has no cascade setup function, then this method for
> +	 * setting up a cascade is not available */
> +	if (!host->ops->cascade_setup) {
> +		pr_err("error: no cascade_setup support on virq %i\n", virq);
> +		return -EINVAL;
> +	}
> +
> +	return host->ops->cascade_setup(virq, get_irq, data);
> +}
> +
>  void irq_dispose_mapping(unsigned int virq)
>  {
>  	struct irq_host *host;
> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
> index 480f806..a91c69b 100644
> --- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c
> +++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
> @@ -437,9 +437,48 @@ static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
>  	return 0;
>  }
>  
> +struct mpc52xx_cascade_data {
> +	int (*get_irq)(void *data);
> +	void *data;
> +};
> +
> +void mpc52xx_handle_level_cascade(unsigned int irq, struct irq_desc *desc)
> +{
> +	struct mpc52xx_cascade_data *cascade_data = get_irq_desc_data(desc);
> +	int cascade_virq;
> +
> +	cascade_virq = cascade_data->get_irq(cascade_data->data);
> +	if (cascade_virq)
> +		generic_handle_irq(cascade_virq);
> +}
> +
> +void mpc52xx_handle_edge_cascade(unsigned int virq, struct irq_desc *desc)
> +{
> +	mpc52xx_handle_level_cascade(virq, desc);
> +	/** TODO: clear edge IRQ here **/
> +}
> +
> +int mpc52xx_cascade_setup(int virq, int (*get_irq)(void *data), void *data)
> +{
> +	struct mpc52xx_cascade_data *cascade_data;
> +
> +	cascade_data = kzalloc(sizeof(struct mpc52xx_cascade_data), GFP_KERNEL);
> +	if (!cascade_data)
> +		return -ENOMEM;
> +
> +	/* TODO: make this handle edge cascades too */
> +	cascade_data->get_irq = get_irq;
> +	cascade_data->data = data;
> +	set_irq_data(virq, cascade_data);
> +	set_irq_chained_handler(virq, mpc52xx_handle_level_cascade);
> +
> +	return 0;
> +}
> +
>  static struct irq_host_ops mpc52xx_irqhost_ops = {
>  	.xlate = mpc52xx_irqhost_xlate,
>  	.map = mpc52xx_irqhost_map,
> +	.cascade_setup = mpc52xx_cascade_setup,
>  };
>  
>  /**
Grant Likely Sept. 12, 2009, 2:05 p.m. UTC | #2
On Sat, Sep 12, 2009 at 12:28 AM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On Fri, 2009-09-11 at 23:46 -0600, Grant Likely wrote:
>> From: Grant Likely <grant.likely@secretlab.ca>
>>
>> prototype implementation.  This probably doesn't work at all right now.
>>
>> Ben, I'm posting this now to get your thoughts before I go too far down
>> this path.
>
> Looks ok. I was initially thinking about putting get_irq() in irq_host,
> but as we discussed on IRC, a host is not necessarily a PIC, and it's
> nice for the parent to have a way to setup/init the cascade in case
> it needs to do some HW tweaking there as well.

Cool.  Thanks for the review.  I'll continue on with this approach and
hopefully get something working this weekend.

> However, why cascade_setup() and not setup_cascade() which sounds
> somewhat more natural ? :-)

I'm a reverse polish kind of guy.  I preferring 'subject'_'action'
over 'action'_'subject' just because it groups like subjects together.
 But it doesn't matter much, especially in this case where 'subject'
is in a group of exactly 1.  :-)

I'll do whichever you prefer.

g.
Benjamin Herrenschmidt Sept. 15, 2009, 10:02 a.m. UTC | #3
> I'm a reverse polish kind of guy.  I preferring 'subject'_'action'
> over 'action'_'subject' just because it groups like subjects together.
>  But it doesn't matter much, especially in this case where 'subject'
> is in a group of exactly 1.  :-)
> 
> I'll do whichever you prefer.

I just caught myself calling something relocs_check instead of
check_relocs so I suppose it's fair game :-)

Whatever you want.

Cheers,
Ben.
Michael Ellerman Sept. 15, 2009, 11:04 a.m. UTC | #4
On Tue, 2009-09-15 at 20:02 +1000, Benjamin Herrenschmidt wrote:
> > I'm a reverse polish kind of guy.  I preferring 'subject'_'action'
> > over 'action'_'subject' just because it groups like subjects together.
> >  But it doesn't matter much, especially in this case where 'subject'
> > is in a group of exactly 1.  :-)
> > 
> > I'll do whichever you prefer.
> 
> I just caught myself calling something relocs_check instead of
> check_relocs so I suppose it's fair game :-)

Yeah but that sounds stupid :)

> Whatever you want.

I want a pony. And setup_cascade() is definitely better IMHO.

cheers
diff mbox

Patch

diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index 0a51376..014e1e8 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -90,6 +90,9 @@  struct irq_host_ops {
 	/* Update of such a mapping  */
 	void (*remap)(struct irq_host *h, unsigned int virq, irq_hw_number_t hw);
 
+	/* Setup hook for cascade irqs */
+	int (*cascade_setup)(int virq, int (*get_irq)(void *data), void *data);
+
 	/* Translate device-tree interrupt specifier from raw format coming
 	 * from the firmware to a irq_hw_number_t (interrupt line number) and
 	 * type (sense) that can be passed to set_irq_type(). In the absence
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index f7f376e..5a9cb46 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -750,6 +750,26 @@  unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
 }
 EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
 
+unsigned int irq_of_cascade_setup(int virq, int (*get_irq)(void *data),
+				  void *data)
+{
+	struct irq_host *host = irq_map[virq].host;
+
+	if (!host) {
+		pr_err("error: no irq host found virq %i !\n", virq);
+		return -ENODEV;
+	}
+
+	/* If host has no cascade setup function, then this method for
+	 * setting up a cascade is not available */
+	if (!host->ops->cascade_setup) {
+		pr_err("error: no cascade_setup support on virq %i\n", virq);
+		return -EINVAL;
+	}
+
+	return host->ops->cascade_setup(virq, get_irq, data);
+}
+
 void irq_dispose_mapping(unsigned int virq)
 {
 	struct irq_host *host;
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
index 480f806..a91c69b 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c
@@ -437,9 +437,48 @@  static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq,
 	return 0;
 }
 
+struct mpc52xx_cascade_data {
+	int (*get_irq)(void *data);
+	void *data;
+};
+
+void mpc52xx_handle_level_cascade(unsigned int irq, struct irq_desc *desc)
+{
+	struct mpc52xx_cascade_data *cascade_data = get_irq_desc_data(desc);
+	int cascade_virq;
+
+	cascade_virq = cascade_data->get_irq(cascade_data->data);
+	if (cascade_virq)
+		generic_handle_irq(cascade_virq);
+}
+
+void mpc52xx_handle_edge_cascade(unsigned int virq, struct irq_desc *desc)
+{
+	mpc52xx_handle_level_cascade(virq, desc);
+	/** TODO: clear edge IRQ here **/
+}
+
+int mpc52xx_cascade_setup(int virq, int (*get_irq)(void *data), void *data)
+{
+	struct mpc52xx_cascade_data *cascade_data;
+
+	cascade_data = kzalloc(sizeof(struct mpc52xx_cascade_data), GFP_KERNEL);
+	if (!cascade_data)
+		return -ENOMEM;
+
+	/* TODO: make this handle edge cascades too */
+	cascade_data->get_irq = get_irq;
+	cascade_data->data = data;
+	set_irq_data(virq, cascade_data);
+	set_irq_chained_handler(virq, mpc52xx_handle_level_cascade);
+
+	return 0;
+}
+
 static struct irq_host_ops mpc52xx_irqhost_ops = {
 	.xlate = mpc52xx_irqhost_xlate,
 	.map = mpc52xx_irqhost_map,
+	.cascade_setup = mpc52xx_cascade_setup,
 };
 
 /**