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