diff options
Diffstat (limited to 'drivers/irqchip/irq-vic.c')
| -rw-r--r-- | drivers/irqchip/irq-vic.c | 73 | 
1 files changed, 65 insertions, 8 deletions
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 2bbb00404cf..7d35287f9e9 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -24,6 +24,7 @@  #include <linux/list.h>  #include <linux/io.h>  #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h>  #include <linux/irqdomain.h>  #include <linux/of.h>  #include <linux/of_address.h> @@ -57,6 +58,7 @@  /**   * struct vic_device - VIC PM device + * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0.   * @irq: The IRQ number for the base of the VIC.   * @base: The register base for the VIC.   * @valid_sources: A bitmask of valid interrupts @@ -224,11 +226,27 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)  	return handled;  } +static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc) +{ +	u32 stat, hwirq; +	struct irq_chip *host_chip = irq_desc_get_chip(desc); +	struct vic_device *vic = irq_desc_get_handler_data(desc); + +	chained_irq_enter(host_chip, desc); + +	while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { +		hwirq = ffs(stat) - 1; +		generic_handle_irq(irq_find_mapping(vic->domain, hwirq)); +	} + +	chained_irq_exit(host_chip, desc); +} +  /*   * Keep iterating over all registered VIC's until there are no pending   * interrupts.   */ -static asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)  {  	int i, handled; @@ -246,6 +264,7 @@ static struct irq_domain_ops vic_irqdomain_ops = {  /**   * vic_register() - Register a VIC.   * @base: The base address of the VIC. + * @parent_irq: The parent IRQ if cascaded, else 0.   * @irq: The base IRQ for the VIC.   * @valid_sources: bitmask of valid interrupts   * @resume_sources: bitmask of interrupts allowed for resume sources. @@ -257,7 +276,8 @@ static struct irq_domain_ops vic_irqdomain_ops = {   *   * This also configures the IRQ domain for the VIC.   */ -static void __init vic_register(void __iomem *base, unsigned int irq, +static void __init vic_register(void __iomem *base, unsigned int parent_irq, +				unsigned int irq,  				u32 valid_sources, u32 resume_sources,  				struct device_node *node)  { @@ -273,15 +293,25 @@ static void __init vic_register(void __iomem *base, unsigned int irq,  	v->base = base;  	v->valid_sources = valid_sources;  	v->resume_sources = resume_sources; -	v->irq = irq;  	set_handle_irq(vic_handle_irq);  	vic_id++; + +	if (parent_irq) { +		irq_set_handler_data(parent_irq, v); +		irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded); +	} +  	v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,  					  &vic_irqdomain_ops, v);  	/* create an IRQ mapping for each valid IRQ */  	for (i = 0; i < fls(valid_sources); i++)  		if (valid_sources & (1 << i))  			irq_create_mapping(v->domain, i); +	/* If no base IRQ was passed, figure out our allocated base */ +	if (irq) +		v->irq = irq; +	else +		v->irq = irq_find_mapping(v->domain, 0);  }  static void vic_ack_irq(struct irq_data *d) @@ -409,10 +439,10 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start,  		writel(32, base + VIC_PL190_DEF_VECT_ADDR);  	} -	vic_register(base, irq_start, vic_sources, 0, node); +	vic_register(base, 0, irq_start, vic_sources, 0, node);  } -void __init __vic_init(void __iomem *base, int irq_start, +void __init __vic_init(void __iomem *base, int parent_irq, int irq_start,  			      u32 vic_sources, u32 resume_sources,  			      struct device_node *node)  { @@ -449,7 +479,7 @@ void __init __vic_init(void __iomem *base, int irq_start,  	vic_init2(base); -	vic_register(base, irq_start, vic_sources, resume_sources, node); +	vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node);  }  /** @@ -462,13 +492,37 @@ void __init __vic_init(void __iomem *base, int irq_start,  void __init vic_init(void __iomem *base, unsigned int irq_start,  		     u32 vic_sources, u32 resume_sources)  { -	__vic_init(base, irq_start, vic_sources, resume_sources, NULL); +	__vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL); +} + +/** + * vic_init_cascaded() - initialise a cascaded vectored interrupt controller + * @base: iomem base address + * @parent_irq: the parent IRQ we're cascaded off + * @irq_start: starting interrupt number, must be muliple of 32 + * @vic_sources: bitmask of interrupt sources to allow + * @resume_sources: bitmask of interrupt sources to allow for resume + * + * This returns the base for the new interrupts or negative on error. + */ +int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq, +			      u32 vic_sources, u32 resume_sources) +{ +	struct vic_device *v; + +	v = &vic_devices[vic_id]; +	__vic_init(base, parent_irq, 0, vic_sources, resume_sources, NULL); +	/* Return out acquired base */ +	return v->irq;  } +EXPORT_SYMBOL_GPL(vic_init_cascaded);  #ifdef CONFIG_OF  int __init vic_of_init(struct device_node *node, struct device_node *parent)  {  	void __iomem *regs; +	u32 interrupt_mask = ~0; +	u32 wakeup_mask = ~0;  	if (WARN(parent, "non-root VICs are not supported"))  		return -EINVAL; @@ -477,10 +531,13 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent)  	if (WARN_ON(!regs))  		return -EIO; +	of_property_read_u32(node, "valid-mask", &interrupt_mask); +	of_property_read_u32(node, "valid-wakeup-mask", &wakeup_mask); +  	/*  	 * Passing 0 as first IRQ makes the simple domain allocate descriptors  	 */ -	__vic_init(regs, 0, ~0, ~0, node); +	__vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);  	return 0;  }  | 
