Commit | Line | Data |
---|---|---|
349ab524 GL |
1 | /* |
2 | * Generic GPIO card-detect helper | |
3 | * | |
4 | * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/err.h> | |
12 | #include <linux/gpio.h> | |
842f4bdd | 13 | #include <linux/gpio/consumer.h> |
349ab524 GL |
14 | #include <linux/interrupt.h> |
15 | #include <linux/jiffies.h> | |
16 | #include <linux/mmc/host.h> | |
fd0ea65d | 17 | #include <linux/mmc/slot-gpio.h> |
349ab524 GL |
18 | #include <linux/module.h> |
19 | #include <linux/slab.h> | |
20 | ||
7f133de1 UH |
21 | #include "slot-gpio.h" |
22 | ||
fd0ea65d | 23 | struct mmc_gpio { |
842f4bdd AH |
24 | struct gpio_desc *ro_gpio; |
25 | struct gpio_desc *cd_gpio; | |
26 | bool override_ro_active_level; | |
27 | bool override_cd_active_level; | |
c7ea834d | 28 | irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); |
5aa7dad3 | 29 | char *ro_label; |
fd0ea65d | 30 | char cd_label[0]; |
349ab524 GL |
31 | }; |
32 | ||
fd0ea65d | 33 | static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) |
349ab524 GL |
34 | { |
35 | /* Schedule a card detection after a debounce timeout */ | |
451c8957 GL |
36 | struct mmc_host *host = dev_id; |
37 | ||
fa372a51 | 38 | host->trigger_card_event = true; |
451c8957 GL |
39 | mmc_detect_change(host, msecs_to_jiffies(200)); |
40 | ||
349ab524 GL |
41 | return IRQ_HANDLED; |
42 | } | |
43 | ||
7f133de1 | 44 | int mmc_gpio_alloc(struct mmc_host *host) |
a7d1a1eb GL |
45 | { |
46 | size_t len = strlen(dev_name(host->parent)) + 4; | |
df8aca16 UH |
47 | struct mmc_gpio *ctx = devm_kzalloc(host->parent, |
48 | sizeof(*ctx) + 2 * len, GFP_KERNEL); | |
49 | ||
50 | if (ctx) { | |
51 | ctx->ro_label = ctx->cd_label + len; | |
52 | snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); | |
53 | snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); | |
54 | host->slot.handler_priv = ctx; | |
55 | host->slot.cd_irq = -EINVAL; | |
a7d1a1eb GL |
56 | } |
57 | ||
a7d1a1eb GL |
58 | return ctx ? 0 : -ENOMEM; |
59 | } | |
60 | ||
5aa7dad3 GL |
61 | int mmc_gpio_get_ro(struct mmc_host *host) |
62 | { | |
63 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
64 | ||
842f4bdd | 65 | if (!ctx || !ctx->ro_gpio) |
5aa7dad3 GL |
66 | return -ENOSYS; |
67 | ||
842f4bdd AH |
68 | if (ctx->override_ro_active_level) |
69 | return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^ | |
70 | !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); | |
71 | ||
72 | return gpiod_get_value_cansleep(ctx->ro_gpio); | |
5aa7dad3 GL |
73 | } |
74 | EXPORT_SYMBOL(mmc_gpio_get_ro); | |
75 | ||
befe4048 GL |
76 | int mmc_gpio_get_cd(struct mmc_host *host) |
77 | { | |
78 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
79 | ||
842f4bdd | 80 | if (!ctx || !ctx->cd_gpio) |
befe4048 GL |
81 | return -ENOSYS; |
82 | ||
842f4bdd AH |
83 | if (ctx->override_cd_active_level) |
84 | return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^ | |
85 | !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); | |
86 | ||
87 | return gpiod_get_value_cansleep(ctx->cd_gpio); | |
befe4048 GL |
88 | } |
89 | EXPORT_SYMBOL(mmc_gpio_get_cd); | |
90 | ||
d65b5ae8 SG |
91 | /** |
92 | * mmc_gpio_request_ro - request a gpio for write-protection | |
93 | * @host: mmc host | |
94 | * @gpio: gpio number requested | |
95 | * | |
96 | * As devm_* managed functions are used in mmc_gpio_request_ro(), client | |
eddbc3ab | 97 | * drivers do not need to worry about freeing up memory. |
d65b5ae8 SG |
98 | * |
99 | * Returns zero on success, else an error. | |
100 | */ | |
5aa7dad3 GL |
101 | int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) |
102 | { | |
df8aca16 | 103 | struct mmc_gpio *ctx = host->slot.handler_priv; |
5aa7dad3 GL |
104 | int ret; |
105 | ||
106 | if (!gpio_is_valid(gpio)) | |
107 | return -EINVAL; | |
108 | ||
b4cc580b | 109 | ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, |
d65b5ae8 | 110 | ctx->ro_label); |
15e8a8e4 CB |
111 | if (ret < 0) |
112 | return ret; | |
113 | ||
842f4bdd AH |
114 | ctx->override_ro_active_level = true; |
115 | ctx->ro_gpio = gpio_to_desc(gpio); | |
15e8a8e4 CB |
116 | |
117 | return 0; | |
5aa7dad3 GL |
118 | } |
119 | EXPORT_SYMBOL(mmc_gpio_request_ro); | |
120 | ||
740a221e | 121 | void mmc_gpiod_request_cd_irq(struct mmc_host *host) |
26652671 AH |
122 | { |
123 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
124 | int ret, irq; | |
125 | ||
126 | if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) | |
127 | return; | |
128 | ||
129 | irq = gpiod_to_irq(ctx->cd_gpio); | |
130 | ||
131 | /* | |
132 | * Even if gpiod_to_irq() returns a valid IRQ number, the platform might | |
133 | * still prefer to poll, e.g., because that IRQ number is already used | |
134 | * by another unit and cannot be shared. | |
135 | */ | |
136 | if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) | |
137 | irq = -EINVAL; | |
138 | ||
139 | if (irq >= 0) { | |
c7ea834d N |
140 | if (!ctx->cd_gpio_isr) |
141 | ctx->cd_gpio_isr = mmc_gpio_cd_irqt; | |
b4cc580b | 142 | ret = devm_request_threaded_irq(host->parent, irq, |
c7ea834d | 143 | NULL, ctx->cd_gpio_isr, |
26652671 AH |
144 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
145 | ctx->cd_label, host); | |
146 | if (ret < 0) | |
147 | irq = ret; | |
148 | } | |
149 | ||
150 | host->slot.cd_irq = irq; | |
151 | ||
152 | if (irq < 0) | |
153 | host->caps |= MMC_CAP_NEEDS_POLL; | |
154 | } | |
740a221e | 155 | EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); |
26652671 | 156 | |
c7ea834d N |
157 | /* Register an alternate interrupt service routine for |
158 | * the card-detect GPIO. | |
159 | */ | |
160 | void mmc_gpio_set_cd_isr(struct mmc_host *host, | |
161 | irqreturn_t (*isr)(int irq, void *dev_id)) | |
162 | { | |
163 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
164 | ||
165 | WARN_ON(ctx->cd_gpio_isr); | |
166 | ctx->cd_gpio_isr = isr; | |
167 | } | |
168 | EXPORT_SYMBOL(mmc_gpio_set_cd_isr); | |
169 | ||
d65b5ae8 SG |
170 | /** |
171 | * mmc_gpio_request_cd - request a gpio for card-detection | |
172 | * @host: mmc host | |
173 | * @gpio: gpio number requested | |
214fc309 | 174 | * @debounce: debounce time in microseconds |
d65b5ae8 SG |
175 | * |
176 | * As devm_* managed functions are used in mmc_gpio_request_cd(), client | |
eddbc3ab | 177 | * drivers do not need to worry about freeing up memory. |
d65b5ae8 | 178 | * |
214fc309 LP |
179 | * If GPIO debouncing is desired, set the debounce parameter to a non-zero |
180 | * value. The caller is responsible for ensuring that the GPIO driver associated | |
181 | * with the GPIO supports debouncing, otherwise an error will be returned. | |
182 | * | |
d65b5ae8 SG |
183 | * Returns zero on success, else an error. |
184 | */ | |
214fc309 LP |
185 | int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, |
186 | unsigned int debounce) | |
349ab524 | 187 | { |
df8aca16 | 188 | struct mmc_gpio *ctx = host->slot.handler_priv; |
349ab524 GL |
189 | int ret; |
190 | ||
b4cc580b | 191 | ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, |
d65b5ae8 | 192 | ctx->cd_label); |
349ab524 | 193 | if (ret < 0) |
a7d1a1eb GL |
194 | /* |
195 | * don't bother freeing memory. It might still get used by other | |
196 | * slot functions, in any case it will be freed, when the device | |
197 | * is destroyed. | |
198 | */ | |
199 | return ret; | |
349ab524 | 200 | |
214fc309 LP |
201 | if (debounce) { |
202 | ret = gpio_set_debounce(gpio, debounce); | |
203 | if (ret < 0) | |
204 | return ret; | |
205 | } | |
206 | ||
842f4bdd AH |
207 | ctx->override_cd_active_level = true; |
208 | ctx->cd_gpio = gpio_to_desc(gpio); | |
349ab524 GL |
209 | |
210 | return 0; | |
349ab524 | 211 | } |
fd0ea65d | 212 | EXPORT_SYMBOL(mmc_gpio_request_cd); |
349ab524 | 213 | |
740a221e AH |
214 | /** |
215 | * mmc_gpiod_request_cd - request a gpio descriptor for card-detection | |
216 | * @host: mmc host | |
217 | * @con_id: function within the GPIO consumer | |
218 | * @idx: index of the GPIO to obtain in the consumer | |
219 | * @override_active_level: ignore %GPIO_ACTIVE_LOW flag | |
220 | * @debounce: debounce time in microseconds | |
89168b48 LW |
221 | * @gpio_invert: will return whether the GPIO line is inverted or not, set |
222 | * to NULL to ignore | |
740a221e AH |
223 | * |
224 | * Use this function in place of mmc_gpio_request_cd() to use the GPIO | |
eddbc3ab | 225 | * descriptor API. Note that it must be called prior to mmc_add_host() |
740a221e AH |
226 | * otherwise the caller must also call mmc_gpiod_request_cd_irq(). |
227 | * | |
228 | * Returns zero on success, else an error. | |
229 | */ | |
230 | int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, | |
231 | unsigned int idx, bool override_active_level, | |
89168b48 | 232 | unsigned int debounce, bool *gpio_invert) |
740a221e | 233 | { |
df8aca16 | 234 | struct mmc_gpio *ctx = host->slot.handler_priv; |
740a221e AH |
235 | struct gpio_desc *desc; |
236 | int ret; | |
237 | ||
740a221e AH |
238 | if (!con_id) |
239 | con_id = ctx->cd_label; | |
240 | ||
9fbc6950 | 241 | desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); |
740a221e AH |
242 | if (IS_ERR(desc)) |
243 | return PTR_ERR(desc); | |
244 | ||
740a221e AH |
245 | if (debounce) { |
246 | ret = gpiod_set_debounce(desc, debounce); | |
247 | if (ret < 0) | |
248 | return ret; | |
249 | } | |
250 | ||
89168b48 LW |
251 | if (gpio_invert) |
252 | *gpio_invert = !gpiod_is_active_low(desc); | |
253 | ||
740a221e AH |
254 | ctx->override_cd_active_level = override_active_level; |
255 | ctx->cd_gpio = desc; | |
256 | ||
257 | return 0; | |
258 | } | |
259 | EXPORT_SYMBOL(mmc_gpiod_request_cd); | |
260 | ||
9d2fa242 LW |
261 | /** |
262 | * mmc_gpiod_request_ro - request a gpio descriptor for write protection | |
263 | * @host: mmc host | |
264 | * @con_id: function within the GPIO consumer | |
265 | * @idx: index of the GPIO to obtain in the consumer | |
266 | * @override_active_level: ignore %GPIO_ACTIVE_LOW flag | |
267 | * @debounce: debounce time in microseconds | |
89168b48 LW |
268 | * @gpio_invert: will return whether the GPIO line is inverted or not, |
269 | * set to NULL to ignore | |
9d2fa242 LW |
270 | * |
271 | * Use this function in place of mmc_gpio_request_ro() to use the GPIO | |
eddbc3ab | 272 | * descriptor API. |
9d2fa242 LW |
273 | * |
274 | * Returns zero on success, else an error. | |
275 | */ | |
276 | int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, | |
277 | unsigned int idx, bool override_active_level, | |
89168b48 | 278 | unsigned int debounce, bool *gpio_invert) |
9d2fa242 | 279 | { |
df8aca16 | 280 | struct mmc_gpio *ctx = host->slot.handler_priv; |
9d2fa242 LW |
281 | struct gpio_desc *desc; |
282 | int ret; | |
283 | ||
9d2fa242 LW |
284 | if (!con_id) |
285 | con_id = ctx->ro_label; | |
286 | ||
287 | desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); | |
288 | if (IS_ERR(desc)) | |
289 | return PTR_ERR(desc); | |
290 | ||
291 | if (debounce) { | |
292 | ret = gpiod_set_debounce(desc, debounce); | |
293 | if (ret < 0) | |
294 | return ret; | |
295 | } | |
296 | ||
89168b48 LW |
297 | if (gpio_invert) |
298 | *gpio_invert = !gpiod_is_active_low(desc); | |
299 | ||
9d2fa242 LW |
300 | ctx->override_ro_active_level = override_active_level; |
301 | ctx->ro_gpio = desc; | |
302 | ||
303 | return 0; | |
304 | } | |
305 | EXPORT_SYMBOL(mmc_gpiod_request_ro); |