From fdc50a9444b9781f4dd5aa5f7453300d2688cc5f Mon Sep 17 00:00:00 2001 From: Yusuke Goda Date: Wed, 26 May 2010 14:41:59 -0700 Subject: mmc: add support MMCIF for SuperH MMCIF is the MMC Host Interface in SuperH. Signed-off-by: Yusuke Goda Cc: Ben Hutchings Cc: Paul Mundt Cc: Magnus Damm Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmc/sh_mmcif.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 include/linux/mmc/sh_mmcif.h (limited to 'include/linux/mmc') diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h new file mode 100644 index 00000000000..aafe832f18a --- /dev/null +++ b/include/linux/mmc/sh_mmcif.h @@ -0,0 +1,39 @@ +/* + * include/linux/mmc/sh_mmcif.h + * + * platform data for eMMC driver + * + * Copyright (C) 2010 Renesas Solutions Corp. + * + * 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. + * + */ + +#ifndef __SH_MMCIF_H__ +#define __SH_MMCIF_H__ + +/* + * MMCIF : CE_CLK_CTRL [19:16] + * 1000 : Peripheral clock / 512 + * 0111 : Peripheral clock / 256 + * 0110 : Peripheral clock / 128 + * 0101 : Peripheral clock / 64 + * 0100 : Peripheral clock / 32 + * 0011 : Peripheral clock / 16 + * 0010 : Peripheral clock / 8 + * 0001 : Peripheral clock / 4 + * 0000 : Peripheral clock / 2 + * 1111 : Peripheral clock (sup_pclk set '1') + */ + +struct sh_mmcif_plat_data { + void (*set_pwr)(struct platform_device *pdev, int state); + void (*down_pwr)(struct platform_device *pdev); + u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ + unsigned long caps; + u32 ocr; +}; + +#endif /* __SH_MMCIF_H__ */ -- cgit v1.2.3-70-g09d2 From 1a13f8fa76c880be41d6b1e6a2b44404bcbfdf9e Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Wed, 26 May 2010 14:42:08 -0700 Subject: mmc: remove the "state" argument to mmc_suspend_host() Even though many mmc host drivers pass a pm_message_t argument to mmc_suspend_host() that argument isn't used the by MMC core. As host drivers are converted to dev_pm_ops they'll have to construct pm_message_t's (as they won't be passed by the PM subsystem any more) just to appease the mmc suspend interface. We might as well just delete the unused paramter. Signed-off-by: Matt Fleming Acked-by: Anton Vorontsov Acked-by: Michal Miroslaw ZZ Acked-by: Sascha Sommer Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/core.c | 3 +-- drivers/mmc/host/at91_mci.c | 2 +- drivers/mmc/host/au1xmmc.c | 2 +- drivers/mmc/host/bfin_sdh.c | 2 +- drivers/mmc/host/cb710-mmc.c | 2 +- drivers/mmc/host/davinci_mmc.c | 3 +-- drivers/mmc/host/imxmmc.c | 2 +- drivers/mmc/host/mmci.c | 2 +- drivers/mmc/host/msm_sdcc.c | 2 +- drivers/mmc/host/mvsdio.c | 2 +- drivers/mmc/host/mxcmmc.c | 2 +- drivers/mmc/host/omap.c | 2 +- drivers/mmc/host/omap_hsmmc.c | 2 +- drivers/mmc/host/pxamci.c | 2 +- drivers/mmc/host/s3cmci.c | 3 +-- drivers/mmc/host/sdhci-of-core.c | 2 +- drivers/mmc/host/sdhci.c | 2 +- drivers/mmc/host/sdricoh_cs.c | 2 +- drivers/mmc/host/tifm_sd.c | 2 +- drivers/mmc/host/tmio_mmc.c | 2 +- drivers/mmc/host/via-sdmmc.c | 2 +- drivers/mmc/host/wbsd.c | 2 +- include/linux/mmc/host.h | 2 +- 23 files changed, 23 insertions(+), 26 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3168ebd616b..569e94da844 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1252,9 +1252,8 @@ EXPORT_SYMBOL(mmc_card_can_sleep); /** * mmc_suspend_host - suspend a host * @host: mmc host - * @state: suspend mode (PM_SUSPEND_xxx) */ -int mmc_suspend_host(struct mmc_host *host, pm_message_t state) +int mmc_suspend_host(struct mmc_host *host) { int err = 0; diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 336d9f553f3..5f3a599ead0 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -1157,7 +1157,7 @@ static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) enable_irq_wake(host->board->det_pin); if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); return ret; } diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index f5834449400..c8da5d30a86 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1142,7 +1142,7 @@ static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state) struct au1xmmc_host *host = platform_get_drvdata(pdev); int ret; - ret = mmc_suspend_host(host->mmc, state); + ret = mmc_suspend_host(host->mmc); if (ret) return ret; diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 6919e844072..4b0e677d729 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -576,7 +576,7 @@ static int sdh_suspend(struct platform_device *dev, pm_message_t state) int ret = 0; if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); bfin_write_SDH_PWR_CTL(bfin_read_SDH_PWR_CTL() & ~PWR_ON); peripheral_free_list(drv_data->pin_req); diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 92a324f7417..ca3bdc83190 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -675,7 +675,7 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) struct mmc_host *mmc = cb710_slot_to_mmc(slot); int err; - err = mmc_suspend_host(mmc, state); + err = mmc_suspend_host(mmc); if (err) return err; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index aae39125b8d..33d9f1b0086 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1337,11 +1337,10 @@ static int davinci_mmcsd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mmc_davinci_host *host = platform_get_drvdata(pdev); - struct pm_message msg = { PM_EVENT_SUSPEND }; int ret; mmc_host_enable(host->mmc); - ret = mmc_suspend_host(host->mmc, msg); + ret = mmc_suspend_host(host->mmc); if (!ret) { writel(0, host->base + DAVINCI_MMCIM); mmc_davinci_reset_ctrl(host, 1); diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c index bf98d7cc928..9a68ff4353a 100644 --- a/drivers/mmc/host/imxmmc.c +++ b/drivers/mmc/host/imxmmc.c @@ -1115,7 +1115,7 @@ static int imxmci_suspend(struct platform_device *dev, pm_message_t state) int ret = 0; if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); return ret; } diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index ff115d92088..4917af96bae 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -824,7 +824,7 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state) if (mmc) { struct mmci_host *host = mmc_priv(mmc); - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); if (ret == 0) writel(0, host->base + MMCIMASK0); } diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 61f1d27fed3..24e09454e52 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1327,7 +1327,7 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) disable_irq(host->stat_irq); if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) - rc = mmc_suspend_host(mmc, state); + rc = mmc_suspend_host(mmc); if (!rc) msmsdcc_writel(host, 0, MMCIMASK0); if (host->clks_on) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 34e23489811..366eefa77c5 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -865,7 +865,7 @@ static int mvsd_suspend(struct platform_device *dev, pm_message_t state) int ret = 0; if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); return ret; } diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index ec18e3b6034..d9d4a72e0ec 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -932,7 +932,7 @@ static int mxcmci_suspend(struct platform_device *dev, pm_message_t state) int ret = 0; if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); return ret; } diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 475490b1857..2b281680e32 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1561,7 +1561,7 @@ static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) struct mmc_omap_slot *slot; slot = host->slots[i]; - ret = mmc_suspend_host(slot->mmc, mesg); + ret = mmc_suspend_host(slot->mmc); if (ret < 0) { while (--i >= 0) { slot = host->slots[i]; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index d25b19b3ca2..b032828c612 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -2292,7 +2292,7 @@ static int omap_hsmmc_suspend(struct device *dev) } cancel_work_sync(&host->mmc_carddetect_work); mmc_host_enable(host->mmc); - ret = mmc_suspend_host(host->mmc, state); + ret = mmc_suspend_host(host->mmc); if (ret == 0) { omap_hsmmc_disable_irq(host); OMAP_HSMMC_WRITE(host->base, HCTL, diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index e4f00e70a74..0a4e43f3714 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -813,7 +813,7 @@ static int pxamci_suspend(struct device *dev) int ret = 0; if (mmc) - ret = mmc_suspend_host(mmc, PMSG_SUSPEND); + ret = mmc_suspend_host(mmc); return ret; } diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 2fdf7689ae6..2e16e0a90a5 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1881,9 +1881,8 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); static int s3cmci_suspend(struct device *dev) { struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - struct pm_message event = { PM_EVENT_SUSPEND }; - return mmc_suspend_host(mmc, event); + return mmc_suspend_host(mmc); } static int s3cmci_resume(struct device *dev) diff --git a/drivers/mmc/host/sdhci-of-core.c b/drivers/mmc/host/sdhci-of-core.c index 7802a543d8f..a2e9820cd42 100644 --- a/drivers/mmc/host/sdhci-of-core.c +++ b/drivers/mmc/host/sdhci-of-core.c @@ -89,7 +89,7 @@ static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state) { struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); - return mmc_suspend_host(host->mmc, state); + return mmc_suspend_host(host->mmc); } static int sdhci_of_resume(struct of_device *ofdev) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d27cc917804..c6d1bd8d4ac 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1597,7 +1597,7 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) sdhci_disable_card_detection(host); - ret = mmc_suspend_host(host->mmc, state); + ret = mmc_suspend_host(host->mmc); if (ret) return ret; diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index cb41e9c3ac0..e7507af3856 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -519,7 +519,7 @@ static int sdricoh_pcmcia_suspend(struct pcmcia_device *link) { struct mmc_host *mmc = link->priv; dev_dbg(&link->dev, "suspend\n"); - mmc_suspend_host(mmc, PMSG_SUSPEND); + mmc_suspend_host(mmc); return 0; } diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 82554ddec6b..cec99958b65 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -1032,7 +1032,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) { - return mmc_suspend_host(tifm_get_drvdata(sock), state); + return mmc_suspend_host(tifm_get_drvdata(sock)); } static int tifm_sd_resume(struct tifm_dev *sock) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 883fcac2100..ee7d0a5a51c 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -768,7 +768,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) struct mmc_host *mmc = platform_get_drvdata(dev); int ret; - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); /* Tell MFD core it can disable us now.*/ if (!ret && cell->disable) diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 632858a9437..19f2d72dbca 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1280,7 +1280,7 @@ static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state) via_save_pcictrlreg(host); via_save_sdcreg(host); - ret = mmc_suspend_host(host->mmc, state); + ret = mmc_suspend_host(host->mmc); pci_save_state(pcidev); pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 69efe01eece..0012f5d13d2 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1819,7 +1819,7 @@ static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) { BUG_ON(host == NULL); - return mmc_suspend_host(host->mmc, state); + return mmc_suspend_host(host->mmc); } static int wbsd_resume(struct wbsd_host *host) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 3196c84cc63..f65913c9f5a 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -230,7 +230,7 @@ static inline void *mmc_priv(struct mmc_host *host) #define mmc_classdev(x) (&(x)->class_dev) #define mmc_hostname(x) (dev_name(&(x)->class_dev)) -extern int mmc_suspend_host(struct mmc_host *, pm_message_t); +extern int mmc_suspend_host(struct mmc_host *); extern int mmc_resume_host(struct mmc_host *); extern void mmc_power_save_host(struct mmc_host *host); -- cgit v1.2.3-70-g09d2 From 6c1f716e8154ee9315534782b9b1eedea0559a24 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Wed, 26 May 2010 14:42:09 -0700 Subject: sdio: add new function for RAW (Read after Write) operation SDIO specification allows RAW (Read after Write) operation using IO_RW_DIRECT command (CMD52) by setting the RAW bit. This operation is similar to ordinary read/write commands, except that both write and read are performed using single command/response pair. The Linux SDIO layer already supports this internaly, only external function is missing for drivers to make use, which is added by this patch. This type of command is required to implement proper power save mode support in wl1251 wifi driver. Android has similar patch for G1 in it's tree for the same reason: http://android.git.kernel.org/?p=kernel/common.git;a=commitdiff;h=74a47786f6ecbe6c1cf9fb15efe6a968451deb52 Signed-off-by: Grazvydas Ignotas Acked-by: Kalle Valo Cc: Dmitry Shmidt Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/core/sdio_io.c | 30 ++++++++++++++++++++++++++++++ include/linux/mmc/sdio_func.h | 3 +++ 2 files changed, 33 insertions(+) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index ff27c8c7135..0f687cdeb06 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -405,6 +405,36 @@ void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret) } EXPORT_SYMBOL_GPL(sdio_writeb); +/** + * sdio_writeb_readb - write and read a byte from SDIO function + * @func: SDIO function to access + * @write_byte: byte to write + * @addr: address to write to + * @err_ret: optional status value from transfer + * + * Performs a RAW (Read after Write) operation as defined by SDIO spec - + * single byte is written to address space of a given SDIO function and + * response is read back from the same address, both using single request. + * If there is a problem with the operation, 0xff is returned and + * @err_ret will contain the error code. + */ +u8 sdio_writeb_readb(struct sdio_func *func, u8 write_byte, + unsigned int addr, int *err_ret) +{ + int ret; + u8 val; + + ret = mmc_io_rw_direct(func->card, 1, func->num, addr, + write_byte, &val); + if (err_ret) + *err_ret = ret; + if (ret) + val = 0xff; + + return val; +} +EXPORT_SYMBOL_GPL(sdio_writeb_readb); + /** * sdio_memcpy_fromio - read a chunk of memory from a SDIO function * @func: SDIO function to access diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index c6c0cceba5f..31baaf82f45 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -145,6 +145,9 @@ extern void sdio_writew(struct sdio_func *func, u16 b, extern void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret); +extern u8 sdio_writeb_readb(struct sdio_func *func, u8 write_byte, + unsigned int addr, int *err_ret); + extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, void *src, int count); extern int sdio_writesb(struct sdio_func *func, unsigned int addr, -- cgit v1.2.3-70-g09d2 From c63b3cba4f47ef9f4b3f952b4f923cf341d250ac Mon Sep 17 00:00:00 2001 From: Viresh KUMAR Date: Wed, 26 May 2010 14:42:10 -0700 Subject: sdhci-spear: ST SPEAr based SDHCI controller glue Add a glue layer to support the sdhci driver on the ST SPEAr platform. Signed-off-by: Viresh Kumar Cc: Cc: Linus Walleij Cc: Russell King Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 6 + drivers/mmc/host/Kconfig | 12 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-spear.c | 298 ++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/sdhci-spear.h | 42 ++++++ 5 files changed, 359 insertions(+) create mode 100644 drivers/mmc/host/sdhci-spear.c create mode 100644 include/linux/mmc/sdhci-spear.h (limited to 'include/linux/mmc') diff --git a/MAINTAINERS b/MAINTAINERS index 18355cc1ce2..8b7eba2de60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5004,6 +5004,12 @@ L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/sdhci-s3c.c +SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) ST SPEAR DRIVER +M: Viresh Kumar +L: linux-mmc@vger.kernel.org +S: Maintained +F: drivers/mmc/host/sdhci-spear.c + SECURITY SUBSYSTEM M: James Morris L: linux-security-module@vger.kernel.org (suggested Cc:) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index aeecbc932fb..e171e77f612 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 2447b66afeb..e30c2ee4889 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 00000000000..d70c54c7b70 --- /dev/null +++ b/drivers/mmc/host/sdhci-spear.c @@ -0,0 +1,298 @@ +/* + * drivers/mmc/host/sdhci-spear.c + * + * Support of SDHCI platform devices for spear soc family + * + * Copyright (C) 2010 ST Microelectronics + * Viresh Kumar + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 = dev_get_platdata(&pdev->dev); + 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 = dev_get_platdata(&pdev->dev); + 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 = dev_get_platdata(&pdev->dev); + 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 "); +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 00000000000..9188c973f3e --- /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 + * + * 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 +/* + * 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 */ -- cgit v1.2.3-70-g09d2 From 487d9fc5016529d7d77dfe35b666fd3a090e2953 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 18 May 2010 14:42:51 +0000 Subject: sh: prepare MMCIF driver header file Update the MMCIF driver to include register information and register access functions in the header file. The MMCIF boot code builds on top of this. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/mmc/host/sh_mmcif.c | 125 +++++++++++++++++-------------------------- include/linux/mmc/sh_mmcif.h | 32 +++++++++++ 2 files changed, 82 insertions(+), 75 deletions(-) (limited to 'include/linux/mmc') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index eb97830c034..5d3f824bb5a 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -30,25 +30,6 @@ #define DRIVER_NAME "sh_mmcif" #define DRIVER_VERSION "2010-04-28" -#define MMCIF_CE_CMD_SET 0x00000000 -#define MMCIF_CE_ARG 0x00000008 -#define MMCIF_CE_ARG_CMD12 0x0000000C -#define MMCIF_CE_CMD_CTRL 0x00000010 -#define MMCIF_CE_BLOCK_SET 0x00000014 -#define MMCIF_CE_CLK_CTRL 0x00000018 -#define MMCIF_CE_BUF_ACC 0x0000001C -#define MMCIF_CE_RESP3 0x00000020 -#define MMCIF_CE_RESP2 0x00000024 -#define MMCIF_CE_RESP1 0x00000028 -#define MMCIF_CE_RESP0 0x0000002C -#define MMCIF_CE_RESP_CMD12 0x00000030 -#define MMCIF_CE_DATA 0x00000034 -#define MMCIF_CE_INT 0x00000040 -#define MMCIF_CE_INT_MASK 0x00000044 -#define MMCIF_CE_HOST_STS1 0x00000048 -#define MMCIF_CE_HOST_STS2 0x0000004C -#define MMCIF_CE_VERSION 0x0000007C - /* CE_CMD_SET */ #define CMD_MASK 0x3f000000 #define CMD_SET_RTYP_NO ((0 << 23) | (0 << 22)) @@ -207,27 +188,17 @@ struct sh_mmcif_host { wait_queue_head_t intr_wait; }; -static inline u32 sh_mmcif_readl(struct sh_mmcif_host *host, unsigned int reg) -{ - return readl(host->addr + reg); -} - -static inline void sh_mmcif_writel(struct sh_mmcif_host *host, - unsigned int reg, u32 val) -{ - writel(val, host->addr + reg); -} static inline void sh_mmcif_bitset(struct sh_mmcif_host *host, unsigned int reg, u32 val) { - writel(val | sh_mmcif_readl(host, reg), host->addr + reg); + writel(val | readl(host->addr + reg), host->addr + reg); } static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, unsigned int reg, u32 val) { - writel(~val & sh_mmcif_readl(host, reg), host->addr + reg); + writel(~val & readl(host->addr + reg), host->addr + reg); } @@ -253,10 +224,10 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) { u32 tmp; - tmp = 0x010f0000 & sh_mmcif_readl(host, MMCIF_CE_CLK_CTRL); + tmp = 0x010f0000 & sh_mmcif_readl(host->addr, MMCIF_CE_CLK_CTRL); - sh_mmcif_writel(host, MMCIF_CE_VERSION, SOFT_RST_ON); - sh_mmcif_writel(host, MMCIF_CE_VERSION, SOFT_RST_OFF); + sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_ON); + sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_OFF); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp | SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29); /* byte swap on */ @@ -271,12 +242,10 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) host->sd_error = 0; host->wait_int = 0; - state1 = sh_mmcif_readl(host, MMCIF_CE_HOST_STS1); - state2 = sh_mmcif_readl(host, MMCIF_CE_HOST_STS2); - pr_debug("%s: ERR HOST_STS1 = %08x\n", \ - DRIVER_NAME, sh_mmcif_readl(host, MMCIF_CE_HOST_STS1)); - pr_debug("%s: ERR HOST_STS2 = %08x\n", \ - DRIVER_NAME, sh_mmcif_readl(host, MMCIF_CE_HOST_STS2)); + state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1); + state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2); + pr_debug("%s: ERR HOST_STS1 = %08x\n", DRIVER_NAME, state1); + pr_debug("%s: ERR HOST_STS2 = %08x\n", DRIVER_NAME, state2); if (state1 & STS1_CMDSEQ) { sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK); @@ -288,7 +257,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) "command sequence timeout err\n"); return -EIO; } - if (!(sh_mmcif_readl(host, MMCIF_CE_HOST_STS1) + if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1) & STS1_CMDSEQ)) break; mdelay(1); @@ -330,9 +299,9 @@ static int sh_mmcif_single_read(struct sh_mmcif_host *host, host->wait_int = 0; blocksize = (BLOCK_SIZE_MASK & - sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET)) + 3; + sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; for (i = 0; i < blocksize / 4; i++) - *p++ = sh_mmcif_readl(host, MMCIF_CE_DATA); + *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); /* buffer read end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); @@ -353,7 +322,8 @@ static int sh_mmcif_multi_read(struct sh_mmcif_host *host, long time; u32 blocksize, i, j, sec, *p; - blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET); + blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, + MMCIF_CE_BLOCK_SET); for (j = 0; j < data->sg_len; j++) { p = sg_virt(data->sg); host->wait_int = 0; @@ -370,7 +340,8 @@ static int sh_mmcif_multi_read(struct sh_mmcif_host *host, host->wait_int = 0; for (i = 0; i < blocksize / 4; i++) - *p++ = sh_mmcif_readl(host, MMCIF_CE_DATA); + *p++ = sh_mmcif_readl(host->addr, + MMCIF_CE_DATA); } if (j < data->sg_len - 1) data->sg++; @@ -397,9 +368,9 @@ static int sh_mmcif_single_write(struct sh_mmcif_host *host, host->wait_int = 0; blocksize = (BLOCK_SIZE_MASK & - sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET)) + 3; + sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; for (i = 0; i < blocksize / 4; i++) - sh_mmcif_writel(host, MMCIF_CE_DATA, *p++); + sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); /* buffer write end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); @@ -421,7 +392,8 @@ static int sh_mmcif_multi_write(struct sh_mmcif_host *host, long time; u32 i, sec, j, blocksize, *p; - blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host, MMCIF_CE_BLOCK_SET); + blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, + MMCIF_CE_BLOCK_SET); for (j = 0; j < data->sg_len; j++) { p = sg_virt(data->sg); @@ -439,7 +411,8 @@ static int sh_mmcif_multi_write(struct sh_mmcif_host *host, host->wait_int = 0; for (i = 0; i < blocksize / 4; i++) - sh_mmcif_writel(host, MMCIF_CE_DATA, *p++); + sh_mmcif_writel(host->addr, + MMCIF_CE_DATA, *p++); } if (j < data->sg_len - 1) data->sg++; @@ -451,18 +424,18 @@ static void sh_mmcif_get_response(struct sh_mmcif_host *host, struct mmc_command *cmd) { if (cmd->flags & MMC_RSP_136) { - cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP3); - cmd->resp[1] = sh_mmcif_readl(host, MMCIF_CE_RESP2); - cmd->resp[2] = sh_mmcif_readl(host, MMCIF_CE_RESP1); - cmd->resp[3] = sh_mmcif_readl(host, MMCIF_CE_RESP0); + cmd->resp[0] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP3); + cmd->resp[1] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP2); + cmd->resp[2] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP1); + cmd->resp[3] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP0); } else - cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP0); + cmd->resp[0] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP0); } static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host, struct mmc_command *cmd) { - cmd->resp[0] = sh_mmcif_readl(host, MMCIF_CE_RESP_CMD12); + cmd->resp[0] = sh_mmcif_readl(host->addr, MMCIF_CE_RESP_CMD12); } static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, @@ -596,18 +569,19 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; if (host->data) { - sh_mmcif_writel(host, MMCIF_CE_BLOCK_SET, 0); - sh_mmcif_writel(host, MMCIF_CE_BLOCK_SET, mrq->data->blksz); + sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); + sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, + mrq->data->blksz); } opc = sh_mmcif_set_cmd(host, mrq, cmd, opc); - sh_mmcif_writel(host, MMCIF_CE_INT, 0xD80430C0); - sh_mmcif_writel(host, MMCIF_CE_INT_MASK, mask); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask); /* set arg */ - sh_mmcif_writel(host, MMCIF_CE_ARG, cmd->arg); + sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg); host->wait_int = 0; /* set cmd */ - sh_mmcif_writel(host, MMCIF_CE_CMD_SET, opc); + sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc); time = wait_event_interruptible_timeout(host->intr_wait, host->wait_int == 1 || host->sd_error == 1, host->timeout); @@ -752,43 +726,44 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) u32 state = 0; int err = 0; - state = sh_mmcif_readl(host, MMCIF_CE_INT); + state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); if (state & INT_RBSYE) { - sh_mmcif_writel(host, MMCIF_CE_INT, ~(INT_RBSYE | INT_CRSPE)); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, + ~(INT_RBSYE | INT_CRSPE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); } else if (state & INT_CRSPE) { - sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_CRSPE); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); } else if (state & INT_BUFREN) { - sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFREN); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } else if (state & INT_BUFWEN) { - sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFWEN); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } else if (state & INT_CMD12DRE) { - sh_mmcif_writel(host, MMCIF_CE_INT, + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE | INT_BUFRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); } else if (state & INT_BUFRE) { - sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_BUFRE); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); } else if (state & INT_DTRANE) { - sh_mmcif_writel(host, MMCIF_CE_INT, ~INT_DTRANE); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_DTRANE); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); } else if (state & INT_CMD12RBE) { - sh_mmcif_writel(host, MMCIF_CE_INT, + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12RBE | INT_CMD12CRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); } else if (state & INT_ERR_STS) { /* err interrupts */ - sh_mmcif_writel(host, MMCIF_CE_INT, ~state); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); err = 1; } else { pr_debug("%s: Not support int\n", DRIVER_NAME); - sh_mmcif_writel(host, MMCIF_CE_INT, ~state); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); err = 1; } @@ -894,12 +869,12 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto clean_up2; } - sh_mmcif_writel(host, MMCIF_CE_INT_MASK, MASK_ALL); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); sh_mmcif_detect(host->mmc); pr_info("%s: driver version %s\n", DRIVER_NAME, DRIVER_VERSION); pr_debug("%s: chip ver H'%04x\n", DRIVER_NAME, - sh_mmcif_readl(host, MMCIF_CE_VERSION) & 0x0000ffff); + sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); return ret; clean_up2: @@ -917,7 +892,7 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) struct sh_mmcif_host *host = platform_get_drvdata(pdev); int irq[2]; - sh_mmcif_writel(host, MMCIF_CE_INT_MASK, MASK_ALL); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index aafe832f18a..e079c6beeb9 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -14,6 +14,9 @@ #ifndef __SH_MMCIF_H__ #define __SH_MMCIF_H__ +#include +#include + /* * MMCIF : CE_CLK_CTRL [19:16] * 1000 : Peripheral clock / 512 @@ -36,4 +39,33 @@ struct sh_mmcif_plat_data { u32 ocr; }; +#define MMCIF_CE_CMD_SET 0x00000000 +#define MMCIF_CE_ARG 0x00000008 +#define MMCIF_CE_ARG_CMD12 0x0000000C +#define MMCIF_CE_CMD_CTRL 0x00000010 +#define MMCIF_CE_BLOCK_SET 0x00000014 +#define MMCIF_CE_CLK_CTRL 0x00000018 +#define MMCIF_CE_BUF_ACC 0x0000001C +#define MMCIF_CE_RESP3 0x00000020 +#define MMCIF_CE_RESP2 0x00000024 +#define MMCIF_CE_RESP1 0x00000028 +#define MMCIF_CE_RESP0 0x0000002C +#define MMCIF_CE_RESP_CMD12 0x00000030 +#define MMCIF_CE_DATA 0x00000034 +#define MMCIF_CE_INT 0x00000040 +#define MMCIF_CE_INT_MASK 0x00000044 +#define MMCIF_CE_HOST_STS1 0x00000048 +#define MMCIF_CE_HOST_STS2 0x0000004C +#define MMCIF_CE_VERSION 0x0000007C + +extern inline u32 sh_mmcif_readl(void __iomem *addr, int reg) +{ + return readl(addr + reg); +} + +extern inline void sh_mmcif_writel(void __iomem *addr, int reg, u32 val) +{ + writel(val, addr + reg); +} + #endif /* __SH_MMCIF_H__ */ -- cgit v1.2.3-70-g09d2 From 8a768952ca8cb5cad98cfa343e6fb131e3bbdc3e Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 18 May 2010 14:43:04 +0000 Subject: sh: add boot code to MMCIF driver header This patch adds a set of MMCIF functions for the romImage boot loader that allows the kernel to be booted directly from an MMC card. Thanks to Jeremy Baker for the initial prototype. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- include/linux/mmc/sh_mmcif.h | 129 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) (limited to 'include/linux/mmc') diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index e079c6beeb9..d4a2ebbdab4 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -68,4 +68,133 @@ extern inline void sh_mmcif_writel(void __iomem *addr, int reg, u32 val) writel(val, addr + reg); } +#define SH_MMCIF_BBS 512 /* boot block size */ + +extern inline void sh_mmcif_boot_cmd_send(void __iomem *base, + unsigned long cmd, unsigned long arg) +{ + sh_mmcif_writel(base, MMCIF_CE_INT, 0); + sh_mmcif_writel(base, MMCIF_CE_ARG, arg); + sh_mmcif_writel(base, MMCIF_CE_CMD_SET, cmd); +} + +extern inline int sh_mmcif_boot_cmd_poll(void __iomem *base, unsigned long mask) +{ + unsigned long tmp; + int cnt; + + for (cnt = 0; cnt < 1000000; cnt++) { + tmp = sh_mmcif_readl(base, MMCIF_CE_INT); + if (tmp & mask) { + sh_mmcif_writel(base, MMCIF_CE_INT, tmp & ~mask); + return 0; + } + } + + return -1; +} + +extern inline int sh_mmcif_boot_cmd(void __iomem *base, + unsigned long cmd, unsigned long arg) +{ + sh_mmcif_boot_cmd_send(base, cmd, arg); + return sh_mmcif_boot_cmd_poll(base, 0x00010000); +} + +extern inline int sh_mmcif_boot_do_read_single(void __iomem *base, + unsigned int block_nr, + unsigned long *buf) +{ + int k; + + /* CMD13 - Status */ + sh_mmcif_boot_cmd(base, 0x0d400000, 0x00010000); + + if (sh_mmcif_readl(base, MMCIF_CE_RESP0) != 0x0900) + return -1; + + /* CMD17 - Read */ + sh_mmcif_boot_cmd(base, 0x11480000, block_nr * SH_MMCIF_BBS); + if (sh_mmcif_boot_cmd_poll(base, 0x00100000) < 0) + return -1; + + for (k = 0; k < (SH_MMCIF_BBS / 4); k++) + buf[k] = sh_mmcif_readl(base, MMCIF_CE_DATA); + + return 0; +} + +extern inline int sh_mmcif_boot_do_read(void __iomem *base, + unsigned long first_block, + unsigned long nr_blocks, + void *buf) +{ + unsigned long k; + int ret = 0; + + /* CMD16 - Set the block size */ + sh_mmcif_boot_cmd(base, 0x10400000, SH_MMCIF_BBS); + + for (k = 0; !ret && k < nr_blocks; k++) + ret = sh_mmcif_boot_do_read_single(base, first_block + k, + buf + (k * SH_MMCIF_BBS)); + + return ret; +} + +extern inline void sh_mmcif_boot_init(void __iomem *base) +{ + unsigned long tmp; + + /* reset */ + tmp = sh_mmcif_readl(base, MMCIF_CE_VERSION); + sh_mmcif_writel(base, MMCIF_CE_VERSION, tmp | 0x80000000); + sh_mmcif_writel(base, MMCIF_CE_VERSION, tmp & ~0x80000000); + + /* byte swap */ + sh_mmcif_writel(base, MMCIF_CE_BUF_ACC, 0x00010000); + + /* Set block size in MMCIF hardware */ + sh_mmcif_writel(base, MMCIF_CE_BLOCK_SET, SH_MMCIF_BBS); + + /* Enable the clock, set it to Bus clock/256 (about 325Khz)*/ + sh_mmcif_writel(base, MMCIF_CE_CLK_CTRL, 0x01072fff); + + /* CMD0 */ + sh_mmcif_boot_cmd(base, 0x00000040, 0); + + /* CMD1 - Get OCR */ + do { + sh_mmcif_boot_cmd(base, 0x01405040, 0x40300000); /* CMD1 */ + } while ((sh_mmcif_readl(base, MMCIF_CE_RESP0) & 0x80000000) + != 0x80000000); + + /* CMD2 - Get CID */ + sh_mmcif_boot_cmd(base, 0x02806040, 0); + + /* CMD3 - Set card relative address */ + sh_mmcif_boot_cmd(base, 0x03400040, 0x00010000); +} + +extern inline void sh_mmcif_boot_slurp(void __iomem *base, + unsigned char *buf, + unsigned long no_bytes) +{ + unsigned long tmp; + + /* In data transfer mode: Set clock to Bus clock/4 (about 20Mhz) */ + sh_mmcif_writel(base, MMCIF_CE_CLK_CTRL, 0x01012fff); + + /* CMD9 - Get CSD */ + sh_mmcif_boot_cmd(base, 0x09806000, 0x00010000); + + /* CMD7 - Select the card */ + sh_mmcif_boot_cmd(base, 0x07400000, 0x00010000); + + tmp = no_bytes / SH_MMCIF_BBS; + tmp += (no_bytes % SH_MMCIF_BBS) ? 1 : 0; + + sh_mmcif_boot_do_read(base, 512, tmp, buf); +} + #endif /* __SH_MMCIF_H__ */ -- cgit v1.2.3-70-g09d2