Root/
1 | /* |
2 | * file: drivers/pcmcia/bfin_cf.c |
3 | * |
4 | * based on: drivers/pcmcia/omap_cf.c |
5 | * omap_cf.c -- OMAP 16xx CompactFlash controller driver |
6 | * |
7 | * Copyright (c) 2005 David Brownell |
8 | * Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc. |
9 | * |
10 | * bugs: enter bugs at http://blackfin.uclinux.org/ |
11 | * |
12 | * this program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the gnu general public license as published by |
14 | * the free software foundation; either version 2, or (at your option) |
15 | * any later version. |
16 | * |
17 | * this program is distributed in the hope that it will be useful, |
18 | * but without any warranty; without even the implied warranty of |
19 | * merchantability or fitness for a particular purpose. see the |
20 | * gnu general public license for more details. |
21 | * |
22 | * you should have received a copy of the gnu general public license |
23 | * along with this program; see the file copying. |
24 | * if not, write to the free software foundation, |
25 | * 59 temple place - suite 330, boston, ma 02111-1307, usa. |
26 | */ |
27 | |
28 | #include <linux/module.h> |
29 | #include <linux/kernel.h> |
30 | #include <linux/sched.h> |
31 | #include <linux/platform_device.h> |
32 | #include <linux/errno.h> |
33 | #include <linux/init.h> |
34 | #include <linux/slab.h> |
35 | #include <linux/delay.h> |
36 | #include <linux/interrupt.h> |
37 | #include <linux/irq.h> |
38 | #include <linux/io.h> |
39 | |
40 | #include <pcmcia/ss.h> |
41 | #include <pcmcia/cisreg.h> |
42 | #include <asm/gpio.h> |
43 | |
44 | #define SZ_1K 0x00000400 |
45 | #define SZ_8K 0x00002000 |
46 | #define SZ_2K (2 * SZ_1K) |
47 | |
48 | #define POLL_INTERVAL (2 * HZ) |
49 | |
50 | #define CF_ATASEL_ENA 0x20311802 /* Inverts RESET */ |
51 | #define CF_ATASEL_DIS 0x20311800 |
52 | |
53 | #define bfin_cf_present(pfx) (gpio_get_value(pfx)) |
54 | |
55 | /*--------------------------------------------------------------------------*/ |
56 | |
57 | static const char driver_name[] = "bfin_cf_pcmcia"; |
58 | |
59 | struct bfin_cf_socket { |
60 | struct pcmcia_socket socket; |
61 | |
62 | struct timer_list timer; |
63 | unsigned present:1; |
64 | unsigned active:1; |
65 | |
66 | struct platform_device *pdev; |
67 | unsigned long phys_cf_io; |
68 | unsigned long phys_cf_attr; |
69 | u_int irq; |
70 | u_short cd_pfx; |
71 | }; |
72 | |
73 | /*--------------------------------------------------------------------------*/ |
74 | static int bfin_cf_reset(void) |
75 | { |
76 | outw(0, CF_ATASEL_ENA); |
77 | mdelay(200); |
78 | outw(0, CF_ATASEL_DIS); |
79 | |
80 | return 0; |
81 | } |
82 | |
83 | static int bfin_cf_ss_init(struct pcmcia_socket *s) |
84 | { |
85 | return 0; |
86 | } |
87 | |
88 | /* the timer is primarily to kick this socket's pccardd */ |
89 | static void bfin_cf_timer(unsigned long _cf) |
90 | { |
91 | struct bfin_cf_socket *cf = (void *)_cf; |
92 | unsigned short present = bfin_cf_present(cf->cd_pfx); |
93 | |
94 | if (present != cf->present) { |
95 | cf->present = present; |
96 | dev_dbg(&cf->pdev->dev, ": card %s\n", |
97 | present ? "present" : "gone"); |
98 | pcmcia_parse_events(&cf->socket, SS_DETECT); |
99 | } |
100 | |
101 | if (cf->active) |
102 | mod_timer(&cf->timer, jiffies + POLL_INTERVAL); |
103 | } |
104 | |
105 | static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp) |
106 | { |
107 | struct bfin_cf_socket *cf; |
108 | |
109 | if (!sp) |
110 | return -EINVAL; |
111 | |
112 | cf = container_of(s, struct bfin_cf_socket, socket); |
113 | |
114 | if (bfin_cf_present(cf->cd_pfx)) { |
115 | *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; |
116 | s->pcmcia_irq = 0; |
117 | s->pci_irq = cf->irq; |
118 | |
119 | } else |
120 | *sp = 0; |
121 | return 0; |
122 | } |
123 | |
124 | static int |
125 | bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) |
126 | { |
127 | |
128 | struct bfin_cf_socket *cf; |
129 | cf = container_of(sock, struct bfin_cf_socket, socket); |
130 | |
131 | switch (s->Vcc) { |
132 | case 0: |
133 | case 33: |
134 | break; |
135 | case 50: |
136 | break; |
137 | default: |
138 | return -EINVAL; |
139 | } |
140 | |
141 | if (s->flags & SS_RESET) { |
142 | disable_irq(cf->irq); |
143 | bfin_cf_reset(); |
144 | enable_irq(cf->irq); |
145 | } |
146 | |
147 | dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n", |
148 | s->Vcc, s->io_irq, s->flags, s->csc_mask); |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | static int bfin_cf_ss_suspend(struct pcmcia_socket *s) |
154 | { |
155 | return bfin_cf_set_socket(s, &dead_socket); |
156 | } |
157 | |
158 | /* regions are 2K each: mem, attrib, io (and reserved-for-ide) */ |
159 | |
160 | static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) |
161 | { |
162 | struct bfin_cf_socket *cf; |
163 | |
164 | cf = container_of(s, struct bfin_cf_socket, socket); |
165 | io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT; |
166 | io->start = cf->phys_cf_io; |
167 | io->stop = io->start + SZ_2K - 1; |
168 | return 0; |
169 | } |
170 | |
171 | static int |
172 | bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) |
173 | { |
174 | struct bfin_cf_socket *cf; |
175 | |
176 | if (map->card_start) |
177 | return -EINVAL; |
178 | cf = container_of(s, struct bfin_cf_socket, socket); |
179 | map->static_start = cf->phys_cf_io; |
180 | map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT; |
181 | if (map->flags & MAP_ATTRIB) |
182 | map->static_start = cf->phys_cf_attr; |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static struct pccard_operations bfin_cf_ops = { |
188 | .init = bfin_cf_ss_init, |
189 | .suspend = bfin_cf_ss_suspend, |
190 | .get_status = bfin_cf_get_status, |
191 | .set_socket = bfin_cf_set_socket, |
192 | .set_io_map = bfin_cf_set_io_map, |
193 | .set_mem_map = bfin_cf_set_mem_map, |
194 | }; |
195 | |
196 | /*--------------------------------------------------------------------------*/ |
197 | |
198 | static int __devinit bfin_cf_probe(struct platform_device *pdev) |
199 | { |
200 | struct bfin_cf_socket *cf; |
201 | struct resource *io_mem, *attr_mem; |
202 | int irq; |
203 | unsigned short cd_pfx; |
204 | int status = 0; |
205 | |
206 | dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n"); |
207 | |
208 | irq = platform_get_irq(pdev, 0); |
209 | if (irq <= 0) |
210 | return -EINVAL; |
211 | |
212 | cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */ |
213 | |
214 | if (gpio_request(cd_pfx, "pcmcia: CD")) { |
215 | dev_err(&pdev->dev, |
216 | "Failed ro request Card Detect GPIO_%d\n", |
217 | cd_pfx); |
218 | return -EBUSY; |
219 | } |
220 | gpio_direction_input(cd_pfx); |
221 | |
222 | cf = kzalloc(sizeof *cf, GFP_KERNEL); |
223 | if (!cf) { |
224 | gpio_free(cd_pfx); |
225 | return -ENOMEM; |
226 | } |
227 | |
228 | cf->cd_pfx = cd_pfx; |
229 | |
230 | setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf); |
231 | |
232 | cf->pdev = pdev; |
233 | platform_set_drvdata(pdev, cf); |
234 | |
235 | cf->irq = irq; |
236 | cf->socket.pci_irq = irq; |
237 | |
238 | irq_set_irq_type(irq, IRQF_TRIGGER_LOW); |
239 | |
240 | io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
241 | attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
242 | |
243 | if (!io_mem || !attr_mem) |
244 | goto fail0; |
245 | |
246 | cf->phys_cf_io = io_mem->start; |
247 | cf->phys_cf_attr = attr_mem->start; |
248 | |
249 | /* pcmcia layer only remaps "real" memory */ |
250 | cf->socket.io_offset = (unsigned long) |
251 | ioremap(cf->phys_cf_io, SZ_2K); |
252 | |
253 | if (!cf->socket.io_offset) |
254 | goto fail0; |
255 | |
256 | dev_err(&pdev->dev, ": on irq %d\n", irq); |
257 | |
258 | dev_dbg(&pdev->dev, ": %s\n", |
259 | bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)"); |
260 | |
261 | cf->socket.owner = THIS_MODULE; |
262 | cf->socket.dev.parent = &pdev->dev; |
263 | cf->socket.ops = &bfin_cf_ops; |
264 | cf->socket.resource_ops = &pccard_static_ops; |
265 | cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP |
266 | | SS_CAP_MEM_ALIGN; |
267 | cf->socket.map_size = SZ_2K; |
268 | |
269 | status = pcmcia_register_socket(&cf->socket); |
270 | if (status < 0) |
271 | goto fail2; |
272 | |
273 | cf->active = 1; |
274 | mod_timer(&cf->timer, jiffies + POLL_INTERVAL); |
275 | return 0; |
276 | |
277 | fail2: |
278 | iounmap((void __iomem *)cf->socket.io_offset); |
279 | release_mem_region(cf->phys_cf_io, SZ_8K); |
280 | |
281 | fail0: |
282 | gpio_free(cf->cd_pfx); |
283 | kfree(cf); |
284 | platform_set_drvdata(pdev, NULL); |
285 | |
286 | return status; |
287 | } |
288 | |
289 | static int __devexit bfin_cf_remove(struct platform_device *pdev) |
290 | { |
291 | struct bfin_cf_socket *cf = platform_get_drvdata(pdev); |
292 | |
293 | gpio_free(cf->cd_pfx); |
294 | cf->active = 0; |
295 | pcmcia_unregister_socket(&cf->socket); |
296 | del_timer_sync(&cf->timer); |
297 | iounmap((void __iomem *)cf->socket.io_offset); |
298 | release_mem_region(cf->phys_cf_io, SZ_8K); |
299 | platform_set_drvdata(pdev, NULL); |
300 | kfree(cf); |
301 | return 0; |
302 | } |
303 | |
304 | static struct platform_driver bfin_cf_driver = { |
305 | .driver = { |
306 | .name = (char *)driver_name, |
307 | .owner = THIS_MODULE, |
308 | }, |
309 | .probe = bfin_cf_probe, |
310 | .remove = __devexit_p(bfin_cf_remove), |
311 | }; |
312 | |
313 | module_platform_driver(bfin_cf_driver); |
314 | |
315 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
316 | MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver"); |
317 | MODULE_LICENSE("GPL"); |
318 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9