|
@@ -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 */
|
|
|
|
|
|
|
|
@@ -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)
|