Commit | Line | Data |
---|---|---|
349ab524 GL |
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.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/jiffies.h> | |
15 | #include <linux/mmc/host.h> | |
fd0ea65d | 16 | #include <linux/mmc/slot-gpio.h> |
349ab524 GL |
17 | #include <linux/module.h> |
18 | #include <linux/slab.h> | |
19 | ||
fd0ea65d | 20 | struct mmc_gpio { |
5aa7dad3 | 21 | int ro_gpio; |
befe4048 | 22 | int cd_gpio; |
5aa7dad3 | 23 | char *ro_label; |
fd0ea65d | 24 | char cd_label[0]; |
349ab524 GL |
25 | }; |
26 | ||
fd0ea65d | 27 | static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) |
349ab524 GL |
28 | { |
29 | /* Schedule a card detection after a debounce timeout */ | |
30 | mmc_detect_change(dev_id, msecs_to_jiffies(100)); | |
31 | return IRQ_HANDLED; | |
32 | } | |
33 | ||
a7d1a1eb GL |
34 | static int mmc_gpio_alloc(struct mmc_host *host) |
35 | { | |
36 | size_t len = strlen(dev_name(host->parent)) + 4; | |
37 | struct mmc_gpio *ctx; | |
38 | ||
39 | mutex_lock(&host->slot.lock); | |
40 | ||
41 | ctx = host->slot.handler_priv; | |
42 | if (!ctx) { | |
43 | /* | |
44 | * devm_kzalloc() can be called after device_initialize(), even | |
45 | * before device_add(), i.e., between mmc_alloc_host() and | |
46 | * mmc_add_host() | |
47 | */ | |
5aa7dad3 | 48 | ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len, |
a7d1a1eb GL |
49 | GFP_KERNEL); |
50 | if (ctx) { | |
5aa7dad3 | 51 | ctx->ro_label = ctx->cd_label + len; |
a7d1a1eb | 52 | snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); |
5aa7dad3 | 53 | snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); |
a7d1a1eb | 54 | ctx->cd_gpio = -EINVAL; |
5aa7dad3 | 55 | ctx->ro_gpio = -EINVAL; |
a7d1a1eb GL |
56 | host->slot.handler_priv = ctx; |
57 | } | |
58 | } | |
59 | ||
60 | mutex_unlock(&host->slot.lock); | |
61 | ||
62 | return ctx ? 0 : -ENOMEM; | |
63 | } | |
64 | ||
5aa7dad3 GL |
65 | int mmc_gpio_get_ro(struct mmc_host *host) |
66 | { | |
67 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
68 | ||
69 | if (!ctx || !gpio_is_valid(ctx->ro_gpio)) | |
70 | return -ENOSYS; | |
71 | ||
72 | return !gpio_get_value_cansleep(ctx->ro_gpio) ^ | |
73 | !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); | |
74 | } | |
75 | EXPORT_SYMBOL(mmc_gpio_get_ro); | |
76 | ||
befe4048 GL |
77 | int mmc_gpio_get_cd(struct mmc_host *host) |
78 | { | |
79 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
80 | ||
81 | if (!ctx || !gpio_is_valid(ctx->cd_gpio)) | |
82 | return -ENOSYS; | |
83 | ||
84 | return !gpio_get_value_cansleep(ctx->cd_gpio) ^ | |
85 | !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); | |
86 | } | |
87 | EXPORT_SYMBOL(mmc_gpio_get_cd); | |
88 | ||
5aa7dad3 GL |
89 | int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) |
90 | { | |
91 | struct mmc_gpio *ctx; | |
92 | int ret; | |
93 | ||
94 | if (!gpio_is_valid(gpio)) | |
95 | return -EINVAL; | |
96 | ||
97 | ret = mmc_gpio_alloc(host); | |
98 | if (ret < 0) | |
99 | return ret; | |
100 | ||
101 | ctx = host->slot.handler_priv; | |
102 | ||
103 | return gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label); | |
104 | } | |
105 | EXPORT_SYMBOL(mmc_gpio_request_ro); | |
106 | ||
fd0ea65d | 107 | int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) |
349ab524 | 108 | { |
fd0ea65d | 109 | struct mmc_gpio *ctx; |
c9b0546a | 110 | int irq = gpio_to_irq(gpio); |
349ab524 GL |
111 | int ret; |
112 | ||
a7d1a1eb GL |
113 | ret = mmc_gpio_alloc(host); |
114 | if (ret < 0) | |
115 | return ret; | |
349ab524 | 116 | |
a7d1a1eb | 117 | ctx = host->slot.handler_priv; |
349ab524 | 118 | |
fd0ea65d | 119 | ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); |
349ab524 | 120 | if (ret < 0) |
a7d1a1eb GL |
121 | /* |
122 | * don't bother freeing memory. It might still get used by other | |
123 | * slot functions, in any case it will be freed, when the device | |
124 | * is destroyed. | |
125 | */ | |
126 | return ret; | |
349ab524 | 127 | |
befe4048 GL |
128 | /* |
129 | * Even if gpio_to_irq() returns a valid IRQ number, the platform might | |
130 | * still prefer to poll, e.g., because that IRQ number is already used | |
131 | * by another unit and cannot be shared. | |
132 | */ | |
133 | if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL) | |
134 | irq = -EINVAL; | |
135 | ||
136 | if (irq >= 0) { | |
137 | ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, | |
fd0ea65d GL |
138 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
139 | ctx->cd_label, host); | |
befe4048 GL |
140 | if (ret < 0) |
141 | irq = ret; | |
142 | } | |
349ab524 | 143 | |
27410ee7 | 144 | host->slot.cd_irq = irq; |
befe4048 GL |
145 | |
146 | if (irq < 0) | |
147 | host->caps |= MMC_CAP_NEEDS_POLL; | |
148 | ||
149 | ctx->cd_gpio = gpio; | |
349ab524 GL |
150 | |
151 | return 0; | |
349ab524 | 152 | } |
fd0ea65d | 153 | EXPORT_SYMBOL(mmc_gpio_request_cd); |
349ab524 | 154 | |
5aa7dad3 GL |
155 | void mmc_gpio_free_ro(struct mmc_host *host) |
156 | { | |
157 | struct mmc_gpio *ctx = host->slot.handler_priv; | |
158 | int gpio; | |
159 | ||
160 | if (!ctx || !gpio_is_valid(ctx->ro_gpio)) | |
161 | return; | |
162 | ||
163 | gpio = ctx->ro_gpio; | |
164 | ctx->ro_gpio = -EINVAL; | |
165 | ||
166 | gpio_free(gpio); | |
167 | } | |
168 | EXPORT_SYMBOL(mmc_gpio_free_ro); | |
169 | ||
fd0ea65d | 170 | void mmc_gpio_free_cd(struct mmc_host *host) |
349ab524 | 171 | { |
27410ee7 | 172 | struct mmc_gpio *ctx = host->slot.handler_priv; |
befe4048 | 173 | int gpio; |
349ab524 | 174 | |
befe4048 | 175 | if (!ctx || !gpio_is_valid(ctx->cd_gpio)) |
0e9f480b GL |
176 | return; |
177 | ||
befe4048 GL |
178 | if (host->slot.cd_irq >= 0) { |
179 | free_irq(host->slot.cd_irq, host); | |
180 | host->slot.cd_irq = -EINVAL; | |
181 | } | |
182 | ||
183 | gpio = ctx->cd_gpio; | |
184 | ctx->cd_gpio = -EINVAL; | |
185 | ||
186 | gpio_free(gpio); | |
349ab524 | 187 | } |
fd0ea65d | 188 | EXPORT_SYMBOL(mmc_gpio_free_cd); |