Implemented callback reset_emmc for MFLD SDHCI host controller to reset eMMC card. MMC host driver use SFI table to get the relevant GPIO lines instead of requesting these GPIO lines by itself.
SFI table defined the name "emmc0_rst" and "emmc1_rst" for the GPIO lines and driver needs to get them through the defined name. Added functions in mrst.c to support this. Signed-off-by: Chuanxiao Dong <[email protected]> --- arch/x86/include/asm/mrst.h | 4 ++ arch/x86/kernel/mrst.c | 113 ++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pci.c | 101 ++++++++++++++++++++++++++++++++++++- drivers/mmc/host/sdhci.h | 4 ++ 4 files changed, 220 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 87b5217..1b1be50 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> @@ -1644,4 +1645,116 @@ 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 +/* + * mfld_emmc_rstgpio_setup - request GPIO line for MFLD sdhci host to + * trigger HW reset signale. + * @pdev: the pci device to determine which GPIO line will be requested. + * + * This function will get the correct GPIO line by reading SFI table and + * request this GPIO line for MFLD sdhci host controller + * return value: + * gpio: return the GPIO line number if there is no error + * -ENODEV: failed to request GPIO line + */ +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); + gpio_free(gpio); + return ret; + } + + return gpio; +} +EXPORT_SYMBOL_GPL(mfld_emmc_rstgpio_setup); + +/* + * mfld_emmc_rstgpio_check - check the GPIO line whether is belong to MFLD + * host controller + * @gpio: the gpio number need to be checked. + * + * return value: + * 0: the GPIO is belong to MFLD host controller and can be used. + * -ENODEV: the GPIO is invalid or not belong to MFLD host controller. + */ +static int mfld_emmc_rstgpio_check(int gpio) +{ + int gpio0, gpio1; + + gpio0 = get_gpio_by_name("emmc0_rst"); + gpio1 = get_gpio_by_name("emmc0_rst"); + + if (gpio != gpio0 && gpio != gpio1) + return -ENODEV; + + if (gpio < 0) + return -ENODEV; + + return 0; +} + +/* + * mfld_emmc_rstgpio_release - release the GPIO line for MFLD sdhci host + */ +void mfld_emmc_rstgpio_release(int gpio) +{ + if (mfld_emmc_rstgpio_check(gpio)) + 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); + +/* + * mfld_emmc_rstgpio_trigger - trigger HW reset signal to reset eMMC card + * @gpio: the GPIO line to be triggered + * + * MMC host driver can use this function to reset eMMC card. + * + * return value: + * 0: successfully triggered HW reset signal + * other: failed. + */ +int mfld_emmc_rstgpio_trigger(int gpio) +{ + int ret = mfld_emmc_rstgpio_check(gpio); + if (ret) + return ret; + + __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..adfc4ea 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,44 @@ 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 + */ + return mfld_emmc_rstgpio_trigger(gpio); +#else + /* + * Just let other platform SDHCI host return + * -ENODEV since there is no implementation for + * other platform + */ + return -ENODEV; +#endif +} + 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 f8b1828..97dd061 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 */ -- 1.6.6.1 _______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
