On 5/12/2010 10:58 AM, Viresh KUMAR wrote: > On 5/10/2010 8:57 AM, Viresh KUMAR wrote: >> On 5/5/2010 3:05 PM, Viresh KUMAR wrote: >>> This patch adds glue layer for support of sdhci driver on ST SPEAr platform. >>> >>> Signed-off-by: Viresh Kumar <viresh.kumar@xxxxxx> >>> --- >>> MAINTAINERS | 6 + >>> drivers/mmc/host/Kconfig | 12 ++ >>> drivers/mmc/host/Makefile | 1 + >>> drivers/mmc/host/sdhci-spear.c | 300 +++++++++++++++++++++++++++++++++++++++ >>> include/linux/mmc/sdhci-spear.h | 42 ++++++ >>> 5 files changed, 361 insertions(+), 0 deletions(-) >>> create mode 100644 drivers/mmc/host/sdhci-spear.c >>> create mode 100644 include/linux/mmc/sdhci-spear.h >>> >>> diff --git a/MAINTAINERS b/MAINTAINERS >>> index 0369b22..bbff70a 100644 >>> --- a/MAINTAINERS >>> +++ b/MAINTAINERS >>> @@ -4938,6 +4938,12 @@ L: linux-mmc@xxxxxxxxxxxxxxx >>> S: Maintained >>> F: drivers/mmc/host/sdhci-s3c.c >>> >>> +SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER >>> +M: Viresh Kumar <viresh.kumar@xxxxxx> >>> +L: linux-mmc@xxxxxxxxxxxxxxx >>> +S: Maintained >>> +F: drivers/mmc/host/sdhci-spear.c >>> + >>> SECURITY SUBSYSTEM >>> M: James Morris <jmorris@xxxxxxxxx> >>> L: linux-security-module@xxxxxxxxxxxxxxx (suggested Cc:) >>> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig >>> index 2e13b94..40eae7e 100644 >>> --- a/drivers/mmc/host/Kconfig >>> +++ b/drivers/mmc/host/Kconfig >>> @@ -136,6 +136,18 @@ config MMC_SDHCI_S3C >>> >>> If unsure, say N. >>> >>> +config MMC_SDHCI_SPEAR >>> + tristate "SDHCI support on ST SPEAr platform" >>> + depends on MMC_SDHCI && PLAT_SPEAR >>> + help >>> + This selects the Secure Digital Host Controller Interface (SDHCI) >>> + often referrered to as the HSMMC block in some of the ST SPEAR range >>> + of SoC >>> + >>> + If you have a controller with this interface, say Y or M here. >>> + >>> + If unsure, say N. >>> + >>> config MMC_SDHCI_S3C_DMA >>> bool "DMA support on S3C SDHCI" >>> depends on MMC_SDHCI_S3C && EXPERIMENTAL >>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile >>> index f480397..d00a9fa 100644 >>> --- a/drivers/mmc/host/Makefile >>> +++ b/drivers/mmc/host/Makefile >>> @@ -14,6 +14,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o >>> obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o >>> obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o >>> obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o >>> +obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o >>> obj-$(CONFIG_MMC_WBSD) += wbsd.o >>> obj-$(CONFIG_MMC_AU1X) += au1xmmc.o >>> obj-$(CONFIG_MMC_OMAP) += omap.o >>> diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c >>> new file mode 100644 >>> index 0000000..6d3f740 >>> --- /dev/null >>> +++ b/drivers/mmc/host/sdhci-spear.c >>> @@ -0,0 +1,300 @@ >>> +/* >>> + * drivers/mmc/host/sdhci-spear.c >>> + * >>> + * Support of SDHCI platform devices for spear soc family >>> + * >>> + * Copyright (C) 2010 ST Microelectronics >>> + * Viresh Kumar<viresh.kumar@xxxxxx> >>> + * >>> + * Inspired by sdhci-pltfm.c >>> + * >>> + * This file is licensed under the terms of the GNU General Public >>> + * License version 2. This program is licensed "as is" without any >>> + * warranty of any kind, whether express or implied. >>> + */ >>> + >>> +#include <linux/clk.h> >>> +#include <linux/delay.h> >>> +#include <linux/gpio.h> >>> +#include <linux/highmem.h> >>> +#include <linux/interrupt.h> >>> +#include <linux/irq.h> >>> +#include <linux/platform_device.h> >>> +#include <linux/slab.h> >>> +#include <linux/mmc/host.h> >>> +#include <linux/mmc/sdhci-spear.h> >>> +#include <linux/io.h> >>> +#include "sdhci.h" >>> + >>> +struct spear_sdhci { >>> + struct clk *clk; >>> + struct sdhci_plat_data *data; >>> +}; >>> + >>> +/* sdhci ops */ >>> +static struct sdhci_ops sdhci_pltfm_ops = { >>> + /* Nothing to do for now. */ >>> +}; >>> + >>> +/* gpio card detection interrupt handler */ >>> +static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id) >>> +{ >>> + struct platform_device *pdev = dev_id; >>> + struct sdhci_host *host = platform_get_drvdata(pdev); >>> + struct spear_sdhci *sdhci = >>> + (struct spear_sdhci *)(pdev->dev.platform_data); >>> + unsigned long gpio_irq_type; >>> + int val; >>> + >>> + val = gpio_get_value(sdhci->data->card_int_gpio); >>> + >>> + /* val == 1 -> card removed, val == 0 -> card inserted */ >>> + /* if card removed - set irq for low level, else vice versa */ >>> + gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH; >>> + set_irq_type(irq, gpio_irq_type); >>> + >>> + if (sdhci->data->card_power_gpio >= 0) { >>> + if (!sdhci->data->power_always_enb) { >>> + /* if card inserted, give power, otherwise remove it */ >>> + val = sdhci->data->power_active_high ? !val : val ; >>> + gpio_set_value(sdhci->data->card_power_gpio, val); >>> + } >>> + } >>> + >>> + /* inform sdhci driver about card insertion/removal */ >>> + tasklet_schedule(&host->card_tasklet); >>> + >>> + return IRQ_HANDLED; >>> +} >>> + >>> +static int __devinit sdhci_probe(struct platform_device *pdev) >>> +{ >>> + struct sdhci_host *host; >>> + struct resource *iomem; >>> + struct spear_sdhci *sdhci; >>> + int ret; >>> + >>> + BUG_ON(pdev == NULL); >>> + >>> + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + if (!iomem) { >>> + ret = -ENOMEM; >>> + dev_dbg(&pdev->dev, "memory resource not defined\n"); >>> + goto err; >>> + } >>> + >>> + if (!request_mem_region(iomem->start, resource_size(iomem), >>> + "spear-sdhci")) { >>> + ret = -EBUSY; >>> + dev_dbg(&pdev->dev, "cannot request region\n"); >>> + goto err; >>> + } >>> + >>> + sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL); >>> + if (!sdhci) { >>> + ret = -ENOMEM; >>> + dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); >>> + goto err_kzalloc; >>> + } >>> + >>> + /* clk enable */ >>> + sdhci->clk = clk_get(&pdev->dev, NULL); >>> + if (IS_ERR(sdhci->clk)) { >>> + ret = PTR_ERR(sdhci->clk); >>> + dev_dbg(&pdev->dev, "Error getting clock\n"); >>> + goto err_clk_get; >>> + } >>> + >>> + ret = clk_enable(sdhci->clk); >>> + if (ret) { >>> + dev_dbg(&pdev->dev, "Error enabling clock\n"); >>> + goto err_clk_enb; >>> + } >>> + >>> + /* overwrite platform_data */ >>> + sdhci->data = (struct sdhci_plat_data *)(pdev->dev.platform_data); >>> + pdev->dev.platform_data = sdhci; >>> + >>> + if (pdev->dev.parent) >>> + host = sdhci_alloc_host(pdev->dev.parent, 0); >>> + else >>> + host = sdhci_alloc_host(&pdev->dev, 0); >>> + >>> + if (IS_ERR(host)) { >>> + ret = PTR_ERR(host); >>> + dev_dbg(&pdev->dev, "error allocating host\n"); >>> + goto err_alloc_host; >>> + } >>> + >>> + host->hw_name = "sdhci"; >>> + host->ops = &sdhci_pltfm_ops; >>> + host->irq = platform_get_irq(pdev, 0); >>> + host->quirks = SDHCI_QUIRK_BROKEN_ADMA; >>> + >>> + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); >>> + if (!host->ioaddr) { >>> + ret = -ENOMEM; >>> + dev_dbg(&pdev->dev, "failed to remap registers\n"); >>> + goto err_ioremap; >>> + } >>> + >>> + ret = sdhci_add_host(host); >>> + if (ret) { >>> + dev_dbg(&pdev->dev, "error adding host\n"); >>> + goto err_add_host; >>> + } >>> + >>> + platform_set_drvdata(pdev, host); >>> + >>> + /* >>> + * It is optional to use GPIOs for sdhci Power control & sdhci card >>> + * interrupt detection. If sdhci->data is NULL, then use original sdhci >>> + * lines otherwise GPIO lines. >>> + * If GPIO is selected for power control, then power should be disabled >>> + * after card removal and should be enabled when card insertion >>> + * interrupt occurs >>> + */ >>> + if (!sdhci->data) >>> + return 0; >>> + >>> + if (sdhci->data->card_power_gpio >= 0) { >>> + int val = 0; >>> + >>> + ret = gpio_request(sdhci->data->card_power_gpio, "sdhci"); >>> + if (ret < 0) { >>> + dev_dbg(&pdev->dev, "gpio request fail: %d\n", >>> + sdhci->data->card_power_gpio); >>> + goto err_pgpio_request; >>> + } >>> + >>> + if (sdhci->data->power_always_enb) >>> + val = sdhci->data->power_active_high; >>> + else >>> + val = !sdhci->data->power_active_high; >>> + >>> + ret = gpio_direction_output(sdhci->data->card_power_gpio, val); >>> + if (ret) { >>> + dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", >>> + sdhci->data->card_power_gpio); >>> + goto err_pgpio_direction; >>> + } >>> + >>> + gpio_set_value(sdhci->data->card_power_gpio, 1); >>> + } >>> + >>> + if (sdhci->data->card_int_gpio >= 0) { >>> + ret = gpio_request(sdhci->data->card_int_gpio, "sdhci"); >>> + if (ret < 0) { >>> + dev_dbg(&pdev->dev, "gpio request fail: %d\n", >>> + sdhci->data->card_int_gpio); >>> + goto err_igpio_request; >>> + } >>> + >>> + ret = gpio_direction_input(sdhci->data->card_int_gpio); >>> + if (ret) { >>> + dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", >>> + sdhci->data->card_int_gpio); >>> + goto err_igpio_direction; >>> + } >>> + ret = request_irq(gpio_to_irq(sdhci->data->card_int_gpio), >>> + sdhci_gpio_irq, IRQF_TRIGGER_LOW, >>> + mmc_hostname(host->mmc), pdev); >>> + if (ret) { >>> + dev_dbg(&pdev->dev, "gpio request irq fail: %d\n", >>> + sdhci->data->card_int_gpio); >>> + goto err_igpio_request_irq; >>> + } >>> + >>> + } >>> + >>> + return 0; >>> + >>> +err_igpio_request_irq: >>> +err_igpio_direction: >>> + if (sdhci->data->card_int_gpio >= 0) >>> + gpio_free(sdhci->data->card_int_gpio); >>> +err_igpio_request: >>> +err_pgpio_direction: >>> + if (sdhci->data->card_power_gpio >= 0) >>> + gpio_free(sdhci->data->card_power_gpio); >>> +err_pgpio_request: >>> + platform_set_drvdata(pdev, NULL); >>> + sdhci_remove_host(host, 1); >>> +err_add_host: >>> + iounmap(host->ioaddr); >>> +err_ioremap: >>> + sdhci_free_host(host); >>> +err_alloc_host: >>> + clk_disable(sdhci->clk); >>> +err_clk_enb: >>> + clk_put(sdhci->clk); >>> +err_clk_get: >>> + kfree(sdhci); >>> +err_kzalloc: >>> + release_mem_region(iomem->start, resource_size(iomem)); >>> +err: >>> + dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); >>> + return ret; >>> +} >>> + >>> +static int __devexit sdhci_remove(struct platform_device *pdev) >>> +{ >>> + struct sdhci_host *host = platform_get_drvdata(pdev); >>> + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>> + struct spear_sdhci *sdhci = >>> + (struct spear_sdhci *)(pdev->dev.platform_data); >>> + int dead; >>> + u32 scratch; >>> + >>> + if (sdhci->data) { >>> + if (sdhci->data->card_int_gpio >= 0) { >>> + free_irq(gpio_to_irq(sdhci->data->card_int_gpio), pdev); >>> + gpio_free(sdhci->data->card_int_gpio); >>> + } >>> + >>> + if (sdhci->data->card_power_gpio >= 0) >>> + gpio_free(sdhci->data->card_power_gpio); >>> + } >>> + >>> + platform_set_drvdata(pdev, NULL); >>> + dead = 0; >>> + scratch = readl(host->ioaddr + SDHCI_INT_STATUS); >>> + if (scratch == (u32)-1) >>> + dead = 1; >>> + >>> + sdhci_remove_host(host, dead); >>> + iounmap(host->ioaddr); >>> + sdhci_free_host(host); >>> + clk_disable(sdhci->clk); >>> + clk_put(sdhci->clk); >>> + kfree(sdhci); >>> + if (iomem) >>> + release_mem_region(iomem->start, resource_size(iomem)); >>> + >>> + return 0; >>> +} >>> + >>> +static struct platform_driver sdhci_driver = { >>> + .driver = { >>> + .name = "sdhci", >>> + .owner = THIS_MODULE, >>> + }, >>> + .probe = sdhci_probe, >>> + .remove = __devexit_p(sdhci_remove), >>> +}; >>> + >>> +static int __init sdhci_init(void) >>> +{ >>> + return platform_driver_register(&sdhci_driver); >>> +} >>> +module_init(sdhci_init); >>> + >>> +static void __exit sdhci_exit(void) >>> +{ >>> + platform_driver_unregister(&sdhci_driver); >>> +} >>> +module_exit(sdhci_exit); >>> + >>> +MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); >>> +MODULE_AUTHOR("Viresh Kumar <viresh.kumar@xxxxxx>"); >>> +MODULE_LICENSE("GPL v2"); >>> diff --git a/include/linux/mmc/sdhci-spear.h b/include/linux/mmc/sdhci-spear.h >>> new file mode 100644 >>> index 0000000..9188c97 >>> --- /dev/null >>> +++ b/include/linux/mmc/sdhci-spear.h >>> @@ -0,0 +1,42 @@ >>> +/* >>> + * include/linux/mmc/sdhci-spear.h >>> + * >>> + * SDHCI declarations specific to ST SPEAr platform >>> + * >>> + * Copyright (C) 2010 ST Microelectronics >>> + * Viresh Kumar<viresh.kumar@xxxxxx> >>> + * >>> + * This file is licensed under the terms of the GNU General Public >>> + * License version 2. This program is licensed "as is" without any >>> + * warranty of any kind, whether express or implied. >>> + */ >>> + >>> +#ifndef MMC_SDHCI_SPEAR_H >>> +#define MMC_SDHCI_SPEAR_H >>> + >>> +#include <linux/platform_device.h> >>> +/* >>> + * struct sdhci_plat_data: spear sdhci platform data structure >>> + * >>> + * @card_power_gpio: gpio pin for enabling/disabling power to sdhci socket >>> + * @power_active_high: if set, enable power to sdhci socket by setting >>> + * card_power_gpio >>> + * @power_always_enb: If set, then enable power on probe, otherwise enable only >>> + * on card insertion and disable on card removal. >>> + * card_int_gpio: gpio pin used for card detection >>> + */ >>> +struct sdhci_plat_data { >>> + int card_power_gpio; >>> + int power_active_high; >>> + int power_always_enb; >>> + int card_int_gpio; >>> +}; >>> + >>> +/* This function is used to set platform_data field of pdev->dev */ >>> +static inline void >>> +sdhci_set_plat_data(struct platform_device *pdev, struct sdhci_plat_data *data) >>> +{ >>> + pdev->dev.platform_data = data; >>> +} >>> + >>> +#endif /* MMC_SDHCI_SPEAR_H */ >> >> Pierre, >> >> Is this patch fine? Or do i need to do some modifications? >> >> regards, >> viresh kumar. >> > > Including linux-arm-kernel@xxxxxxxxxxxxxxxxxxx in CC. > Hello, MAINTAINER of SDHCI is Orphan in MAINTAINERS file, and i am not sure whom should i redirect this patch to. Show this be reviewed and pushed through arch specific list (ARM in our case) or should it be done through linux-mmc@xxxxxxxxxxxxxxx Can somebody please help? regards, viresh kumar. -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html