[PATCH 1/2] Add OF binding helpers for MMC drivers

Domenico Andreoli dr.domenico.andreoli at gmail.com
Thu Apr 21 23:19:57 EST 2011


From: Domenico Andreoli <cavokz at gmail.com>

This patch adds helpers to manage OF binding of MMC DeviceTree configs.
They don't cover all the MMC configuration cases, indeed are only a
slight generalization of those found in the MMC-over-SPI driver. More
will come later.

Signed-off-by: Domenico Andreoli <cavokz at gmail.com>
---
 drivers/of/Kconfig     |    4 +
 drivers/of/Makefile    |    1 +
 drivers/of/of_mmc.c    |  165 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_mmc.h |   50 +++++++++++++++
 4 files changed, 220 insertions(+)

Index: b/drivers/of/Kconfig
===================================================================
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -59,6 +59,10 @@ config OF_I2C
 	help
 	  OpenFirmware I2C accessors
 
+config OF_MMC
+	depends on MMC
+	def_bool y
+
 config OF_NET
 	depends on NETDEVICES
 	def_bool y
Index: b/drivers/of/Makefile
===================================================================
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_OF_DEVICE) += device.o plat
 obj-$(CONFIG_OF_GPIO)   += gpio.o
 obj-$(CONFIG_OF_CLOCK)	+= clock.o
 obj-$(CONFIG_OF_I2C)	+= of_i2c.o
+obj-$(CONFIG_OF_MMC)	+= of_mmc.o
 obj-$(CONFIG_OF_NET)	+= of_net.o
 obj-$(CONFIG_OF_SPI)	+= of_spi.o
 obj-$(CONFIG_OF_MDIO)	+= of_mdio.o
Index: b/drivers/of/of_mmc.c
===================================================================
--- /dev/null
+++ b/drivers/of/of_mmc.c
@@ -0,0 +1,165 @@
+/*
+ * OF helpers for the MMC API
+ *
+ * Copyright (c) 2011 Domenico Andreoli
+ *
+ * Heavily inspired by the OF support to the MMC-over-SPI driver made
+ * by Anton Vorontsov
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mmc/core.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_mmc.h>
+#include <linux/of_gpio.h>
+
+static int of_read_mmc_gpio(struct of_mmc_crg *crg, int gpio_num)
+{
+	int value, active_low;
+
+	BUG_ON(gpio_num >= NUM_MMC_GPIOS);
+
+	/* hitting this means that DeviceTree left this gpio unspecified
+	 * by purpose but driver didn't take any measure to define its
+	 * behavior (i.e. aborting probe phase or disabling the feature).
+	 * driver needs to call of_is_valid_mmc_crg() for each expected
+	 * gpio to detect this case.
+	 */
+	if (WARN_ON(crg->gpios[gpio_num] < 0))
+		return -1;
+
+	value = gpio_get_value(crg->gpios[gpio_num]);
+	active_low = crg->alow_gpios[gpio_num];
+	return value ^ active_low;
+}
+
+int of_get_mmc_cd_gpio(struct of_mmc_crg *crg)
+{
+	return of_read_mmc_gpio(crg, CD_MMC_GPIO);
+}
+EXPORT_SYMBOL(of_get_mmc_cd_gpio);
+
+int of_get_mmc_ro_gpio(struct of_mmc_crg *crg)
+{
+	return of_read_mmc_gpio(crg, WP_MMC_GPIO);
+}
+EXPORT_SYMBOL(of_get_mmc_ro_gpio);
+
+int of_is_valid_mmc_crg(struct of_mmc_crg *crg, int gpio_num)
+{
+	BUG_ON(gpio_num >= NUM_MMC_GPIOS);
+	return gpio_is_valid(crg->gpios[gpio_num]);
+}
+EXPORT_SYMBOL(of_is_valid_mmc_crg);
+
+int of_get_mmc_crg(struct device *dev, struct device_node *np,
+                   int cd_off, struct of_mmc_crg *crg)
+{
+	int *gpio, *alow;
+	int i, ret;
+
+	memset(crg, 0, sizeof(*crg));
+	crg->cd_irq = -1;
+
+	gpio = crg->gpios;
+	alow = crg->alow_gpios;
+	for (i = 0; i < NUM_MMC_GPIOS; i++, gpio++, alow++) {
+		enum of_gpio_flags gpio_flags;
+		*gpio = of_get_gpio_flags(np, cd_off+i, &gpio_flags);
+		*alow = !!(gpio_flags & OF_GPIO_ACTIVE_LOW);
+
+		if (*gpio == -EEXIST || *gpio == -ENOENT) {
+			/* driver needs to define proper meaning of this missing
+			   gpio (i.e. abort probe or disable the feature) */
+			pr_debug("%s: gpio #%d is not specified\n", __func__, i);
+			continue;
+		}
+		if (*gpio < 0) {
+			pr_debug("%s: invalid configuration\n", __func__);
+			ret = *gpio;
+			break;
+		}
+		if (!gpio_is_valid(*gpio)) {
+			pr_debug("%s: gpio #%d is not valid: %d\n", __func__, i, *gpio);
+			ret = -EINVAL;
+			break;
+		}
+		ret = gpio_request(*gpio, dev_name(dev));
+		if (ret < 0) {
+			pr_debug("%s: gpio #%d is not available: %d\n", __func__, i, *gpio);
+			break;
+		}
+	}
+
+	if (i < NUM_MMC_GPIOS) {
+		while (--gpio >= crg->gpios)
+			if (gpio_is_valid(*gpio))
+				gpio_free(*gpio);
+		return ret;
+	}
+
+	if (gpio_is_valid(crg->gpios[CD_MMC_GPIO])) {
+		gpio_direction_input(crg->gpios[CD_MMC_GPIO]);
+		crg->cd_irq = gpio_to_irq(crg->gpios[CD_MMC_GPIO]);
+		if (crg->cd_irq < 0)
+			pr_debug("%s: cannot get cd irq number\n", __func__);
+	}
+	if (gpio_is_valid(crg->gpios[WP_MMC_GPIO]))
+		gpio_direction_input(crg->gpios[WP_MMC_GPIO]);
+
+	return 0;
+}
+EXPORT_SYMBOL(of_get_mmc_crg);
+
+void of_put_mmc_crg(struct of_mmc_crg *crg)
+{
+	int i;
+	for (i = 0; i < NUM_MMC_GPIOS; i++)
+		if (gpio_is_valid(crg->gpios[i]))
+			gpio_free(crg->gpios[i]);
+}
+EXPORT_SYMBOL(of_put_mmc_crg);
+
+u32 of_get_mmc_ocr_mask(struct device_node *np)
+{
+	const u32 *voltage_ranges;
+	int num_ranges;
+	u32 ocr_mask;
+	int i, j;
+
+	voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
+	num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
+	if (!voltage_ranges || !num_ranges) {
+		pr_debug("%s: voltage-ranges unspecified\n", __func__);
+		return 0;
+	}
+
+	ocr_mask = 0;
+	for (i = 0, j = 0; i < num_ranges; i++) {
+		int vdd_min = be32_to_cpup(voltage_ranges++);
+		int vdd_max = be32_to_cpup(voltage_ranges++);
+		u32 mask = mmc_vddrange_to_ocrmask(vdd_min, vdd_max);
+
+		if (!mask) {
+			pr_debug("%s: voltage-range #%d is invalid\n", __func__, i);
+			return 0;
+		}
+		ocr_mask |= mask;
+	}
+
+	return ocr_mask;
+}
+EXPORT_SYMBOL(of_get_mmc_ocr_mask);
+
+MODULE_AUTHOR("Domenico Andreoli <cavok at gmail.com>");
+MODULE_LICENSE("GPL");
Index: b/include/linux/of_mmc.h
===================================================================
--- /dev/null
+++ b/include/linux/of_mmc.h
@@ -0,0 +1,50 @@
+/*
+ * OF helpers for the MMC API
+ *
+ * Copyright (c) 2011 Domenico Andreoli
+ *
+ * Heavily inspired by the OF support to the MMC-over-SPI driver made
+ * by Anton Vorontsov
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __LINUX_OF_MMC_H
+#define __LINUX_OF_MMC_H
+
+#include <linux/of.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_OF_MMC
+
+enum {
+	CD_MMC_GPIO = 0,
+	WP_MMC_GPIO,
+	NUM_MMC_GPIOS,
+};
+
+/* Card detect and Read only Gpio data */
+struct of_mmc_crg {
+	int gpios[NUM_MMC_GPIOS];
+	int alow_gpios[NUM_MMC_GPIOS];
+	int cd_irq;
+};
+
+extern u32 of_get_mmc_ocr_mask(struct device_node *);
+extern int of_get_mmc_crg(struct device *, struct device_node *,
+                          int cd_off, struct of_mmc_crg *);
+
+/* if board does not use cd interrupts, driver can optimize polling
+   using this function. */
+extern int of_get_mmc_cd_gpio(struct of_mmc_crg *);
+/* sense switch on sd cards */
+extern int of_get_mmc_ro_gpio(struct of_mmc_crg *);
+extern int of_is_valid_mmc_crg(struct of_mmc_crg *, int gpio_num);
+extern void of_put_mmc_crg(struct of_mmc_crg *);
+
+#endif /* CONFIG_OF_MMC */
+
+#endif /* __LINUX_OF_MMC_H */


More information about the devicetree-discuss mailing list