Merge remote-tracking branches 'asoc/fix/dpcm', 'asoc/fix/imx', 'asoc/fix/msm8916...
[sfrench/cifs-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                 break;
79
80 #ifndef CONFIG_MACH_TRIZEPS_CONXS
81         /* on ConXS we only have one slot. Second is inactive */
82         case 1:
83                 state->detect = 0;
84                 state->ready  = 0;
85                 state->bvd1   = 0;
86                 state->bvd2   = 0;
87                 state->vs_3v  = 0;
88                 state->vs_Xv  = 0;
89                 break;
90
91 #endif
92         }
93 }
94
95 static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
96                                 const socket_state_t *state)
97 {
98         int ret = 0;
99         unsigned short power = 0;
100
101         /* we do nothing here just check a bit */
102         switch (state->Vcc) {
103         case 0:  power &= 0xfc; break;
104         case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
105         case 50:
106                 pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
107                 break;
108         default:
109                 pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
110                 ret = -1;
111         }
112
113         switch (state->Vpp) {
114         case 0:  power &= 0xf3; break;
115         case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
116         case 120:
117                 pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
118                 break;
119         default:
120                 if (state->Vpp != state->Vcc) {
121                         pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
122                         ret = -1;
123                 }
124         }
125
126         switch (skt->nr) {
127         case 0:                  /* we only have 3.3V */
128                 board_pcmcia_power(power);
129                 break;
130
131 #ifndef CONFIG_MACH_TRIZEPS_CONXS
132         /* on ConXS we only have one slot. Second is inactive */
133         case 1:
134 #endif
135         default:
136                 break;
137         }
138
139         return ret;
140 }
141
142 static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
143 {
144         /* default is on */
145         board_pcmcia_power(0x9);
146 }
147
148 static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
149 {
150         board_pcmcia_power(0x0);
151 }
152
153 static struct pcmcia_low_level trizeps_pcmcia_ops = {
154         .owner                  = THIS_MODULE,
155         .hw_init                = trizeps_pcmcia_hw_init,
156         .socket_state           = trizeps_pcmcia_socket_state,
157         .configure_socket       = trizeps_pcmcia_configure_socket,
158         .socket_init            = trizeps_pcmcia_socket_init,
159         .socket_suspend         = trizeps_pcmcia_socket_suspend,
160 #ifdef CONFIG_MACH_TRIZEPS_CONXS
161         .nr                     = 1,
162 #else
163         .nr                     = 2,
164 #endif
165         .first                  = 0,
166 };
167
168 static struct platform_device *trizeps_pcmcia_device;
169
170 static int __init trizeps_pcmcia_init(void)
171 {
172         int ret;
173
174         if (!machine_is_trizeps4() && !machine_is_trizeps4wl())
175                 return -ENODEV;
176
177         trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
178         if (!trizeps_pcmcia_device)
179                 return -ENOMEM;
180
181         ret = platform_device_add_data(trizeps_pcmcia_device,
182                         &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops));
183
184         if (ret == 0)
185                 ret = platform_device_add(trizeps_pcmcia_device);
186
187         if (ret)
188                 platform_device_put(trizeps_pcmcia_device);
189
190         return ret;
191 }
192
193 static void __exit trizeps_pcmcia_exit(void)
194 {
195         platform_device_unregister(trizeps_pcmcia_device);
196 }
197
198 fs_initcall(trizeps_pcmcia_init);
199 module_exit(trizeps_pcmcia_exit);
200
201 MODULE_LICENSE("GPL");
202 MODULE_AUTHOR("Juergen Schindele");
203 MODULE_ALIAS("platform:pxa2xx-pcmcia");