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