diff options
Diffstat (limited to 'arch/powerpc/sysdev/mpic.c')
| -rw-r--r-- | arch/powerpc/sysdev/mpic.c | 201 | 
1 files changed, 122 insertions, 79 deletions
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 8c7e8528e7c..4e9ccb1015d 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -154,7 +154,7 @@ static inline unsigned int mpic_processor_id(struct mpic *mpic)  {  	unsigned int cpu = 0; -	if (mpic->flags & MPIC_PRIMARY) +	if (!(mpic->flags & MPIC_SECONDARY))  		cpu = hard_smp_processor_id();  	return cpu; @@ -315,29 +315,25 @@ static void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr,  }  #ifdef CONFIG_PPC_DCR -static void _mpic_map_dcr(struct mpic *mpic, struct device_node *node, -			  struct mpic_reg_bank *rb, +static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb,  			  unsigned int offset, unsigned int size)  { -	const u32 *dbasep; - -	dbasep = of_get_property(node, "dcr-reg", NULL); - -	rb->dhost = dcr_map(node, *dbasep + offset, size); +	phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0); +	rb->dhost = dcr_map(mpic->node, phys_addr + offset, size);  	BUG_ON(!DCR_MAP_OK(rb->dhost));  } -static inline void mpic_map(struct mpic *mpic, struct device_node *node, +static inline void mpic_map(struct mpic *mpic,  			    phys_addr_t phys_addr, struct mpic_reg_bank *rb,  			    unsigned int offset, unsigned int size)  {  	if (mpic->flags & MPIC_USES_DCR) -		_mpic_map_dcr(mpic, node, rb, offset, size); +		_mpic_map_dcr(mpic, rb, offset, size);  	else  		_mpic_map_mmio(mpic, phys_addr, rb, offset, size);  }  #else /* CONFIG_PPC_DCR */ -#define mpic_map(m,n,p,b,o,s)	_mpic_map_mmio(m,p,b,o,s) +#define mpic_map(m,p,b,o,s)	_mpic_map_mmio(m,p,b,o,s)  #endif /* !CONFIG_PPC_DCR */ @@ -901,7 +897,7 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type)  	if (vold != vnew)  		mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); -	return IRQ_SET_MASK_OK_NOCOPY;; +	return IRQ_SET_MASK_OK_NOCOPY;  }  void mpic_set_vector(unsigned int virq, unsigned int vector) @@ -990,7 +986,7 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,  #ifdef CONFIG_SMP  	else if (hw >= mpic->ipi_vecs[0]) { -		WARN_ON(!(mpic->flags & MPIC_PRIMARY)); +		WARN_ON(mpic->flags & MPIC_SECONDARY);  		DBG("mpic: mapping as IPI\n");  		irq_set_chip_data(virq, mpic); @@ -1001,7 +997,7 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,  #endif /* CONFIG_SMP */  	if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) { -		WARN_ON(!(mpic->flags & MPIC_PRIMARY)); +		WARN_ON(mpic->flags & MPIC_SECONDARY);  		DBG("mpic: mapping as timer\n");  		irq_set_chip_data(virq, mpic); @@ -1115,17 +1111,28 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,  	return 0;  } +/* IRQ handler for a secondary MPIC cascaded from another IRQ controller */ +static void mpic_cascade(unsigned int irq, struct irq_desc *desc) +{ +	struct irq_chip *chip = irq_desc_get_chip(desc); +	struct mpic *mpic = irq_desc_get_handler_data(desc); +	unsigned int virq; + +	BUG_ON(!(mpic->flags & MPIC_SECONDARY)); + +	virq = mpic_get_one_irq(mpic); +	if (virq != NO_IRQ) +		generic_handle_irq(virq); + +	chip->irq_eoi(&desc->irq_data); +} +  static struct irq_host_ops mpic_host_ops = {  	.match = mpic_host_match,  	.map = mpic_host_map,  	.xlate = mpic_host_xlate,  }; -static int mpic_reset_prohibited(struct device_node *node) -{ -	return node && of_get_property(node, "pic-no-reset", NULL); -} -  /*   * Exported functions   */ @@ -1137,27 +1144,60 @@ struct mpic * __init mpic_alloc(struct device_node *node,  				unsigned int irq_count,  				const char *name)  { -	struct mpic	*mpic; -	u32		greg_feature; -	const char	*vers; -	int		i; -	int		intvec_top; -	u64		paddr = phys_addr; +	int i, psize, intvec_top; +	struct mpic *mpic; +	u32 greg_feature; +	const char *vers; +	const u32 *psrc; + +	/* Default MPIC search parameters */ +	static const struct of_device_id __initconst mpic_device_id[] = { +		{ .type	      = "open-pic", }, +		{ .compatible = "open-pic", }, +		{}, +	}; + +	/* +	 * If we were not passed a device-tree node, then perform the default +	 * search for standardized a standardized OpenPIC. +	 */ +	if (node) { +		node = of_node_get(node); +	} else { +		node = of_find_matching_node(NULL, mpic_device_id); +		if (!node) +			return NULL; +	} + +	/* Pick the physical address from the device tree if unspecified */ +	if (!phys_addr) { +		/* Check if it is DCR-based */ +		if (of_get_property(node, "dcr-reg", NULL)) { +			flags |= MPIC_USES_DCR; +		} else { +			struct resource r; +			if (of_address_to_resource(node, 0, &r)) +				goto err_of_node_put; +			phys_addr = r.start; +		} +	}  	mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL);  	if (mpic == NULL) -		return NULL; +		goto err_of_node_put;  	mpic->name = name; +	mpic->node = node; +	mpic->paddr = phys_addr;  	mpic->hc_irq = mpic_irq_chip;  	mpic->hc_irq.name = name; -	if (flags & MPIC_PRIMARY) +	if (!(flags & MPIC_SECONDARY))  		mpic->hc_irq.irq_set_affinity = mpic_set_affinity;  #ifdef CONFIG_MPIC_U3_HT_IRQS  	mpic->hc_ht_irq = mpic_irq_ht_chip;  	mpic->hc_ht_irq.name = name; -	if (flags & MPIC_PRIMARY) +	if (!(flags & MPIC_SECONDARY))  		mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity;  #endif /* CONFIG_MPIC_U3_HT_IRQS */ @@ -1194,28 +1234,22 @@ struct mpic * __init mpic_alloc(struct device_node *node,  	mpic->spurious_vec  = intvec_top;  	/* Check for "big-endian" in device-tree */ -	if (node && of_get_property(node, "big-endian", NULL) != NULL) +	if (of_get_property(mpic->node, "big-endian", NULL) != NULL)  		mpic->flags |= MPIC_BIG_ENDIAN; -	if (node && of_device_is_compatible(node, "fsl,mpic")) +	if (of_device_is_compatible(mpic->node, "fsl,mpic"))  		mpic->flags |= MPIC_FSL;  	/* Look for protected sources */ -	if (node) { -		int psize; -		unsigned int bits, mapsize; -		const u32 *psrc = -			of_get_property(node, "protected-sources", &psize); -		if (psrc) { -			psize /= 4; -			bits = intvec_top + 1; -			mapsize = BITS_TO_LONGS(bits) * sizeof(unsigned long); -			mpic->protected = kzalloc(mapsize, GFP_KERNEL); -			BUG_ON(mpic->protected == NULL); -			for (i = 0; i < psize; i++) { -				if (psrc[i] > intvec_top) -					continue; -				__set_bit(psrc[i], mpic->protected); -			} +	psrc = of_get_property(mpic->node, "protected-sources", &psize); +	if (psrc) { +		/* Allocate a bitmap with one bit per interrupt */ +		unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1); +		mpic->protected = kzalloc(mapsize*sizeof(long), GFP_KERNEL); +		BUG_ON(mpic->protected == NULL); +		for (i = 0; i < psize/sizeof(u32); i++) { +			if (psrc[i] > intvec_top) +				continue; +			__set_bit(psrc[i], mpic->protected);  		}  	} @@ -1224,42 +1258,32 @@ struct mpic * __init mpic_alloc(struct device_node *node,  #endif  	/* default register type */ -	mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ? -		mpic_access_mmio_be : mpic_access_mmio_le; - -	/* If no physical address is passed in, a device-node is mandatory */ -	BUG_ON(paddr == 0 && node == NULL); +	if (flags & MPIC_BIG_ENDIAN) +		mpic->reg_type = mpic_access_mmio_be; +	else +		mpic->reg_type = mpic_access_mmio_le; -	/* If no physical address passed in, check if it's dcr based */ -	if (paddr == 0 && of_get_property(node, "dcr-reg", NULL) != NULL) { +	/* +	 * An MPIC with a "dcr-reg" property must be accessed that way, but +	 * only if the kernel includes DCR support. +	 */  #ifdef CONFIG_PPC_DCR -		mpic->flags |= MPIC_USES_DCR; +	if (flags & MPIC_USES_DCR)  		mpic->reg_type = mpic_access_dcr;  #else -		BUG(); -#endif /* CONFIG_PPC_DCR */ -	} - -	/* If the MPIC is not DCR based, and no physical address was passed -	 * in, try to obtain one -	 */ -	if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) { -		const u32 *reg = of_get_property(node, "reg", NULL); -		BUG_ON(reg == NULL); -		paddr = of_translate_address(node, reg); -		BUG_ON(paddr == OF_BAD_ADDR); -	} +	BUG_ON(flags & MPIC_USES_DCR); +#endif  	/* Map the global registers */ -	mpic_map(mpic, node, paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); -	mpic_map(mpic, node, paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); +	mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); +	mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);  	/* Reset */  	/* When using a device-node, reset requests are only honored if the MPIC  	 * is allowed to reset.  	 */ -	if (mpic_reset_prohibited(node)) +	if (of_get_property(mpic->node, "pic-no-reset", NULL))  		mpic->flags |= MPIC_NO_RESET;  	if ((flags & MPIC_WANTS_RESET) && !(mpic->flags & MPIC_NO_RESET)) { @@ -1307,7 +1331,7 @@ struct mpic * __init mpic_alloc(struct device_node *node,  	for_each_possible_cpu(i) {  		unsigned int cpu = get_hard_smp_processor_id(i); -		mpic_map(mpic, node, paddr, &mpic->cpuregs[cpu], +		mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu],  			 MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE),  			 0x1000);  	} @@ -1315,16 +1339,21 @@ struct mpic * __init mpic_alloc(struct device_node *node,  	/* Initialize main ISU if none provided */  	if (mpic->isu_size == 0) {  		mpic->isu_size = mpic->num_sources; -		mpic_map(mpic, node, paddr, &mpic->isus[0], +		mpic_map(mpic, mpic->paddr, &mpic->isus[0],  			 MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);  	}  	mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);  	mpic->isu_mask = (1 << mpic->isu_shift) - 1; -	mpic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, +	mpic->irqhost = irq_alloc_host(mpic->node, IRQ_HOST_MAP_LINEAR,  				       isu_size ? isu_size : mpic->num_sources,  				       &mpic_host_ops,  				       flags & MPIC_LARGE_VECTORS ? 2048 : 256); + +	/* +	 * FIXME: The code leaks the MPIC object and mappings here; this +	 * is very unlikely to fail but it ought to be fixed anyways. +	 */  	if (mpic->irqhost == NULL)  		return NULL; @@ -1347,19 +1376,23 @@ struct mpic * __init mpic_alloc(struct device_node *node,  	}  	printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx,"  	       " max %d CPUs\n", -	       name, vers, (unsigned long long)paddr, num_possible_cpus()); +	       name, vers, (unsigned long long)mpic->paddr, num_possible_cpus());  	printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n",  	       mpic->isu_size, mpic->isu_shift, mpic->isu_mask);  	mpic->next = mpics;  	mpics = mpic; -	if (flags & MPIC_PRIMARY) { +	if (!(flags & MPIC_SECONDARY)) {  		mpic_primary = mpic;  		irq_set_default_host(mpic->irqhost);  	}  	return mpic; + +err_of_node_put: +	of_node_put(node); +	return NULL;  }  void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, @@ -1369,7 +1402,7 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,  	BUG_ON(isu_num >= MPIC_MAX_ISU); -	mpic_map(mpic, mpic->irqhost->of_node, +	mpic_map(mpic,  		 paddr, &mpic->isus[isu_num], 0,  		 MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); @@ -1385,8 +1418,7 @@ void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count)  void __init mpic_init(struct mpic *mpic)  { -	int i; -	int cpu; +	int i, cpu;  	BUG_ON(mpic->num_sources == 0); @@ -1424,7 +1456,7 @@ void __init mpic_init(struct mpic *mpic)  	/* Do the HT PIC fixups on U3 broken mpic */  	DBG("MPIC flags: %x\n", mpic->flags); -	if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) { +	if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) {  		mpic_scan_ht_pics(mpic);  		mpic_u3msi_init(mpic);  	} @@ -1471,6 +1503,17 @@ void __init mpic_init(struct mpic *mpic)  				  GFP_KERNEL);  	BUG_ON(mpic->save_data == NULL);  #endif + +	/* Check if this MPIC is chained from a parent interrupt controller */ +	if (mpic->flags & MPIC_SECONDARY) { +		int virq = irq_of_parse_and_map(mpic->node, 0); +		if (virq != NO_IRQ) { +			printk(KERN_INFO "%s: hooking up to IRQ %d\n", +					mpic->node->full_name, virq); +			irq_set_handler_data(virq, mpic); +			irq_set_chained_handler(virq, &mpic_cascade); +		} +	}  }  void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio)  |