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