Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/arm/mach-sa1100/neponset.c | |
1da177e4 | 3 | */ |
1da177e4 | 4 | #include <linux/init.h> |
1da177e4 | 5 | #include <linux/ioport.h> |
92e617d9 | 6 | #include <linux/kernel.h> |
ae14c2e2 | 7 | #include <linux/module.h> |
d052d1be | 8 | #include <linux/platform_device.h> |
92e617d9 | 9 | #include <linux/serial_core.h> |
ae14c2e2 | 10 | #include <linux/slab.h> |
1da177e4 | 11 | |
1da177e4 LT |
12 | #include <asm/mach-types.h> |
13 | #include <asm/irq.h> | |
14 | #include <asm/mach/map.h> | |
15 | #include <asm/mach/irq.h> | |
16 | #include <asm/mach/serial_sa1100.h> | |
1da177e4 LT |
17 | #include <asm/hardware/sa1111.h> |
18 | #include <asm/sizes.h> | |
19 | ||
92e617d9 RK |
20 | #include <mach/hardware.h> |
21 | #include <mach/assabet.h> | |
22 | #include <mach/neponset.h> | |
23 | ||
ae14c2e2 RK |
24 | struct neponset_drvdata { |
25 | #ifdef CONFIG_PM_SLEEP | |
26 | u32 ncr0; | |
27 | u32 mdm_ctl_0; | |
28 | #endif | |
29 | }; | |
30 | ||
6ad1b614 RK |
31 | void neponset_ncr_frob(unsigned int mask, unsigned int val) |
32 | { | |
33 | unsigned long flags; | |
34 | ||
35 | local_irq_save(flags); | |
36 | NCR_0 = (NCR_0 & ~mask) | val; | |
37 | local_irq_restore(flags); | |
38 | } | |
39 | ||
92e617d9 RK |
40 | static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) |
41 | { | |
42 | u_int mdm_ctl0 = MDM_CTL_0; | |
43 | ||
44 | if (port->mapbase == _Ser1UTCR0) { | |
45 | if (mctrl & TIOCM_RTS) | |
46 | mdm_ctl0 &= ~MDM_CTL0_RTS2; | |
47 | else | |
48 | mdm_ctl0 |= MDM_CTL0_RTS2; | |
49 | ||
50 | if (mctrl & TIOCM_DTR) | |
51 | mdm_ctl0 &= ~MDM_CTL0_DTR2; | |
52 | else | |
53 | mdm_ctl0 |= MDM_CTL0_DTR2; | |
54 | } else if (port->mapbase == _Ser3UTCR0) { | |
55 | if (mctrl & TIOCM_RTS) | |
56 | mdm_ctl0 &= ~MDM_CTL0_RTS1; | |
57 | else | |
58 | mdm_ctl0 |= MDM_CTL0_RTS1; | |
59 | ||
60 | if (mctrl & TIOCM_DTR) | |
61 | mdm_ctl0 &= ~MDM_CTL0_DTR1; | |
62 | else | |
63 | mdm_ctl0 |= MDM_CTL0_DTR1; | |
64 | } | |
65 | ||
66 | MDM_CTL_0 = mdm_ctl0; | |
67 | } | |
68 | ||
69 | static u_int neponset_get_mctrl(struct uart_port *port) | |
70 | { | |
71 | u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; | |
72 | u_int mdm_ctl1 = MDM_CTL_1; | |
73 | ||
74 | if (port->mapbase == _Ser1UTCR0) { | |
75 | if (mdm_ctl1 & MDM_CTL1_DCD2) | |
76 | ret &= ~TIOCM_CD; | |
77 | if (mdm_ctl1 & MDM_CTL1_CTS2) | |
78 | ret &= ~TIOCM_CTS; | |
79 | if (mdm_ctl1 & MDM_CTL1_DSR2) | |
80 | ret &= ~TIOCM_DSR; | |
81 | } else if (port->mapbase == _Ser3UTCR0) { | |
82 | if (mdm_ctl1 & MDM_CTL1_DCD1) | |
83 | ret &= ~TIOCM_CD; | |
84 | if (mdm_ctl1 & MDM_CTL1_CTS1) | |
85 | ret &= ~TIOCM_CTS; | |
86 | if (mdm_ctl1 & MDM_CTL1_DSR1) | |
87 | ret &= ~TIOCM_DSR; | |
88 | } | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | static struct sa1100_port_fns neponset_port_fns __devinitdata = { | |
94 | .set_mctrl = neponset_set_mctrl, | |
95 | .get_mctrl = neponset_get_mctrl, | |
96 | }; | |
97 | ||
1da177e4 LT |
98 | /* |
99 | * Install handler for Neponset IRQ. Note that we have to loop here | |
100 | * since the ETHERNET and USAR IRQs are level based, and we need to | |
101 | * ensure that the IRQ signal is deasserted before returning. This | |
102 | * is rather unfortunate. | |
103 | */ | |
104 | static void | |
10dd5ce2 | 105 | neponset_irq_handler(unsigned int irq, struct irq_desc *desc) |
1da177e4 LT |
106 | { |
107 | unsigned int irr; | |
108 | ||
109 | while (1) { | |
1da177e4 LT |
110 | /* |
111 | * Acknowledge the parent IRQ. | |
112 | */ | |
c4e8964e | 113 | desc->irq_data.chip->irq_ack(&desc->irq_data); |
1da177e4 LT |
114 | |
115 | /* | |
116 | * Read the interrupt reason register. Let's have all | |
117 | * active IRQ bits high. Note: there is a typo in the | |
118 | * Neponset user's guide for the SA1111 IRR level. | |
119 | */ | |
120 | irr = IRR ^ (IRR_ETHERNET | IRR_USAR); | |
121 | ||
122 | if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0) | |
123 | break; | |
124 | ||
125 | /* | |
126 | * Since there is no individual mask, we have to | |
127 | * mask the parent IRQ. This is safe, since we'll | |
128 | * recheck the register for any pending IRQs. | |
129 | */ | |
130 | if (irr & (IRR_ETHERNET | IRR_USAR)) { | |
c4e8964e | 131 | desc->irq_data.chip->irq_mask(&desc->irq_data); |
1da177e4 | 132 | |
d782f33d RK |
133 | /* |
134 | * Ack the interrupt now to prevent re-entering | |
135 | * this neponset handler. Again, this is safe | |
136 | * since we'll check the IRR register prior to | |
137 | * leaving. | |
138 | */ | |
c4e8964e | 139 | desc->irq_data.chip->irq_ack(&desc->irq_data); |
d782f33d | 140 | |
1da177e4 | 141 | if (irr & IRR_ETHERNET) { |
d8aa0251 | 142 | generic_handle_irq(IRQ_NEPONSET_SMC9196); |
1da177e4 LT |
143 | } |
144 | ||
145 | if (irr & IRR_USAR) { | |
d8aa0251 | 146 | generic_handle_irq(IRQ_NEPONSET_USAR); |
1da177e4 LT |
147 | } |
148 | ||
c4e8964e | 149 | desc->irq_data.chip->irq_unmask(&desc->irq_data); |
1da177e4 LT |
150 | } |
151 | ||
152 | if (irr & IRR_SA1111) { | |
d8aa0251 | 153 | generic_handle_irq(IRQ_NEPONSET_SA1111); |
1da177e4 LT |
154 | } |
155 | } | |
156 | } | |
157 | ||
71045520 RK |
158 | /* |
159 | * Yes, we really do not have any kind of masking or unmasking | |
160 | */ | |
161 | static void nochip_noop(struct irq_data *irq) | |
162 | { | |
163 | } | |
164 | ||
165 | static struct irq_chip nochip = { | |
166 | .name = "neponset", | |
167 | .irq_ack = nochip_noop, | |
168 | .irq_mask = nochip_noop, | |
169 | .irq_unmask = nochip_noop, | |
170 | }; | |
171 | ||
92e617d9 RK |
172 | static struct resource sa1111_resources[] = { |
173 | [0] = DEFINE_RES_MEM(0x40000000, SZ_8K), | |
174 | [1] = DEFINE_RES_IRQ(IRQ_NEPONSET_SA1111), | |
175 | }; | |
176 | ||
177 | static struct sa1111_platform_data sa1111_info = { | |
178 | .irq_base = IRQ_BOARD_END, | |
179 | }; | |
180 | ||
181 | static u64 sa1111_dmamask = 0xffffffffUL; | |
182 | ||
183 | static struct platform_device sa1111_device = { | |
184 | .name = "sa1111", | |
185 | .id = 0, | |
186 | .dev = { | |
187 | .dma_mask = &sa1111_dmamask, | |
188 | .coherent_dma_mask = 0xffffffff, | |
189 | .platform_data = &sa1111_info, | |
190 | }, | |
191 | .num_resources = ARRAY_SIZE(sa1111_resources), | |
192 | .resource = sa1111_resources, | |
193 | }; | |
194 | ||
195 | static struct resource smc91x_resources[] = { | |
196 | [0] = DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS, 0x02000000, "smc91x-regs"), | |
197 | [1] = DEFINE_RES_IRQ(IRQ_NEPONSET_SMC9196), | |
198 | [2] = DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS + 0x02000000, | |
199 | 0x02000000, "smc91x-attrib"), | |
200 | }; | |
201 | ||
202 | static struct platform_device smc91x_device = { | |
203 | .name = "smc91x", | |
204 | .id = 0, | |
205 | .num_resources = ARRAY_SIZE(smc91x_resources), | |
206 | .resource = smc91x_resources, | |
207 | }; | |
208 | ||
cdea4606 | 209 | static int __devinit neponset_probe(struct platform_device *dev) |
1da177e4 | 210 | { |
ae14c2e2 RK |
211 | struct neponset_drvdata *d; |
212 | int ret; | |
213 | ||
214 | d = kzalloc(sizeof(*d), GFP_KERNEL); | |
215 | if (!d) { | |
216 | ret = -ENOMEM; | |
217 | goto err_alloc; | |
218 | } | |
219 | ||
1da177e4 LT |
220 | sa1100_register_uart_fns(&neponset_port_fns); |
221 | ||
222 | /* | |
223 | * Install handler for GPIO25. | |
224 | */ | |
6845664a TG |
225 | irq_set_irq_type(IRQ_GPIO25, IRQ_TYPE_EDGE_RISING); |
226 | irq_set_chained_handler(IRQ_GPIO25, neponset_irq_handler); | |
1da177e4 LT |
227 | |
228 | /* | |
229 | * We would set IRQ_GPIO25 to be a wake-up IRQ, but | |
230 | * unfortunately something on the Neponset activates | |
231 | * this IRQ on sleep (ethernet?) | |
232 | */ | |
233 | #if 0 | |
234 | enable_irq_wake(IRQ_GPIO25); | |
235 | #endif | |
236 | ||
237 | /* | |
238 | * Setup other Neponset IRQs. SA1111 will be done by the | |
239 | * generic SA1111 code. | |
240 | */ | |
71045520 RK |
241 | irq_set_chip_and_handler(IRQ_NEPONSET_SMC9196, &nochip, |
242 | handle_simple_irq); | |
1da177e4 | 243 | set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); |
71045520 RK |
244 | irq_set_chip_and_handler(IRQ_NEPONSET_USAR, &nochip, |
245 | handle_simple_irq); | |
1da177e4 | 246 | set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); |
71045520 | 247 | irq_set_chip(IRQ_NEPONSET_SA1111, &nochip); |
1da177e4 LT |
248 | |
249 | /* | |
250 | * Disable GPIO 0/1 drivers so the buttons work on the module. | |
251 | */ | |
252 | NCR_0 = NCR_GP01_OFF; | |
253 | ||
ae14c2e2 RK |
254 | platform_set_drvdata(dev, d); |
255 | ||
1da177e4 | 256 | return 0; |
ae14c2e2 RK |
257 | |
258 | err_alloc: | |
259 | return ret; | |
1da177e4 LT |
260 | } |
261 | ||
ae14c2e2 RK |
262 | static int __devexit neponset_remove(struct platform_device *dev) |
263 | { | |
264 | struct neponset_drvdata *d = platform_get_drvdata(dev); | |
1da177e4 | 265 | |
ae14c2e2 RK |
266 | irq_set_chained_handler(IRQ_GPIO25, NULL); |
267 | ||
268 | kfree(d); | |
269 | ||
270 | return 0; | |
271 | } | |
93160c63 | 272 | |
ae14c2e2 | 273 | #ifdef CONFIG_PM |
3ae5eaec | 274 | static int neponset_suspend(struct platform_device *dev, pm_message_t state) |
1da177e4 | 275 | { |
ae14c2e2 RK |
276 | struct neponset_drvdata *d = platform_get_drvdata(dev); |
277 | ||
278 | d->ncr0 = NCR_0; | |
279 | d->mdm_ctl_0 = MDM_CTL_0; | |
1da177e4 LT |
280 | |
281 | return 0; | |
282 | } | |
283 | ||
3ae5eaec | 284 | static int neponset_resume(struct platform_device *dev) |
1da177e4 | 285 | { |
ae14c2e2 RK |
286 | struct neponset_drvdata *d = platform_get_drvdata(dev); |
287 | ||
288 | NCR_0 = d->ncr0; | |
289 | MDM_CTL_0 = d->mdm_ctl_0; | |
1da177e4 LT |
290 | |
291 | return 0; | |
292 | } | |
293 | ||
294 | #else | |
295 | #define neponset_suspend NULL | |
296 | #define neponset_resume NULL | |
297 | #endif | |
298 | ||
3ae5eaec | 299 | static struct platform_driver neponset_device_driver = { |
1da177e4 | 300 | .probe = neponset_probe, |
ae14c2e2 | 301 | .remove = __devexit_p(neponset_remove), |
1da177e4 LT |
302 | .suspend = neponset_suspend, |
303 | .resume = neponset_resume, | |
3ae5eaec RK |
304 | .driver = { |
305 | .name = "neponset", | |
398e58d0 | 306 | .owner = THIS_MODULE, |
3ae5eaec | 307 | }, |
1da177e4 LT |
308 | }; |
309 | ||
310 | static struct resource neponset_resources[] = { | |
a181099e | 311 | [0] = DEFINE_RES_MEM(0x10000000, 0x08000000), |
1da177e4 LT |
312 | }; |
313 | ||
314 | static struct platform_device neponset_device = { | |
315 | .name = "neponset", | |
316 | .id = 0, | |
317 | .num_resources = ARRAY_SIZE(neponset_resources), | |
318 | .resource = neponset_resources, | |
319 | }; | |
320 | ||
1da177e4 LT |
321 | static struct platform_device *devices[] __initdata = { |
322 | &neponset_device, | |
323 | &sa1111_device, | |
324 | &smc91x_device, | |
325 | }; | |
326 | ||
cdcb81f7 RK |
327 | extern void sa1110_mb_disable(void); |
328 | ||
1da177e4 LT |
329 | static int __init neponset_init(void) |
330 | { | |
3ae5eaec | 331 | platform_driver_register(&neponset_device_driver); |
1da177e4 LT |
332 | |
333 | /* | |
334 | * The Neponset is only present on the Assabet machine type. | |
335 | */ | |
336 | if (!machine_is_assabet()) | |
337 | return -ENODEV; | |
338 | ||
339 | /* | |
340 | * Ensure that the memory bus request/grant signals are setup, | |
341 | * and the grant is held in its inactive state, whether or not | |
342 | * we actually have a Neponset attached. | |
343 | */ | |
344 | sa1110_mb_disable(); | |
345 | ||
346 | if (!machine_has_neponset()) { | |
347 | printk(KERN_DEBUG "Neponset expansion board not present\n"); | |
348 | return -ENODEV; | |
349 | } | |
350 | ||
351 | if (WHOAMI != 0x11) { | |
352 | printk(KERN_WARNING "Neponset board detected, but " | |
353 | "wrong ID: %02x\n", WHOAMI); | |
354 | return -ENODEV; | |
355 | } | |
356 | ||
357 | return platform_add_devices(devices, ARRAY_SIZE(devices)); | |
358 | } | |
359 | ||
360 | subsys_initcall(neponset_init); | |
361 | ||
362 | static struct map_desc neponset_io_desc[] __initdata = { | |
92519d82 DS |
363 | { /* System Registers */ |
364 | .virtual = 0xf3000000, | |
365 | .pfn = __phys_to_pfn(0x10000000), | |
366 | .length = SZ_1M, | |
367 | .type = MT_DEVICE | |
368 | }, { /* SA-1111 */ | |
369 | .virtual = 0xf4000000, | |
370 | .pfn = __phys_to_pfn(0x40000000), | |
371 | .length = SZ_1M, | |
372 | .type = MT_DEVICE | |
373 | } | |
1da177e4 LT |
374 | }; |
375 | ||
376 | void __init neponset_map_io(void) | |
377 | { | |
378 | iotable_init(neponset_io_desc, ARRAY_SIZE(neponset_io_desc)); | |
379 | } |