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