Path: blob/master/arch/powerpc/platforms/cell/interrupt.c
10818 views
/*1* Cell Internal Interrupt Controller2*3* Copyright (C) 2006 Benjamin Herrenschmidt ([email protected])4* IBM, Corp.5*6* (C) Copyright IBM Deutschland Entwicklung GmbH 20057*8* Author: Arnd Bergmann <[email protected]>9*10* This program is free software; you can redistribute it and/or modify11* it under the terms of the GNU General Public License as published by12* the Free Software Foundation; either version 2, or (at your option)13* any later version.14*15* This program is distributed in the hope that it will be useful,16* but WITHOUT ANY WARRANTY; without even the implied warranty of17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the18* GNU General Public License for more details.19*20* You should have received a copy of the GNU General Public License21* along with this program; if not, write to the Free Software22* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.23*24* TODO:25* - Fix various assumptions related to HW CPU numbers vs. linux CPU numbers26* vs node numbers in the setup code27* - Implement proper handling of maxcpus=1/2 (that is, routing of irqs from28* a non-active node to the active node)29*/3031#include <linux/interrupt.h>32#include <linux/irq.h>33#include <linux/module.h>34#include <linux/percpu.h>35#include <linux/types.h>36#include <linux/ioport.h>37#include <linux/kernel_stat.h>3839#include <asm/io.h>40#include <asm/pgtable.h>41#include <asm/prom.h>42#include <asm/ptrace.h>43#include <asm/machdep.h>44#include <asm/cell-regs.h>4546#include "interrupt.h"4748struct iic {49struct cbe_iic_thread_regs __iomem *regs;50u8 target_id;51u8 eoi_stack[16];52int eoi_ptr;53struct device_node *node;54};5556static DEFINE_PER_CPU(struct iic, cpu_iic);57#define IIC_NODE_COUNT 258static struct irq_host *iic_host;5960/* Convert between "pending" bits and hw irq number */61static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits)62{63unsigned char unit = bits.source & 0xf;64unsigned char node = bits.source >> 4;65unsigned char class = bits.class & 3;6667/* Decode IPIs */68if (bits.flags & CBE_IIC_IRQ_IPI)69return IIC_IRQ_TYPE_IPI | (bits.prio >> 4);70else71return (node << IIC_IRQ_NODE_SHIFT) | (class << 4) | unit;72}7374static void iic_mask(struct irq_data *d)75{76}7778static void iic_unmask(struct irq_data *d)79{80}8182static void iic_eoi(struct irq_data *d)83{84struct iic *iic = &__get_cpu_var(cpu_iic);85out_be64(&iic->regs->prio, iic->eoi_stack[--iic->eoi_ptr]);86BUG_ON(iic->eoi_ptr < 0);87}8889static struct irq_chip iic_chip = {90.name = "CELL-IIC",91.irq_mask = iic_mask,92.irq_unmask = iic_unmask,93.irq_eoi = iic_eoi,94};959697static void iic_ioexc_eoi(struct irq_data *d)98{99}100101static void iic_ioexc_cascade(unsigned int irq, struct irq_desc *desc)102{103struct irq_chip *chip = irq_desc_get_chip(desc);104struct cbe_iic_regs __iomem *node_iic =105(void __iomem *)irq_desc_get_handler_data(desc);106unsigned int base = (irq & 0xffffff00) | IIC_IRQ_TYPE_IOEXC;107unsigned long bits, ack;108int cascade;109110for (;;) {111bits = in_be64(&node_iic->iic_is);112if (bits == 0)113break;114/* pre-ack edge interrupts */115ack = bits & IIC_ISR_EDGE_MASK;116if (ack)117out_be64(&node_iic->iic_is, ack);118/* handle them */119for (cascade = 63; cascade >= 0; cascade--)120if (bits & (0x8000000000000000UL >> cascade)) {121unsigned int cirq =122irq_linear_revmap(iic_host,123base | cascade);124if (cirq != NO_IRQ)125generic_handle_irq(cirq);126}127/* post-ack level interrupts */128ack = bits & ~IIC_ISR_EDGE_MASK;129if (ack)130out_be64(&node_iic->iic_is, ack);131}132chip->irq_eoi(&desc->irq_data);133}134135136static struct irq_chip iic_ioexc_chip = {137.name = "CELL-IOEX",138.irq_mask = iic_mask,139.irq_unmask = iic_unmask,140.irq_eoi = iic_ioexc_eoi,141};142143/* Get an IRQ number from the pending state register of the IIC */144static unsigned int iic_get_irq(void)145{146struct cbe_iic_pending_bits pending;147struct iic *iic;148unsigned int virq;149150iic = &__get_cpu_var(cpu_iic);151*(unsigned long *) &pending =152in_be64((u64 __iomem *) &iic->regs->pending_destr);153if (!(pending.flags & CBE_IIC_IRQ_VALID))154return NO_IRQ;155virq = irq_linear_revmap(iic_host, iic_pending_to_hwnum(pending));156if (virq == NO_IRQ)157return NO_IRQ;158iic->eoi_stack[++iic->eoi_ptr] = pending.prio;159BUG_ON(iic->eoi_ptr > 15);160return virq;161}162163void iic_setup_cpu(void)164{165out_be64(&__get_cpu_var(cpu_iic).regs->prio, 0xff);166}167168u8 iic_get_target_id(int cpu)169{170return per_cpu(cpu_iic, cpu).target_id;171}172173EXPORT_SYMBOL_GPL(iic_get_target_id);174175#ifdef CONFIG_SMP176177/* Use the highest interrupt priorities for IPI */178static inline int iic_msg_to_irq(int msg)179{180return IIC_IRQ_TYPE_IPI + 0xf - msg;181}182183void iic_message_pass(int cpu, int msg)184{185out_be64(&per_cpu(cpu_iic, cpu).regs->generate, (0xf - msg) << 4);186}187188struct irq_host *iic_get_irq_host(int node)189{190return iic_host;191}192EXPORT_SYMBOL_GPL(iic_get_irq_host);193194static void iic_request_ipi(int msg)195{196int virq;197198virq = irq_create_mapping(iic_host, iic_msg_to_irq(msg));199if (virq == NO_IRQ) {200printk(KERN_ERR201"iic: failed to map IPI %s\n", smp_ipi_name[msg]);202return;203}204205/*206* If smp_request_message_ipi encounters an error it will notify207* the error. If a message is not needed it will return non-zero.208*/209if (smp_request_message_ipi(virq, msg))210irq_dispose_mapping(virq);211}212213void iic_request_IPIs(void)214{215iic_request_ipi(PPC_MSG_CALL_FUNCTION);216iic_request_ipi(PPC_MSG_RESCHEDULE);217iic_request_ipi(PPC_MSG_CALL_FUNC_SINGLE);218iic_request_ipi(PPC_MSG_DEBUGGER_BREAK);219}220221#endif /* CONFIG_SMP */222223224static int iic_host_match(struct irq_host *h, struct device_node *node)225{226return of_device_is_compatible(node,227"IBM,CBEA-Internal-Interrupt-Controller");228}229230static int iic_host_map(struct irq_host *h, unsigned int virq,231irq_hw_number_t hw)232{233switch (hw & IIC_IRQ_TYPE_MASK) {234case IIC_IRQ_TYPE_IPI:235irq_set_chip_and_handler(virq, &iic_chip, handle_percpu_irq);236break;237case IIC_IRQ_TYPE_IOEXC:238irq_set_chip_and_handler(virq, &iic_ioexc_chip,239handle_edge_eoi_irq);240break;241default:242irq_set_chip_and_handler(virq, &iic_chip, handle_edge_eoi_irq);243}244return 0;245}246247static int iic_host_xlate(struct irq_host *h, struct device_node *ct,248const u32 *intspec, unsigned int intsize,249irq_hw_number_t *out_hwirq, unsigned int *out_flags)250251{252unsigned int node, ext, unit, class;253const u32 *val;254255if (!of_device_is_compatible(ct,256"IBM,CBEA-Internal-Interrupt-Controller"))257return -ENODEV;258if (intsize != 1)259return -ENODEV;260val = of_get_property(ct, "#interrupt-cells", NULL);261if (val == NULL || *val != 1)262return -ENODEV;263264node = intspec[0] >> 24;265ext = (intspec[0] >> 16) & 0xff;266class = (intspec[0] >> 8) & 0xff;267unit = intspec[0] & 0xff;268269/* Check if node is in supported range */270if (node > 1)271return -EINVAL;272273/* Build up interrupt number, special case for IO exceptions */274*out_hwirq = (node << IIC_IRQ_NODE_SHIFT);275if (unit == IIC_UNIT_IIC && class == 1)276*out_hwirq |= IIC_IRQ_TYPE_IOEXC | ext;277else278*out_hwirq |= IIC_IRQ_TYPE_NORMAL |279(class << IIC_IRQ_CLASS_SHIFT) | unit;280281/* Dummy flags, ignored by iic code */282*out_flags = IRQ_TYPE_EDGE_RISING;283284return 0;285}286287static struct irq_host_ops iic_host_ops = {288.match = iic_host_match,289.map = iic_host_map,290.xlate = iic_host_xlate,291};292293static void __init init_one_iic(unsigned int hw_cpu, unsigned long addr,294struct device_node *node)295{296/* XXX FIXME: should locate the linux CPU number from the HW cpu297* number properly. We are lucky for now298*/299struct iic *iic = &per_cpu(cpu_iic, hw_cpu);300301iic->regs = ioremap(addr, sizeof(struct cbe_iic_thread_regs));302BUG_ON(iic->regs == NULL);303304iic->target_id = ((hw_cpu & 2) << 3) | ((hw_cpu & 1) ? 0xf : 0xe);305iic->eoi_stack[0] = 0xff;306iic->node = of_node_get(node);307out_be64(&iic->regs->prio, 0);308309printk(KERN_INFO "IIC for CPU %d target id 0x%x : %s\n",310hw_cpu, iic->target_id, node->full_name);311}312313static int __init setup_iic(void)314{315struct device_node *dn;316struct resource r0, r1;317unsigned int node, cascade, found = 0;318struct cbe_iic_regs __iomem *node_iic;319const u32 *np;320321for (dn = NULL;322(dn = of_find_node_by_name(dn,"interrupt-controller")) != NULL;) {323if (!of_device_is_compatible(dn,324"IBM,CBEA-Internal-Interrupt-Controller"))325continue;326np = of_get_property(dn, "ibm,interrupt-server-ranges", NULL);327if (np == NULL) {328printk(KERN_WARNING "IIC: CPU association not found\n");329of_node_put(dn);330return -ENODEV;331}332if (of_address_to_resource(dn, 0, &r0) ||333of_address_to_resource(dn, 1, &r1)) {334printk(KERN_WARNING "IIC: Can't resolve addresses\n");335of_node_put(dn);336return -ENODEV;337}338found++;339init_one_iic(np[0], r0.start, dn);340init_one_iic(np[1], r1.start, dn);341342/* Setup cascade for IO exceptions. XXX cleanup tricks to get343* node vs CPU etc...344* Note that we configure the IIC_IRR here with a hard coded345* priority of 1. We might want to improve that later.346*/347node = np[0] >> 1;348node_iic = cbe_get_cpu_iic_regs(np[0]);349cascade = node << IIC_IRQ_NODE_SHIFT;350cascade |= 1 << IIC_IRQ_CLASS_SHIFT;351cascade |= IIC_UNIT_IIC;352cascade = irq_create_mapping(iic_host, cascade);353if (cascade == NO_IRQ)354continue;355/*356* irq_data is a generic pointer that gets passed back357* to us later, so the forced cast is fine.358*/359irq_set_handler_data(cascade, (void __force *)node_iic);360irq_set_chained_handler(cascade, iic_ioexc_cascade);361out_be64(&node_iic->iic_ir,362(1 << 12) /* priority */ |363(node << 4) /* dest node */ |364IIC_UNIT_THREAD_0 /* route them to thread 0 */);365/* Flush pending (make sure it triggers if there is366* anything pending367*/368out_be64(&node_iic->iic_is, 0xfffffffffffffffful);369}370371if (found)372return 0;373else374return -ENODEV;375}376377void __init iic_init_IRQ(void)378{379/* Setup an irq host data structure */380iic_host = irq_alloc_host(NULL, IRQ_HOST_MAP_LINEAR, IIC_SOURCE_COUNT,381&iic_host_ops, IIC_IRQ_INVALID);382BUG_ON(iic_host == NULL);383irq_set_default_host(iic_host);384385/* Discover and initialize iics */386if (setup_iic() < 0)387panic("IIC: Failed to initialize !\n");388389/* Set master interrupt handling function */390ppc_md.get_irq = iic_get_irq;391392/* Enable on current CPU */393iic_setup_cpu();394}395396void iic_set_interrupt_routing(int cpu, int thread, int priority)397{398struct cbe_iic_regs __iomem *iic_regs = cbe_get_cpu_iic_regs(cpu);399u64 iic_ir = 0;400int node = cpu >> 1;401402/* Set which node and thread will handle the next interrupt */403iic_ir |= CBE_IIC_IR_PRIO(priority) |404CBE_IIC_IR_DEST_NODE(node);405if (thread == 0)406iic_ir |= CBE_IIC_IR_DEST_UNIT(CBE_IIC_IR_PT_0);407else408iic_ir |= CBE_IIC_IR_DEST_UNIT(CBE_IIC_IR_PT_1);409out_be64(&iic_regs->iic_ir, iic_ir);410}411412413