1/* 2 * linux/arch/arm/common/vic.c 3 * 4 * Copyright (C) 1999 - 2003 ARM Limited 5 * Copyright (C) 2000 Deep Blue Solutions Ltd 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) 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 of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22#include <linux/init.h> 23#include <linux/list.h> 24#include <linux/io.h> 25#include <linux/sysdev.h> 26#include <linux/device.h> 27#include <linux/amba/bus.h> 28 29#include <asm/mach/irq.h> 30#include <asm/hardware/vic.h> 31 32#if defined(CONFIG_PM) 33/** 34 * struct vic_device - VIC PM device 35 * @sysdev: The system device which is registered. 36 * @irq: The IRQ number for the base of the VIC. 37 * @base: The register base for the VIC. 38 * @resume_sources: A bitmask of interrupts for resume. 39 * @resume_irqs: The IRQs enabled for resume. 40 * @int_select: Save for VIC_INT_SELECT. 41 * @int_enable: Save for VIC_INT_ENABLE. 42 * @soft_int: Save for VIC_INT_SOFT. 43 * @protect: Save for VIC_PROTECT. 44 */ 45struct vic_device { 46 struct sys_device sysdev; 47 48 void __iomem *base; 49 int irq; 50 u32 resume_sources; 51 u32 resume_irqs; 52 u32 int_select; 53 u32 int_enable; 54 u32 soft_int; 55 u32 protect; 56}; 57 58/* we cannot allocate memory when VICs are initially registered */ 59static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; 60 61static int vic_id; 62 63static inline struct vic_device *to_vic(struct sys_device *sys) 64{ 65 return container_of(sys, struct vic_device, sysdev); 66} 67#endif /* CONFIG_PM */ 68 69/** 70 * vic_init2 - common initialisation code 71 * @base: Base of the VIC. 72 * 73 * Common initialisation code for registeration 74 * and resume. 75*/ 76static void vic_init2(void __iomem *base) 77{ 78 int i; 79 80 for (i = 0; i < 16; i++) { 81 void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 82 writel(VIC_VECT_CNTL_ENABLE | i, reg); 83 } 84 85 writel(32, base + VIC_PL190_DEF_VECT_ADDR); 86} 87 88#if defined(CONFIG_PM) 89static int vic_class_resume(struct sys_device *dev) 90{ 91 struct vic_device *vic = to_vic(dev); 92 void __iomem *base = vic->base; 93 94 printk(KERN_DEBUG "%s: resuming vic at %p\n", __func__, base); 95 96 /* re-initialise static settings */ 97 vic_init2(base); 98 99 writel(vic->int_select, base + VIC_INT_SELECT); 100 writel(vic->protect, base + VIC_PROTECT); 101 102 /* set the enabled ints and then clear the non-enabled */ 103 writel(vic->int_enable, base + VIC_INT_ENABLE); 104 writel(~vic->int_enable, base + VIC_INT_ENABLE_CLEAR); 105 106 /* and the same for the soft-int register */ 107 108 writel(vic->soft_int, base + VIC_INT_SOFT); 109 writel(~vic->soft_int, base + VIC_INT_SOFT_CLEAR); 110 111 return 0; 112} 113 114static int vic_class_suspend(struct sys_device *dev, pm_message_t state) 115{ 116 struct vic_device *vic = to_vic(dev); 117 void __iomem *base = vic->base; 118 119 printk(KERN_DEBUG "%s: suspending vic at %p\n", __func__, base); 120 121 vic->int_select = readl(base + VIC_INT_SELECT); 122 vic->int_enable = readl(base + VIC_INT_ENABLE); 123 vic->soft_int = readl(base + VIC_INT_SOFT); 124 vic->protect = readl(base + VIC_PROTECT); 125 126 /* set the interrupts (if any) that are used for 127 * resuming the system */ 128 129 writel(vic->resume_irqs, base + VIC_INT_ENABLE); 130 writel(~vic->resume_irqs, base + VIC_INT_ENABLE_CLEAR); 131 132 return 0; 133} 134 135struct sysdev_class vic_class = { 136 .name = "vic", 137 .suspend = vic_class_suspend, 138 .resume = vic_class_resume, 139}; 140 141/** 142 * vic_pm_init - initicall to register VIC pm 143 * 144 * This is called via late_initcall() to register 145 * the resources for the VICs due to the early 146 * nature of the VIC's registration. 147*/ 148static int __init vic_pm_init(void) 149{ 150 struct vic_device *dev = vic_devices; 151 int err; 152 int id; 153 154 if (vic_id == 0) 155 return 0; 156 157 err = sysdev_class_register(&vic_class); 158 if (err) { 159 printk(KERN_ERR "%s: cannot register class\n", __func__); 160 return err; 161 } 162 163 for (id = 0; id < vic_id; id++, dev++) { 164 dev->sysdev.id = id; 165 dev->sysdev.cls = &vic_class; 166 167 err = sysdev_register(&dev->sysdev); 168 if (err) { 169 printk(KERN_ERR "%s: failed to register device\n", 170 __func__); 171 return err; 172 } 173 } 174 175 return 0; 176} 177late_initcall(vic_pm_init); 178 179/** 180 * vic_pm_register - Register a VIC for later power management control 181 * @base: The base address of the VIC. 182 * @irq: The base IRQ for the VIC. 183 * @resume_sources: bitmask of interrupts allowed for resume sources. 184 * 185 * Register the VIC with the system device tree so that it can be notified 186 * of suspend and resume requests and ensure that the correct actions are 187 * taken to re-instate the settings on resume. 188 */ 189static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) 190{ 191 struct vic_device *v; 192 193 if (vic_id >= ARRAY_SIZE(vic_devices)) 194 printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); 195 else { 196 v = &vic_devices[vic_id]; 197 v->base = base; 198 v->resume_sources = resume_sources; 199 v->irq = irq; 200 vic_id++; 201 } 202} 203#else 204static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } 205#endif /* CONFIG_PM */ 206 207static void vic_ack_irq(unsigned int irq) 208{ 209 void __iomem *base = get_irq_chip_data(irq); 210 irq &= 31; 211 writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); 212 /* moreover, clear the soft-triggered, in case it was the reason */ 213 writel(1 << irq, base + VIC_INT_SOFT_CLEAR); 214} 215 216static void vic_mask_irq(unsigned int irq) 217{ 218 void __iomem *base = get_irq_chip_data(irq); 219 irq &= 31; 220 writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); 221} 222 223static void vic_unmask_irq(unsigned int irq) 224{ 225 void __iomem *base = get_irq_chip_data(irq); 226 irq &= 31; 227 writel(1 << irq, base + VIC_INT_ENABLE); 228} 229 230#if defined(CONFIG_PM) 231static struct vic_device *vic_from_irq(unsigned int irq) 232{ 233 struct vic_device *v = vic_devices; 234 unsigned int base_irq = irq & ~31; 235 int id; 236 237 for (id = 0; id < vic_id; id++, v++) { 238 if (v->irq == base_irq) 239 return v; 240 } 241 242 return NULL; 243} 244 245static int vic_set_wake(unsigned int irq, unsigned int on) 246{ 247 struct vic_device *v = vic_from_irq(irq); 248 unsigned int off = irq & 31; 249 u32 bit = 1 << off; 250 251 if (!v) 252 return -EINVAL; 253 254 if (!(bit & v->resume_sources)) 255 return -EINVAL; 256 257 if (on) 258 v->resume_irqs |= bit; 259 else 260 v->resume_irqs &= ~bit; 261 262 return 0; 263} 264#else 265#define vic_set_wake NULL 266#endif /* CONFIG_PM */ 267 268static struct irq_chip vic_chip = { 269 .name = "VIC", 270 .ack = vic_ack_irq, 271 .mask = vic_mask_irq, 272 .unmask = vic_unmask_irq, 273 .set_wake = vic_set_wake, 274}; 275 276static void __init vic_disable(void __iomem *base) 277{ 278 writel(0, base + VIC_INT_SELECT); 279 writel(0, base + VIC_INT_ENABLE); 280 writel(~0, base + VIC_INT_ENABLE_CLEAR); 281 writel(0, base + VIC_IRQ_STATUS); 282 writel(0, base + VIC_ITCR); 283 writel(~0, base + VIC_INT_SOFT_CLEAR); 284} 285 286static void __init vic_clear_interrupts(void __iomem *base) 287{ 288 unsigned int i; 289 290 writel(0, base + VIC_PL190_VECT_ADDR); 291 for (i = 0; i < 19; i++) { 292 unsigned int value; 293 294 value = readl(base + VIC_PL190_VECT_ADDR); 295 writel(value, base + VIC_PL190_VECT_ADDR); 296 } 297} 298 299static void __init vic_set_irq_sources(void __iomem *base, 300 unsigned int irq_start, u32 vic_sources) 301{ 302 unsigned int i; 303 304 for (i = 0; i < 32; i++) { 305 if (vic_sources & (1 << i)) { 306 unsigned int irq = irq_start + i; 307 308 set_irq_chip(irq, &vic_chip); 309 set_irq_chip_data(irq, base); 310 set_irq_handler(irq, handle_level_irq); 311 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 312 } 313 } 314} 315 316/* 317 * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. 318 * The original cell has 32 interrupts, while the modified one has 64, 319 * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case 320 * the probe function is called twice, with base set to offset 000 321 * and 020 within the page. We call this "second block". 322 */ 323static void __init vic_init_st(void __iomem *base, unsigned int irq_start, 324 u32 vic_sources) 325{ 326 unsigned int i; 327 int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; 328 329 /* Disable all interrupts initially. */ 330 vic_disable(base); 331 332 /* 333 * Make sure we clear all existing interrupts. The vector registers 334 * in this cell are after the second block of general registers, 335 * so we can address them using standard offsets, but only from 336 * the second base address, which is 0x20 in the page 337 */ 338 if (vic_2nd_block) { 339 vic_clear_interrupts(base); 340 341 /* ST has 16 vectors as well, but we don't enable them by now */ 342 for (i = 0; i < 16; i++) { 343 void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 344 writel(0, reg); 345 } 346 347 writel(32, base + VIC_PL190_DEF_VECT_ADDR); 348 } 349 350 vic_set_irq_sources(base, irq_start, vic_sources); 351} 352 353/** 354 * vic_init - initialise a vectored interrupt controller 355 * @base: iomem base address 356 * @irq_start: starting interrupt number, must be muliple of 32 357 * @vic_sources: bitmask of interrupt sources to allow 358 * @resume_sources: bitmask of interrupt sources to allow for resume 359 */ 360void __init vic_init(void __iomem *base, unsigned int irq_start, 361 u32 vic_sources, u32 resume_sources) 362{ 363 unsigned int i; 364 u32 cellid = 0; 365 enum amba_vendor vendor; 366 367 /* Identify which VIC cell this one is, by reading the ID */ 368 for (i = 0; i < 4; i++) { 369 u32 addr = ((u32)base & PAGE_MASK) + 0xfe0 + (i * 4); 370 cellid |= (readl(addr) & 0xff) << (8 * i); 371 } 372 vendor = (cellid >> 12) & 0xff; 373 printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n", 374 base, cellid, vendor); 375 376 switch(vendor) { 377 case AMBA_VENDOR_ST: 378 vic_init_st(base, irq_start, vic_sources); 379 return; 380 default: 381 printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); 382 /* fall through */ 383 case AMBA_VENDOR_ARM: 384 break; 385 } 386 387 /* Disable all interrupts initially. */ 388 vic_disable(base); 389 390 /* Make sure we clear all existing interrupts */ 391 vic_clear_interrupts(base); 392 393 vic_init2(base); 394 395 vic_set_irq_sources(base, irq_start, vic_sources); 396 397 vic_pm_register(base, irq_start, resume_sources); 398} 399