diff options
Diffstat (limited to 'arch/arm/mach-sa1100/neponset.c')
-rw-r--r-- | arch/arm/mach-sa1100/neponset.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c new file mode 100644 index 000000000000..1405383463ea --- /dev/null +++ b/arch/arm/mach-sa1100/neponset.c | |||
@@ -0,0 +1,342 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-sa1100/neponset.c | ||
3 | * | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/init.h> | ||
7 | #include <linux/ptrace.h> | ||
8 | #include <linux/tty.h> | ||
9 | #include <linux/ioport.h> | ||
10 | #include <linux/serial_core.h> | ||
11 | #include <linux/device.h> | ||
12 | #include <linux/slab.h> | ||
13 | |||
14 | #include <asm/hardware.h> | ||
15 | #include <asm/mach-types.h> | ||
16 | #include <asm/irq.h> | ||
17 | #include <asm/mach/map.h> | ||
18 | #include <asm/mach/irq.h> | ||
19 | #include <asm/mach/serial_sa1100.h> | ||
20 | #include <asm/arch/assabet.h> | ||
21 | #include <asm/arch/neponset.h> | ||
22 | #include <asm/hardware/sa1111.h> | ||
23 | #include <asm/sizes.h> | ||
24 | |||
25 | /* | ||
26 | * Install handler for Neponset IRQ. Note that we have to loop here | ||
27 | * since the ETHERNET and USAR IRQs are level based, and we need to | ||
28 | * ensure that the IRQ signal is deasserted before returning. This | ||
29 | * is rather unfortunate. | ||
30 | */ | ||
31 | static void | ||
32 | neponset_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) | ||
33 | { | ||
34 | unsigned int irr; | ||
35 | |||
36 | while (1) { | ||
37 | struct irqdesc *d; | ||
38 | |||
39 | /* | ||
40 | * Acknowledge the parent IRQ. | ||
41 | */ | ||
42 | desc->chip->ack(irq); | ||
43 | |||
44 | /* | ||
45 | * Read the interrupt reason register. Let's have all | ||
46 | * active IRQ bits high. Note: there is a typo in the | ||
47 | * Neponset user's guide for the SA1111 IRR level. | ||
48 | */ | ||
49 | irr = IRR ^ (IRR_ETHERNET | IRR_USAR); | ||
50 | |||
51 | if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0) | ||
52 | break; | ||
53 | |||
54 | /* | ||
55 | * Since there is no individual mask, we have to | ||
56 | * mask the parent IRQ. This is safe, since we'll | ||
57 | * recheck the register for any pending IRQs. | ||
58 | */ | ||
59 | if (irr & (IRR_ETHERNET | IRR_USAR)) { | ||
60 | desc->chip->mask(irq); | ||
61 | |||
62 | if (irr & IRR_ETHERNET) { | ||
63 | d = irq_desc + IRQ_NEPONSET_SMC9196; | ||
64 | d->handle(IRQ_NEPONSET_SMC9196, d, regs); | ||
65 | } | ||
66 | |||
67 | if (irr & IRR_USAR) { | ||
68 | d = irq_desc + IRQ_NEPONSET_USAR; | ||
69 | d->handle(IRQ_NEPONSET_USAR, d, regs); | ||
70 | } | ||
71 | |||
72 | desc->chip->unmask(irq); | ||
73 | } | ||
74 | |||
75 | if (irr & IRR_SA1111) { | ||
76 | d = irq_desc + IRQ_NEPONSET_SA1111; | ||
77 | d->handle(IRQ_NEPONSET_SA1111, d, regs); | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) | ||
83 | { | ||
84 | u_int mdm_ctl0 = MDM_CTL_0; | ||
85 | |||
86 | if (port->mapbase == _Ser1UTCR0) { | ||
87 | if (mctrl & TIOCM_RTS) | ||
88 | mdm_ctl0 &= ~MDM_CTL0_RTS2; | ||
89 | else | ||
90 | mdm_ctl0 |= MDM_CTL0_RTS2; | ||
91 | |||
92 | if (mctrl & TIOCM_DTR) | ||
93 | mdm_ctl0 &= ~MDM_CTL0_DTR2; | ||
94 | else | ||
95 | mdm_ctl0 |= MDM_CTL0_DTR2; | ||
96 | } else if (port->mapbase == _Ser3UTCR0) { | ||
97 | if (mctrl & TIOCM_RTS) | ||
98 | mdm_ctl0 &= ~MDM_CTL0_RTS1; | ||
99 | else | ||
100 | mdm_ctl0 |= MDM_CTL0_RTS1; | ||
101 | |||
102 | if (mctrl & TIOCM_DTR) | ||
103 | mdm_ctl0 &= ~MDM_CTL0_DTR1; | ||
104 | else | ||
105 | mdm_ctl0 |= MDM_CTL0_DTR1; | ||
106 | } | ||
107 | |||
108 | MDM_CTL_0 = mdm_ctl0; | ||
109 | } | ||
110 | |||
111 | static u_int neponset_get_mctrl(struct uart_port *port) | ||
112 | { | ||
113 | u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR; | ||
114 | u_int mdm_ctl1 = MDM_CTL_1; | ||
115 | |||
116 | if (port->mapbase == _Ser1UTCR0) { | ||
117 | if (mdm_ctl1 & MDM_CTL1_DCD2) | ||
118 | ret &= ~TIOCM_CD; | ||
119 | if (mdm_ctl1 & MDM_CTL1_CTS2) | ||
120 | ret &= ~TIOCM_CTS; | ||
121 | if (mdm_ctl1 & MDM_CTL1_DSR2) | ||
122 | ret &= ~TIOCM_DSR; | ||
123 | } else if (port->mapbase == _Ser3UTCR0) { | ||
124 | if (mdm_ctl1 & MDM_CTL1_DCD1) | ||
125 | ret &= ~TIOCM_CD; | ||
126 | if (mdm_ctl1 & MDM_CTL1_CTS1) | ||
127 | ret &= ~TIOCM_CTS; | ||
128 | if (mdm_ctl1 & MDM_CTL1_DSR1) | ||
129 | ret &= ~TIOCM_DSR; | ||
130 | } | ||
131 | |||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | static struct sa1100_port_fns neponset_port_fns __initdata = { | ||
136 | .set_mctrl = neponset_set_mctrl, | ||
137 | .get_mctrl = neponset_get_mctrl, | ||
138 | }; | ||
139 | |||
140 | static int neponset_probe(struct device *dev) | ||
141 | { | ||
142 | sa1100_register_uart_fns(&neponset_port_fns); | ||
143 | |||
144 | /* | ||
145 | * Install handler for GPIO25. | ||
146 | */ | ||
147 | set_irq_type(IRQ_GPIO25, IRQT_RISING); | ||
148 | set_irq_chained_handler(IRQ_GPIO25, neponset_irq_handler); | ||
149 | |||
150 | /* | ||
151 | * We would set IRQ_GPIO25 to be a wake-up IRQ, but | ||
152 | * unfortunately something on the Neponset activates | ||
153 | * this IRQ on sleep (ethernet?) | ||
154 | */ | ||
155 | #if 0 | ||
156 | enable_irq_wake(IRQ_GPIO25); | ||
157 | #endif | ||
158 | |||
159 | /* | ||
160 | * Setup other Neponset IRQs. SA1111 will be done by the | ||
161 | * generic SA1111 code. | ||
162 | */ | ||
163 | set_irq_handler(IRQ_NEPONSET_SMC9196, do_simple_IRQ); | ||
164 | set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); | ||
165 | set_irq_handler(IRQ_NEPONSET_USAR, do_simple_IRQ); | ||
166 | set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); | ||
167 | |||
168 | /* | ||
169 | * Disable GPIO 0/1 drivers so the buttons work on the module. | ||
170 | */ | ||
171 | NCR_0 = NCR_GP01_OFF; | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | #ifdef CONFIG_PM | ||
177 | |||
178 | /* | ||
179 | * LDM power management. | ||
180 | */ | ||
181 | static int neponset_suspend(struct device *dev, pm_message_t state, u32 level) | ||
182 | { | ||
183 | /* | ||
184 | * Save state. | ||
185 | */ | ||
186 | if (level == SUSPEND_SAVE_STATE || | ||
187 | level == SUSPEND_DISABLE || | ||
188 | level == SUSPEND_POWER_DOWN) { | ||
189 | if (!dev->power.saved_state) | ||
190 | dev->power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL); | ||
191 | if (!dev->power.saved_state) | ||
192 | return -ENOMEM; | ||
193 | |||
194 | *(unsigned int *)dev->power.saved_state = NCR_0; | ||
195 | } | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static int neponset_resume(struct device *dev, u32 level) | ||
201 | { | ||
202 | if (level == RESUME_RESTORE_STATE || level == RESUME_ENABLE) { | ||
203 | if (dev->power.saved_state) { | ||
204 | NCR_0 = *(unsigned int *)dev->power.saved_state; | ||
205 | kfree(dev->power.saved_state); | ||
206 | dev->power.saved_state = NULL; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | #else | ||
214 | #define neponset_suspend NULL | ||
215 | #define neponset_resume NULL | ||
216 | #endif | ||
217 | |||
218 | static struct device_driver neponset_device_driver = { | ||
219 | .name = "neponset", | ||
220 | .bus = &platform_bus_type, | ||
221 | .probe = neponset_probe, | ||
222 | .suspend = neponset_suspend, | ||
223 | .resume = neponset_resume, | ||
224 | }; | ||
225 | |||
226 | static struct resource neponset_resources[] = { | ||
227 | [0] = { | ||
228 | .start = 0x10000000, | ||
229 | .end = 0x17ffffff, | ||
230 | .flags = IORESOURCE_MEM, | ||
231 | }, | ||
232 | }; | ||
233 | |||
234 | static struct platform_device neponset_device = { | ||
235 | .name = "neponset", | ||
236 | .id = 0, | ||
237 | .num_resources = ARRAY_SIZE(neponset_resources), | ||
238 | .resource = neponset_resources, | ||
239 | }; | ||
240 | |||
241 | static struct resource sa1111_resources[] = { | ||
242 | [0] = { | ||
243 | .start = 0x40000000, | ||
244 | .end = 0x40001fff, | ||
245 | .flags = IORESOURCE_MEM, | ||
246 | }, | ||
247 | [1] = { | ||
248 | .start = IRQ_NEPONSET_SA1111, | ||
249 | .end = IRQ_NEPONSET_SA1111, | ||
250 | .flags = IORESOURCE_IRQ, | ||
251 | }, | ||
252 | }; | ||
253 | |||
254 | static u64 sa1111_dmamask = 0xffffffffUL; | ||
255 | |||
256 | static struct platform_device sa1111_device = { | ||
257 | .name = "sa1111", | ||
258 | .id = 0, | ||
259 | .dev = { | ||
260 | .dma_mask = &sa1111_dmamask, | ||
261 | .coherent_dma_mask = 0xffffffff, | ||
262 | }, | ||
263 | .num_resources = ARRAY_SIZE(sa1111_resources), | ||
264 | .resource = sa1111_resources, | ||
265 | }; | ||
266 | |||
267 | static struct resource smc91x_resources[] = { | ||
268 | [0] = { | ||
269 | .name = "smc91x-regs", | ||
270 | .start = SA1100_CS3_PHYS, | ||
271 | .end = SA1100_CS3_PHYS + 0x01ffffff, | ||
272 | .flags = IORESOURCE_MEM, | ||
273 | }, | ||
274 | [1] = { | ||
275 | .start = IRQ_NEPONSET_SMC9196, | ||
276 | .end = IRQ_NEPONSET_SMC9196, | ||
277 | .flags = IORESOURCE_IRQ, | ||
278 | }, | ||
279 | [2] = { | ||
280 | .name = "smc91x-attrib", | ||
281 | .start = SA1100_CS3_PHYS + 0x02000000, | ||
282 | .end = SA1100_CS3_PHYS + 0x03ffffff, | ||
283 | .flags = IORESOURCE_MEM, | ||
284 | }, | ||
285 | }; | ||
286 | |||
287 | static struct platform_device smc91x_device = { | ||
288 | .name = "smc91x", | ||
289 | .id = 0, | ||
290 | .num_resources = ARRAY_SIZE(smc91x_resources), | ||
291 | .resource = smc91x_resources, | ||
292 | }; | ||
293 | |||
294 | static struct platform_device *devices[] __initdata = { | ||
295 | &neponset_device, | ||
296 | &sa1111_device, | ||
297 | &smc91x_device, | ||
298 | }; | ||
299 | |||
300 | static int __init neponset_init(void) | ||
301 | { | ||
302 | driver_register(&neponset_device_driver); | ||
303 | |||
304 | /* | ||
305 | * The Neponset is only present on the Assabet machine type. | ||
306 | */ | ||
307 | if (!machine_is_assabet()) | ||
308 | return -ENODEV; | ||
309 | |||
310 | /* | ||
311 | * Ensure that the memory bus request/grant signals are setup, | ||
312 | * and the grant is held in its inactive state, whether or not | ||
313 | * we actually have a Neponset attached. | ||
314 | */ | ||
315 | sa1110_mb_disable(); | ||
316 | |||
317 | if (!machine_has_neponset()) { | ||
318 | printk(KERN_DEBUG "Neponset expansion board not present\n"); | ||
319 | return -ENODEV; | ||
320 | } | ||
321 | |||
322 | if (WHOAMI != 0x11) { | ||
323 | printk(KERN_WARNING "Neponset board detected, but " | ||
324 | "wrong ID: %02x\n", WHOAMI); | ||
325 | return -ENODEV; | ||
326 | } | ||
327 | |||
328 | return platform_add_devices(devices, ARRAY_SIZE(devices)); | ||
329 | } | ||
330 | |||
331 | subsys_initcall(neponset_init); | ||
332 | |||
333 | static struct map_desc neponset_io_desc[] __initdata = { | ||
334 | /* virtual physical length type */ | ||
335 | { 0xf3000000, 0x10000000, SZ_1M, MT_DEVICE }, /* System Registers */ | ||
336 | { 0xf4000000, 0x40000000, SZ_1M, MT_DEVICE } /* SA-1111 */ | ||
337 | }; | ||
338 | |||
339 | void __init neponset_map_io(void) | ||
340 | { | ||
341 | iotable_init(neponset_io_desc, ARRAY_SIZE(neponset_io_desc)); | ||
342 | } | ||