PCMCIA: sa11x0: nanoengine: convert reset handling to use GPIO subsystem
[linux-2.6.git] / drivers / pcmcia / pxa2xx_trizeps4.c
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 int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
33 {
34         /* we dont have voltage/card/ready detection
35          * so we dont need interrupts for it
36          */
37         switch (skt->nr) {
38         case 0:
39                 skt->stat[SOC_STAT_CD].gpio = GPIO_PCD;
40                 skt->stat[SOC_STAT_CD].name = "cs0_cd";
41                 skt->stat[SOC_STAT_RDY].gpio = GPIO_PRDY;
42                 skt->stat[SOC_STAT_RDY].name = "cs0_rdy";
43                 break;
44         default:
45                 break;
46         }
47         /* release the reset of this card */
48         pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq);
49
50         return 0;
51 }
52
53 static unsigned long trizeps_pcmcia_status[2];
54
55 static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
56                                 struct pcmcia_state *state)
57 {
58         unsigned short status = 0, change;
59         status = CFSR_readw();
60         change = (status ^ trizeps_pcmcia_status[skt->nr]) &
61                                 ConXS_CFSR_BVD_MASK;
62         if (change) {
63                 trizeps_pcmcia_status[skt->nr] = status;
64                 if (status & ConXS_CFSR_BVD1) {
65                         /* enable_irq empty */
66                 } else {
67                         /* disable_irq empty */
68                 }
69         }
70
71         switch (skt->nr) {
72         case 0:
73                 /* just fill in fix states */
74                 state->bvd1   = (status & ConXS_CFSR_BVD1) ? 1 : 0;
75                 state->bvd2   = (status & ConXS_CFSR_BVD2) ? 1 : 0;
76                 state->vs_3v  = (status & ConXS_CFSR_VS1) ? 0 : 1;
77                 state->vs_Xv  = (status & ConXS_CFSR_VS2) ? 0 : 1;
78                 state->wrprot = 0;      /* not available */
79                 break;
80
81 #ifndef CONFIG_MACH_TRIZEPS_CONXS
82         /* on ConXS we only have one slot. Second is inactive */
83         case 1:
84                 state->detect = 0;
85                 state->ready  = 0;
86                 state->bvd1   = 0;
87                 state->bvd2   = 0;
88                 state->vs_3v  = 0;
89                 state->vs_Xv  = 0;
90                 state->wrprot = 0;
91                 break;
92
93 #endif
94         }
95 }
96
97 static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
98                                 const socket_state_t *state)
99 {
100         int ret = 0;
101         unsigned short power = 0;
102
103         /* we do nothing here just check a bit */
104         switch (state->Vcc) {
105         case 0:  power &= 0xfc; break;
106         case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
107         case 50:
108                 pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
109                 break;
110         default:
111                 pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
112                 ret = -1;
113         }
114
115         switch (state->Vpp) {
116         case 0:  power &= 0xf3; break;
117         case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
118         case 120:
119                 pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
120                 break;
121         default:
122                 if (state->Vpp != state->Vcc) {
123                         pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
124                         ret = -1;
125                 }
126         }
127
128         switch (skt->nr) {
129         case 0:                  /* we only have 3.3V */
130                 board_pcmcia_power(power);
131                 break;
132
133 #ifndef CONFIG_MACH_TRIZEPS_CONXS
134         /* on ConXS we only have one slot. Second is inactive */
135         case 1:
136 #endif
137         default:
138                 break;
139         }
140
141         return ret;
142 }
143
144 static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
145 {
146         /* default is on */
147         board_pcmcia_power(0x9);
148 }
149
150 static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
151 {
152         board_pcmcia_power(0x0);
153 }
154
155 static struct pcmcia_low_level trizeps_pcmcia_ops = {
156         .owner                  = THIS_MODULE,
157         .hw_init                = trizeps_pcmcia_hw_init,
158         .socket_state           = trizeps_pcmcia_socket_state,
159         .configure_socket       = trizeps_pcmcia_configure_socket,
160         .socket_init            = trizeps_pcmcia_socket_init,
161         .socket_suspend         = trizeps_pcmcia_socket_suspend,
162 #ifdef CONFIG_MACH_TRIZEPS_CONXS
163         .nr                     = 1,
164 #else
165         .nr                     = 2,
166 #endif
167         .first                  = 0,
168 };
169
170 static struct platform_device *trizeps_pcmcia_device;
171
172 static int __init trizeps_pcmcia_init(void)
173 {
174         int ret;
175
176         if (!machine_is_trizeps4() && !machine_is_trizeps4wl())
177                 return -ENODEV;
178
179         trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
180         if (!trizeps_pcmcia_device)
181                 return -ENOMEM;
182
183         ret = platform_device_add_data(trizeps_pcmcia_device,
184                         &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops));
185
186         if (ret == 0)
187                 ret = platform_device_add(trizeps_pcmcia_device);
188
189         if (ret)
190                 platform_device_put(trizeps_pcmcia_device);
191
192         return ret;
193 }
194
195 static void __exit trizeps_pcmcia_exit(void)
196 {
197         platform_device_unregister(trizeps_pcmcia_device);
198 }
199
200 fs_initcall(trizeps_pcmcia_init);
201 module_exit(trizeps_pcmcia_exit);
202
203 MODULE_LICENSE("GPL");
204 MODULE_AUTHOR("Juergen Schindele");
205 MODULE_ALIAS("platform:pxa2xx-pcmcia");