1 /* 2 * External Interrupt Controller on Spider South Bridge 3 * 4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 5 * 6 * Author: Arnd Bergmann <arndb@de.ibm.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include <linux/interrupt.h> 24 #include <linux/irq.h> 25 26 #include <asm/pgtable.h> 27 #include <asm/prom.h> 28 #include <asm/io.h> 29 30 #include "interrupt.h" 31 32 /* register layout taken from Spider spec, table 7.4-4 */ 33 enum { 34 TIR_DEN = 0x004, /* Detection Enable Register */ 35 TIR_MSK = 0x084, /* Mask Level Register */ 36 TIR_EDC = 0x0c0, /* Edge Detection Clear Register */ 37 TIR_PNDA = 0x100, /* Pending Register A */ 38 TIR_PNDB = 0x104, /* Pending Register B */ 39 TIR_CS = 0x144, /* Current Status Register */ 40 TIR_LCSA = 0x150, /* Level Current Status Register A */ 41 TIR_LCSB = 0x154, /* Level Current Status Register B */ 42 TIR_LCSC = 0x158, /* Level Current Status Register C */ 43 TIR_LCSD = 0x15c, /* Level Current Status Register D */ 44 TIR_CFGA = 0x200, /* Setting Register A0 */ 45 TIR_CFGB = 0x204, /* Setting Register B0 */ 46 /* 0x208 ... 0x3ff Setting Register An/Bn */ 47 TIR_PPNDA = 0x400, /* Packet Pending Register A */ 48 TIR_PPNDB = 0x404, /* Packet Pending Register B */ 49 TIR_PIERA = 0x408, /* Packet Output Error Register A */ 50 TIR_PIERB = 0x40c, /* Packet Output Error Register B */ 51 TIR_PIEN = 0x444, /* Packet Output Enable Register */ 52 TIR_PIPND = 0x454, /* Packet Output Pending Register */ 53 TIRDID = 0x484, /* Spider Device ID Register */ 54 REISTIM = 0x500, /* Reissue Command Timeout Time Setting */ 55 REISTIMEN = 0x504, /* Reissue Command Timeout Setting */ 56 REISWAITEN = 0x508, /* Reissue Wait Control*/ 57 }; 58 59 static void __iomem *spider_pics[4]; 60 61 static void __iomem *spider_get_pic(int irq) 62 { 63 int node = irq / IIC_NODE_STRIDE; 64 irq %= IIC_NODE_STRIDE; 65 66 if (irq >= IIC_EXT_OFFSET && 67 irq < IIC_EXT_OFFSET + IIC_NUM_EXT && 68 spider_pics) 69 return spider_pics[node]; 70 return NULL; 71 } 72 73 static int spider_get_nr(unsigned int irq) 74 { 75 return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET; 76 } 77 78 static void __iomem *spider_get_irq_config(int irq) 79 { 80 void __iomem *pic; 81 pic = spider_get_pic(irq); 82 return pic + TIR_CFGA + 8 * spider_get_nr(irq); 83 } 84 85 static void spider_enable_irq(unsigned int irq) 86 { 87 void __iomem *cfg = spider_get_irq_config(irq); 88 irq = spider_get_nr(irq); 89 90 out_be32(cfg, in_be32(cfg) | 0x3107000eu); 91 out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq); 92 } 93 94 static void spider_disable_irq(unsigned int irq) 95 { 96 void __iomem *cfg = spider_get_irq_config(irq); 97 irq = spider_get_nr(irq); 98 99 out_be32(cfg, in_be32(cfg) & ~0x30000000u); 100 } 101 102 static unsigned int spider_startup_irq(unsigned int irq) 103 { 104 spider_enable_irq(irq); 105 return 0; 106 } 107 108 static void spider_shutdown_irq(unsigned int irq) 109 { 110 spider_disable_irq(irq); 111 } 112 113 static void spider_end_irq(unsigned int irq) 114 { 115 spider_enable_irq(irq); 116 } 117 118 static void spider_ack_irq(unsigned int irq) 119 { 120 spider_disable_irq(irq); 121 iic_local_enable(); 122 } 123 124 static struct hw_interrupt_type spider_pic = { 125 .typename = " SPIDER ", 126 .startup = spider_startup_irq, 127 .shutdown = spider_shutdown_irq, 128 .enable = spider_enable_irq, 129 .disable = spider_disable_irq, 130 .ack = spider_ack_irq, 131 .end = spider_end_irq, 132 }; 133 134 135 int spider_get_irq(unsigned long int_pending) 136 { 137 void __iomem *regs = spider_get_pic(int_pending); 138 unsigned long cs; 139 int irq; 140 141 cs = in_be32(regs + TIR_CS); 142 143 irq = cs >> 24; 144 if (irq != 63) 145 return irq; 146 147 return -1; 148 } 149 150 void spider_init_IRQ(void) 151 { 152 int node; 153 struct device_node *dn; 154 unsigned int *property; 155 long spiderpic; 156 int n; 157 158 /* FIXME: detect multiple PICs as soon as the device tree has them */ 159 for (node = 0; node < 1; node++) { 160 dn = of_find_node_by_path("/"); 161 n = prom_n_addr_cells(dn); 162 property = (unsigned int *) get_property(dn, 163 "platform-spider-pic", NULL); 164 165 if (!property) 166 continue; 167 for (spiderpic = 0; n > 0; --n) 168 spiderpic = (spiderpic << 32) + *property++; 169 printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic); 170 spider_pics[node] = __ioremap(spiderpic, 0x800, _PAGE_NO_CACHE); 171 for (n = 0; n < IIC_NUM_EXT; n++) { 172 int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE; 173 get_irq_desc(irq)->handler = &spider_pic; 174 175 /* do not mask any interrupts because of level */ 176 out_be32(spider_pics[node] + TIR_MSK, 0x0); 177 178 /* disable edge detection clear */ 179 /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */ 180 181 /* enable interrupt packets to be output */ 182 out_be32(spider_pics[node] + TIR_PIEN, 183 in_be32(spider_pics[node] + TIR_PIEN) | 0x1); 184 185 /* Enable the interrupt detection enable bit. Do this last! */ 186 out_be32(spider_pics[node] + TIR_DEN, 187 in_be32(spider_pics[node] +TIR_DEN) | 0x1); 188 189 } 190 } 191 } 192