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-2011 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 struct gpio palmtc_pcmcia_gpios[] = {
25 	{ GPIO_NR_PALMTC_PCMCIA_POWER1,	GPIOF_INIT_LOW,	"PCMCIA Power 1" },
26 	{ GPIO_NR_PALMTC_PCMCIA_POWER2,	GPIOF_INIT_LOW,	"PCMCIA Power 2" },
27 	{ GPIO_NR_PALMTC_PCMCIA_POWER3,	GPIOF_INIT_LOW,	"PCMCIA Power 3" },
28 	{ GPIO_NR_PALMTC_PCMCIA_RESET,	GPIOF_INIT_HIGH,"PCMCIA Reset" },
29 	{ GPIO_NR_PALMTC_PCMCIA_READY,	GPIOF_IN,	"PCMCIA Ready" },
30 	{ GPIO_NR_PALMTC_PCMCIA_PWRREADY, GPIOF_IN,	"PCMCIA Power Ready" },
31 };
32 
palmtc_pcmcia_hw_init(struct soc_pcmcia_socket * skt)33 static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
34 {
35 	int ret;
36 
37 	ret = gpio_request_array(palmtc_pcmcia_gpios,
38 				ARRAY_SIZE(palmtc_pcmcia_gpios));
39 
40 	skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMTC_PCMCIA_READY);
41 
42 	return ret;
43 }
44 
palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket * skt)45 static void palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
46 {
47 	gpio_free_array(palmtc_pcmcia_gpios, ARRAY_SIZE(palmtc_pcmcia_gpios));
48 }
49 
palmtc_pcmcia_socket_state(struct soc_pcmcia_socket * skt,struct pcmcia_state * state)50 static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
51 					struct pcmcia_state *state)
52 {
53 	state->detect = 1; /* always inserted */
54 	state->ready  = !!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_READY);
55 	state->bvd1   = 1;
56 	state->bvd2   = 1;
57 	state->wrprot = 0;
58 	state->vs_3v  = 1;
59 	state->vs_Xv  = 0;
60 }
61 
palmtc_wifi_powerdown(void)62 static int palmtc_wifi_powerdown(void)
63 {
64 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
65 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
66 	mdelay(40);
67 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
68 	return 0;
69 }
70 
palmtc_wifi_powerup(void)71 static int palmtc_wifi_powerup(void)
72 {
73 	int timeout = 50;
74 
75 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 1);
76 	mdelay(50);
77 
78 	/* Power up the card, 1.8V first, after a while 3.3V */
79 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 1);
80 	mdelay(100);
81 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 1);
82 
83 	/* Wait till the card is ready */
84 	while (!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_PWRREADY) &&
85 		timeout) {
86 		mdelay(1);
87 		timeout--;
88 	}
89 
90 	/* Power down the WiFi in case of error */
91 	if (!timeout) {
92 		palmtc_wifi_powerdown();
93 		return 1;
94 	}
95 
96 	/* Reset the card */
97 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
98 	mdelay(20);
99 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 0);
100 	mdelay(25);
101 
102 	gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
103 
104 	return 0;
105 }
106 
palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket * skt,const socket_state_t * state)107 static int palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
108 					const socket_state_t *state)
109 {
110 	int ret = 1;
111 
112 	if (state->Vcc == 0)
113 		ret = palmtc_wifi_powerdown();
114 	else if (state->Vcc == 33)
115 		ret = palmtc_wifi_powerup();
116 
117 	return ret;
118 }
119 
120 static struct pcmcia_low_level palmtc_pcmcia_ops = {
121 	.owner			= THIS_MODULE,
122 
123 	.first			= 0,
124 	.nr			= 1,
125 
126 	.hw_init		= palmtc_pcmcia_hw_init,
127 	.hw_shutdown		= palmtc_pcmcia_hw_shutdown,
128 
129 	.socket_state		= palmtc_pcmcia_socket_state,
130 	.configure_socket	= palmtc_pcmcia_configure_socket,
131 };
132 
133 static struct platform_device *palmtc_pcmcia_device;
134 
palmtc_pcmcia_init(void)135 static int __init palmtc_pcmcia_init(void)
136 {
137 	int ret;
138 
139 	if (!machine_is_palmtc())
140 		return -ENODEV;
141 
142 	palmtc_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
143 	if (!palmtc_pcmcia_device)
144 		return -ENOMEM;
145 
146 	ret = platform_device_add_data(palmtc_pcmcia_device, &palmtc_pcmcia_ops,
147 					sizeof(palmtc_pcmcia_ops));
148 
149 	if (!ret)
150 		ret = platform_device_add(palmtc_pcmcia_device);
151 
152 	if (ret)
153 		platform_device_put(palmtc_pcmcia_device);
154 
155 	return ret;
156 }
157 
palmtc_pcmcia_exit(void)158 static void __exit palmtc_pcmcia_exit(void)
159 {
160 	platform_device_unregister(palmtc_pcmcia_device);
161 }
162 
163 module_init(palmtc_pcmcia_init);
164 module_exit(palmtc_pcmcia_exit);
165 
166 MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
167 	    " Marek Vasut <marek.vasut@gmail.com>");
168 MODULE_DESCRIPTION("PCMCIA support for Palm Tungsten|C");
169 MODULE_ALIAS("platform:pxa2xx-pcmcia");
170 MODULE_LICENSE("GPL");
171