blob: 92cba02c04befc141488e014c5c07cbd2c3eed94 [file] [log] [blame]
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +02001/*
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.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
20struct mmc_gpio {
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +020021 int cd_gpio;
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020022 char cd_label[0];
23};
24
25static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
26{
27 /* Schedule a card detection after a debounce timeout */
28 mmc_detect_change(dev_id, msecs_to_jiffies(100));
29 return IRQ_HANDLED;
30}
31
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +020032int mmc_gpio_get_cd(struct mmc_host *host)
33{
34 struct mmc_gpio *ctx = host->slot.handler_priv;
35
36 if (!ctx || !gpio_is_valid(ctx->cd_gpio))
37 return -ENOSYS;
38
39 return !gpio_get_value_cansleep(ctx->cd_gpio) ^
40 !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
41}
42EXPORT_SYMBOL(mmc_gpio_get_cd);
43
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020044int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
45{
46 size_t len = strlen(dev_name(host->parent)) + 4;
47 struct mmc_gpio *ctx;
48 int irq = gpio_to_irq(gpio);
49 int ret;
50
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020051 ctx = kmalloc(sizeof(*ctx) + len, GFP_KERNEL);
52 if (!ctx)
53 return -ENOMEM;
54
55 snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
56
57 ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label);
58 if (ret < 0)
59 goto egpioreq;
60
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +020061 /*
62 * Even if gpio_to_irq() returns a valid IRQ number, the platform might
63 * still prefer to poll, e.g., because that IRQ number is already used
64 * by another unit and cannot be shared.
65 */
66 if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
67 irq = -EINVAL;
68
69 if (irq >= 0) {
70 ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt,
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020071 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
72 ctx->cd_label, host);
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +020073 if (ret < 0)
74 irq = ret;
75 }
76
77 host->slot.cd_irq = irq;
78
79 if (irq < 0)
80 host->caps |= MMC_CAP_NEEDS_POLL;
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020081
82 ctx->cd_gpio = gpio;
Guennadi Liakhovetski27410ee2012-05-01 15:40:15 +020083 host->slot.handler_priv = ctx;
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020084
85 return 0;
86
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020087egpioreq:
88 kfree(ctx);
89 return ret;
90}
91EXPORT_SYMBOL(mmc_gpio_request_cd);
92
93void mmc_gpio_free_cd(struct mmc_host *host)
94{
Guennadi Liakhovetski27410ee2012-05-01 15:40:15 +020095 struct mmc_gpio *ctx = host->slot.handler_priv;
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +020096 int gpio;
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020097
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +020098 if (!ctx || !gpio_is_valid(ctx->cd_gpio))
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +020099 return;
100
Guennadi Liakhovetskibefe4042012-05-01 16:27:25 +0200101 if (host->slot.cd_irq >= 0) {
102 free_irq(host->slot.cd_irq, host);
103 host->slot.cd_irq = -EINVAL;
104 }
105
106 gpio = ctx->cd_gpio;
107 ctx->cd_gpio = -EINVAL;
108
109 gpio_free(gpio);
110 host->slot.handler_priv = NULL;
Guennadi Liakhovetskifd0ea652012-04-30 23:31:57 +0200111 kfree(ctx);
112}
113EXPORT_SYMBOL(mmc_gpio_free_cd);