GNU Linux-libre 5.19-rc6-gnu
[releases.git] / arch / arm / mach-pxa / viper-pcmcia.c
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
33 static inline struct arcom_pcmcia_pdata *viper_get_pdata(void)
34 {
35         return arcom_pcmcia_dev->dev.platform_data;
36 }
37
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  */
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
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
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
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
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");