[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