blob: 14dd313459f58d5dba7a2bae4518bf7a0b15dfdb [file] [log] [blame]
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +01001/*
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>
H Hartley Sweeten5ca65182012-04-17 13:03:38 -070015#include <linux/mmc/cd-gpio.h>
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010016#include <linux/mmc/host.h>
17#include <linux/module.h>
18#include <linux/slab.h>
19
20struct mmc_cd_gpio {
21 unsigned int gpio;
22 char label[0];
Sujit Reddy Thummaf7608e72013-06-03 09:58:04 +053023 bool status;
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010024};
25
Sujit Reddy Thummaf7608e72013-06-03 09:58:04 +053026static int mmc_cd_get_status(struct mmc_host *host)
27{
28 int ret = -ENOSYS;
29 struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
30
31 if (!cd || !gpio_is_valid(cd->gpio))
32 goto out;
33
34 ret = !gpio_get_value_cansleep(cd->gpio) ^
35 !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
36out:
37 return ret;
38}
39
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010040static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
41{
Sujit Reddy Thummaf7608e72013-06-03 09:58:04 +053042 struct mmc_host *host = dev_id;
43 struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
44 int status;
45
46 status = mmc_cd_get_status(host);
47 if (unlikely(status < 0))
48 goto out;
49
50 if (status ^ cd->status) {
51 pr_info("%s: slot status change detected (%d -> %d), GPIO_ACTIVE_%s\n",
52 mmc_hostname(host), cd->status, status,
53 (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) ?
54 "HIGH" : "LOW");
55 cd->status = status;
56
57 /* Schedule a card detection after a debounce timeout */
58 mmc_detect_change(host, msecs_to_jiffies(100));
59 }
60out:
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010061 return IRQ_HANDLED;
62}
63
Guennadi Liakhovetskic9b05462012-02-09 22:57:07 +010064int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio)
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010065{
66 size_t len = strlen(dev_name(host->parent)) + 4;
Guennadi Liakhovetskic9b05462012-02-09 22:57:07 +010067 struct mmc_cd_gpio *cd;
68 int irq = gpio_to_irq(gpio);
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010069 int ret;
70
Guennadi Liakhovetskic9b05462012-02-09 22:57:07 +010071 if (irq < 0)
72 return irq;
73
74 cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010075 if (!cd)
76 return -ENOMEM;
77
78 snprintf(cd->label, len, "%s cd", dev_name(host->parent));
79
80 ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label);
81 if (ret < 0)
82 goto egpioreq;
83
Sujit Reddy Thummaf7608e72013-06-03 09:58:04 +053084 cd->gpio = gpio;
85 host->hotplug.irq = irq;
86 host->hotplug.handler_priv = cd;
87
88 ret = mmc_cd_get_status(host);
89 if (ret < 0)
90 goto eirqreq;
91
92 cd->status = ret;
93
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010094 ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
Guennadi Liakhovetskic9b05462012-02-09 22:57:07 +010095 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
96 cd->label, host);
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +010097 if (ret < 0)
98 goto eirqreq;
99
Guennadi Liakhovetski349ab522011-12-25 21:36:02 +0100100 return 0;
101
102eirqreq:
103 gpio_free(gpio);
104egpioreq:
105 kfree(cd);
106 return ret;
107}
108EXPORT_SYMBOL(mmc_cd_gpio_request);
109
110void mmc_cd_gpio_free(struct mmc_host *host)
111{
112 struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
113
114 free_irq(host->hotplug.irq, host);
115 gpio_free(cd->gpio);
116 kfree(cd);
117}
118EXPORT_SYMBOL(mmc_cd_gpio_free);