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 | |
79 | if (!ctx || !ctx->ro_gpio) |
80 | return -ENOSYS; |
81 | |
82 | return gpiod_get_value_cansleep(desc: ctx->ro_gpio); |
83 | } |
84 | EXPORT_SYMBOL(mmc_gpio_get_ro); |
85 | |
86 | int mmc_gpio_get_cd(struct mmc_host *host) |
87 | { |
88 | struct mmc_gpio *ctx = host->slot.handler_priv; |
89 | int cansleep; |
90 | |
91 | if (!ctx || !ctx->cd_gpio) |
92 | return -ENOSYS; |
93 | |
94 | cansleep = gpiod_cansleep(desc: ctx->cd_gpio); |
95 | return cansleep ? |
96 | gpiod_get_value_cansleep(desc: ctx->cd_gpio) : |
97 | gpiod_get_value(desc: ctx->cd_gpio); |
98 | } |
99 | EXPORT_SYMBOL(mmc_gpio_get_cd); |
100 | |
101 | void mmc_gpiod_request_cd_irq(struct mmc_host *host) |
102 | { |
103 | struct mmc_gpio *ctx = host->slot.handler_priv; |
104 | int irq = -EINVAL; |
105 | int ret; |
106 | |
107 | if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) |
108 | return; |
109 | |
110 | /* |
111 | * Do not use IRQ if the platform prefers to poll, e.g., because that |
112 | * IRQ number is already used by another unit and cannot be shared. |
113 | */ |
114 | if (ctx->cd_irq >= 0) |
115 | irq = ctx->cd_irq; |
116 | else if (!(host->caps & MMC_CAP_NEEDS_POLL)) |
117 | irq = gpiod_to_irq(desc: ctx->cd_gpio); |
118 | |
119 | if (irq >= 0) { |
120 | if (!ctx->cd_gpio_isr) |
121 | ctx->cd_gpio_isr = mmc_gpio_cd_irqt; |
122 | ret = devm_request_threaded_irq(dev: host->parent, irq, |
123 | NULL, thread_fn: ctx->cd_gpio_isr, |
124 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
125 | devname: ctx->cd_label, dev_id: host); |
126 | if (ret < 0) |
127 | irq = ret; |
128 | } |
129 | |
130 | host->slot.cd_irq = irq; |
131 | |
132 | if (irq < 0) |
133 | host->caps |= MMC_CAP_NEEDS_POLL; |
134 | } |
135 | EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); |
136 | |
137 | int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on) |
138 | { |
139 | int ret = 0; |
140 | |
141 | if (!(host->caps & MMC_CAP_CD_WAKE) || |
142 | host->slot.cd_irq < 0 || |
143 | on == host->slot.cd_wake_enabled) |
144 | return 0; |
145 | |
146 | if (on) { |
147 | ret = enable_irq_wake(irq: host->slot.cd_irq); |
148 | host->slot.cd_wake_enabled = !ret; |
149 | } else { |
150 | disable_irq_wake(irq: host->slot.cd_irq); |
151 | host->slot.cd_wake_enabled = false; |
152 | } |
153 | |
154 | return ret; |
155 | } |
156 | EXPORT_SYMBOL(mmc_gpio_set_cd_wake); |
157 | |
158 | /* Register an alternate interrupt service routine for |
159 | * the card-detect GPIO. |
160 | */ |
161 | void mmc_gpio_set_cd_isr(struct mmc_host *host, |
162 | irqreturn_t (*isr)(int irq, void *dev_id)) |
163 | { |
164 | struct mmc_gpio *ctx = host->slot.handler_priv; |
165 | |
166 | WARN_ON(ctx->cd_gpio_isr); |
167 | ctx->cd_gpio_isr = isr; |
168 | } |
169 | EXPORT_SYMBOL(mmc_gpio_set_cd_isr); |
170 | |
171 | /** |
172 | * mmc_gpiod_request_cd - request a gpio descriptor for card-detection |
173 | * @host: mmc host |
174 | * @con_id: function within the GPIO consumer |
175 | * @idx: index of the GPIO to obtain in the consumer |
176 | * @override_active_level: ignore %GPIO_ACTIVE_LOW flag |
177 | * @debounce: debounce time in microseconds |
178 | * |
179 | * Note that this must be called prior to mmc_add_host() |
180 | * otherwise the caller must also call mmc_gpiod_request_cd_irq(). |
181 | * |
182 | * Returns zero on success, else an error. |
183 | */ |
184 | int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, |
185 | unsigned int idx, bool override_active_level, |
186 | unsigned int debounce) |
187 | { |
188 | struct mmc_gpio *ctx = host->slot.handler_priv; |
189 | struct gpio_desc *desc; |
190 | int ret; |
191 | |
192 | desc = devm_gpiod_get_index(dev: host->parent, con_id, idx, flags: GPIOD_IN); |
193 | if (IS_ERR(ptr: desc)) |
194 | return PTR_ERR(ptr: desc); |
195 | |
196 | /* Update default label if no con_id provided */ |
197 | if (!con_id) |
198 | gpiod_set_consumer_name(desc, name: ctx->cd_label); |
199 | |
200 | if (debounce) { |
201 | ret = gpiod_set_debounce(desc, debounce); |
202 | if (ret < 0) |
203 | ctx->cd_debounce_delay_ms = debounce / 1000; |
204 | } |
205 | |
206 | /* override forces default (active-low) polarity ... */ |
207 | if (override_active_level && !gpiod_is_active_low(desc)) |
208 | gpiod_toggle_active_low(desc); |
209 | |
210 | /* ... or active-high */ |
211 | if (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) |
212 | gpiod_toggle_active_low(desc); |
213 | |
214 | ctx->cd_gpio = desc; |
215 | |
216 | return 0; |
217 | } |
218 | EXPORT_SYMBOL(mmc_gpiod_request_cd); |
219 | |
220 | bool mmc_can_gpio_cd(struct mmc_host *host) |
221 | { |
222 | struct mmc_gpio *ctx = host->slot.handler_priv; |
223 | |
224 | return ctx->cd_gpio ? true : false; |
225 | } |
226 | EXPORT_SYMBOL(mmc_can_gpio_cd); |
227 | |
228 | /** |
229 | * mmc_gpiod_request_ro - request a gpio descriptor for write protection |
230 | * @host: mmc host |
231 | * @con_id: function within the GPIO consumer |
232 | * @idx: index of the GPIO to obtain in the consumer |
233 | * @debounce: debounce time in microseconds |
234 | * |
235 | * Returns zero on success, else an error. |
236 | */ |
237 | int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, |
238 | unsigned int idx, unsigned int debounce) |
239 | { |
240 | struct mmc_gpio *ctx = host->slot.handler_priv; |
241 | struct gpio_desc *desc; |
242 | int ret; |
243 | |
244 | desc = devm_gpiod_get_index(dev: host->parent, con_id, idx, flags: GPIOD_IN); |
245 | if (IS_ERR(ptr: desc)) |
246 | return PTR_ERR(ptr: desc); |
247 | |
248 | /* Update default label if no con_id provided */ |
249 | if (!con_id) |
250 | gpiod_set_consumer_name(desc, name: ctx->ro_label); |
251 | |
252 | if (debounce) { |
253 | ret = gpiod_set_debounce(desc, debounce); |
254 | if (ret < 0) |
255 | return ret; |
256 | } |
257 | |
258 | if (host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH) |
259 | gpiod_toggle_active_low(desc); |
260 | |
261 | ctx->ro_gpio = desc; |
262 | |
263 | return 0; |
264 | } |
265 | EXPORT_SYMBOL(mmc_gpiod_request_ro); |
266 | |
267 | bool mmc_can_gpio_ro(struct mmc_host *host) |
268 | { |
269 | struct mmc_gpio *ctx = host->slot.handler_priv; |
270 | |
271 | return ctx->ro_gpio ? true : false; |
272 | } |
273 | EXPORT_SYMBOL(mmc_can_gpio_ro); |
274 | |