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

Grant Likely grant.likely at secretlab.ca
Fri Apr 22 00:14:50 EST 2011


On Thu, Apr 21, 2011 at 03:19:57PM +0200, Domenico Andreoli wrote:
> 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.

Can the mmc-over-spi driver be generalized to use this code too?

> 
> 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 +++++++++++++++

Personally I think it makes more sense for this code to live in drivers/mmc/core.

>  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;
> +};

Many drivers currently provide this data via a platform_data.  It
would probably be more useful and require less code if both DT and
non-DT users shared the same structure for MMC gpios.  The structure
could both be embedded in pdata and populated by this DT parsing code.
It would also mean that each driver wouldn't need separate DT and
non-DT code for consuming the information.

> +
> +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 */
> 
> ------------------------------------------------------------------------------
> Benefiting from Server Virtualization: Beyond Initial Workload 
> Consolidation -- Increasing the use of server virtualization is a top
> priority.Virtualization can reduce costs, simplify management, and improve 
> application availability and disaster protection. Learn more about boosting 
> the value of server virtualization. http://p.sf.net/sfu/vmware-sfdev2dev
> _______________________________________________
> spi-devel-general mailing list
> spi-devel-general at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/spi-devel-general


More information about the devicetree-discuss mailing list