Hi Arjan, This patch is the version 3 of the serial hardware reset feature patches but only the 3rd one. Patch1 and patch2 I will submit to linux-mmc.
patch1: implemented reset eMMC card ops for MFLD eMMC HC. changelog: In this patch, fixed the comment from Alan, thanks Alan. Add a new callback in sdhci_ops. I am not much clear where is the exactly place for me to add document for this new callback, so I just add some comment in the head file. Alan, I have fixed all of your comment. Could you please help to review the fix and point it out if I have done something wrong? Thanks. Regards Chuanxiao >From 9a3c615c0adafa74033f3f7ecef90e0afcfb0f04 Mon Sep 17 00:00:00 2001 From: Chuanxiao Dong <[email protected]> Date: Wed, 17 Nov 2010 18:20:19 +0800 Subject: [PATCH] mmc: implemented reset eMMC card ops for MFLD eMMC HC Hardware reset need host controller to trigger a RST_n signal, and driver needs to pull up/down the relavent GPIO to implement this. Since GPIO need FW to add a name in SFI table, add two interface in arch/x86/kernel/mrst.c to setup/release the needed GPIO. Add a new callback in sdhci_ops for sdhci core layer using. This callback will be used to help implement eMMC4.4 hardware reset feature. Signed-off-by: Chuanxiao Dong <[email protected]> --- arch/x86/include/asm/mrst.h | 4 ++ arch/x86/kernel/mrst.c | 77 ++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pci.c | 95 +++++++++++++++++++++++++++++++++++++++++- drivers/mmc/host/sdhci.h | 14 ++++++ 4 files changed, 188 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index 3f4687d..52e17a8 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h @@ -69,4 +69,8 @@ extern void mrst_early_printk(const char *fmt, ...); extern void intel_scu_devices_create(void); extern void intel_scu_devices_destroy(void); + +extern int mfld_emmc_rstgpio_setup(struct pci_dev *pdev); +extern void mfld_emmc_rstgpio_release(int gpio); +extern int mfld_emmc_rstgpio_trigger(int gpio); #endif /* _ASM_X86_MRST_H */ diff --git a/arch/x86/kernel/mrst.c b/arch/x86/kernel/mrst.c index dce79e2..3a0dc39 100644 --- a/arch/x86/kernel/mrst.c +++ b/arch/x86/kernel/mrst.c @@ -34,6 +34,7 @@ #include <linux/i2c/tc35894xbg.h> #include <linux/bh1770glc.h> #include <linux/leds-lp5523.h> +#include <linux/pci.h> #include <asm/setup.h> #include <asm/mpspec_def.h> @@ -1645,4 +1646,80 @@ static int __init setup_hsu_dma_enable_flag(char *p) } early_param("hsu_dma", setup_hsu_dma_enable_flag); +#define PCI_DEVID_MFL_EMMC0 0x0823 +#define PCI_DEVID_MFL_EMMC1 0x0824 +/* + * Help eMMC host controller to get the correct + * GPIO lines. + * return value: + * successful: return the correct GPIO line number; + * fail: return -ENODEV for no valid GPIO line, + * or other failure value returned by gpio routine. + */ +int mfld_emmc_rstgpio_setup(struct pci_dev *pdev) +{ + int gpio; + int ret; + + if (pdev->device == PCI_DEVID_MFL_EMMC0) + gpio = get_gpio_by_name("emmc0_rst"); + else if (pdev->device == PCI_DEVID_MFL_EMMC1) + gpio = get_gpio_by_name("emmc1_rst"); + else + return -ENODEV; + + /* if the gpio number is less than 0, return + * -ENODEV indicates no gpio is avalible + * for hardware reset + * */ + if (gpio < 0) + return -ENODEV; + + /* request reset pin for eMMC */ + ret = gpio_request(gpio, "eMMC_rst_pin"); + if (ret < 0) { + dev_err(&pdev->dev, "gpio %d request failed\n", gpio); + return ret; + } + /* set to be output and to be low state */ + ret = gpio_direction_output(gpio, 0); + if (ret < 0) { + dev_err(&pdev->dev, "gpio %d direction output failed\n", gpio); + goto gpio_output_failed; + } + return gpio; + +gpio_output_failed: + gpio_free(gpio); + return ret; +} +EXPORT_SYMBOL_GPL(mfld_emmc_rstgpio_setup); + +void mfld_emmc_rstgpio_release(int gpio) +{ + if (gpio < 0) + return; + /* set to be output and to be low state */ + gpio_direction_output(gpio, 0); + /* free reset gpio pin */ + gpio_free(gpio); +} +EXPORT_SYMBOL_GPL(mfld_emmc_rstgpio_release); + +/* + * Trigger RST_n signale to reset eMMC card + * 0: successfully reset the eMMC card. + * -ENODEV: no valid GPIO line to be used. + */ +int mfld_emmc_rstgpio_trigger(int gpio) +{ + if (gpio < 0) + return -ENODEV; + + __gpio_set_value(gpio, 1); + udelay(300); + __gpio_set_value(gpio, 0); + return 0; +} +EXPORT_SYMBOL_GPL(mfld_emmc_rstgpio_trigger); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 7bdbd9c..6697e4e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -66,6 +66,8 @@ struct sdhci_pci_slot { struct sdhci_host *host; int pci_bar; + + void *private; /* slot private things */ }; struct sdhci_pci_chip { @@ -183,6 +185,65 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, }; +/* + * Let MFLD eMMC host controller request/release the reset + * GPIO lines. + */ +static int mfld_emmc_gpio_get_put(struct sdhci_pci_slot *slot, int get) +{ + struct sdhci_pci_chip *chip = slot->chip; + if (!chip) + return -ENODEV; +#if defined(CONFIG_X86_MRST) + if (get) { + /* use private data as reset GPIO number */ + return mfld_emmc_rstgpio_setup(chip->pdev); + } else { + int gpio; + if (slot->private == NULL) + return 0; + else + gpio = *(int *)slot->private; + + mfld_emmc_rstgpio_release(gpio); + return 0; + } +#endif + return -ENODEV; +} + +/* + * Let MFLD eMMC host controller request GPIO which is + * used to reset eMMC card. The GPIO number should be get + * from SFI table. + */ +static int mfld_emmc_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->private = kzalloc(sizeof(int), GFP_KERNEL); + if (!slot->private) + return -ENOMEM; + + *(int *)slot->private = mfld_emmc_gpio_get_put(slot, 1); + return 0; +} + +/* + * Let MFLD eMMC host controller release GPIO which is + * used to reset eMMC card. + */ +static void mfld_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead) +{ + mfld_emmc_gpio_get_put(slot, 0); + kfree(slot->private); +} + +static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { + .quirks = SDHCI_QUIRK_MFD_EMMC_SDIO_RESTRICTION | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .probe_slot = mfld_emmc_probe_slot, + .remove_slot = mfld_emmc_remove_slot, +}; + static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc_sdio = { .quirks = SDHCI_QUIRK_MFD_EMMC_SDIO_RESTRICTION | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, @@ -582,7 +643,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .device = PCI_DEVICE_ID_INTEL_MFD_EMMC0, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, }, { @@ -590,7 +651,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .device = PCI_DEVICE_ID_INTEL_MFD_EMMC1, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc_sdio, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, }, { /* Generic SD host controller */ @@ -633,8 +694,38 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) return 0; } +/* + * HW reset eMMC4.4 card callback + * In this function, driver need to trigger RST_n signal + * as eMMC4.4 standard says. + * 0: reset emmc successfully + * -ENODEV: reset emmc failed, no gpio pin to to the reset + */ +static int sdhci_pci_reset_emmc(struct sdhci_host *host) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + int gpio; + int ret = -ENODEV; + if (slot->private == NULL) + return ret; + /* + * private stored the GPIO line number + */ + gpio = *(int *)slot->private; + if (gpio < 0) + return ret; +#if defined(CONFIG_X86_MRST) + /* + * trigger a RST_n signal + */ + ret = mfld_emmc_rstgpio_trigger(gpio); +#endif + return ret; +} + static struct sdhci_ops sdhci_pci_ops = { .enable_dma = sdhci_pci_enable_dma, + .reset_emmc = sdhci_pci_reset_emmc, }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 74b72f7..216fe66 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -20,6 +20,10 @@ #include <linux/mmc/sdhci.h> +#if defined(CONFIG_X86_MRST) +#include <asm/mrst.h> +#endif + /* * Controller registers */ @@ -216,6 +220,16 @@ struct sdhci_ops { void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); unsigned int (*get_ro)(struct sdhci_host *host); + + /* + * eMMC card reset callback. + * This callback can be used by sdhci layer to support eMMC4.4 + * hardware reset feature. + * return value: + * 0: successfully reset eMMC card. + * -ENODEV: no valid GPIO lines to reset eMMC card. + */ + int (*reset_emmc)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- 1.6.6.1 _______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
