Message ID | 20110421131957.GB15194@dandreoli.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Apr 21, 2011 at 03:19:57PM +0200, Domenico Andreoli wrote: > From: Domenico Andreoli <cavokz@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@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@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@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/spi-devel-general -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Apr 21, 2011 at 4:14 PM, Grant Likely <grant.likely@secretlab.ca> wrote: > On Thu, Apr 21, 2011 at 03:19:57PM +0200, Domenico Andreoli wrote: >> From: Domenico Andreoli <cavokz@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? I made a mess with the patches... the second one of this set _is_ mmc-over-spi converted to this generalization. Instead I sent GPIO OF stuff... sorry. >> +/* 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. I agree, indeed I'm not able to use these helpers within the s3c-mmc driver without duplicating and writing ugly code. I made some "research" so I have few patches of tries which made me learn a bit. There is not a common management of these as well as of the set_power() callbacks, to say those I learned about looking at S3C MMC. In practice also MMC platform datas require some consolidation. Kgene, Ben, do you plan to work on the S3C MCI driver? Can I spend some time on it without duplicating the effort of anybody? Regards, Domenico -----[ Domenico Andreoli, aka cavok --[ http://cavokz.wordpress.com/gpgkey/ ---[ 3A0F 2F80 F79C 678A 8936 4FEE 0677 9033 A20E BC50 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
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@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 */