IRQCHIP: bcm7120-l2: Add support for BCM3380-style controllers
These controllers support multiple enable/status pairs (64+ IRQs), can put the enable/status words at different offsets, and do not support multiple parent IRQs. Signed-off-by: Kevin Cernekee <cernekee@gmail.com> Cc: f.fainelli@gmail.com Cc: jaedon.shin@gmail.com Cc: abrestic@chromium.org Cc: tglx@linutronix.de Cc: jason@lakedaemon.net Cc: jogo@openwrt.org Cc: arnd@arndb.de Cc: computersforpeace@gmail.com Cc: linux-mips@linux-mips.org Cc: devicetree@vger.kernel.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/8843/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
		
							parent
							
								
									ca40f1b23d
								
							
						
					
					
						commit
						7b7230e70e
					
				| @ -0,0 +1,41 @@ | ||||
| Broadcom BCM3380-style Level 1 / Level 2 interrupt controller | ||||
| 
 | ||||
| This interrupt controller shows up in various forms on many BCM338x/BCM63xx | ||||
| chipsets.  It has the following properties: | ||||
| 
 | ||||
| - outputs a single interrupt signal to its interrupt controller parent | ||||
| 
 | ||||
| - contains one or more enable/status word pairs, which often appear at | ||||
|   different offsets in different blocks | ||||
| 
 | ||||
| - no atomic set/clear operations | ||||
| 
 | ||||
| Required properties: | ||||
| 
 | ||||
| - compatible: should be "brcm,bcm3380-l2-intc" | ||||
| - reg: specifies one or more enable/status pairs, in the following format: | ||||
|   <enable_reg 0x4 status_reg 0x4>... | ||||
| - interrupt-controller: identifies the node as an interrupt controller | ||||
| - #interrupt-cells: specifies the number of cells needed to encode an interrupt | ||||
|   source, should be 1. | ||||
| - interrupt-parent: specifies the phandle to the parent interrupt controller | ||||
|   this one is cascaded from | ||||
| - interrupts: specifies the interrupt line in the interrupt-parent controller | ||||
|   node, valid values depend on the type of parent interrupt controller | ||||
| 
 | ||||
| Optional properties: | ||||
| 
 | ||||
| - brcm,irq-can-wake: if present, this means the L2 controller can be used as a | ||||
|   wakeup source for system suspend/resume. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| irq0_intc: interrupt-controller@10000020 { | ||||
| 	compatible = "brcm,bcm3380-l2-intc"; | ||||
| 	reg = <0x10000024 0x4 0x1000002c 0x4>, | ||||
| 	      <0x10000020 0x4 0x10000028 0x4>; | ||||
| 	interrupt-controller; | ||||
| 	#interrupt-cells = <1>; | ||||
| 	interrupt-parent = <&cpu_intc>; | ||||
| 	interrupts = <2>; | ||||
| }; | ||||
| @ -14,6 +14,7 @@ | ||||
| #include <linux/slab.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/kconfig.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_irq.h> | ||||
| @ -120,10 +121,15 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, | ||||
| 	/* For multiple parent IRQs with multiple words, this looks like:
 | ||||
| 	 * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...> | ||||
| 	 */ | ||||
| 	for (idx = 0; idx < data->n_words; idx++) | ||||
| 		data->irq_map_mask[idx] |= | ||||
| 			be32_to_cpup(data->map_mask_prop + | ||||
| 				     irq * data->n_words + idx); | ||||
| 	for (idx = 0; idx < data->n_words; idx++) { | ||||
| 		if (data->map_mask_prop) { | ||||
| 			data->irq_map_mask[idx] |= | ||||
| 				be32_to_cpup(data->map_mask_prop + | ||||
| 					     irq * data->n_words + idx); | ||||
| 		} else { | ||||
| 			data->irq_map_mask[idx] = 0xffffffff; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	irq_set_handler_data(parent_irq, data); | ||||
| 	irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); | ||||
| @ -165,6 +171,37 @@ static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn, | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn, | ||||
| 					     struct bcm7120_l2_intc_data *data) | ||||
| { | ||||
| 	unsigned int gc_idx; | ||||
| 
 | ||||
| 	for (gc_idx = 0; gc_idx < MAX_WORDS; gc_idx++) { | ||||
| 		unsigned int map_idx = gc_idx * 2; | ||||
| 		void __iomem *en = of_iomap(dn, map_idx + 0); | ||||
| 		void __iomem *stat = of_iomap(dn, map_idx + 1); | ||||
| 		void __iomem *base = min(en, stat); | ||||
| 
 | ||||
| 		data->map_base[map_idx + 0] = en; | ||||
| 		data->map_base[map_idx + 1] = stat; | ||||
| 
 | ||||
| 		if (!base) | ||||
| 			break; | ||||
| 
 | ||||
| 		data->pair_base[gc_idx] = base; | ||||
| 		data->en_offset[gc_idx] = en - base; | ||||
| 		data->stat_offset[gc_idx] = stat - base; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!gc_idx) { | ||||
| 		pr_err("unable to map registers\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	data->n_words = gc_idx; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int __init bcm7120_l2_intc_probe(struct device_node *dn, | ||||
| 				 struct device_node *parent, | ||||
| 				 int (*iomap_regs_fn)(struct device_node *, | ||||
| @ -279,5 +316,15 @@ int __init bcm7120_l2_intc_probe_7120(struct device_node *dn, | ||||
| 				     "BCM7120 L2"); | ||||
| } | ||||
| 
 | ||||
| int __init bcm7120_l2_intc_probe_3380(struct device_node *dn, | ||||
| 				      struct device_node *parent) | ||||
| { | ||||
| 	return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380, | ||||
| 				     "BCM3380 L2"); | ||||
| } | ||||
| 
 | ||||
| IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc", | ||||
| 		bcm7120_l2_intc_probe_7120); | ||||
| 
 | ||||
| IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc", | ||||
| 		bcm7120_l2_intc_probe_3380); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user