diff options
Diffstat (limited to 'arch/arm/common/vic.c')
-rw-r--r-- | arch/arm/common/vic.c | 221 |
1 files changed, 214 insertions, 7 deletions
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index b2a781d9ce05..887c6eb3a18a 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,219 @@ 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 | |||
233 | if (!v) | ||
234 | return -EINVAL; | ||
235 | |||
236 | if (on) | ||
237 | v->resume_irqs |= 1 << off; | ||
238 | else | ||
239 | v->resume_irqs &= ~(1 << off); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | #else | ||
245 | static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } | ||
246 | |||
247 | #define vic_set_wake NULL | ||
248 | #endif /* CONFIG_PM */ | ||
249 | |||
42 | static struct irq_chip vic_chip = { | 250 | static struct irq_chip vic_chip = { |
43 | .name = "VIC", | 251 | .name = "VIC", |
44 | .ack = vic_mask_irq, | 252 | .ack = vic_mask_irq, |
45 | .mask = vic_mask_irq, | 253 | .mask = vic_mask_irq, |
46 | .unmask = vic_unmask_irq, | 254 | .unmask = vic_unmask_irq, |
255 | .set_wake = vic_set_wake, | ||
47 | }; | 256 | }; |
48 | 257 | ||
49 | /** | 258 | /** |
@@ -51,9 +260,10 @@ static struct irq_chip vic_chip = { | |||
51 | * @base: iomem base address | 260 | * @base: iomem base address |
52 | * @irq_start: starting interrupt number, must be muliple of 32 | 261 | * @irq_start: starting interrupt number, must be muliple of 32 |
53 | * @vic_sources: bitmask of interrupt sources to allow | 262 | * @vic_sources: bitmask of interrupt sources to allow |
263 | * @resume_sources: bitmask of interrupt sources to allow for resume | ||
54 | */ | 264 | */ |
55 | void __init vic_init(void __iomem *base, unsigned int irq_start, | 265 | void __init vic_init(void __iomem *base, unsigned int irq_start, |
56 | u32 vic_sources) | 266 | u32 vic_sources, u32 resume_sources) |
57 | { | 267 | { |
58 | unsigned int i; | 268 | unsigned int i; |
59 | 269 | ||
@@ -77,12 +287,7 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
77 | writel(value, base + VIC_PL190_VECT_ADDR); | 287 | writel(value, base + VIC_PL190_VECT_ADDR); |
78 | } | 288 | } |
79 | 289 | ||
80 | for (i = 0; i < 16; i++) { | 290 | 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 | 291 | ||
87 | for (i = 0; i < 32; i++) { | 292 | for (i = 0; i < 32; i++) { |
88 | if (vic_sources & (1 << i)) { | 293 | if (vic_sources & (1 << i)) { |
@@ -94,4 +299,6 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
94 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 299 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
95 | } | 300 | } |
96 | } | 301 | } |
302 | |||
303 | vic_pm_register(base, irq_start, resume_sources); | ||
97 | } | 304 | } |