From: Domenico Andreoli <cav...@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 <cav...@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 <ca...@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 */
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to