1 /*
2  * linux/drivers/pcmcia/pxa2xx_trizeps4.c
3  *
4  * TRIZEPS PCMCIA specific routines.
5  *
6  * Author:	Jürgen Schindele
7  * Created:	20 02, 2006
8  * Copyright:	Jürgen Schindele
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14 
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/gpio.h>
19 #include <linux/interrupt.h>
20 #include <linux/platform_device.h>
21 
22 #include <asm/mach-types.h>
23 #include <asm/irq.h>
24 
25 #include <mach/pxa2xx-regs.h>
26 #include <mach/trizeps4.h>
27 
28 #include "soc_common.h"
29 
30 extern void board_pcmcia_power(int power);
31 
32 static struct pcmcia_irqs irqs[] = {
33 	{ .sock = 0, .str = "cs0_cd" }
34 	/* on other baseboards we can have more inputs */
35 };
36 
trizeps_pcmcia_hw_init(struct soc_pcmcia_socket * skt)37 static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
38 {
39 	int ret, i;
40 	/* we dont have voltage/card/ready detection
41 	 * so we dont need interrupts for it
42 	 */
43 	switch (skt->nr) {
44 	case 0:
45 		if (gpio_request(GPIO_PRDY, "cf_irq") < 0) {
46 			pr_err("%s: sock %d unable to request gpio %d\n", __func__,
47 				skt->nr, GPIO_PRDY);
48 			return -EBUSY;
49 		}
50 		if (gpio_direction_input(GPIO_PRDY) < 0) {
51 			pr_err("%s: sock %d unable to set input gpio %d\n", __func__,
52 				skt->nr, GPIO_PRDY);
53 			gpio_free(GPIO_PRDY);
54 			return -EINVAL;
55 		}
56 		skt->socket.pci_irq = gpio_to_irq(GPIO_PRDY);
57 		irqs[0].irq = gpio_to_irq(GPIO_PCD);
58 		break;
59 	default:
60 		break;
61 	}
62 	/* release the reset of this card */
63 	pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq);
64 
65 	/* supplementory irqs for the socket */
66 	for (i = 0; i < ARRAY_SIZE(irqs); i++) {
67 		if (irqs[i].sock != skt->nr)
68 			continue;
69 		if (gpio_request(irq_to_gpio(irqs[i].irq), irqs[i].str) < 0) {
70 			pr_err("%s: sock %d unable to request gpio %d\n",
71 				__func__, skt->nr, irq_to_gpio(irqs[i].irq));
72 			ret = -EBUSY;
73 			goto error;
74 		}
75 		if (gpio_direction_input(irq_to_gpio(irqs[i].irq)) < 0) {
76 			pr_err("%s: sock %d unable to set input gpio %d\n",
77 				__func__, skt->nr, irq_to_gpio(irqs[i].irq));
78 			ret = -EINVAL;
79 			goto error;
80 		}
81 	}
82 	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
83 
84 error:
85 	for (; i >= 0; i--) {
86 		gpio_free(irq_to_gpio(irqs[i].irq));
87 	}
88 	return (ret);
89 }
90 
trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket * skt)91 static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
92 {
93 	int i;
94 	/* free allocated gpio's */
95 	gpio_free(GPIO_PRDY);
96 	for (i = 0; i < ARRAY_SIZE(irqs); i++)
97 		gpio_free(irq_to_gpio(irqs[i].irq));
98 }
99 
100 static unsigned long trizeps_pcmcia_status[2];
101 
trizeps_pcmcia_socket_state(struct soc_pcmcia_socket * skt,struct pcmcia_state * state)102 static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
103 				struct pcmcia_state *state)
104 {
105 	unsigned short status = 0, change;
106 	status = CFSR_readw();
107 	change = (status ^ trizeps_pcmcia_status[skt->nr]) &
108 				ConXS_CFSR_BVD_MASK;
109 	if (change) {
110 		trizeps_pcmcia_status[skt->nr] = status;
111 		if (status & ConXS_CFSR_BVD1) {
112 			/* enable_irq empty */
113 		} else {
114 			/* disable_irq empty */
115 		}
116 	}
117 
118 	switch (skt->nr) {
119 	case 0:
120 		/* just fill in fix states */
121 		state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1;
122 		state->ready  = gpio_get_value(GPIO_PRDY) ? 1 : 0;
123 		state->bvd1   = (status & ConXS_CFSR_BVD1) ? 1 : 0;
124 		state->bvd2   = (status & ConXS_CFSR_BVD2) ? 1 : 0;
125 		state->vs_3v  = (status & ConXS_CFSR_VS1) ? 0 : 1;
126 		state->vs_Xv  = (status & ConXS_CFSR_VS2) ? 0 : 1;
127 		state->wrprot = 0;	/* not available */
128 		break;
129 
130 #ifndef CONFIG_MACH_TRIZEPS_CONXS
131 	/* on ConXS we only have one slot. Second is inactive */
132 	case 1:
133 		state->detect = 0;
134 		state->ready  = 0;
135 		state->bvd1   = 0;
136 		state->bvd2   = 0;
137 		state->vs_3v  = 0;
138 		state->vs_Xv  = 0;
139 		state->wrprot = 0;
140 		break;
141 
142 #endif
143 	}
144 }
145 
trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket * skt,const socket_state_t * state)146 static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
147 				const socket_state_t *state)
148 {
149 	int ret = 0;
150 	unsigned short power = 0;
151 
152 	/* we do nothing here just check a bit */
153 	switch (state->Vcc) {
154 	case 0:  power &= 0xfc; break;
155 	case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
156 	case 50:
157 		pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
158 		break;
159 	default:
160 		pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
161 		ret = -1;
162 	}
163 
164 	switch (state->Vpp) {
165 	case 0:  power &= 0xf3; break;
166 	case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
167 	case 120:
168 		pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
169 		break;
170 	default:
171 		if (state->Vpp != state->Vcc) {
172 			pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
173 			ret = -1;
174 		}
175 	}
176 
177 	switch (skt->nr) {
178 	case 0:			 /* we only have 3.3V */
179 		board_pcmcia_power(power);
180 		break;
181 
182 #ifndef CONFIG_MACH_TRIZEPS_CONXS
183 	/* on ConXS we only have one slot. Second is inactive */
184 	case 1:
185 #endif
186 	default:
187 		break;
188 	}
189 
190 	return ret;
191 }
192 
trizeps_pcmcia_socket_init(struct soc_pcmcia_socket * skt)193 static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
194 {
195 	/* default is on */
196 	board_pcmcia_power(0x9);
197 }
198 
trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket * skt)199 static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
200 {
201 	board_pcmcia_power(0x0);
202 }
203 
204 static struct pcmcia_low_level trizeps_pcmcia_ops = {
205 	.owner			= THIS_MODULE,
206 	.hw_init		= trizeps_pcmcia_hw_init,
207 	.hw_shutdown		= trizeps_pcmcia_hw_shutdown,
208 	.socket_state		= trizeps_pcmcia_socket_state,
209 	.configure_socket	= trizeps_pcmcia_configure_socket,
210 	.socket_init		= trizeps_pcmcia_socket_init,
211 	.socket_suspend		= trizeps_pcmcia_socket_suspend,
212 #ifdef CONFIG_MACH_TRIZEPS_CONXS
213 	.nr			= 1,
214 #else
215 	.nr			= 2,
216 #endif
217 	.first			= 0,
218 };
219 
220 static struct platform_device *trizeps_pcmcia_device;
221 
trizeps_pcmcia_init(void)222 static int __init trizeps_pcmcia_init(void)
223 {
224 	int ret;
225 
226 	if (!machine_is_trizeps4() && !machine_is_trizeps4wl())
227 		return -ENODEV;
228 
229 	trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
230 	if (!trizeps_pcmcia_device)
231 		return -ENOMEM;
232 
233 	ret = platform_device_add_data(trizeps_pcmcia_device,
234 			&trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops));
235 
236 	if (ret == 0)
237 		ret = platform_device_add(trizeps_pcmcia_device);
238 
239 	if (ret)
240 		platform_device_put(trizeps_pcmcia_device);
241 
242 	return ret;
243 }
244 
trizeps_pcmcia_exit(void)245 static void __exit trizeps_pcmcia_exit(void)
246 {
247 	platform_device_unregister(trizeps_pcmcia_device);
248 }
249 
250 fs_initcall(trizeps_pcmcia_init);
251 module_exit(trizeps_pcmcia_exit);
252 
253 MODULE_LICENSE("GPL");
254 MODULE_AUTHOR("Juergen Schindele");
255 MODULE_ALIAS("platform:pxa2xx-pcmcia");
256