diff options
Diffstat (limited to 'drivers/pcmcia/pxa2xx_viper.c')
-rw-r--r-- | drivers/pcmcia/pxa2xx_viper.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c new file mode 100644 index 000000000000..dd10481be7bf --- /dev/null +++ b/drivers/pcmcia/pxa2xx_viper.c | |||
@@ -0,0 +1,179 @@ | |||
1 | /* | ||
2 | * VIPER PCMCIA support | ||
3 | * Copyright 2004 Arcom Control Systems | ||
4 | * | ||
5 | * Maintained by Marc Zyngier <maz@misterjones.org> | ||
6 | * <marc.zyngier@altran.com> | ||
7 | * | ||
8 | * Based on: | ||
9 | * iPAQ h2200 PCMCIA support | ||
10 | * Copyright 2004 Koen Kooi <koen@vestingbar.nl> | ||
11 | * | ||
12 | * This file is subject to the terms and conditions of the GNU General Public | ||
13 | * License. See the file COPYING in the main directory of this archive for | ||
14 | * more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/gpio.h> | ||
24 | |||
25 | #include <pcmcia/ss.h> | ||
26 | |||
27 | #include <asm/irq.h> | ||
28 | |||
29 | #include <mach/pxa-regs.h> | ||
30 | #include <mach/viper.h> | ||
31 | #include <asm/mach-types.h> | ||
32 | |||
33 | #include "soc_common.h" | ||
34 | #include "pxa2xx_base.h" | ||
35 | |||
36 | static struct pcmcia_irqs irqs[] = { | ||
37 | { 0, gpio_to_irq(VIPER_CF_CD_GPIO), "PCMCIA_CD" } | ||
38 | }; | ||
39 | |||
40 | static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt) | ||
41 | { | ||
42 | unsigned long flags; | ||
43 | |||
44 | skt->irq = gpio_to_irq(VIPER_CF_RDY_GPIO); | ||
45 | |||
46 | if (gpio_request(VIPER_CF_CD_GPIO, "CF detect")) | ||
47 | goto err_request_cd; | ||
48 | |||
49 | if (gpio_request(VIPER_CF_RDY_GPIO, "CF ready")) | ||
50 | goto err_request_rdy; | ||
51 | |||
52 | if (gpio_request(VIPER_CF_POWER_GPIO, "CF power")) | ||
53 | goto err_request_pwr; | ||
54 | |||
55 | local_irq_save(flags); | ||
56 | |||
57 | /* GPIO 82 is the CF power enable line. initially off */ | ||
58 | if (gpio_direction_output(VIPER_CF_POWER_GPIO, 0) || | ||
59 | gpio_direction_input(VIPER_CF_CD_GPIO) || | ||
60 | gpio_direction_input(VIPER_CF_RDY_GPIO)) { | ||
61 | local_irq_restore(flags); | ||
62 | goto err_dir; | ||
63 | } | ||
64 | |||
65 | local_irq_restore(flags); | ||
66 | |||
67 | return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); | ||
68 | |||
69 | err_dir: | ||
70 | gpio_free(VIPER_CF_POWER_GPIO); | ||
71 | err_request_pwr: | ||
72 | gpio_free(VIPER_CF_RDY_GPIO); | ||
73 | err_request_rdy: | ||
74 | gpio_free(VIPER_CF_CD_GPIO); | ||
75 | err_request_cd: | ||
76 | printk(KERN_ERR "viper: Failed to setup PCMCIA GPIOs\n"); | ||
77 | return -1; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * Release all resources. | ||
82 | */ | ||
83 | static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) | ||
84 | { | ||
85 | soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); | ||
86 | gpio_free(VIPER_CF_POWER_GPIO); | ||
87 | gpio_free(VIPER_CF_RDY_GPIO); | ||
88 | gpio_free(VIPER_CF_CD_GPIO); | ||
89 | } | ||
90 | |||
91 | static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt, | ||
92 | struct pcmcia_state *state) | ||
93 | { | ||
94 | state->detect = gpio_get_value(VIPER_CF_CD_GPIO) ? 0 : 1; | ||
95 | state->ready = gpio_get_value(VIPER_CF_RDY_GPIO) ? 1 : 0; | ||
96 | state->bvd1 = 1; | ||
97 | state->bvd2 = 1; | ||
98 | state->wrprot = 0; | ||
99 | state->vs_3v = 1; /* Can only apply 3.3V */ | ||
100 | state->vs_Xv = 0; | ||
101 | } | ||
102 | |||
103 | static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, | ||
104 | const socket_state_t *state) | ||
105 | { | ||
106 | /* Silently ignore Vpp, output enable, speaker enable. */ | ||
107 | viper_cf_rst(state->flags & SS_RESET); | ||
108 | |||
109 | /* Apply socket voltage */ | ||
110 | switch (state->Vcc) { | ||
111 | case 0: | ||
112 | gpio_set_value(VIPER_CF_POWER_GPIO, 0); | ||
113 | break; | ||
114 | case 33: | ||
115 | gpio_set_value(VIPER_CF_POWER_GPIO, 1); | ||
116 | break; | ||
117 | default: | ||
118 | printk(KERN_ERR "%s: Unsupported Vcc:%d\n", | ||
119 | __func__, state->Vcc); | ||
120 | return -1; | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static void viper_pcmcia_socket_init(struct soc_pcmcia_socket *skt) | ||
127 | { | ||
128 | } | ||
129 | |||
130 | static void viper_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) | ||
131 | { | ||
132 | } | ||
133 | |||
134 | static struct pcmcia_low_level viper_pcmcia_ops __initdata = { | ||
135 | .owner = THIS_MODULE, | ||
136 | .hw_init = viper_pcmcia_hw_init, | ||
137 | .hw_shutdown = viper_pcmcia_hw_shutdown, | ||
138 | .socket_state = viper_pcmcia_socket_state, | ||
139 | .configure_socket = viper_pcmcia_configure_socket, | ||
140 | .socket_init = viper_pcmcia_socket_init, | ||
141 | .socket_suspend = viper_pcmcia_socket_suspend, | ||
142 | .nr = 1, | ||
143 | }; | ||
144 | |||
145 | static struct platform_device *viper_pcmcia_device; | ||
146 | |||
147 | static int __init viper_pcmcia_init(void) | ||
148 | { | ||
149 | int ret; | ||
150 | |||
151 | if (!machine_is_viper()) | ||
152 | return -ENODEV; | ||
153 | |||
154 | viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); | ||
155 | if (!viper_pcmcia_device) | ||
156 | return -ENOMEM; | ||
157 | |||
158 | ret = platform_device_add_data(viper_pcmcia_device, | ||
159 | &viper_pcmcia_ops, | ||
160 | sizeof(viper_pcmcia_ops)); | ||
161 | |||
162 | if (!ret) | ||
163 | ret = platform_device_add(viper_pcmcia_device); | ||
164 | |||
165 | if (ret) | ||
166 | platform_device_put(viper_pcmcia_device); | ||
167 | |||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | static void __exit viper_pcmcia_exit(void) | ||
172 | { | ||
173 | platform_device_unregister(viper_pcmcia_device); | ||
174 | } | ||
175 | |||
176 | module_init(viper_pcmcia_init); | ||
177 | module_exit(viper_pcmcia_exit); | ||
178 | |||
179 | MODULE_LICENSE("GPL"); | ||