diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-12-19 17:00:22 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-01-26 14:57:24 -0500 |
commit | d9dc878769f521f494d1617d7cd0c92073df75d8 (patch) | |
tree | 79e79c50c211af91e48f38a7beed61e7425495bc /drivers/pcmcia | |
parent | e0d21178ceb06f5bfa81a5697f68384f74af054a (diff) |
PCMCIA: soc_common: add GPIO support for card status signals
Add GPIO support for reading the card status (card detect, ready,
battery voltage detect) signals into soc_common code. As we want
interrupts from these GPIOs, this takes over the old irq handling
infrastructure for card status signals, which will now be managed
entirely by the soc_common code.
Acked-by: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/soc_common.c | 120 | ||||
-rw-r--r-- | drivers/pcmcia/soc_common.h | 10 |
2 files changed, 127 insertions, 3 deletions
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 84d90d5220ce..09a9a52ad650 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c | |||
@@ -32,6 +32,7 @@ | |||
32 | 32 | ||
33 | 33 | ||
34 | #include <linux/cpufreq.h> | 34 | #include <linux/cpufreq.h> |
35 | #include <linux/gpio.h> | ||
35 | #include <linux/init.h> | 36 | #include <linux/init.h> |
36 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
37 | #include <linux/io.h> | 38 | #include <linux/io.h> |
@@ -49,6 +50,8 @@ | |||
49 | 50 | ||
50 | #include "soc_common.h" | 51 | #include "soc_common.h" |
51 | 52 | ||
53 | static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev); | ||
54 | |||
52 | #ifdef CONFIG_PCMCIA_DEBUG | 55 | #ifdef CONFIG_PCMCIA_DEBUG |
53 | 56 | ||
54 | static int pc_debug; | 57 | static int pc_debug; |
@@ -104,6 +107,93 @@ void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, | |||
104 | } | 107 | } |
105 | EXPORT_SYMBOL(soc_common_pcmcia_get_timing); | 108 | EXPORT_SYMBOL(soc_common_pcmcia_get_timing); |
106 | 109 | ||
110 | static void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt, | ||
111 | unsigned int nr) | ||
112 | { | ||
113 | unsigned int i; | ||
114 | |||
115 | for (i = 0; i < nr; i++) { | ||
116 | if (skt->stat[i].irq) | ||
117 | free_irq(skt->stat[i].irq, skt); | ||
118 | if (gpio_is_valid(skt->stat[i].gpio)) | ||
119 | gpio_free(skt->stat[i].gpio); | ||
120 | } | ||
121 | |||
122 | if (skt->ops->hw_shutdown) | ||
123 | skt->ops->hw_shutdown(skt); | ||
124 | } | ||
125 | |||
126 | static void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) | ||
127 | { | ||
128 | __soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat)); | ||
129 | } | ||
130 | |||
131 | static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt) | ||
132 | { | ||
133 | int ret = 0, i; | ||
134 | |||
135 | if (skt->ops->hw_init) { | ||
136 | ret = skt->ops->hw_init(skt); | ||
137 | if (ret) | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | for (i = 0; i < ARRAY_SIZE(skt->stat); i++) { | ||
142 | if (gpio_is_valid(skt->stat[i].gpio)) { | ||
143 | int irq; | ||
144 | |||
145 | ret = gpio_request_one(skt->stat[i].gpio, GPIOF_IN, | ||
146 | skt->stat[i].name); | ||
147 | if (ret) { | ||
148 | __soc_pcmcia_hw_shutdown(skt, i); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | irq = gpio_to_irq(skt->stat[i].gpio); | ||
153 | |||
154 | if (i == SOC_STAT_RDY) | ||
155 | skt->socket.pci_irq = irq; | ||
156 | else | ||
157 | skt->stat[i].irq = irq; | ||
158 | } | ||
159 | |||
160 | if (skt->stat[i].irq) { | ||
161 | ret = request_irq(skt->stat[i].irq, | ||
162 | soc_common_pcmcia_interrupt, | ||
163 | IRQF_TRIGGER_NONE, | ||
164 | skt->stat[i].name, skt); | ||
165 | if (ret) { | ||
166 | if (gpio_is_valid(skt->stat[i].gpio)) | ||
167 | gpio_free(skt->stat[i].gpio); | ||
168 | __soc_pcmcia_hw_shutdown(skt, i); | ||
169 | return ret; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static void soc_pcmcia_hw_enable(struct soc_pcmcia_socket *skt) | ||
178 | { | ||
179 | int i; | ||
180 | |||
181 | for (i = 0; i < ARRAY_SIZE(skt->stat); i++) | ||
182 | if (skt->stat[i].irq) { | ||
183 | irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_RISING); | ||
184 | irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_BOTH); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | static void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt) | ||
189 | { | ||
190 | int i; | ||
191 | |||
192 | for (i = 0; i < ARRAY_SIZE(skt->stat); i++) | ||
193 | if (skt->stat[i].irq) | ||
194 | irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE); | ||
195 | } | ||
196 | |||
107 | static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) | 197 | static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) |
108 | { | 198 | { |
109 | struct pcmcia_state state; | 199 | struct pcmcia_state state; |
@@ -111,6 +201,22 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt) | |||
111 | 201 | ||
112 | memset(&state, 0, sizeof(struct pcmcia_state)); | 202 | memset(&state, 0, sizeof(struct pcmcia_state)); |
113 | 203 | ||
204 | /* Make battery voltage state report 'good' */ | ||
205 | state.bvd1 = 1; | ||
206 | state.bvd2 = 1; | ||
207 | |||
208 | /* CD is active low by default */ | ||
209 | if (gpio_is_valid(skt->stat[SOC_STAT_CD].gpio)) | ||
210 | state.detect = !gpio_get_value(skt->stat[SOC_STAT_CD].gpio); | ||
211 | |||
212 | /* RDY and BVD are active high by default */ | ||
213 | if (gpio_is_valid(skt->stat[SOC_STAT_RDY].gpio)) | ||
214 | state.ready = !!gpio_get_value(skt->stat[SOC_STAT_RDY].gpio); | ||
215 | if (gpio_is_valid(skt->stat[SOC_STAT_BVD1].gpio)) | ||
216 | state.bvd1 = !!gpio_get_value(skt->stat[SOC_STAT_BVD1].gpio); | ||
217 | if (gpio_is_valid(skt->stat[SOC_STAT_BVD2].gpio)) | ||
218 | state.bvd2 = !!gpio_get_value(skt->stat[SOC_STAT_BVD2].gpio); | ||
219 | |||
114 | skt->ops->socket_state(skt, &state); | 220 | skt->ops->socket_state(skt, &state); |
115 | 221 | ||
116 | stat = state.detect ? SS_DETECT : 0; | 222 | stat = state.detect ? SS_DETECT : 0; |
@@ -188,6 +294,7 @@ static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock) | |||
188 | debug(skt, 2, "initializing socket\n"); | 294 | debug(skt, 2, "initializing socket\n"); |
189 | if (skt->ops->socket_init) | 295 | if (skt->ops->socket_init) |
190 | skt->ops->socket_init(skt); | 296 | skt->ops->socket_init(skt); |
297 | soc_pcmcia_hw_enable(skt); | ||
191 | return 0; | 298 | return 0; |
192 | } | 299 | } |
193 | 300 | ||
@@ -207,6 +314,7 @@ static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock) | |||
207 | 314 | ||
208 | debug(skt, 2, "suspending socket\n"); | 315 | debug(skt, 2, "suspending socket\n"); |
209 | 316 | ||
317 | soc_pcmcia_hw_disable(skt); | ||
210 | if (skt->ops->socket_suspend) | 318 | if (skt->ops->socket_suspend) |
211 | skt->ops->socket_suspend(skt); | 319 | skt->ops->socket_suspend(skt); |
212 | 320 | ||
@@ -638,10 +746,15 @@ module_exit(soc_pcmcia_cpufreq_unregister); | |||
638 | void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt, | 746 | void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt, |
639 | struct pcmcia_low_level *ops, struct device *dev) | 747 | struct pcmcia_low_level *ops, struct device *dev) |
640 | { | 748 | { |
749 | int i; | ||
750 | |||
641 | skt->ops = ops; | 751 | skt->ops = ops; |
642 | skt->socket.owner = ops->owner; | 752 | skt->socket.owner = ops->owner; |
643 | skt->socket.dev.parent = dev; | 753 | skt->socket.dev.parent = dev; |
644 | skt->socket.pci_irq = NO_IRQ; | 754 | skt->socket.pci_irq = NO_IRQ; |
755 | |||
756 | for (i = 0; i < ARRAY_SIZE(skt->stat); i++) | ||
757 | skt->stat[i].gpio = -EINVAL; | ||
645 | } | 758 | } |
646 | EXPORT_SYMBOL(soc_pcmcia_init_one); | 759 | EXPORT_SYMBOL(soc_pcmcia_init_one); |
647 | 760 | ||
@@ -652,8 +765,9 @@ void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt) | |||
652 | 765 | ||
653 | pcmcia_unregister_socket(&skt->socket); | 766 | pcmcia_unregister_socket(&skt->socket); |
654 | 767 | ||
655 | skt->ops->hw_shutdown(skt); | 768 | soc_pcmcia_hw_shutdown(skt); |
656 | 769 | ||
770 | /* should not be required; violates some lowlevel drivers */ | ||
657 | soc_common_pcmcia_config_skt(skt, &dead_socket); | 771 | soc_common_pcmcia_config_skt(skt, &dead_socket); |
658 | 772 | ||
659 | list_del(&skt->node); | 773 | list_del(&skt->node); |
@@ -710,7 +824,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) | |||
710 | */ | 824 | */ |
711 | skt->ops->set_timing(skt); | 825 | skt->ops->set_timing(skt); |
712 | 826 | ||
713 | ret = skt->ops->hw_init(skt); | 827 | ret = soc_pcmcia_hw_init(skt); |
714 | if (ret) | 828 | if (ret) |
715 | goto out_err_6; | 829 | goto out_err_6; |
716 | 830 | ||
@@ -743,7 +857,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt) | |||
743 | pcmcia_unregister_socket(&skt->socket); | 857 | pcmcia_unregister_socket(&skt->socket); |
744 | 858 | ||
745 | out_err_7: | 859 | out_err_7: |
746 | skt->ops->hw_shutdown(skt); | 860 | soc_pcmcia_hw_shutdown(skt); |
747 | out_err_6: | 861 | out_err_6: |
748 | list_del(&skt->node); | 862 | list_del(&skt->node); |
749 | mutex_unlock(&soc_pcmcia_sockets_lock); | 863 | mutex_unlock(&soc_pcmcia_sockets_lock); |
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index 3ff7ead11b1d..ebdfa6c16308 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h | |||
@@ -50,6 +50,16 @@ struct soc_pcmcia_socket { | |||
50 | struct resource res_attr; | 50 | struct resource res_attr; |
51 | void __iomem *virt_io; | 51 | void __iomem *virt_io; |
52 | 52 | ||
53 | struct { | ||
54 | int gpio; | ||
55 | unsigned int irq; | ||
56 | const char *name; | ||
57 | } stat[4]; | ||
58 | #define SOC_STAT_CD 0 /* Card detect */ | ||
59 | #define SOC_STAT_BVD1 1 /* BATDEAD / IOSTSCHG */ | ||
60 | #define SOC_STAT_BVD2 2 /* BATWARN / IOSPKR */ | ||
61 | #define SOC_STAT_RDY 3 /* Ready / Interrupt */ | ||
62 | |||
53 | unsigned int irq_state; | 63 | unsigned int irq_state; |
54 | 64 | ||
55 | struct timer_list poll_timer; | 65 | struct timer_list poll_timer; |