diff options
| -rw-r--r-- | arch/arm/common/vic.c | 265 |
1 files changed, 132 insertions, 133 deletions
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index f232941de8ab..1cf999ade4bc 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | * along with this program; if not, write to the Free Software | 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 | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 20 | */ | 20 | */ |
| 21 | |||
| 21 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| 22 | #include <linux/list.h> | 23 | #include <linux/list.h> |
| 23 | #include <linux/io.h> | 24 | #include <linux/io.h> |
| @@ -28,48 +29,6 @@ | |||
| 28 | #include <asm/mach/irq.h> | 29 | #include <asm/mach/irq.h> |
| 29 | #include <asm/hardware/vic.h> | 30 | #include <asm/hardware/vic.h> |
| 30 | 31 | ||
| 31 | static void vic_ack_irq(unsigned int irq) | ||
| 32 | { | ||
| 33 | void __iomem *base = get_irq_chip_data(irq); | ||
| 34 | irq &= 31; | ||
| 35 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | ||
| 36 | /* moreover, clear the soft-triggered, in case it was the reason */ | ||
| 37 | writel(1 << irq, base + VIC_INT_SOFT_CLEAR); | ||
| 38 | } | ||
| 39 | |||
| 40 | static void vic_mask_irq(unsigned int irq) | ||
| 41 | { | ||
| 42 | void __iomem *base = get_irq_chip_data(irq); | ||
| 43 | irq &= 31; | ||
| 44 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | ||
| 45 | } | ||
| 46 | |||
| 47 | static void vic_unmask_irq(unsigned int irq) | ||
| 48 | { | ||
| 49 | void __iomem *base = get_irq_chip_data(irq); | ||
| 50 | irq &= 31; | ||
| 51 | writel(1 << irq, base + VIC_INT_ENABLE); | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * vic_init2 - common initialisation code | ||
| 56 | * @base: Base of the VIC. | ||
| 57 | * | ||
| 58 | * Common initialisation code for registeration | ||
| 59 | * and resume. | ||
| 60 | */ | ||
| 61 | static void vic_init2(void __iomem *base) | ||
| 62 | { | ||
| 63 | int i; | ||
| 64 | |||
| 65 | for (i = 0; i < 16; i++) { | ||
| 66 | void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); | ||
| 67 | writel(VIC_VECT_CNTL_ENABLE | i, reg); | ||
| 68 | } | ||
| 69 | |||
| 70 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | ||
| 71 | } | ||
| 72 | |||
| 73 | #if defined(CONFIG_PM) | 32 | #if defined(CONFIG_PM) |
| 74 | /** | 33 | /** |
| 75 | * struct vic_device - VIC PM device | 34 | * struct vic_device - VIC PM device |
| @@ -99,13 +58,34 @@ struct vic_device { | |||
| 99 | /* we cannot allocate memory when VICs are initially registered */ | 58 | /* we cannot allocate memory when VICs are initially registered */ |
| 100 | static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; | 59 | static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; |
| 101 | 60 | ||
| 61 | static int vic_id; | ||
| 62 | |||
| 102 | static inline struct vic_device *to_vic(struct sys_device *sys) | 63 | static inline struct vic_device *to_vic(struct sys_device *sys) |
| 103 | { | 64 | { |
| 104 | return container_of(sys, struct vic_device, sysdev); | 65 | return container_of(sys, struct vic_device, sysdev); |
| 105 | } | 66 | } |
| 67 | #endif /* CONFIG_PM */ | ||
| 106 | 68 | ||
| 107 | static int vic_id; | 69 | /** |
| 70 | * vic_init2 - common initialisation code | ||
| 71 | * @base: Base of the VIC. | ||
| 72 | * | ||
| 73 | * Common initialisation code for registeration | ||
| 74 | * and resume. | ||
| 75 | */ | ||
| 76 | static 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 | } | ||
| 108 | 87 | ||
| 88 | #if defined(CONFIG_PM) | ||
| 109 | static int vic_class_resume(struct sys_device *dev) | 89 | static int vic_class_resume(struct sys_device *dev) |
| 110 | { | 90 | { |
| 111 | struct vic_device *vic = to_vic(dev); | 91 | struct vic_device *vic = to_vic(dev); |
| @@ -159,31 +139,6 @@ struct sysdev_class vic_class = { | |||
| 159 | }; | 139 | }; |
| 160 | 140 | ||
| 161 | /** | 141 | /** |
| 162 | * vic_pm_register - Register a VIC for later power management control | ||
| 163 | * @base: The base address of the VIC. | ||
| 164 | * @irq: The base IRQ for the VIC. | ||
| 165 | * @resume_sources: bitmask of interrupts allowed for resume sources. | ||
| 166 | * | ||
| 167 | * Register the VIC with the system device tree so that it can be notified | ||
| 168 | * of suspend and resume requests and ensure that the correct actions are | ||
| 169 | * taken to re-instate the settings on resume. | ||
| 170 | */ | ||
| 171 | static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) | ||
| 172 | { | ||
| 173 | struct vic_device *v; | ||
| 174 | |||
| 175 | if (vic_id >= ARRAY_SIZE(vic_devices)) | ||
| 176 | printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); | ||
| 177 | else { | ||
| 178 | v = &vic_devices[vic_id]; | ||
| 179 | v->base = base; | ||
| 180 | v->resume_sources = resume_sources; | ||
| 181 | v->irq = irq; | ||
| 182 | vic_id++; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | /** | ||
| 187 | * vic_pm_init - initicall to register VIC pm | 142 | * vic_pm_init - initicall to register VIC pm |
| 188 | * | 143 | * |
| 189 | * This is called via late_initcall() to register | 144 | * This is called via late_initcall() to register |
| @@ -219,9 +174,60 @@ static int __init vic_pm_init(void) | |||
| 219 | 174 | ||
| 220 | return 0; | 175 | return 0; |
| 221 | } | 176 | } |
| 222 | |||
| 223 | late_initcall(vic_pm_init); | 177 | late_initcall(vic_pm_init); |
| 224 | 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 | */ | ||
| 189 | static 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 | ||
| 204 | static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } | ||
| 205 | #endif /* CONFIG_PM */ | ||
| 206 | |||
| 207 | static 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 | |||
| 216 | static 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 | |||
| 223 | static 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) | ||
| 225 | static struct vic_device *vic_from_irq(unsigned int irq) | 231 | static struct vic_device *vic_from_irq(unsigned int irq) |
| 226 | { | 232 | { |
| 227 | struct vic_device *v = vic_devices; | 233 | struct vic_device *v = vic_devices; |
| @@ -255,10 +261,7 @@ static int vic_set_wake(unsigned int irq, unsigned int on) | |||
| 255 | 261 | ||
| 256 | return 0; | 262 | return 0; |
| 257 | } | 263 | } |
| 258 | |||
| 259 | #else | 264 | #else |
| 260 | static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } | ||
| 261 | |||
| 262 | #define vic_set_wake NULL | 265 | #define vic_set_wake NULL |
| 263 | #endif /* CONFIG_PM */ | 266 | #endif /* CONFIG_PM */ |
| 264 | 267 | ||
| @@ -270,9 +273,62 @@ static struct irq_chip vic_chip = { | |||
| 270 | .set_wake = vic_set_wake, | 273 | .set_wake = vic_set_wake, |
| 271 | }; | 274 | }; |
| 272 | 275 | ||
| 273 | /* The PL190 cell from ARM has been modified by ST, so handle both here */ | 276 | /* |
| 274 | static void vik_init_st(void __iomem *base, unsigned int irq_start, | 277 | * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. |
| 275 | u32 vic_sources); | 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 | */ | ||
| 283 | static 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 | } | ||
| 276 | 332 | ||
| 277 | /** | 333 | /** |
| 278 | * vic_init - initialise a vectored interrupt controller | 334 | * vic_init - initialise a vectored interrupt controller |
| @@ -299,7 +355,7 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
| 299 | 355 | ||
| 300 | switch(vendor) { | 356 | switch(vendor) { |
| 301 | case AMBA_VENDOR_ST: | 357 | case AMBA_VENDOR_ST: |
| 302 | vik_init_st(base, irq_start, vic_sources); | 358 | vic_init_st(base, irq_start, vic_sources); |
| 303 | return; | 359 | return; |
| 304 | default: | 360 | default: |
| 305 | printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); | 361 | printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); |
| @@ -343,60 +399,3 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
| 343 | 399 | ||
| 344 | vic_pm_register(base, irq_start, resume_sources); | 400 | vic_pm_register(base, irq_start, resume_sources); |
| 345 | } | 401 | } |
| 346 | |||
| 347 | /* | ||
| 348 | * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. | ||
| 349 | * The original cell has 32 interrupts, while the modified one has 64, | ||
| 350 | * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case | ||
| 351 | * the probe function is called twice, with base set to offset 000 | ||
| 352 | * and 020 within the page. We call this "second block". | ||
| 353 | */ | ||
| 354 | static void __init vik_init_st(void __iomem *base, unsigned int irq_start, | ||
| 355 | u32 vic_sources) | ||
| 356 | { | ||
| 357 | unsigned int i; | ||
| 358 | int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; | ||
| 359 | |||
| 360 | /* Disable all interrupts initially. */ | ||
| 361 | |||
| 362 | writel(0, base + VIC_INT_SELECT); | ||
| 363 | writel(0, base + VIC_INT_ENABLE); | ||
| 364 | writel(~0, base + VIC_INT_ENABLE_CLEAR); | ||
| 365 | writel(0, base + VIC_IRQ_STATUS); | ||
| 366 | writel(0, base + VIC_ITCR); | ||
| 367 | writel(~0, base + VIC_INT_SOFT_CLEAR); | ||
| 368 | |||
| 369 | /* | ||
| 370 | * Make sure we clear all existing interrupts. The vector registers | ||
| 371 | * in this cell are after the second block of general registers, | ||
| 372 | * so we can address them using standard offsets, but only from | ||
| 373 | * the second base address, which is 0x20 in the page | ||
| 374 | */ | ||
| 375 | if (vic_2nd_block) { | ||
| 376 | writel(0, base + VIC_PL190_VECT_ADDR); | ||
| 377 | for (i = 0; i < 19; i++) { | ||
| 378 | unsigned int value; | ||
| 379 | |||
| 380 | value = readl(base + VIC_PL190_VECT_ADDR); | ||
| 381 | writel(value, base + VIC_PL190_VECT_ADDR); | ||
| 382 | } | ||
| 383 | /* ST has 16 vectors as well, but we don't enable them by now */ | ||
| 384 | for (i = 0; i < 16; i++) { | ||
| 385 | void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); | ||
| 386 | writel(0, reg); | ||
| 387 | } | ||
| 388 | |||
| 389 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | ||
| 390 | } | ||
| 391 | |||
| 392 | for (i = 0; i < 32; i++) { | ||
| 393 | if (vic_sources & (1 << i)) { | ||
| 394 | unsigned int irq = irq_start + i; | ||
| 395 | |||
| 396 | set_irq_chip(irq, &vic_chip); | ||
| 397 | set_irq_chip_data(irq, base); | ||
| 398 | set_irq_handler(irq, handle_level_irq); | ||
| 399 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | } | ||
