diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2011-08-29 09:42:11 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-10-26 16:32:05 -0400 |
commit | b2499518b5ad7e28bb3ed348fd3f370eeb1e36c0 (patch) | |
tree | c4c9597d0631554bffeecbcc6a2a7ec3037ea78c /drivers/mmc | |
parent | 8e3336b1e4378f7d205af9b25dcc9e645c8a9609 (diff) |
mmc: core: add eMMC hardware reset support
eMMC's may have a hardware reset line. This patch provides a
host controller operation to implement hardware reset and
a function to reset and reinitialize the card. Also, for MMC,
the reset is always performed before initialization.
The host must set the new host capability MMC_CAP_HW_RESET
to enable hardware reset.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 94 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 4 |
2 files changed, 97 insertions, 1 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0b4c2ed22bce..da6bd95fa4bb 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -1776,6 +1776,94 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) | |||
1776 | } | 1776 | } |
1777 | EXPORT_SYMBOL(mmc_set_blocklen); | 1777 | EXPORT_SYMBOL(mmc_set_blocklen); |
1778 | 1778 | ||
1779 | static void mmc_hw_reset_for_init(struct mmc_host *host) | ||
1780 | { | ||
1781 | if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) | ||
1782 | return; | ||
1783 | mmc_host_clk_hold(host); | ||
1784 | host->ops->hw_reset(host); | ||
1785 | mmc_host_clk_release(host); | ||
1786 | } | ||
1787 | |||
1788 | int mmc_can_reset(struct mmc_card *card) | ||
1789 | { | ||
1790 | u8 rst_n_function; | ||
1791 | |||
1792 | if (!mmc_card_mmc(card)) | ||
1793 | return 0; | ||
1794 | rst_n_function = card->ext_csd.rst_n_function; | ||
1795 | if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) | ||
1796 | return 0; | ||
1797 | return 1; | ||
1798 | } | ||
1799 | EXPORT_SYMBOL(mmc_can_reset); | ||
1800 | |||
1801 | static int mmc_do_hw_reset(struct mmc_host *host, int check) | ||
1802 | { | ||
1803 | struct mmc_card *card = host->card; | ||
1804 | |||
1805 | if (!host->bus_ops->power_restore) | ||
1806 | return -EOPNOTSUPP; | ||
1807 | |||
1808 | if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) | ||
1809 | return -EOPNOTSUPP; | ||
1810 | |||
1811 | if (!card) | ||
1812 | return -EINVAL; | ||
1813 | |||
1814 | if (!mmc_can_reset(card)) | ||
1815 | return -EOPNOTSUPP; | ||
1816 | |||
1817 | mmc_host_clk_hold(host); | ||
1818 | mmc_set_clock(host, host->f_init); | ||
1819 | |||
1820 | host->ops->hw_reset(host); | ||
1821 | |||
1822 | /* If the reset has happened, then a status command will fail */ | ||
1823 | if (check) { | ||
1824 | struct mmc_command cmd = {0}; | ||
1825 | int err; | ||
1826 | |||
1827 | cmd.opcode = MMC_SEND_STATUS; | ||
1828 | if (!mmc_host_is_spi(card->host)) | ||
1829 | cmd.arg = card->rca << 16; | ||
1830 | cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; | ||
1831 | err = mmc_wait_for_cmd(card->host, &cmd, 0); | ||
1832 | if (!err) { | ||
1833 | mmc_host_clk_release(host); | ||
1834 | return -ENOSYS; | ||
1835 | } | ||
1836 | } | ||
1837 | |||
1838 | host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR); | ||
1839 | if (mmc_host_is_spi(host)) { | ||
1840 | host->ios.chip_select = MMC_CS_HIGH; | ||
1841 | host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; | ||
1842 | } else { | ||
1843 | host->ios.chip_select = MMC_CS_DONTCARE; | ||
1844 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; | ||
1845 | } | ||
1846 | host->ios.bus_width = MMC_BUS_WIDTH_1; | ||
1847 | host->ios.timing = MMC_TIMING_LEGACY; | ||
1848 | mmc_set_ios(host); | ||
1849 | |||
1850 | mmc_host_clk_release(host); | ||
1851 | |||
1852 | return host->bus_ops->power_restore(host); | ||
1853 | } | ||
1854 | |||
1855 | int mmc_hw_reset(struct mmc_host *host) | ||
1856 | { | ||
1857 | return mmc_do_hw_reset(host, 0); | ||
1858 | } | ||
1859 | EXPORT_SYMBOL(mmc_hw_reset); | ||
1860 | |||
1861 | int mmc_hw_reset_check(struct mmc_host *host) | ||
1862 | { | ||
1863 | return mmc_do_hw_reset(host, 1); | ||
1864 | } | ||
1865 | EXPORT_SYMBOL(mmc_hw_reset_check); | ||
1866 | |||
1779 | static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) | 1867 | static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) |
1780 | { | 1868 | { |
1781 | host->f_init = freq; | 1869 | host->f_init = freq; |
@@ -1787,6 +1875,12 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) | |||
1787 | mmc_power_up(host); | 1875 | mmc_power_up(host); |
1788 | 1876 | ||
1789 | /* | 1877 | /* |
1878 | * Some eMMCs (with VCCQ always on) may not be reset after power up, so | ||
1879 | * do a hardware reset if possible. | ||
1880 | */ | ||
1881 | mmc_hw_reset_for_init(host); | ||
1882 | |||
1883 | /* | ||
1790 | * sdio_reset sends CMD52 to reset card. Since we do not know | 1884 | * sdio_reset sends CMD52 to reset card. Since we do not know |
1791 | * if the card is being re-initialized, just send it. CMD52 | 1885 | * if the card is being re-initialized, just send it. CMD52 |
1792 | * should be ignored by SD/eMMC cards. | 1886 | * should be ignored by SD/eMMC cards. |
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index b74e6f14b3ac..7adc30da8366 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -402,8 +402,10 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) | |||
402 | ext_csd[EXT_CSD_TRIM_MULT]; | 402 | ext_csd[EXT_CSD_TRIM_MULT]; |
403 | } | 403 | } |
404 | 404 | ||
405 | if (card->ext_csd.rev >= 5) | 405 | if (card->ext_csd.rev >= 5) { |
406 | card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; | 406 | card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM]; |
407 | card->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION]; | ||
408 | } | ||
407 | 409 | ||
408 | if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) | 410 | if (ext_csd[EXT_CSD_ERASED_MEM_CONT]) |
409 | card->erased_byte = 0xFF; | 411 | card->erased_byte = 0xFF; |