]> err.no Git - linux-2.6/blob - drivers/pcmcia/bfin_cf_pcmcia.c
Merge branch 'bkl-removal' of git://git.lwn.net/linux-2.6
[linux-2.6] / drivers / pcmcia / bfin_cf_pcmcia.c
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/delay.h>
35 #include <linux/interrupt.h>
36 #include <linux/irq.h>
37 #include <linux/io.h>
38
39 #include <pcmcia/ss.h>
40 #include <pcmcia/cisreg.h>
41 #include <asm/gpio.h>
42
43 #define SZ_1K   0x00000400
44 #define SZ_8K   0x00002000
45 #define SZ_2K   (2 * SZ_1K)
46
47 #define POLL_INTERVAL   (2 * HZ)
48
49 #define CF_ATASEL_ENA   0x20311802      /* Inverts RESET */
50 #define CF_ATASEL_DIS   0x20311800
51
52 #define bfin_cf_present(pfx) (gpio_get_value(pfx))
53
54 /*--------------------------------------------------------------------------*/
55
56 static const char driver_name[] = "bfin_cf_pcmcia";
57
58 struct bfin_cf_socket {
59         struct pcmcia_socket socket;
60
61         struct timer_list timer;
62         unsigned present:1;
63         unsigned active:1;
64
65         struct platform_device *pdev;
66         unsigned long phys_cf_io;
67         unsigned long phys_cf_attr;
68         u_int irq;
69         u_short cd_pfx;
70 };
71
72 /*--------------------------------------------------------------------------*/
73 static int bfin_cf_reset(void)
74 {
75         outw(0, CF_ATASEL_ENA);
76         mdelay(200);
77         outw(0, CF_ATASEL_DIS);
78
79         return 0;
80 }
81
82 static int bfin_cf_ss_init(struct pcmcia_socket *s)
83 {
84         return 0;
85 }
86
87 /* the timer is primarily to kick this socket's pccardd */
88 static void bfin_cf_timer(unsigned long _cf)
89 {
90         struct bfin_cf_socket *cf = (void *)_cf;
91         unsigned short present = bfin_cf_present(cf->cd_pfx);
92
93         if (present != cf->present) {
94                 cf->present = present;
95                 dev_dbg(&cf->pdev->dev, ": card %s\n",
96                          present ? "present" : "gone");
97                 pcmcia_parse_events(&cf->socket, SS_DETECT);
98         }
99
100         if (cf->active)
101                 mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
102 }
103
104 static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
105 {
106         struct bfin_cf_socket *cf;
107
108         if (!sp)
109                 return -EINVAL;
110
111         cf = container_of(s, struct bfin_cf_socket, socket);
112
113         if (bfin_cf_present(cf->cd_pfx)) {
114                 *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
115                 s->irq.AssignedIRQ = 0;
116                 s->pci_irq = cf->irq;
117
118         } else
119                 *sp = 0;
120         return 0;
121 }
122
123 static int
124 bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
125 {
126
127         struct bfin_cf_socket *cf;
128         cf = container_of(sock, struct bfin_cf_socket, socket);
129
130         switch (s->Vcc) {
131         case 0:
132         case 33:
133                 break;
134         case 50:
135                 break;
136         default:
137                 return -EINVAL;
138         }
139
140         if (s->flags & SS_RESET) {
141                 disable_irq(cf->irq);
142                 bfin_cf_reset();
143                 enable_irq(cf->irq);
144         }
145
146         dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
147                  s->Vcc, s->io_irq, s->flags, s->csc_mask);
148
149         return 0;
150 }
151
152 static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
153 {
154         return bfin_cf_set_socket(s, &dead_socket);
155 }
156
157 /* regions are 2K each:  mem, attrib, io (and reserved-for-ide) */
158
159 static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
160 {
161         struct bfin_cf_socket *cf;
162
163         cf = container_of(s, struct bfin_cf_socket, socket);
164         io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
165         io->start = cf->phys_cf_io;
166         io->stop = io->start + SZ_2K - 1;
167         return 0;
168 }
169
170 static int
171 bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
172 {
173         struct bfin_cf_socket *cf;
174
175         if (map->card_start)
176                 return -EINVAL;
177         cf = container_of(s, struct bfin_cf_socket, socket);
178         map->static_start = cf->phys_cf_io;
179         map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
180         if (map->flags & MAP_ATTRIB)
181                 map->static_start = cf->phys_cf_attr;
182
183         return 0;
184 }
185
186 static struct pccard_operations bfin_cf_ops = {
187         .init = bfin_cf_ss_init,
188         .suspend = bfin_cf_ss_suspend,
189         .get_status = bfin_cf_get_status,
190         .set_socket = bfin_cf_set_socket,
191         .set_io_map = bfin_cf_set_io_map,
192         .set_mem_map = bfin_cf_set_mem_map,
193 };
194
195 /*--------------------------------------------------------------------------*/
196
197 static int __devinit bfin_cf_probe(struct platform_device *pdev)
198 {
199         struct bfin_cf_socket *cf;
200         struct resource *io_mem, *attr_mem;
201         int irq;
202         unsigned short cd_pfx;
203         int status = 0;
204
205         dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
206
207         irq = platform_get_irq(pdev, 0);
208         if (!irq)
209                 return -EINVAL;
210
211         cd_pfx = platform_get_irq(pdev, 1);     /*Card Detect GPIO PIN */
212
213         if (gpio_request(cd_pfx, "pcmcia: CD")) {
214                 dev_err(&pdev->dev,
215                        "Failed ro request Card Detect GPIO_%d\n",
216                        cd_pfx);
217                 return -EBUSY;
218         }
219         gpio_direction_input(cd_pfx);
220
221         cf = kzalloc(sizeof *cf, GFP_KERNEL);
222         if (!cf) {
223                 gpio_free(cd_pfx);
224                 return -ENOMEM;
225         }
226
227         cf->cd_pfx = cd_pfx;
228
229         setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf);
230
231         cf->pdev = pdev;
232         platform_set_drvdata(pdev, cf);
233
234         cf->irq = irq;
235         cf->socket.pci_irq = irq;
236
237         set_irq_type(irq, IRQF_TRIGGER_LOW);
238
239         io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
240         attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
241
242         if (!io_mem || !attr_mem)
243                 goto fail0;
244
245         cf->phys_cf_io = io_mem->start;
246         cf->phys_cf_attr = attr_mem->start;
247
248         /* pcmcia layer only remaps "real" memory */
249         cf->socket.io_offset = (unsigned long)
250             ioremap(cf->phys_cf_io, SZ_2K);
251
252         if (!cf->socket.io_offset)
253                 goto fail0;
254
255         dev_err(&pdev->dev, ": on irq %d\n", irq);
256
257         dev_dbg(&pdev->dev, ": %s\n",
258                  bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
259
260         cf->socket.owner = THIS_MODULE;
261         cf->socket.dev.parent = &pdev->dev;
262         cf->socket.ops = &bfin_cf_ops;
263         cf->socket.resource_ops = &pccard_static_ops;
264         cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
265             | SS_CAP_MEM_ALIGN;
266         cf->socket.map_size = SZ_2K;
267
268         status = pcmcia_register_socket(&cf->socket);
269         if (status < 0)
270                 goto fail2;
271
272         cf->active = 1;
273         mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
274         return 0;
275
276 fail2:
277         iounmap((void __iomem *)cf->socket.io_offset);
278         release_mem_region(cf->phys_cf_io, SZ_8K);
279
280 fail0:
281         gpio_free(cf->cd_pfx);
282         kfree(cf);
283         platform_set_drvdata(pdev, NULL);
284
285         return status;
286 }
287
288 static int __devexit bfin_cf_remove(struct platform_device *pdev)
289 {
290         struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
291
292         gpio_free(cf->cd_pfx);
293         cf->active = 0;
294         pcmcia_unregister_socket(&cf->socket);
295         del_timer_sync(&cf->timer);
296         iounmap((void __iomem *)cf->socket.io_offset);
297         release_mem_region(cf->phys_cf_io, SZ_8K);
298         platform_set_drvdata(pdev, NULL);
299         kfree(cf);
300         return 0;
301 }
302
303 static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
304 {
305         return pcmcia_socket_dev_suspend(&pdev->dev, mesg);
306 }
307
308 static int bfin_cf_resume(struct platform_device *pdev)
309 {
310         return pcmcia_socket_dev_resume(&pdev->dev);
311 }
312
313 static struct platform_driver bfin_cf_driver = {
314         .driver = {
315                    .name = (char *)driver_name,
316                    .owner = THIS_MODULE,
317                    },
318         .probe = bfin_cf_probe,
319         .remove = __devexit_p(bfin_cf_remove),
320         .suspend = bfin_cf_suspend,
321         .resume = bfin_cf_resume,
322 };
323
324 static int __init bfin_cf_init(void)
325 {
326         return platform_driver_register(&bfin_cf_driver);
327 }
328
329 static void __exit bfin_cf_exit(void)
330 {
331         platform_driver_unregister(&bfin_cf_driver);
332 }
333
334 module_init(bfin_cf_init);
335 module_exit(bfin_cf_exit);
336
337 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>")
338 MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
339 MODULE_LICENSE("GPL");