1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Generic GPIO card-detect helper |
4 | * |
5 | * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> |
6 | */ |
7 | |
8 | #include <linux/err.h> |
9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/jiffies.h> |
12 | #include <linux/mmc/host.h> |
13 | #include <linux/mmc/slot-gpio.h> |
14 | #include <linux/module.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include "slot-gpio.h" |
18 | |
19 | struct mmc_gpio { |
20 | struct gpio_desc *ro_gpio; |
21 | struct gpio_desc *cd_gpio; |
22 | irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); |
23 | char *ro_label; |
24 | char *cd_label; |
25 | u32 cd_debounce_delay_ms; |
26 | int cd_irq; |
27 | }; |
28 | |
29 | static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) |
30 | { |
31 | /* Schedule a card detection after a debounce timeout */ |
32 | struct mmc_host *host = dev_id; |
33 | struct mmc_gpio *ctx = host->slot.handler_priv; |
34 | |
35 | host->trigger_card_event = true; |
36 | mmc_detect_change(host, delay: msecs_to_jiffies(m: ctx->cd_debounce_delay_ms)); |
37 | |
38 | return IRQ_HANDLED; |
39 | } |
40 | |
41 | int mmc_gpio_alloc(struct mmc_host *host) |
42 | { |
43 | const char *devname = dev_name(dev: host->parent); |
44 | struct mmc_gpio *ctx; |
45 | |
46 | ctx = devm_kzalloc(dev: host->parent, size: sizeof(*ctx), GFP_KERNEL); |
47 | if (!ctx) |
48 | return -ENOMEM; |
49 | |
50 | ctx->cd_debounce_delay_ms = 200; |
51 | ctx->cd_label = devm_kasprintf(dev: host->parent, GFP_KERNEL, fmt: "%s cd" , devname); |
52 | if (!ctx->cd_label) |
53 | return -ENOMEM; |
54 | ctx->ro_label = devm_kasprintf(dev: host->parent, GFP_KERNEL, fmt: "%s ro" , devname); |
55 | if (!ctx->ro_label) |
56 | return -ENOMEM; |
57 | ctx->cd_irq = -EINVAL; |
58 | host->slot.handler_priv = ctx; |
59 | host->slot.cd_irq = -EINVAL; |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | void mmc_gpio_set_cd_irq(struct mmc_host *host, int irq) |
65 | { |
66 | struct mmc_gpio *ctx = host->slot.handler_priv; |
67 | |
68 | if (!ctx || irq < 0) |
69 | return; |
70 | |
71 | ctx->cd_irq = irq; |
72 | } |
73 | EXPORT_SYMBOL(mmc_gpio_set_cd_irq); |
74 | |
75 | int mmc_gpio_get_ro(struct mmc_host *host) |
76 | { |
77 | struct mmc_gpio *ctx = host->slot.handler_priv; |
78 | int cansleep; |
79 | |
80 | if (!ctx || !ctx->ro_gpio) |
81 | return -ENOSYS; |
82 | |
83 | cansleep = gpiod_cansleep(desc: ctx->ro_gpio); |
84 | return cansleep ? |
85 | gpiod_get_value_cansleep(desc: ctx->ro_gpio) : |
86 | gpiod_get_value(desc: ctx->ro_gpio); |
87 | } |
88 | EXPORT_SYMBOL(mmc_gpio_get_ro); |
89 | |
90 | int mmc_gpio_get_cd(struct mmc_host *host) |
91 | { |
92 | struct mmc_gpio *ctx = host->slot.handler_priv; |
93 | int cansleep; |
94 | |
95 | if (!ctx || !ctx->cd_gpio) |
96 | return -ENOSYS; |
97 | |
98 | cansleep = gpiod_cansleep(desc: ctx->cd_gpio); |
99 | return cansleep ? |
100 | gpiod_get_value_cansleep(desc: ctx->cd_gpio) : |
101 | gpiod_get_value(desc: ctx->cd_gpio); |
102 | } |
103 | EXPORT_SYMBOL(mmc_gpio_get_cd); |
104 | |
105 | void mmc_gpiod_request_cd_irq(struct mmc_host *host) |
106 | { |
107 | struct mmc_gpio *ctx = host->slot.handler_priv; |
108 | int irq = -EINVAL; |
109 | int ret; |
110 | |
111 | if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) |
112 | return; |
113 | |
114 | /* |
115 | * Do not use IRQ if the platform prefers to poll, e.g., because that |
116 | * IRQ number is already used by another unit and cannot be shared. |
117 | */ |
118 | if (ctx->cd_irq >= 0) |
119 | irq = ctx->cd_irq; |
120 | else if (!(host->caps & MMC_CAP_NEEDS_POLL)) |
121 | irq = gpiod_to_irq(desc: ctx->cd_gpio); |
122 | |
123 | if (irq >= 0) { |
124 | if (!ctx->cd_gpio_isr) |
125 | ctx->cd_gpio_isr = mmc_gpio_cd_irqt; |
126 | ret = devm_request_threaded_irq(dev: host->parent, irq, |
127 | NULL, thread_fn: ctx->cd_gpio_isr, |
128 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
129 | devname: ctx->cd_label, dev_id: host); |
130 | if (ret < 0) |
131 | irq = ret; |
132 | } |
133 | |
134 | host->slot.cd_irq = irq; |
135 | |
136 | if (irq < 0) |
137 | host->caps |= MMC_CAP_NEEDS_POLL; |
138 | } |
139 | EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); |
140 | |
141 | int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on) |
142 | { |
143 | int ret = 0; |
144 | |
145 | if (!(host->caps & MMC_CAP_CD_WAKE) || |
146 | host->slot.cd_irq < 0 || |
147 | on == host->slot.cd_wake_enabled) |
148 | return 0; |
149 | |
150 | if (on) { |
151 | ret = enable_irq_wake(irq: host->slot.cd_irq); |
152 | host->slot.cd_wake_enabled = !ret; |
153 | } else { |
154 | disable_irq_wake(irq: host->slot.cd_irq); |
155 | host->slot.cd_wake_enabled = false; |
156 | } |
157 | |
158 | return ret; |
159 | } |
160 | EXPORT_SYMBOL(mmc_gpio_set_cd_wake); |
161 | |
162 | /* Register an alternate interrupt service routine for |
163 | * the card-detect GPIO. |
164 | */ |
165 | void mmc_gpio_set_cd_isr(struct mmc_host *host, |
166 | irqreturn_t (*isr)(int irq, void *dev_id)) |
167 | { |
168 | struct mmc_gpio *ctx = host->slot.handler_priv; |
169 | |
170 | WARN_ON(ctx->cd_gpio_isr); |
171 | ctx->cd_gpio_isr = isr; |
172 | } |
173 | EXPORT_SYMBOL(mmc_gpio_set_cd_isr); |
174 | |
175 | /** |
176 | * mmc_gpiod_request_cd - request a gpio descriptor for card-detection |
177 | * @host: mmc host |
178 | * @con_id: function within the GPIO consumer |
179 | * @idx: index of the GPIO to obtain in the consumer |
180 | * @override_active_level: ignore %GPIO_ACTIVE_LOW flag |
181 | * @debounce: debounce time in microseconds |
182 | * |
183 | * Note that this must be called prior to mmc_add_host() |
184 | * otherwise the caller must also call mmc_gpiod_request_cd_irq(). |
185 | * |
186 | * Returns zero on success, else an error. |
187 | */ |
188 | int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, |
189 | unsigned int idx, bool override_active_level, |
190 | unsigned int debounce) |
191 | { |
192 | struct mmc_gpio *ctx = host->slot.handler_priv; |
193 | struct gpio_desc *desc; |
194 | int ret; |
195 | |
196 | desc = devm_gpiod_get_index(dev: host->parent, con_id, idx, flags: GPIOD_IN); |
197 | if (IS_ERR(ptr: desc)) |
198 | return PTR_ERR(ptr: desc); |
199 | |
200 | /* Update default label if no con_id provided */ |
201 | if (!con_id) |
202 | gpiod_set_consumer_name(desc, name: ctx->cd_label); |
203 | |
204 | if (debounce) { |
205 | ret = gpiod_set_debounce(desc, debounce); |
206 | if (ret < 0) |
207 | ctx->cd_debounce_delay_ms = debounce / 1000; |
208 | } |
209 | |
210 | /* override forces default (active-low) polarity ... */ |
211 | if (override_active_level && !gpiod_is_active_low(desc)) |
212 | gpiod_toggle_active_low(desc); |
213 | |
214 | /* ... or active-high */ |
215 | if (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) |
216 | gpiod_toggle_active_low(desc); |
217 | |
218 | ctx->cd_gpio = desc; |
219 | |
220 | return 0; |
221 | } |
222 | EXPORT_SYMBOL(mmc_gpiod_request_cd); |
223 | |
224 | bool mmc_can_gpio_cd(struct mmc_host *host) |
225 | { |
226 | struct mmc_gpio *ctx = host->slot.handler_priv; |
227 | |
228 | return ctx->cd_gpio ? true : false; |
229 | } |
230 | EXPORT_SYMBOL(mmc_can_gpio_cd); |
231 | |
232 | /** |
233 | * mmc_gpiod_request_ro - request a gpio descriptor for write protection |
234 | * @host: mmc host |
235 | * @con_id: function within the GPIO consumer |
236 | * @idx: index of the GPIO to obtain in the consumer |
237 | * @debounce: debounce time in microseconds |
238 | * |
239 | * Returns zero on success, else an error. |
240 | */ |
241 | int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, |
242 | unsigned int idx, unsigned int debounce) |
243 | { |
244 | struct mmc_gpio *ctx = host->slot.handler_priv; |
245 | struct gpio_desc *desc; |
246 | int ret; |
247 | |
248 | desc = devm_gpiod_get_index(dev: host->parent, con_id, idx, flags: GPIOD_IN); |
249 | if (IS_ERR(ptr: desc)) |
250 | return PTR_ERR(ptr: desc); |
251 | |
252 | /* Update default label if no con_id provided */ |
253 | if (!con_id) |
254 | gpiod_set_consumer_name(desc, name: ctx->ro_label); |
255 | |
256 | if (debounce) { |
257 | ret = gpiod_set_debounce(desc, debounce); |
258 | if (ret < 0) |
259 | return ret; |
260 | } |
261 | |
262 | if (host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH) |
263 | gpiod_toggle_active_low(desc); |
264 | |
265 | ctx->ro_gpio = desc; |
266 | |
267 | return 0; |
268 | } |
269 | EXPORT_SYMBOL(mmc_gpiod_request_ro); |
270 | |
271 | bool mmc_can_gpio_ro(struct mmc_host *host) |
272 | { |
273 | struct mmc_gpio *ctx = host->slot.handler_priv; |
274 | |
275 | return ctx->ro_gpio ? true : false; |
276 | } |
277 | EXPORT_SYMBOL(mmc_can_gpio_ro); |
278 | |