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 bool override_cd_active_level; 23 irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); 24 char *ro_label; 25 char *cd_label; 26 u32 cd_debounce_delay_ms; 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, msecs_to_jiffies(ctx->cd_debounce_delay_ms)); 37 38 return IRQ_HANDLED; 39 } 40 41 int mmc_gpio_alloc(struct mmc_host *host) 42 { 43 struct mmc_gpio *ctx = devm_kzalloc(host->parent, 44 sizeof(*ctx), GFP_KERNEL); 45 46 if (ctx) { 47 ctx->cd_debounce_delay_ms = 200; 48 ctx->cd_label = devm_kasprintf(host->parent, GFP_KERNEL, 49 "%s cd", dev_name(host->parent)); 50 if (!ctx->cd_label) 51 return -ENOMEM; 52 ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, 53 "%s ro", dev_name(host->parent)); 54 if (!ctx->ro_label) 55 return -ENOMEM; 56 host->slot.handler_priv = ctx; 57 host->slot.cd_irq = -EINVAL; 58 } 59 60 return ctx ? 0 : -ENOMEM; 61 } 62 63 int mmc_gpio_get_ro(struct mmc_host *host) 64 { 65 struct mmc_gpio *ctx = host->slot.handler_priv; 66 67 if (!ctx || !ctx->ro_gpio) 68 return -ENOSYS; 69 70 return gpiod_get_value_cansleep(ctx->ro_gpio); 71 } 72 EXPORT_SYMBOL(mmc_gpio_get_ro); 73 74 int mmc_gpio_get_cd(struct mmc_host *host) 75 { 76 struct mmc_gpio *ctx = host->slot.handler_priv; 77 int cansleep; 78 79 if (!ctx || !ctx->cd_gpio) 80 return -ENOSYS; 81 82 cansleep = gpiod_cansleep(ctx->cd_gpio); 83 if (ctx->override_cd_active_level) { 84 int value = cansleep ? 85 gpiod_get_raw_value_cansleep(ctx->cd_gpio) : 86 gpiod_get_raw_value(ctx->cd_gpio); 87 return !value ^ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); 88 } 89 90 return cansleep ? 91 gpiod_get_value_cansleep(ctx->cd_gpio) : 92 gpiod_get_value(ctx->cd_gpio); 93 } 94 EXPORT_SYMBOL(mmc_gpio_get_cd); 95 96 void mmc_gpiod_request_cd_irq(struct mmc_host *host) 97 { 98 struct mmc_gpio *ctx = host->slot.handler_priv; 99 int irq = -EINVAL; 100 int ret; 101 102 if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio) 103 return; 104 105 /* 106 * Do not use IRQ if the platform prefers to poll, e.g., because that 107 * IRQ number is already used by another unit and cannot be shared. 108 */ 109 if (!(host->caps & MMC_CAP_NEEDS_POLL)) 110 irq = gpiod_to_irq(ctx->cd_gpio); 111 112 if (irq >= 0) { 113 if (!ctx->cd_gpio_isr) 114 ctx->cd_gpio_isr = mmc_gpio_cd_irqt; 115 ret = devm_request_threaded_irq(host->parent, irq, 116 NULL, ctx->cd_gpio_isr, 117 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 118 ctx->cd_label, host); 119 if (ret < 0) 120 irq = ret; 121 } 122 123 host->slot.cd_irq = irq; 124 125 if (irq < 0) 126 host->caps |= MMC_CAP_NEEDS_POLL; 127 } 128 EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); 129 130 int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on) 131 { 132 int ret = 0; 133 134 if (!(host->caps & MMC_CAP_CD_WAKE) || 135 host->slot.cd_irq < 0 || 136 on == host->slot.cd_wake_enabled) 137 return 0; 138 139 if (on) { 140 ret = enable_irq_wake(host->slot.cd_irq); 141 host->slot.cd_wake_enabled = !ret; 142 } else { 143 disable_irq_wake(host->slot.cd_irq); 144 host->slot.cd_wake_enabled = false; 145 } 146 147 return ret; 148 } 149 EXPORT_SYMBOL(mmc_gpio_set_cd_wake); 150 151 /* Register an alternate interrupt service routine for 152 * the card-detect GPIO. 153 */ 154 void mmc_gpio_set_cd_isr(struct mmc_host *host, 155 irqreturn_t (*isr)(int irq, void *dev_id)) 156 { 157 struct mmc_gpio *ctx = host->slot.handler_priv; 158 159 WARN_ON(ctx->cd_gpio_isr); 160 ctx->cd_gpio_isr = isr; 161 } 162 EXPORT_SYMBOL(mmc_gpio_set_cd_isr); 163 164 /** 165 * mmc_gpiod_request_cd - request a gpio descriptor for card-detection 166 * @host: mmc host 167 * @con_id: function within the GPIO consumer 168 * @idx: index of the GPIO to obtain in the consumer 169 * @override_active_level: ignore %GPIO_ACTIVE_LOW flag 170 * @debounce: debounce time in microseconds 171 * @gpio_invert: will return whether the GPIO line is inverted or not, set 172 * to NULL to ignore 173 * 174 * Note that this must be called prior to mmc_add_host() 175 * otherwise the caller must also call mmc_gpiod_request_cd_irq(). 176 * 177 * Returns zero on success, else an error. 178 */ 179 int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, 180 unsigned int idx, bool override_active_level, 181 unsigned int debounce, bool *gpio_invert) 182 { 183 struct mmc_gpio *ctx = host->slot.handler_priv; 184 struct gpio_desc *desc; 185 int ret; 186 187 desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); 188 if (IS_ERR(desc)) 189 return PTR_ERR(desc); 190 191 if (debounce) { 192 ret = gpiod_set_debounce(desc, debounce); 193 if (ret < 0) 194 ctx->cd_debounce_delay_ms = debounce / 1000; 195 } 196 197 if (gpio_invert) 198 *gpio_invert = !gpiod_is_active_low(desc); 199 200 ctx->override_cd_active_level = override_active_level; 201 ctx->cd_gpio = desc; 202 203 return 0; 204 } 205 EXPORT_SYMBOL(mmc_gpiod_request_cd); 206 207 bool mmc_can_gpio_cd(struct mmc_host *host) 208 { 209 struct mmc_gpio *ctx = host->slot.handler_priv; 210 211 return ctx->cd_gpio ? true : false; 212 } 213 EXPORT_SYMBOL(mmc_can_gpio_cd); 214 215 /** 216 * mmc_gpiod_request_ro - request a gpio descriptor for write protection 217 * @host: mmc host 218 * @con_id: function within the GPIO consumer 219 * @idx: index of the GPIO to obtain in the consumer 220 * @debounce: debounce time in microseconds 221 * @gpio_invert: will return whether the GPIO line is inverted or not, 222 * set to NULL to ignore 223 * 224 * Returns zero on success, else an error. 225 */ 226 int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, 227 unsigned int idx, 228 unsigned int debounce, bool *gpio_invert) 229 { 230 struct mmc_gpio *ctx = host->slot.handler_priv; 231 struct gpio_desc *desc; 232 int ret; 233 234 desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); 235 if (IS_ERR(desc)) 236 return PTR_ERR(desc); 237 238 if (debounce) { 239 ret = gpiod_set_debounce(desc, debounce); 240 if (ret < 0) 241 return ret; 242 } 243 244 if (gpio_invert) 245 *gpio_invert = !gpiod_is_active_low(desc); 246 247 ctx->ro_gpio = desc; 248 249 return 0; 250 } 251 EXPORT_SYMBOL(mmc_gpiod_request_ro); 252 253 bool mmc_can_gpio_ro(struct mmc_host *host) 254 { 255 struct mmc_gpio *ctx = host->slot.handler_priv; 256 257 return ctx->ro_gpio ? true : false; 258 } 259 EXPORT_SYMBOL(mmc_can_gpio_ro); 260