slot-gpio.c 7.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Generic GPIO card-detect helper
 *
 * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/err.h>
#include <linux/gpio.h>
13
#include <linux/gpio/consumer.h>
14 15 16
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mmc/host.h>
17
#include <linux/mmc/slot-gpio.h>
18 19 20
#include <linux/module.h>
#include <linux/slab.h>

21 22
#include "slot-gpio.h"

23
struct mmc_gpio {
24 25 26 27
	struct gpio_desc *ro_gpio;
	struct gpio_desc *cd_gpio;
	bool override_ro_active_level;
	bool override_cd_active_level;
28
	char *ro_label;
29
	char cd_label[0];
30 31
};

32
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
33 34
{
	/* Schedule a card detection after a debounce timeout */
35 36
	struct mmc_host *host = dev_id;

37
	host->trigger_card_event = true;
38 39
	mmc_detect_change(host, msecs_to_jiffies(200));

40 41 42
	return IRQ_HANDLED;
}

43
int mmc_gpio_alloc(struct mmc_host *host)
44 45
{
	size_t len = strlen(dev_name(host->parent)) + 4;
46 47 48 49 50 51 52 53 54
	struct mmc_gpio *ctx = devm_kzalloc(host->parent,
				sizeof(*ctx) + 2 * len,	GFP_KERNEL);

	if (ctx) {
		ctx->ro_label = ctx->cd_label + len;
		snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
		snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
		host->slot.handler_priv = ctx;
		host->slot.cd_irq = -EINVAL;
55 56 57 58 59
	}

	return ctx ? 0 : -ENOMEM;
}

60 61 62 63
int mmc_gpio_get_ro(struct mmc_host *host)
{
	struct mmc_gpio *ctx = host->slot.handler_priv;

64
	if (!ctx || !ctx->ro_gpio)
65 66
		return -ENOSYS;

67 68 69 70 71
	if (ctx->override_ro_active_level)
		return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^
			!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);

	return gpiod_get_value_cansleep(ctx->ro_gpio);
72 73 74
}
EXPORT_SYMBOL(mmc_gpio_get_ro);

75 76 77 78
int mmc_gpio_get_cd(struct mmc_host *host)
{
	struct mmc_gpio *ctx = host->slot.handler_priv;

79
	if (!ctx || !ctx->cd_gpio)
80 81
		return -ENOSYS;

82 83 84 85 86
	if (ctx->override_cd_active_level)
		return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^
			!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);

	return gpiod_get_value_cansleep(ctx->cd_gpio);
87 88 89
}
EXPORT_SYMBOL(mmc_gpio_get_cd);

90 91 92 93 94 95
/**
 * mmc_gpio_request_ro - request a gpio for write-protection
 * @host: mmc host
 * @gpio: gpio number requested
 *
 * As devm_* managed functions are used in mmc_gpio_request_ro(), client
96
 * drivers do not need to worry about freeing up memory.
97 98 99
 *
 * Returns zero on success, else an error.
 */
100 101
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
{
102
	struct mmc_gpio *ctx = host->slot.handler_priv;
103 104 105 106 107
	int ret;

	if (!gpio_is_valid(gpio))
		return -EINVAL;

108
	ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
109
				    ctx->ro_label);
110 111 112
	if (ret < 0)
		return ret;

113 114
	ctx->override_ro_active_level = true;
	ctx->ro_gpio = gpio_to_desc(gpio);
115 116

	return 0;
117 118 119
}
EXPORT_SYMBOL(mmc_gpio_request_ro);

120
void mmc_gpiod_request_cd_irq(struct mmc_host *host)
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
{
	struct mmc_gpio *ctx = host->slot.handler_priv;
	int ret, irq;

	if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
		return;

	irq = gpiod_to_irq(ctx->cd_gpio);

	/*
	 * Even if gpiod_to_irq() returns a valid IRQ number, the platform might
	 * still prefer to poll, e.g., because that IRQ number is already used
	 * by another unit and cannot be shared.
	 */
	if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
		irq = -EINVAL;

	if (irq >= 0) {
139
		ret = devm_request_threaded_irq(host->parent, irq,
140 141 142 143 144 145 146 147 148 149 150 151
			NULL, mmc_gpio_cd_irqt,
			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
			ctx->cd_label, host);
		if (ret < 0)
			irq = ret;
	}

	host->slot.cd_irq = irq;

	if (irq < 0)
		host->caps |= MMC_CAP_NEEDS_POLL;
}
152
EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
153

154 155 156 157
/**
 * mmc_gpio_request_cd - request a gpio for card-detection
 * @host: mmc host
 * @gpio: gpio number requested
158
 * @debounce: debounce time in microseconds
159 160
 *
 * As devm_* managed functions are used in mmc_gpio_request_cd(), client
161
 * drivers do not need to worry about freeing up memory.
162
 *
163 164 165 166
 * If GPIO debouncing is desired, set the debounce parameter to a non-zero
 * value. The caller is responsible for ensuring that the GPIO driver associated
 * with the GPIO supports debouncing, otherwise an error will be returned.
 *
167 168
 * Returns zero on success, else an error.
 */
169 170
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
			unsigned int debounce)
171
{
172
	struct mmc_gpio *ctx = host->slot.handler_priv;
173 174
	int ret;

175
	ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,
176
				    ctx->cd_label);
177
	if (ret < 0)
178 179 180 181 182 183
		/*
		 * don't bother freeing memory. It might still get used by other
		 * slot functions, in any case it will be freed, when the device
		 * is destroyed.
		 */
		return ret;
184

185 186 187 188 189 190
	if (debounce) {
		ret = gpio_set_debounce(gpio, debounce);
		if (ret < 0)
			return ret;
	}

191 192
	ctx->override_cd_active_level = true;
	ctx->cd_gpio = gpio_to_desc(gpio);
193 194 195

	return 0;
}
196
EXPORT_SYMBOL(mmc_gpio_request_cd);
197

198 199 200 201 202 203 204
/**
 * mmc_gpiod_request_cd - request a gpio descriptor for card-detection
 * @host: mmc host
 * @con_id: function within the GPIO consumer
 * @idx: index of the GPIO to obtain in the consumer
 * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
 * @debounce: debounce time in microseconds
205 206
 * @gpio_invert: will return whether the GPIO line is inverted or not, set
 * to NULL to ignore
207 208
 *
 * Use this function in place of mmc_gpio_request_cd() to use the GPIO
209
 * descriptor API.  Note that it must be called prior to mmc_add_host()
210 211 212 213 214 215
 * otherwise the caller must also call mmc_gpiod_request_cd_irq().
 *
 * Returns zero on success, else an error.
 */
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
			 unsigned int idx, bool override_active_level,
216
			 unsigned int debounce, bool *gpio_invert)
217
{
218
	struct mmc_gpio *ctx = host->slot.handler_priv;
219 220 221 222 223 224
	struct gpio_desc *desc;
	int ret;

	if (!con_id)
		con_id = ctx->cd_label;

225
	desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
226 227 228 229 230 231 232 233 234
	if (IS_ERR(desc))
		return PTR_ERR(desc);

	if (debounce) {
		ret = gpiod_set_debounce(desc, debounce);
		if (ret < 0)
			return ret;
	}

235 236 237
	if (gpio_invert)
		*gpio_invert = !gpiod_is_active_low(desc);

238 239 240 241 242 243 244
	ctx->override_cd_active_level = override_active_level;
	ctx->cd_gpio = desc;

	return 0;
}
EXPORT_SYMBOL(mmc_gpiod_request_cd);

245 246 247 248 249 250 251
/**
 * mmc_gpiod_request_ro - request a gpio descriptor for write protection
 * @host: mmc host
 * @con_id: function within the GPIO consumer
 * @idx: index of the GPIO to obtain in the consumer
 * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
 * @debounce: debounce time in microseconds
252 253
 * @gpio_invert: will return whether the GPIO line is inverted or not,
 * set to NULL to ignore
254 255
 *
 * Use this function in place of mmc_gpio_request_ro() to use the GPIO
256
 * descriptor API.
257 258 259 260 261
 *
 * Returns zero on success, else an error.
 */
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
			 unsigned int idx, bool override_active_level,
262
			 unsigned int debounce, bool *gpio_invert)
263
{
264
	struct mmc_gpio *ctx = host->slot.handler_priv;
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
	struct gpio_desc *desc;
	int ret;

	if (!con_id)
		con_id = ctx->ro_label;

	desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
	if (IS_ERR(desc))
		return PTR_ERR(desc);

	if (debounce) {
		ret = gpiod_set_debounce(desc, debounce);
		if (ret < 0)
			return ret;
	}

281 282 283
	if (gpio_invert)
		*gpio_invert = !gpiod_is_active_low(desc);

284 285 286 287 288 289
	ctx->override_ro_active_level = override_active_level;
	ctx->ro_gpio = desc;

	return 0;
}
EXPORT_SYMBOL(mmc_gpiod_request_ro);