Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.34 401 lines 10 kB view raw
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 276/* 277 * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. 278 * The original cell has 32 interrupts, while the modified one has 64, 279 * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case 280 * the probe function is called twice, with base set to offset 000 281 * and 020 within the page. We call this "second block". 282 */ 283static void __init vic_init_st(void __iomem *base, unsigned int irq_start, 284 u32 vic_sources) 285{ 286 unsigned int i; 287 int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; 288 289 /* Disable all interrupts initially. */ 290 291 writel(0, base + VIC_INT_SELECT); 292 writel(0, base + VIC_INT_ENABLE); 293 writel(~0, base + VIC_INT_ENABLE_CLEAR); 294 writel(0, base + VIC_IRQ_STATUS); 295 writel(0, base + VIC_ITCR); 296 writel(~0, base + VIC_INT_SOFT_CLEAR); 297 298 /* 299 * Make sure we clear all existing interrupts. The vector registers 300 * in this cell are after the second block of general registers, 301 * so we can address them using standard offsets, but only from 302 * the second base address, which is 0x20 in the page 303 */ 304 if (vic_2nd_block) { 305 writel(0, base + VIC_PL190_VECT_ADDR); 306 for (i = 0; i < 19; i++) { 307 unsigned int value; 308 309 value = readl(base + VIC_PL190_VECT_ADDR); 310 writel(value, base + VIC_PL190_VECT_ADDR); 311 } 312 /* ST has 16 vectors as well, but we don't enable them by now */ 313 for (i = 0; i < 16; i++) { 314 void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 315 writel(0, reg); 316 } 317 318 writel(32, base + VIC_PL190_DEF_VECT_ADDR); 319 } 320 321 for (i = 0; i < 32; i++) { 322 if (vic_sources & (1 << i)) { 323 unsigned int irq = irq_start + i; 324 325 set_irq_chip(irq, &vic_chip); 326 set_irq_chip_data(irq, base); 327 set_irq_handler(irq, handle_level_irq); 328 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 329 } 330 } 331} 332 333/** 334 * vic_init - initialise a vectored interrupt controller 335 * @base: iomem base address 336 * @irq_start: starting interrupt number, must be muliple of 32 337 * @vic_sources: bitmask of interrupt sources to allow 338 * @resume_sources: bitmask of interrupt sources to allow for resume 339 */ 340void __init vic_init(void __iomem *base, unsigned int irq_start, 341 u32 vic_sources, u32 resume_sources) 342{ 343 unsigned int i; 344 u32 cellid = 0; 345 enum amba_vendor vendor; 346 347 /* Identify which VIC cell this one is, by reading the ID */ 348 for (i = 0; i < 4; i++) { 349 u32 addr = ((u32)base & PAGE_MASK) + 0xfe0 + (i * 4); 350 cellid |= (readl(addr) & 0xff) << (8 * i); 351 } 352 vendor = (cellid >> 12) & 0xff; 353 printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n", 354 base, cellid, vendor); 355 356 switch(vendor) { 357 case AMBA_VENDOR_ST: 358 vic_init_st(base, irq_start, vic_sources); 359 return; 360 default: 361 printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); 362 /* fall through */ 363 case AMBA_VENDOR_ARM: 364 break; 365 } 366 367 /* Disable all interrupts initially. */ 368 369 writel(0, base + VIC_INT_SELECT); 370 writel(0, base + VIC_INT_ENABLE); 371 writel(~0, base + VIC_INT_ENABLE_CLEAR); 372 writel(0, base + VIC_IRQ_STATUS); 373 writel(0, base + VIC_ITCR); 374 writel(~0, base + VIC_INT_SOFT_CLEAR); 375 376 /* 377 * Make sure we clear all existing interrupts 378 */ 379 writel(0, base + VIC_PL190_VECT_ADDR); 380 for (i = 0; i < 19; i++) { 381 unsigned int value; 382 383 value = readl(base + VIC_PL190_VECT_ADDR); 384 writel(value, base + VIC_PL190_VECT_ADDR); 385 } 386 387 vic_init2(base); 388 389 for (i = 0; i < 32; i++) { 390 if (vic_sources & (1 << i)) { 391 unsigned int irq = irq_start + i; 392 393 set_irq_chip(irq, &vic_chip); 394 set_irq_chip_data(irq, base); 395 set_irq_handler(irq, handle_level_irq); 396 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 397 } 398 } 399 400 vic_pm_register(base, irq_start, resume_sources); 401}