Merge branch 'linus' into stackprotector
[linux-2.6] / drivers / pcmcia / pxa2xx_sharpsl.c
1 /*
2  * Sharp SL-C7xx Series PCMCIA routines
3  *
4  * Copyright (c) 2004-2005 Richard Purdie
5  *
6  * Based on Sharp's 2.4 kernel patches and pxa2xx_mainstone.c
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  */
13
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/errno.h>
18 #include <linux/interrupt.h>
19 #include <linux/platform_device.h>
20
21 #include <asm/mach-types.h>
22 #include <mach/hardware.h>
23 #include <asm/irq.h>
24 #include <asm/hardware/scoop.h>
25
26 #include "soc_common.h"
27
28 #define NO_KEEP_VS 0x0001
29 #define SCOOP_DEV platform_scoop_config->devs
30
31 static void sharpsl_pcmcia_init_reset(struct soc_pcmcia_socket *skt)
32 {
33         struct scoop_pcmcia_dev *scoopdev = &SCOOP_DEV[skt->nr];
34
35         reset_scoop(scoopdev->dev);
36
37         /* Shared power controls need to be handled carefully */
38         if (platform_scoop_config->power_ctrl)
39                 platform_scoop_config->power_ctrl(scoopdev->dev, 0x0000, skt->nr);
40         else
41                 write_scoop_reg(scoopdev->dev, SCOOP_CPR, 0x0000);
42
43         scoopdev->keep_vs = NO_KEEP_VS;
44         scoopdev->keep_rd = 0;
45 }
46
47 static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
48 {
49         int ret;
50
51         if (platform_scoop_config->pcmcia_init)
52                 platform_scoop_config->pcmcia_init();
53
54         /* Register interrupts */
55         if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
56                 struct pcmcia_irqs cd_irq;
57
58                 cd_irq.sock = skt->nr;
59                 cd_irq.irq  = SCOOP_DEV[skt->nr].cd_irq;
60                 cd_irq.str  = SCOOP_DEV[skt->nr].cd_irq_str;
61                 ret = soc_pcmcia_request_irqs(skt, &cd_irq, 1);
62
63                 if (ret) {
64                         printk(KERN_ERR "Request for Compact Flash IRQ failed\n");
65                         return ret;
66                 }
67         }
68
69         skt->irq = SCOOP_DEV[skt->nr].irq;
70
71         return 0;
72 }
73
74 static void sharpsl_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
75 {
76         if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
77                 struct pcmcia_irqs cd_irq;
78
79                 cd_irq.sock = skt->nr;
80                 cd_irq.irq  = SCOOP_DEV[skt->nr].cd_irq;
81                 cd_irq.str  = SCOOP_DEV[skt->nr].cd_irq_str;
82                 soc_pcmcia_free_irqs(skt, &cd_irq, 1);
83         }
84 }
85
86
87 static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
88                                     struct pcmcia_state *state)
89 {
90         unsigned short cpr, csr;
91         struct device *scoop = SCOOP_DEV[skt->nr].dev;
92
93         cpr = read_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_CPR);
94
95         write_scoop_reg(scoop, SCOOP_IRM, 0x00FF);
96         write_scoop_reg(scoop, SCOOP_ISR, 0x0000);
97         write_scoop_reg(scoop, SCOOP_IRM, 0x0000);
98         csr = read_scoop_reg(scoop, SCOOP_CSR);
99         if (csr & 0x0004) {
100                 /* card eject */
101                 write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
102                 SCOOP_DEV[skt->nr].keep_vs = NO_KEEP_VS;
103         }
104         else if (!(SCOOP_DEV[skt->nr].keep_vs & NO_KEEP_VS)) {
105                 /* keep vs1,vs2 */
106                 write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
107                 csr |= SCOOP_DEV[skt->nr].keep_vs;
108         }
109         else if (cpr & 0x0003) {
110                 /* power on */
111                 write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
112                 SCOOP_DEV[skt->nr].keep_vs = (csr & 0x00C0);
113         }
114         else {
115                 /* card detect */
116                 if ((machine_is_spitz() || machine_is_borzoi()) && skt->nr == 1) {
117                         write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
118                 } else {
119                         write_scoop_reg(scoop, SCOOP_CDR, 0x0002);
120                 }
121         }
122
123         state->detect = (csr & 0x0004) ? 0 : 1;
124         state->ready  = (csr & 0x0002) ? 1 : 0;
125         state->bvd1   = (csr & 0x0010) ? 1 : 0;
126         state->bvd2   = (csr & 0x0020) ? 1 : 0;
127         state->wrprot = (csr & 0x0008) ? 1 : 0;
128         state->vs_3v  = (csr & 0x0040) ? 0 : 1;
129         state->vs_Xv  = (csr & 0x0080) ? 0 : 1;
130
131         if ((cpr & 0x0080) && ((cpr & 0x8040) != 0x8040)) {
132                 printk(KERN_ERR "sharpsl_pcmcia_socket_state(): CPR=%04X, Low voltage!\n", cpr);
133         }
134 }
135
136
137 static int sharpsl_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
138                                        const socket_state_t *state)
139 {
140         unsigned long flags;
141         struct device *scoop = SCOOP_DEV[skt->nr].dev;
142
143         unsigned short cpr, ncpr, ccr, nccr, mcr, nmcr, imr, nimr;
144
145         switch (state->Vcc) {
146         case    0:      break;
147         case    33:     break;
148         case    50:     break;
149         default:
150                  printk(KERN_ERR "sharpsl_pcmcia_configure_socket(): bad Vcc %u\n", state->Vcc);
151                  return -1;
152         }
153
154         if ((state->Vpp!=state->Vcc) && (state->Vpp!=0)) {
155                 printk(KERN_ERR "CF slot cannot support Vpp %u\n", state->Vpp);
156                 return -1;
157         }
158
159         local_irq_save(flags);
160
161         nmcr = (mcr = read_scoop_reg(scoop, SCOOP_MCR)) & ~0x0010;
162         ncpr = (cpr = read_scoop_reg(scoop, SCOOP_CPR)) & ~0x0083;
163         nccr = (ccr = read_scoop_reg(scoop, SCOOP_CCR)) & ~0x0080;
164         nimr = (imr = read_scoop_reg(scoop, SCOOP_IMR)) & ~0x003E;
165
166         if ((machine_is_spitz() || machine_is_borzoi() || machine_is_akita()) && skt->nr == 0) {
167                 ncpr |= (state->Vcc == 33) ? 0x0002 :
168                         (state->Vcc == 50) ? 0x0002 : 0;
169         } else {
170                 ncpr |= (state->Vcc == 33) ? 0x0001 :
171                         (state->Vcc == 50) ? 0x0002 : 0;
172         }
173         nmcr |= (state->flags&SS_IOCARD) ? 0x0010 : 0;
174         ncpr |= (state->flags&SS_OUTPUT_ENA) ? 0x0080 : 0;
175         nccr |= (state->flags&SS_RESET)? 0x0080: 0;
176         nimr |= ((skt->status&SS_DETECT) ? 0x0004 : 0)|
177                         ((skt->status&SS_READY)  ? 0x0002 : 0)|
178                         ((skt->status&SS_BATDEAD)? 0x0010 : 0)|
179                         ((skt->status&SS_BATWARN)? 0x0020 : 0)|
180                         ((skt->status&SS_STSCHG) ? 0x0010 : 0)|
181                         ((skt->status&SS_WRPROT) ? 0x0008 : 0);
182
183         if (!(ncpr & 0x0003)) {
184                 SCOOP_DEV[skt->nr].keep_rd = 0;
185         } else if (!SCOOP_DEV[skt->nr].keep_rd) {
186                 if (nccr & 0x0080)
187                         SCOOP_DEV[skt->nr].keep_rd = 1;
188                 else
189                         nccr |= 0x0080;
190         }
191
192         if (mcr != nmcr)
193                 write_scoop_reg(scoop, SCOOP_MCR, nmcr);
194         if (cpr != ncpr) {
195                 if (platform_scoop_config->power_ctrl)
196                         platform_scoop_config->power_ctrl(scoop, ncpr , skt->nr);
197                 else
198                         write_scoop_reg(scoop, SCOOP_CPR, ncpr);
199         }
200         if (ccr != nccr)
201                 write_scoop_reg(scoop, SCOOP_CCR, nccr);
202         if (imr != nimr)
203                 write_scoop_reg(scoop, SCOOP_IMR, nimr);
204
205         local_irq_restore(flags);
206
207         return 0;
208 }
209
210 static void sharpsl_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
211 {
212         sharpsl_pcmcia_init_reset(skt);
213
214         /* Enable interrupt */
215         write_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_IMR, 0x00C0);
216         write_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_MCR, 0x0101);
217         SCOOP_DEV[skt->nr].keep_vs = NO_KEEP_VS;
218 }
219
220 static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
221 {
222         sharpsl_pcmcia_init_reset(skt);
223 }
224
225 static struct pcmcia_low_level sharpsl_pcmcia_ops __initdata = {
226         .owner                  = THIS_MODULE,
227         .hw_init                = sharpsl_pcmcia_hw_init,
228         .hw_shutdown            = sharpsl_pcmcia_hw_shutdown,
229         .socket_state           = sharpsl_pcmcia_socket_state,
230         .configure_socket       = sharpsl_pcmcia_configure_socket,
231         .socket_init            = sharpsl_pcmcia_socket_init,
232         .socket_suspend         = sharpsl_pcmcia_socket_suspend,
233         .first                  = 0,
234         .nr                     = 0,
235 };
236
237 #ifdef CONFIG_SA1100_COLLIE
238 #include "sa11xx_base.h"
239
240 int __init pcmcia_collie_init(struct device *dev)
241 {
242        int ret = -ENODEV;
243
244        if (machine_is_collie())
245                ret = sa11xx_drv_pcmcia_probe(dev, &sharpsl_pcmcia_ops, 0, 1);
246
247        return ret;
248 }
249
250 #else
251
252 static struct platform_device *sharpsl_pcmcia_device;
253
254 static int __init sharpsl_pcmcia_init(void)
255 {
256         int ret;
257
258         sharpsl_pcmcia_ops.nr = platform_scoop_config->num_devs;
259         sharpsl_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
260
261         if (!sharpsl_pcmcia_device)
262                 return -ENOMEM;
263
264         ret = platform_device_add_data(sharpsl_pcmcia_device,
265                         &sharpsl_pcmcia_ops, sizeof(sharpsl_pcmcia_ops));
266         if (ret == 0) {
267                 sharpsl_pcmcia_device->dev.parent = platform_scoop_config->devs[0].dev;
268                 ret = platform_device_add(sharpsl_pcmcia_device);
269         }
270
271         if (ret)
272                 platform_device_put(sharpsl_pcmcia_device);
273
274         return ret;
275 }
276
277 static void __exit sharpsl_pcmcia_exit(void)
278 {
279         platform_device_unregister(sharpsl_pcmcia_device);
280 }
281
282 fs_initcall(sharpsl_pcmcia_init);
283 module_exit(sharpsl_pcmcia_exit);
284 #endif
285
286 MODULE_DESCRIPTION("Sharp SL Series PCMCIA Support");
287 MODULE_LICENSE("GPL");
288 MODULE_ALIAS("platform:pxa2xx-pcmcia");