Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ARM] VIC: Add power management device

Add power management support to the VIC by registering
each VIC as a system device to get suspend/resume
events going.

Since the VIC registeration is done early, we need to
record the VICs in a static array which is used to add
the system devices later once the initcalls are run. This
means there is now a configuration value for the number
of VICs in the system.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>

Ben Dooks c07f87f2 d87964c4

+228 -14
+7
arch/arm/common/Kconfig
··· 4 4 config ARM_VIC 5 5 bool 6 6 7 + config ARM_VIC_NR 8 + int 9 + default 2 10 + help 11 + The maximum number of VICs available in the system, for 12 + power management. 13 + 7 14 config ICST525 8 15 bool 9 16
+214 -7
arch/arm/common/vic.c
··· 21 21 #include <linux/init.h> 22 22 #include <linux/list.h> 23 23 #include <linux/io.h> 24 + #include <linux/sysdev.h> 24 25 25 26 #include <asm/mach/irq.h> 26 27 #include <asm/hardware/vic.h> ··· 40 39 writel(1 << irq, base + VIC_INT_ENABLE); 41 40 } 42 41 42 + /** 43 + * vic_init2 - common initialisation code 44 + * @base: Base of the VIC. 45 + * 46 + * Common initialisation code for registeration 47 + * and resume. 48 + */ 49 + static void vic_init2(void __iomem *base) 50 + { 51 + int i; 52 + 53 + for (i = 0; i < 16; i++) { 54 + void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 55 + writel(VIC_VECT_CNTL_ENABLE | i, reg); 56 + } 57 + 58 + writel(32, base + VIC_PL190_DEF_VECT_ADDR); 59 + } 60 + 61 + #if defined(CONFIG_PM) 62 + /** 63 + * struct vic_device - VIC PM device 64 + * @sysdev: The system device which is registered. 65 + * @irq: The IRQ number for the base of the VIC. 66 + * @base: The register base for the VIC. 67 + * @resume_sources: A bitmask of interrupts for resume. 68 + * @resume_irqs: The IRQs enabled for resume. 69 + * @int_select: Save for VIC_INT_SELECT. 70 + * @int_enable: Save for VIC_INT_ENABLE. 71 + * @soft_int: Save for VIC_INT_SOFT. 72 + * @protect: Save for VIC_PROTECT. 73 + */ 74 + struct vic_device { 75 + struct sys_device sysdev; 76 + 77 + void __iomem *base; 78 + int irq; 79 + u32 resume_sources; 80 + u32 resume_irqs; 81 + u32 int_select; 82 + u32 int_enable; 83 + u32 soft_int; 84 + u32 protect; 85 + }; 86 + 87 + /* we cannot allocate memory when VICs are initially registered */ 88 + static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; 89 + 90 + static inline struct vic_device *to_vic(struct sys_device *sys) 91 + { 92 + return container_of(sys, struct vic_device, sysdev); 93 + } 94 + 95 + static int vic_id; 96 + 97 + static int vic_class_resume(struct sys_device *dev) 98 + { 99 + struct vic_device *vic = to_vic(dev); 100 + void __iomem *base = vic->base; 101 + 102 + printk(KERN_DEBUG "%s: resuming vic at %p\n", __func__, base); 103 + 104 + /* re-initialise static settings */ 105 + vic_init2(base); 106 + 107 + writel(vic->int_select, base + VIC_INT_SELECT); 108 + writel(vic->protect, base + VIC_PROTECT); 109 + 110 + /* set the enabled ints and then clear the non-enabled */ 111 + writel(vic->int_enable, base + VIC_INT_ENABLE); 112 + writel(~vic->int_enable, base + VIC_INT_ENABLE_CLEAR); 113 + 114 + /* and the same for the soft-int register */ 115 + 116 + writel(vic->soft_int, base + VIC_INT_SOFT); 117 + writel(~vic->soft_int, base + VIC_INT_SOFT_CLEAR); 118 + 119 + return 0; 120 + } 121 + 122 + static int vic_class_suspend(struct sys_device *dev, pm_message_t state) 123 + { 124 + struct vic_device *vic = to_vic(dev); 125 + void __iomem *base = vic->base; 126 + 127 + printk(KERN_DEBUG "%s: suspending vic at %p\n", __func__, base); 128 + 129 + vic->int_select = readl(base + VIC_INT_SELECT); 130 + vic->int_enable = readl(base + VIC_INT_ENABLE); 131 + vic->soft_int = readl(base + VIC_INT_SOFT); 132 + vic->protect = readl(base + VIC_PROTECT); 133 + 134 + /* set the interrupts (if any) that are used for 135 + * resuming the system */ 136 + 137 + writel(vic->resume_irqs, base + VIC_INT_ENABLE); 138 + writel(~vic->resume_irqs, base + VIC_INT_ENABLE_CLEAR); 139 + 140 + return 0; 141 + } 142 + 143 + struct sysdev_class vic_class = { 144 + .name = "vic", 145 + .suspend = vic_class_suspend, 146 + .resume = vic_class_resume, 147 + }; 148 + 149 + /** 150 + * vic_pm_register - Register a VIC for later power management control 151 + * @base: The base address of the VIC. 152 + * @irq: The base IRQ for the VIC. 153 + * @resume_sources: bitmask of interrupts allowed for resume sources. 154 + * 155 + * Register the VIC with the system device tree so that it can be notified 156 + * of suspend and resume requests and ensure that the correct actions are 157 + * taken to re-instate the settings on resume. 158 + */ 159 + static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) 160 + { 161 + struct vic_device *v; 162 + 163 + if (vic_id >= ARRAY_SIZE(vic_devices)) 164 + printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); 165 + else { 166 + v = &vic_devices[vic_id]; 167 + v->base = base; 168 + v->resume_sources = resume_sources; 169 + v->irq = irq; 170 + vic_id++; 171 + } 172 + } 173 + 174 + /** 175 + * vic_pm_init - initicall to register VIC pm 176 + * 177 + * This is called via late_initcall() to register 178 + * the resources for the VICs due to the early 179 + * nature of the VIC's registration. 180 + */ 181 + static int __init vic_pm_init(void) 182 + { 183 + struct vic_device *dev = vic_devices; 184 + int err; 185 + int id; 186 + 187 + if (vic_id == 0) 188 + return 0; 189 + 190 + err = sysdev_class_register(&vic_class); 191 + if (err) { 192 + printk(KERN_ERR "%s: cannot register class\n", __func__); 193 + return err; 194 + } 195 + 196 + for (id = 0; id < vic_id; id++, dev++) { 197 + dev->sysdev.id = id; 198 + dev->sysdev.cls = &vic_class; 199 + 200 + err = sysdev_register(&dev->sysdev); 201 + if (err) { 202 + printk(KERN_ERR "%s: failed to register device\n", 203 + __func__); 204 + return err; 205 + } 206 + } 207 + 208 + return 0; 209 + } 210 + 211 + late_initcall(vic_pm_init); 212 + 213 + static struct vic_device *vic_from_irq(unsigned int irq) 214 + { 215 + struct vic_device *v = vic_devices; 216 + unsigned int base_irq = irq & ~31; 217 + int id; 218 + 219 + for (id = 0; id < vic_id; id++, v++) { 220 + if (v->irq == base_irq) 221 + return v; 222 + } 223 + 224 + return NULL; 225 + } 226 + 227 + static int vic_set_wake(unsigned int irq, unsigned int on) 228 + { 229 + struct vic_device *v = vic_from_irq(irq); 230 + unsigned int off = irq & 31; 231 + 232 + if (!v) 233 + return -EINVAL; 234 + 235 + if (on) 236 + v->resume_irqs |= 1 << off; 237 + else 238 + v->resume_irqs &= ~(1 << off); 239 + 240 + return 0; 241 + } 242 + 243 + #else 244 + static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } 245 + 246 + #define vic_set_wake NULL 247 + #endif /* CONFIG_PM */ 248 + 43 249 static struct irq_chip vic_chip = { 44 250 .name = "VIC", 45 251 .ack = vic_mask_irq, 46 252 .mask = vic_mask_irq, 47 253 .unmask = vic_unmask_irq, 254 + .set_wake = vic_set_wake, 48 255 }; 49 256 50 257 /** ··· 260 51 * @base: iomem base address 261 52 * @irq_start: starting interrupt number, must be muliple of 32 262 53 * @vic_sources: bitmask of interrupt sources to allow 54 + * @resume_sources: bitmask of interrupt sources to allow for resume 263 55 */ 264 56 void __init vic_init(void __iomem *base, unsigned int irq_start, 265 - u32 vic_sources) 57 + u32 vic_sources, u32 resume_sources) 266 58 { 267 59 unsigned int i; 268 60 ··· 287 77 writel(value, base + VIC_PL190_VECT_ADDR); 288 78 } 289 79 290 - for (i = 0; i < 16; i++) { 291 - void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 292 - writel(VIC_VECT_CNTL_ENABLE | i, reg); 293 - } 294 - 295 - writel(32, base + VIC_PL190_DEF_VECT_ADDR); 80 + vic_init2(base); 296 81 297 82 for (i = 0; i < 32; i++) { 298 83 if (vic_sources & (1 << i)) { ··· 299 94 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 300 95 } 301 96 } 97 + 98 + vic_pm_register(base, irq_start, resume_sources); 302 99 }
+1 -1
arch/arm/include/asm/hardware/vic.h
··· 41 41 #define VIC_PL192_VECT_ADDR 0xF00 42 42 43 43 #ifndef __ASSEMBLY__ 44 - void vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources); 44 + void vic_init(void __iomem *base, unsigned int irq_start, u32 vic_sources, u32 resume_sources); 45 45 #endif 46 46 47 47 #endif
+2 -2
arch/arm/mach-ep93xx/core.c
··· 362 362 { 363 363 int gpio_irq; 364 364 365 - vic_init((void *)EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK); 366 - vic_init((void *)EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK); 365 + vic_init((void *)EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK, 0); 366 + vic_init((void *)EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK, 0); 367 367 368 368 for (gpio_irq = gpio_to_irq(0); 369 369 gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) {
+1 -1
arch/arm/mach-netx/generic.c
··· 168 168 { 169 169 int irq; 170 170 171 - vic_init(__io(io_p2v(NETX_PA_VIC)), 0, ~0); 171 + vic_init(__io(io_p2v(NETX_PA_VIC)), 0, ~0, 0); 172 172 173 173 for (irq = NETX_IRQ_HIF_CHAINED(0); irq <= NETX_IRQ_HIF_LAST; irq++) { 174 174 set_irq_chip(irq, &netx_hif_chip);
+1 -1
arch/arm/mach-versatile/core.c
··· 116 116 { 117 117 unsigned int i; 118 118 119 - vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0); 119 + vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0); 120 120 121 121 set_irq_chained_handler(IRQ_VICSOURCE31, sic_handle_irq); 122 122
+2 -2
arch/arm/plat-s3c64xx/irq.c
··· 232 232 printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); 233 233 234 234 /* initialise the pair of VICs */ 235 - vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid); 236 - vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid); 235 + vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid, 0); 236 + vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid, 0); 237 237 238 238 /* add the timer sub-irqs */ 239 239