Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[sfrench/cifs-2.6.git] / drivers / pcmcia / pxa2xx_palmtc.c
1 /*
2  * linux/drivers/pcmcia/pxa2xx_palmtc.c
3  *
4  * Driver for Palm Tungsten|C PCMCIA
5  *
6  * Copyright (C) 2008 Alex Osborne <ato@meshy.org>
7  * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  */
14
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/gpio.h>
18 #include <linux/delay.h>
19
20 #include <asm/mach-types.h>
21 #include <mach/palmtc.h>
22 #include "soc_common.h"
23
24 static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
25 {
26         int ret;
27
28         ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER1, "PCMCIA PWR1");
29         if (ret)
30                 goto err1;
31         ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
32         if (ret)
33                 goto err2;
34
35         ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER2, "PCMCIA PWR2");
36         if (ret)
37                 goto err2;
38         ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
39         if (ret)
40                 goto err3;
41
42         ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER3, "PCMCIA PWR3");
43         if (ret)
44                 goto err3;
45         ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
46         if (ret)
47                 goto err4;
48
49         ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_RESET, "PCMCIA RST");
50         if (ret)
51                 goto err4;
52         ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
53         if (ret)
54                 goto err5;
55
56         ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_READY, "PCMCIA RDY");
57         if (ret)
58                 goto err5;
59         ret = gpio_direction_input(GPIO_NR_PALMTC_PCMCIA_READY);
60         if (ret)
61                 goto err6;
62
63         ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_PWRREADY, "PCMCIA PWRRDY");
64         if (ret)
65                 goto err6;
66         ret = gpio_direction_input(GPIO_NR_PALMTC_PCMCIA_PWRREADY);
67         if (ret)
68                 goto err7;
69
70         skt->irq = IRQ_GPIO(GPIO_NR_PALMTC_PCMCIA_READY);
71         return 0;
72
73 err7:
74         gpio_free(GPIO_NR_PALMTC_PCMCIA_PWRREADY);
75 err6:
76         gpio_free(GPIO_NR_PALMTC_PCMCIA_READY);
77 err5:
78         gpio_free(GPIO_NR_PALMTC_PCMCIA_RESET);
79 err4:
80         gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER3);
81 err3:
82         gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER2);
83 err2:
84         gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER1);
85 err1:
86         return ret;
87 }
88
89 static void palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
90 {
91         gpio_free(GPIO_NR_PALMTC_PCMCIA_PWRREADY);
92         gpio_free(GPIO_NR_PALMTC_PCMCIA_READY);
93         gpio_free(GPIO_NR_PALMTC_PCMCIA_RESET);
94         gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER3);
95         gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER2);
96         gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER1);
97 }
98
99 static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
100                                         struct pcmcia_state *state)
101 {
102         state->detect = 1; /* always inserted */
103         state->ready  = !!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_READY);
104         state->bvd1   = 1;
105         state->bvd2   = 1;
106         state->wrprot = 0;
107         state->vs_3v  = 1;
108         state->vs_Xv  = 0;
109 }
110
111 static int palmtc_wifi_powerdown(void)
112 {
113         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
114         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
115         mdelay(40);
116         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
117         return 0;
118 }
119
120 static int palmtc_wifi_powerup(void)
121 {
122         int timeout = 50;
123
124         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 1);
125         mdelay(50);
126
127         /* Power up the card, 1.8V first, after a while 3.3V */
128         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 1);
129         mdelay(100);
130         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 1);
131
132         /* Wait till the card is ready */
133         while (!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_PWRREADY) &&
134                 timeout) {
135                 mdelay(1);
136                 timeout--;
137         }
138
139         /* Power down the WiFi in case of error */
140         if (!timeout) {
141                 palmtc_wifi_powerdown();
142                 return 1;
143         }
144
145         /* Reset the card */
146         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
147         mdelay(20);
148         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 0);
149         mdelay(25);
150
151         gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
152
153         return 0;
154 }
155
156 static int palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
157                                         const socket_state_t *state)
158 {
159         int ret = 1;
160
161         if (state->Vcc == 0)
162                 ret = palmtc_wifi_powerdown();
163         else if (state->Vcc == 33)
164                 ret = palmtc_wifi_powerup();
165
166         return ret;
167 }
168
169 static void palmtc_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
170 {
171 }
172
173 static void palmtc_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
174 {
175 }
176
177 static struct pcmcia_low_level palmtc_pcmcia_ops = {
178         .owner                  = THIS_MODULE,
179
180         .first                  = 0,
181         .nr                     = 1,
182
183         .hw_init                = palmtc_pcmcia_hw_init,
184         .hw_shutdown            = palmtc_pcmcia_hw_shutdown,
185
186         .socket_state           = palmtc_pcmcia_socket_state,
187         .configure_socket       = palmtc_pcmcia_configure_socket,
188
189         .socket_init            = palmtc_pcmcia_socket_init,
190         .socket_suspend         = palmtc_pcmcia_socket_suspend,
191 };
192
193 static struct platform_device *palmtc_pcmcia_device;
194
195 static int __init palmtc_pcmcia_init(void)
196 {
197         int ret;
198
199         if (!machine_is_palmtc())
200                 return -ENODEV;
201
202         palmtc_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
203         if (!palmtc_pcmcia_device)
204                 return -ENOMEM;
205
206         ret = platform_device_add_data(palmtc_pcmcia_device, &palmtc_pcmcia_ops,
207                                         sizeof(palmtc_pcmcia_ops));
208
209         if (!ret)
210                 ret = platform_device_add(palmtc_pcmcia_device);
211
212         if (ret)
213                 platform_device_put(palmtc_pcmcia_device);
214
215         return ret;
216 }
217
218 static void __exit palmtc_pcmcia_exit(void)
219 {
220         platform_device_unregister(palmtc_pcmcia_device);
221 }
222
223 module_init(palmtc_pcmcia_init);
224 module_exit(palmtc_pcmcia_exit);
225
226 MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
227             " Marek Vasut <marek.vasut@gmail.com>");
228 MODULE_DESCRIPTION("PCMCIA support for Palm Tungsten|C");
229 MODULE_ALIAS("platform:pxa2xx-pcmcia");
230 MODULE_LICENSE("GPL");