Path: blob/master/arch/powerpc/platforms/cell/spider-pic.c
10818 views
/*1* External Interrupt Controller on Spider South Bridge2*3* (C) Copyright IBM Deutschland Entwicklung GmbH 20054*5* Author: Arnd Bergmann <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2, or (at your option)10* any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.20*/2122#include <linux/interrupt.h>23#include <linux/irq.h>24#include <linux/ioport.h>2526#include <asm/pgtable.h>27#include <asm/prom.h>28#include <asm/io.h>2930#include "interrupt.h"3132/* register layout taken from Spider spec, table 7.4-4 */33enum {34TIR_DEN = 0x004, /* Detection Enable Register */35TIR_MSK = 0x084, /* Mask Level Register */36TIR_EDC = 0x0c0, /* Edge Detection Clear Register */37TIR_PNDA = 0x100, /* Pending Register A */38TIR_PNDB = 0x104, /* Pending Register B */39TIR_CS = 0x144, /* Current Status Register */40TIR_LCSA = 0x150, /* Level Current Status Register A */41TIR_LCSB = 0x154, /* Level Current Status Register B */42TIR_LCSC = 0x158, /* Level Current Status Register C */43TIR_LCSD = 0x15c, /* Level Current Status Register D */44TIR_CFGA = 0x200, /* Setting Register A0 */45TIR_CFGB = 0x204, /* Setting Register B0 */46/* 0x208 ... 0x3ff Setting Register An/Bn */47TIR_PPNDA = 0x400, /* Packet Pending Register A */48TIR_PPNDB = 0x404, /* Packet Pending Register B */49TIR_PIERA = 0x408, /* Packet Output Error Register A */50TIR_PIERB = 0x40c, /* Packet Output Error Register B */51TIR_PIEN = 0x444, /* Packet Output Enable Register */52TIR_PIPND = 0x454, /* Packet Output Pending Register */53TIRDID = 0x484, /* Spider Device ID Register */54REISTIM = 0x500, /* Reissue Command Timeout Time Setting */55REISTIMEN = 0x504, /* Reissue Command Timeout Setting */56REISWAITEN = 0x508, /* Reissue Wait Control*/57};5859#define SPIDER_CHIP_COUNT 460#define SPIDER_SRC_COUNT 6461#define SPIDER_IRQ_INVALID 636263struct spider_pic {64struct irq_host *host;65void __iomem *regs;66unsigned int node_id;67};68static struct spider_pic spider_pics[SPIDER_CHIP_COUNT];6970static struct spider_pic *spider_irq_data_to_pic(struct irq_data *d)71{72return irq_data_get_irq_chip_data(d);73}7475static void __iomem *spider_get_irq_config(struct spider_pic *pic,76unsigned int src)77{78return pic->regs + TIR_CFGA + 8 * src;79}8081static void spider_unmask_irq(struct irq_data *d)82{83struct spider_pic *pic = spider_irq_data_to_pic(d);84void __iomem *cfg = spider_get_irq_config(pic, irqd_to_hwirq(d));8586out_be32(cfg, in_be32(cfg) | 0x30000000u);87}8889static void spider_mask_irq(struct irq_data *d)90{91struct spider_pic *pic = spider_irq_data_to_pic(d);92void __iomem *cfg = spider_get_irq_config(pic, irqd_to_hwirq(d));9394out_be32(cfg, in_be32(cfg) & ~0x30000000u);95}9697static void spider_ack_irq(struct irq_data *d)98{99struct spider_pic *pic = spider_irq_data_to_pic(d);100unsigned int src = irqd_to_hwirq(d);101102/* Reset edge detection logic if necessary103*/104if (irqd_is_level_type(d))105return;106107/* Only interrupts 47 to 50 can be set to edge */108if (src < 47 || src > 50)109return;110111/* Perform the clear of the edge logic */112out_be32(pic->regs + TIR_EDC, 0x100 | (src & 0xf));113}114115static int spider_set_irq_type(struct irq_data *d, unsigned int type)116{117unsigned int sense = type & IRQ_TYPE_SENSE_MASK;118struct spider_pic *pic = spider_irq_data_to_pic(d);119unsigned int hw = irqd_to_hwirq(d);120void __iomem *cfg = spider_get_irq_config(pic, hw);121u32 old_mask;122u32 ic;123124/* Note that only level high is supported for most interrupts */125if (sense != IRQ_TYPE_NONE && sense != IRQ_TYPE_LEVEL_HIGH &&126(hw < 47 || hw > 50))127return -EINVAL;128129/* Decode sense type */130switch(sense) {131case IRQ_TYPE_EDGE_RISING:132ic = 0x3;133break;134case IRQ_TYPE_EDGE_FALLING:135ic = 0x2;136break;137case IRQ_TYPE_LEVEL_LOW:138ic = 0x0;139break;140case IRQ_TYPE_LEVEL_HIGH:141case IRQ_TYPE_NONE:142ic = 0x1;143break;144default:145return -EINVAL;146}147148/* Configure the source. One gross hack that was there before and149* that I've kept around is the priority to the BE which I set to150* be the same as the interrupt source number. I don't know wether151* that's supposed to make any kind of sense however, we'll have to152* decide that, but for now, I'm not changing the behaviour.153*/154old_mask = in_be32(cfg) & 0x30000000u;155out_be32(cfg, old_mask | (ic << 24) | (0x7 << 16) |156(pic->node_id << 4) | 0xe);157out_be32(cfg + 4, (0x2 << 16) | (hw & 0xff));158159return 0;160}161162static struct irq_chip spider_pic = {163.name = "SPIDER",164.irq_unmask = spider_unmask_irq,165.irq_mask = spider_mask_irq,166.irq_ack = spider_ack_irq,167.irq_set_type = spider_set_irq_type,168};169170static int spider_host_map(struct irq_host *h, unsigned int virq,171irq_hw_number_t hw)172{173irq_set_chip_data(virq, h->host_data);174irq_set_chip_and_handler(virq, &spider_pic, handle_level_irq);175176/* Set default irq type */177irq_set_irq_type(virq, IRQ_TYPE_NONE);178179return 0;180}181182static int spider_host_xlate(struct irq_host *h, struct device_node *ct,183const u32 *intspec, unsigned int intsize,184irq_hw_number_t *out_hwirq, unsigned int *out_flags)185186{187/* Spider interrupts have 2 cells, first is the interrupt source,188* second, well, I don't know for sure yet ... We mask the top bits189* because old device-trees encode a node number in there190*/191*out_hwirq = intspec[0] & 0x3f;192*out_flags = IRQ_TYPE_LEVEL_HIGH;193return 0;194}195196static struct irq_host_ops spider_host_ops = {197.map = spider_host_map,198.xlate = spider_host_xlate,199};200201static void spider_irq_cascade(unsigned int irq, struct irq_desc *desc)202{203struct irq_chip *chip = irq_desc_get_chip(desc);204struct spider_pic *pic = irq_desc_get_handler_data(desc);205unsigned int cs, virq;206207cs = in_be32(pic->regs + TIR_CS) >> 24;208if (cs == SPIDER_IRQ_INVALID)209virq = NO_IRQ;210else211virq = irq_linear_revmap(pic->host, cs);212213if (virq != NO_IRQ)214generic_handle_irq(virq);215216chip->irq_eoi(&desc->irq_data);217}218219/* For hooking up the cascace we have a problem. Our device-tree is220* crap and we don't know on which BE iic interrupt we are hooked on at221* least not the "standard" way. We can reconstitute it based on two222* informations though: which BE node we are connected to and wether223* we are connected to IOIF0 or IOIF1. Right now, we really only care224* about the IBM cell blade and we know that its firmware gives us an225* interrupt-map property which is pretty strange.226*/227static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)228{229unsigned int virq;230const u32 *imap, *tmp;231int imaplen, intsize, unit;232struct device_node *iic;233234/* First, we check wether we have a real "interrupts" in the device235* tree in case the device-tree is ever fixed236*/237struct of_irq oirq;238if (of_irq_map_one(pic->host->of_node, 0, &oirq) == 0) {239virq = irq_create_of_mapping(oirq.controller, oirq.specifier,240oirq.size);241return virq;242}243244/* Now do the horrible hacks */245tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);246if (tmp == NULL)247return NO_IRQ;248intsize = *tmp;249imap = of_get_property(pic->host->of_node, "interrupt-map", &imaplen);250if (imap == NULL || imaplen < (intsize + 1))251return NO_IRQ;252iic = of_find_node_by_phandle(imap[intsize]);253if (iic == NULL)254return NO_IRQ;255imap += intsize + 1;256tmp = of_get_property(iic, "#interrupt-cells", NULL);257if (tmp == NULL) {258of_node_put(iic);259return NO_IRQ;260}261intsize = *tmp;262/* Assume unit is last entry of interrupt specifier */263unit = imap[intsize - 1];264/* Ok, we have a unit, now let's try to get the node */265tmp = of_get_property(iic, "ibm,interrupt-server-ranges", NULL);266if (tmp == NULL) {267of_node_put(iic);268return NO_IRQ;269}270/* ugly as hell but works for now */271pic->node_id = (*tmp) >> 1;272of_node_put(iic);273274/* Ok, now let's get cracking. You may ask me why I just didn't match275* the iic host from the iic OF node, but that way I'm still compatible276* with really really old old firmwares for which we don't have a node277*/278/* Manufacture an IIC interrupt number of class 2 */279virq = irq_create_mapping(NULL,280(pic->node_id << IIC_IRQ_NODE_SHIFT) |281(2 << IIC_IRQ_CLASS_SHIFT) |282unit);283if (virq == NO_IRQ)284printk(KERN_ERR "spider_pic: failed to map cascade !");285return virq;286}287288289static void __init spider_init_one(struct device_node *of_node, int chip,290unsigned long addr)291{292struct spider_pic *pic = &spider_pics[chip];293int i, virq;294295/* Map registers */296pic->regs = ioremap(addr, 0x1000);297if (pic->regs == NULL)298panic("spider_pic: can't map registers !");299300/* Allocate a host */301pic->host = irq_alloc_host(of_node, IRQ_HOST_MAP_LINEAR,302SPIDER_SRC_COUNT, &spider_host_ops,303SPIDER_IRQ_INVALID);304if (pic->host == NULL)305panic("spider_pic: can't allocate irq host !");306pic->host->host_data = pic;307308/* Go through all sources and disable them */309for (i = 0; i < SPIDER_SRC_COUNT; i++) {310void __iomem *cfg = pic->regs + TIR_CFGA + 8 * i;311out_be32(cfg, in_be32(cfg) & ~0x30000000u);312}313314/* do not mask any interrupts because of level */315out_be32(pic->regs + TIR_MSK, 0x0);316317/* enable interrupt packets to be output */318out_be32(pic->regs + TIR_PIEN, in_be32(pic->regs + TIR_PIEN) | 0x1);319320/* Hook up the cascade interrupt to the iic and nodeid */321virq = spider_find_cascade_and_node(pic);322if (virq == NO_IRQ)323return;324irq_set_handler_data(virq, pic);325irq_set_chained_handler(virq, spider_irq_cascade);326327printk(KERN_INFO "spider_pic: node %d, addr: 0x%lx %s\n",328pic->node_id, addr, of_node->full_name);329330/* Enable the interrupt detection enable bit. Do this last! */331out_be32(pic->regs + TIR_DEN, in_be32(pic->regs + TIR_DEN) | 0x1);332}333334void __init spider_init_IRQ(void)335{336struct resource r;337struct device_node *dn;338int chip = 0;339340/* XXX node numbers are totally bogus. We _hope_ we get the device341* nodes in the right order here but that's definitely not guaranteed,342* we need to get the node from the device tree instead.343* There is currently no proper property for it (but our whole344* device-tree is bogus anyway) so all we can do is pray or maybe test345* the address and deduce the node-id346*/347for (dn = NULL;348(dn = of_find_node_by_name(dn, "interrupt-controller"));) {349if (of_device_is_compatible(dn, "CBEA,platform-spider-pic")) {350if (of_address_to_resource(dn, 0, &r)) {351printk(KERN_WARNING "spider-pic: Failed\n");352continue;353}354} else if (of_device_is_compatible(dn, "sti,platform-spider-pic")355&& (chip < 2)) {356static long hard_coded_pics[] =357{ 0x24000008000ul, 0x34000008000ul};358r.start = hard_coded_pics[chip];359} else360continue;361spider_init_one(dn, chip++, r.start);362}363}364365366