diff options
author | Andrew Victor <andrew@sanpeople.com> | 2006-02-21 05:56:52 -0500 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2006-03-31 10:05:41 -0500 |
commit | 2c1f3b7a30286c16ba151fadb0abf0b20e2a1e45 (patch) | |
tree | 8d917b469b8b18828da7b733fdf43f173bd6b5cf /drivers/pcmcia/at91_cf.c | |
parent | 552dc85dfed6b6a74a3a01c4ba277ee09797dd0a (diff) |
[PATCH] pcmcia: AT91RM9200 Compact Flash driver
This patch adds support for the Compact Flash controller integrated in
the Atmel AT91RM9200 processor.
Signed-off-by: Andrew Victor <andrew@sanpeople.com
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia/at91_cf.c')
-rw-r--r-- | drivers/pcmcia/at91_cf.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c new file mode 100644 index 000000000000..67cc5f7d0c90 --- /dev/null +++ b/drivers/pcmcia/at91_cf.c | |||
@@ -0,0 +1,365 @@ | |||
1 | /* | ||
2 | * at91_cf.c -- AT91 CompactFlash controller driver | ||
3 | * | ||
4 | * Copyright (C) 2005 David Brownell | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | |||
20 | #include <pcmcia/ss.h> | ||
21 | |||
22 | #include <asm/hardware.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/sizes.h> | ||
25 | |||
26 | #include <asm/arch/at91rm9200.h> | ||
27 | #include <asm/arch/board.h> | ||
28 | #include <asm/arch/gpio.h> | ||
29 | |||
30 | |||
31 | #define CF_SIZE 0x30000000 /* CS5+CS6: unavailable */ | ||
32 | |||
33 | /* | ||
34 | * A0..A10 work in each range; A23 indicates I/O space; A25 is CFRNW; | ||
35 | * some other bit in {A24,A22..A11} is nREG to flag memory access | ||
36 | * (vs attributes). So more than 2KB/region would just be waste. | ||
37 | */ | ||
38 | #define CF_ATTR_PHYS (AT91_CF_BASE) | ||
39 | #define CF_IO_PHYS (AT91_CF_BASE + (1 << 23)) | ||
40 | #define CF_MEM_PHYS (AT91_CF_BASE + 0x017ff800) | ||
41 | |||
42 | /*--------------------------------------------------------------------------*/ | ||
43 | |||
44 | static const char driver_name[] = "at91_cf"; | ||
45 | |||
46 | struct at91_cf_socket { | ||
47 | struct pcmcia_socket socket; | ||
48 | |||
49 | unsigned present:1; | ||
50 | |||
51 | struct platform_device *pdev; | ||
52 | struct at91_cf_data *board; | ||
53 | }; | ||
54 | |||
55 | #define SZ_2K (2 * SZ_1K) | ||
56 | |||
57 | static inline int at91_cf_present(struct at91_cf_socket *cf) | ||
58 | { | ||
59 | return !at91_get_gpio_value(cf->board->det_pin); | ||
60 | } | ||
61 | |||
62 | /*--------------------------------------------------------------------------*/ | ||
63 | |||
64 | static int at91_cf_ss_init(struct pcmcia_socket *s) | ||
65 | { | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static irqreturn_t at91_cf_irq(int irq, void *_cf, struct pt_regs *r) | ||
70 | { | ||
71 | struct at91_cf_socket *cf = (struct at91_cf_socket *) _cf; | ||
72 | |||
73 | if (irq == cf->board->det_pin) { | ||
74 | unsigned present = at91_cf_present(cf); | ||
75 | |||
76 | /* kick pccard as needed */ | ||
77 | if (present != cf->present) { | ||
78 | cf->present = present; | ||
79 | pr_debug("%s: card %s\n", driver_name, present ? "present" : "gone"); | ||
80 | pcmcia_parse_events(&cf->socket, SS_DETECT); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | return IRQ_HANDLED; | ||
85 | } | ||
86 | |||
87 | static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp) | ||
88 | { | ||
89 | struct at91_cf_socket *cf; | ||
90 | |||
91 | if (!sp) | ||
92 | return -EINVAL; | ||
93 | |||
94 | cf = container_of(s, struct at91_cf_socket, socket); | ||
95 | |||
96 | /* NOTE: we assume 3VCARD, not XVCARD... */ | ||
97 | if (at91_cf_present(cf)) { | ||
98 | int rdy = cf->board->irq_pin; /* RDY/nIRQ */ | ||
99 | int vcc = cf->board->vcc_pin; | ||
100 | |||
101 | *sp = SS_DETECT | SS_3VCARD; | ||
102 | if (!rdy || at91_get_gpio_value(rdy)) | ||
103 | *sp |= SS_READY; | ||
104 | if (!vcc || at91_get_gpio_value(vcc)) | ||
105 | *sp |= SS_POWERON; | ||
106 | } else | ||
107 | *sp = 0; | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) | ||
113 | { | ||
114 | struct at91_cf_socket *cf; | ||
115 | |||
116 | cf = container_of(sock, struct at91_cf_socket, socket); | ||
117 | |||
118 | /* switch Vcc if needed and possible */ | ||
119 | if (cf->board->vcc_pin) { | ||
120 | switch (s->Vcc) { | ||
121 | case 0: | ||
122 | at91_set_gpio_value(cf->board->vcc_pin, 0); | ||
123 | break; | ||
124 | case 33: | ||
125 | at91_set_gpio_value(cf->board->vcc_pin, 1); | ||
126 | break; | ||
127 | default: | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | /* toggle reset if needed */ | ||
133 | at91_set_gpio_value(cf->board->rst_pin, s->flags & SS_RESET); | ||
134 | |||
135 | pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", | ||
136 | driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int at91_cf_ss_suspend(struct pcmcia_socket *s) | ||
142 | { | ||
143 | return at91_cf_set_socket(s, &dead_socket); | ||
144 | } | ||
145 | |||
146 | /* we already mapped the I/O region */ | ||
147 | static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) | ||
148 | { | ||
149 | struct at91_cf_socket *cf; | ||
150 | u32 csr; | ||
151 | |||
152 | cf = container_of(s, struct at91_cf_socket, socket); | ||
153 | io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ); | ||
154 | |||
155 | /* | ||
156 | * Use 16 bit accesses unless/until we need 8-bit i/o space. | ||
157 | * Always set CSR4 ... PCMCIA won't always unmap things. | ||
158 | */ | ||
159 | csr = at91_sys_read(AT91_SMC_CSR(4)) & ~AT91_SMC_DBW; | ||
160 | |||
161 | /* | ||
162 | * NOTE: this CF controller ignores IOIS16, so we can't really do | ||
163 | * MAP_AUTOSZ. The 16bit mode allows single byte access on either | ||
164 | * D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many | ||
165 | * purposes (and handles ide-cs). | ||
166 | * | ||
167 | * The 8bit mode is needed for odd byte access on D0-D7. It seems | ||
168 | * some cards only like that way to get at the odd byte, despite | ||
169 | * CF 3.0 spec table 35 also giving the D8-D15 option. | ||
170 | */ | ||
171 | if (!(io->flags & (MAP_16BIT|MAP_AUTOSZ))) { | ||
172 | csr |= AT91_SMC_DBW_8; | ||
173 | pr_debug("%s: 8bit i/o bus\n", driver_name); | ||
174 | } else { | ||
175 | csr |= AT91_SMC_DBW_16; | ||
176 | pr_debug("%s: 16bit i/o bus\n", driver_name); | ||
177 | } | ||
178 | at91_sys_write(AT91_SMC_CSR(4), csr); | ||
179 | |||
180 | io->start = cf->socket.io_offset; | ||
181 | io->stop = io->start + SZ_2K - 1; | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | /* pcmcia layer maps/unmaps mem regions */ | ||
187 | static int at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) | ||
188 | { | ||
189 | struct at91_cf_socket *cf; | ||
190 | |||
191 | if (map->card_start) | ||
192 | return -EINVAL; | ||
193 | |||
194 | cf = container_of(s, struct at91_cf_socket, socket); | ||
195 | |||
196 | map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; | ||
197 | if (map->flags & MAP_ATTRIB) | ||
198 | map->static_start = CF_ATTR_PHYS; | ||
199 | else | ||
200 | map->static_start = CF_MEM_PHYS; | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static struct pccard_operations at91_cf_ops = { | ||
206 | .init = at91_cf_ss_init, | ||
207 | .suspend = at91_cf_ss_suspend, | ||
208 | .get_status = at91_cf_get_status, | ||
209 | .set_socket = at91_cf_set_socket, | ||
210 | .set_io_map = at91_cf_set_io_map, | ||
211 | .set_mem_map = at91_cf_set_mem_map, | ||
212 | }; | ||
213 | |||
214 | /*--------------------------------------------------------------------------*/ | ||
215 | |||
216 | static int __init at91_cf_probe(struct device *dev) | ||
217 | { | ||
218 | struct at91_cf_socket *cf; | ||
219 | struct at91_cf_data *board = dev->platform_data; | ||
220 | struct platform_device *pdev = to_platform_device(dev); | ||
221 | unsigned int csa; | ||
222 | int status; | ||
223 | |||
224 | if (!board || !board->det_pin || !board->rst_pin) | ||
225 | return -ENODEV; | ||
226 | |||
227 | cf = kcalloc(1, sizeof *cf, GFP_KERNEL); | ||
228 | if (!cf) | ||
229 | return -ENOMEM; | ||
230 | |||
231 | cf->board = board; | ||
232 | cf->pdev = pdev; | ||
233 | dev_set_drvdata(dev, cf); | ||
234 | |||
235 | /* CF takes over CS4, CS5, CS6 */ | ||
236 | csa = at91_sys_read(AT91_EBI_CSA); | ||
237 | at91_sys_write(AT91_EBI_CSA, csa | AT91_EBI_CS4A_SMC_COMPACTFLASH); | ||
238 | |||
239 | /* force poweron defaults for these pins ... */ | ||
240 | (void) at91_set_A_periph(AT91_PIN_PC9, 0); /* A25/CFRNW */ | ||
241 | (void) at91_set_A_periph(AT91_PIN_PC10, 0); /* NCS4/CFCS */ | ||
242 | (void) at91_set_A_periph(AT91_PIN_PC11, 0); /* NCS5/CFCE1 */ | ||
243 | (void) at91_set_A_periph(AT91_PIN_PC12, 0); /* NCS6/CFCE2 */ | ||
244 | |||
245 | /* nWAIT is _not_ a default setting */ | ||
246 | (void) at91_set_A_periph(AT91_PIN_PC6, 1); /* nWAIT */ | ||
247 | |||
248 | /* | ||
249 | * Static memory controller timing adjustments. | ||
250 | * REVISIT: these timings are in terms of MCK cycles, so | ||
251 | * when MCK changes (cpufreq etc) so must these values... | ||
252 | */ | ||
253 | at91_sys_write(AT91_SMC_CSR(4), AT91_SMC_ACSS_STD | AT91_SMC_DBW_16 | AT91_SMC_BAT | AT91_SMC_WSEN | ||
254 | | AT91_SMC_NWS_(32) /* wait states */ | ||
255 | | AT91_SMC_RWSETUP_(6) /* setup time */ | ||
256 | | AT91_SMC_RWHOLD_(4) /* hold time */ | ||
257 | ); | ||
258 | |||
259 | /* must be a GPIO; ergo must trigger on both edges */ | ||
260 | status = request_irq(board->det_pin, at91_cf_irq, | ||
261 | SA_SAMPLE_RANDOM, driver_name, cf); | ||
262 | if (status < 0) | ||
263 | goto fail0; | ||
264 | |||
265 | /* | ||
266 | * The card driver will request this irq later as needed. | ||
267 | * but it causes lots of "irqNN: nobody cared" messages | ||
268 | * unless we report that we handle everything (sigh). | ||
269 | * (Note: DK board doesn't wire the IRQ pin...) | ||
270 | */ | ||
271 | if (board->irq_pin) { | ||
272 | status = request_irq(board->irq_pin, at91_cf_irq, | ||
273 | SA_SHIRQ, driver_name, cf); | ||
274 | if (status < 0) | ||
275 | goto fail0a; | ||
276 | cf->socket.pci_irq = board->irq_pin; | ||
277 | } | ||
278 | else | ||
279 | cf->socket.pci_irq = NR_IRQS + 1; | ||
280 | |||
281 | /* pcmcia layer only remaps "real" memory not iospace */ | ||
282 | cf->socket.io_offset = (unsigned long) ioremap(CF_IO_PHYS, SZ_2K); | ||
283 | if (!cf->socket.io_offset) | ||
284 | goto fail1; | ||
285 | |||
286 | /* reserve CS4, CS5, and CS6 regions; but use just CS4 */ | ||
287 | if (!request_mem_region(AT91_CF_BASE, CF_SIZE, driver_name)) | ||
288 | goto fail1; | ||
289 | |||
290 | pr_info("%s: irqs det #%d, io #%d\n", driver_name, | ||
291 | board->det_pin, board->irq_pin); | ||
292 | |||
293 | cf->socket.owner = THIS_MODULE; | ||
294 | cf->socket.dev.dev = dev; | ||
295 | cf->socket.ops = &at91_cf_ops; | ||
296 | cf->socket.resource_ops = &pccard_static_ops; | ||
297 | cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | ||
298 | | SS_CAP_MEM_ALIGN; | ||
299 | cf->socket.map_size = SZ_2K; | ||
300 | cf->socket.io[0].NumPorts = SZ_2K; | ||
301 | |||
302 | status = pcmcia_register_socket(&cf->socket); | ||
303 | if (status < 0) | ||
304 | goto fail2; | ||
305 | |||
306 | return 0; | ||
307 | |||
308 | fail2: | ||
309 | iounmap((void __iomem *) cf->socket.io_offset); | ||
310 | release_mem_region(AT91_CF_BASE, CF_SIZE); | ||
311 | fail1: | ||
312 | if (board->irq_pin) | ||
313 | free_irq(board->irq_pin, cf); | ||
314 | fail0a: | ||
315 | free_irq(board->det_pin, cf); | ||
316 | fail0: | ||
317 | at91_sys_write(AT91_EBI_CSA, csa); | ||
318 | kfree(cf); | ||
319 | return status; | ||
320 | } | ||
321 | |||
322 | static int __exit at91_cf_remove(struct device *dev) | ||
323 | { | ||
324 | struct at91_cf_socket *cf = dev_get_drvdata(dev); | ||
325 | unsigned int csa; | ||
326 | |||
327 | pcmcia_unregister_socket(&cf->socket); | ||
328 | free_irq(cf->board->irq_pin, cf); | ||
329 | free_irq(cf->board->det_pin, cf); | ||
330 | iounmap((void __iomem *) cf->socket.io_offset); | ||
331 | release_mem_region(AT91_CF_BASE, CF_SIZE); | ||
332 | |||
333 | csa = at91_sys_read(AT91_EBI_CSA); | ||
334 | at91_sys_write(AT91_EBI_CSA, csa & ~AT91_EBI_CS4A); | ||
335 | |||
336 | kfree(cf); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static struct device_driver at91_cf_driver = { | ||
341 | .name = (char *) driver_name, | ||
342 | .bus = &platform_bus_type, | ||
343 | .probe = at91_cf_probe, | ||
344 | .remove = __exit_p(at91_cf_remove), | ||
345 | .suspend = pcmcia_socket_dev_suspend, | ||
346 | .resume = pcmcia_socket_dev_resume, | ||
347 | }; | ||
348 | |||
349 | /*--------------------------------------------------------------------------*/ | ||
350 | |||
351 | static int __init at91_cf_init(void) | ||
352 | { | ||
353 | return driver_register(&at91_cf_driver); | ||
354 | } | ||
355 | module_init(at91_cf_init); | ||
356 | |||
357 | static void __exit at91_cf_exit(void) | ||
358 | { | ||
359 | driver_unregister(&at91_cf_driver); | ||
360 | } | ||
361 | module_exit(at91_cf_exit); | ||
362 | |||
363 | MODULE_DESCRIPTION("AT91 Compact Flash Driver"); | ||
364 | MODULE_AUTHOR("David Brownell"); | ||
365 | MODULE_LICENSE("GPL"); | ||