1 /*
2 * Viper/Zeus PCMCIA support
3 * Copyright 2004 Arcom Control Systems
4 *
5 * Maintained by Marc Zyngier <maz@misterjones.org>
6 *
7 * Based on:
8 * iPAQ h2200 PCMCIA support
9 * Copyright 2004 Koen Kooi <koen@vestingbar.nl>
10 *
11 * This file is subject to the terms and conditions of the GNU General Public
12 * License. See the file COPYING in the main directory of this archive for
13 * more details.
14 */
15
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/kernel.h>
19 #include <linux/errno.h>
20 #include <linux/interrupt.h>
21 #include <linux/platform_device.h>
22 #include <linux/gpio.h>
23
24 #include <pcmcia/ss.h>
25 #include <pcmcia/soc_common.h>
26
27 #include <asm/irq.h>
28
29 #include "viper-pcmcia.h"
30
31 static struct platform_device *arcom_pcmcia_dev;
32
viper_get_pdata(void)33 static inline struct arcom_pcmcia_pdata *viper_get_pdata(void)
34 {
35 return arcom_pcmcia_dev->dev.platform_data;
36 }
37
viper_pcmcia_hw_init(struct soc_pcmcia_socket * skt)38 static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
39 {
40 struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
41 unsigned long flags;
42
43 skt->stat[SOC_STAT_CD].gpio = pdata->cd_gpio;
44 skt->stat[SOC_STAT_CD].name = "PCMCIA_CD";
45 skt->stat[SOC_STAT_RDY].gpio = pdata->rdy_gpio;
46 skt->stat[SOC_STAT_RDY].name = "CF ready";
47
48 if (gpio_request(pdata->pwr_gpio, "CF power"))
49 goto err_request_pwr;
50
51 local_irq_save(flags);
52
53 if (gpio_direction_output(pdata->pwr_gpio, 0)) {
54 local_irq_restore(flags);
55 goto err_dir;
56 }
57
58 local_irq_restore(flags);
59
60 return 0;
61
62 err_dir:
63 gpio_free(pdata->pwr_gpio);
64 err_request_pwr:
65 dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n");
66 return -1;
67 }
68
69 /*
70 * Release all resources.
71 */
viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket * skt)72 static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
73 {
74 struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
75
76 gpio_free(pdata->pwr_gpio);
77 }
78
viper_pcmcia_socket_state(struct soc_pcmcia_socket * skt,struct pcmcia_state * state)79 static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
80 struct pcmcia_state *state)
81 {
82 state->vs_3v = 1; /* Can only apply 3.3V */
83 state->vs_Xv = 0;
84 }
85
viper_pcmcia_configure_socket(struct soc_pcmcia_socket * skt,const socket_state_t * state)86 static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
87 const socket_state_t *state)
88 {
89 struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
90
91 /* Silently ignore Vpp, output enable, speaker enable. */
92 pdata->reset(state->flags & SS_RESET);
93
94 /* Apply socket voltage */
95 switch (state->Vcc) {
96 case 0:
97 gpio_set_value(pdata->pwr_gpio, 0);
98 break;
99 case 33:
100 gpio_set_value(pdata->pwr_gpio, 1);
101 break;
102 default:
103 dev_err(&arcom_pcmcia_dev->dev, "Unsupported Vcc:%d\n", state->Vcc);
104 return -1;
105 }
106
107 return 0;
108 }
109
110 static struct pcmcia_low_level viper_pcmcia_ops = {
111 .owner = THIS_MODULE,
112 .hw_init = viper_pcmcia_hw_init,
113 .hw_shutdown = viper_pcmcia_hw_shutdown,
114 .socket_state = viper_pcmcia_socket_state,
115 .configure_socket = viper_pcmcia_configure_socket,
116 .nr = 1,
117 };
118
119 static struct platform_device *viper_pcmcia_device;
120
viper_pcmcia_probe(struct platform_device * pdev)121 static int viper_pcmcia_probe(struct platform_device *pdev)
122 {
123 int ret;
124
125 /* I can't imagine more than one device, but you never know... */
126 if (arcom_pcmcia_dev)
127 return -EEXIST;
128
129 if (!pdev->dev.platform_data)
130 return -EINVAL;
131
132 viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
133 if (!viper_pcmcia_device)
134 return -ENOMEM;
135
136 arcom_pcmcia_dev = pdev;
137
138 viper_pcmcia_device->dev.parent = &pdev->dev;
139
140 ret = platform_device_add_data(viper_pcmcia_device,
141 &viper_pcmcia_ops,
142 sizeof(viper_pcmcia_ops));
143
144 if (!ret)
145 ret = platform_device_add(viper_pcmcia_device);
146
147 if (ret) {
148 platform_device_put(viper_pcmcia_device);
149 arcom_pcmcia_dev = NULL;
150 }
151
152 return ret;
153 }
154
viper_pcmcia_remove(struct platform_device * pdev)155 static int viper_pcmcia_remove(struct platform_device *pdev)
156 {
157 platform_device_unregister(viper_pcmcia_device);
158 arcom_pcmcia_dev = NULL;
159 return 0;
160 }
161
162 static struct platform_device_id viper_pcmcia_id_table[] = {
163 { .name = "viper-pcmcia", },
164 { .name = "zeus-pcmcia", },
165 { },
166 };
167
168 static struct platform_driver viper_pcmcia_driver = {
169 .probe = viper_pcmcia_probe,
170 .remove = viper_pcmcia_remove,
171 .driver = {
172 .name = "arcom-pcmcia",
173 },
174 .id_table = viper_pcmcia_id_table,
175 };
176
177 module_platform_driver(viper_pcmcia_driver);
178
179 MODULE_DEVICE_TABLE(platform, viper_pcmcia_id_table);
180 MODULE_LICENSE("GPL");
181