diff options
author | Arindam Nath <arindam.nath@amd.com> | 2011-05-05 02:49:07 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-05-24 23:53:48 -0400 |
commit | cf2b5eea1ea0ff9b3184bc6771bcb93a9fdcd1d9 (patch) | |
tree | 36288f760fb7556952f8365c3c0cad6b445b04f6 /drivers/mmc | |
parent | c3ed3877625f10d600b0eca2ca48a68c46aed660 (diff) |
mmc: sdhci: add support for retuning mode 1
Host Controller v3.00 can support retuning modes 1,2 or 3 depending on
the bits 46-47 of the Capabilities register. Also, the timer count for
retuning is indicated by bits 40-43 of the same register. We initialize
timer_list for retuning the first time we execute tuning procedure. This
condition is indicated by SDHCI_NEEDS_RETUNING not being set. Since
retuning mode 1 sets a limit of 4MB on the maximum data length, we set
max_blk_count appropriately. Once the tuning timer expires, we set
SDHCI_NEEDS_RETUNING flag, and if the flag is set, we execute tuning
procedure before sending the next command. We need to restore mmc_request
structure after executing retuning procedure since host->mrq is used
inside the procedure to send CMD19. We also disable and re-enable this
flag during suspend and resume respectively, as per the spec v3.00.
Tested by Zhangfei Gao with a Toshiba uhs card and general hs card,
on mmp2 in SDMA mode.
Signed-off-by: Arindam Nath <arindam.nath@amd.com>
Reviewed-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.com>
Acked-by: Zhangfei Gao <zhangfei.gao@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 109 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 6 |
2 files changed, 112 insertions, 3 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 07cca557c4b5..fa3301644b15 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
@@ -46,6 +46,8 @@ static void sdhci_finish_data(struct sdhci_host *); | |||
46 | 46 | ||
47 | static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); | 47 | static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); |
48 | static void sdhci_finish_command(struct sdhci_host *); | 48 | static void sdhci_finish_command(struct sdhci_host *); |
49 | static int sdhci_execute_tuning(struct mmc_host *mmc); | ||
50 | static void sdhci_tuning_timer(unsigned long data); | ||
49 | 51 | ||
50 | static void sdhci_dumpregs(struct sdhci_host *host) | 52 | static void sdhci_dumpregs(struct sdhci_host *host) |
51 | { | 53 | { |
@@ -1206,8 +1208,27 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
1206 | if (!present || host->flags & SDHCI_DEVICE_DEAD) { | 1208 | if (!present || host->flags & SDHCI_DEVICE_DEAD) { |
1207 | host->mrq->cmd->error = -ENOMEDIUM; | 1209 | host->mrq->cmd->error = -ENOMEDIUM; |
1208 | tasklet_schedule(&host->finish_tasklet); | 1210 | tasklet_schedule(&host->finish_tasklet); |
1209 | } else | 1211 | } else { |
1212 | u32 present_state; | ||
1213 | |||
1214 | present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); | ||
1215 | /* | ||
1216 | * Check if the re-tuning timer has already expired and there | ||
1217 | * is no on-going data transfer. If so, we need to execute | ||
1218 | * tuning procedure before sending command. | ||
1219 | */ | ||
1220 | if ((host->flags & SDHCI_NEEDS_RETUNING) && | ||
1221 | !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { | ||
1222 | spin_unlock_irqrestore(&host->lock, flags); | ||
1223 | sdhci_execute_tuning(mmc); | ||
1224 | spin_lock_irqsave(&host->lock, flags); | ||
1225 | |||
1226 | /* Restore original mmc_request structure */ | ||
1227 | host->mrq = mrq; | ||
1228 | } | ||
1229 | |||
1210 | sdhci_send_command(host, mrq->cmd); | 1230 | sdhci_send_command(host, mrq->cmd); |
1231 | } | ||
1211 | 1232 | ||
1212 | mmiowb(); | 1233 | mmiowb(); |
1213 | spin_unlock_irqrestore(&host->lock, flags); | 1234 | spin_unlock_irqrestore(&host->lock, flags); |
@@ -1673,6 +1694,37 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) | |||
1673 | } | 1694 | } |
1674 | 1695 | ||
1675 | out: | 1696 | out: |
1697 | /* | ||
1698 | * If this is the very first time we are here, we start the retuning | ||
1699 | * timer. Since only during the first time, SDHCI_NEEDS_RETUNING | ||
1700 | * flag won't be set, we check this condition before actually starting | ||
1701 | * the timer. | ||
1702 | */ | ||
1703 | if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count && | ||
1704 | (host->tuning_mode == SDHCI_TUNING_MODE_1)) { | ||
1705 | mod_timer(&host->tuning_timer, jiffies + | ||
1706 | host->tuning_count * HZ); | ||
1707 | /* Tuning mode 1 limits the maximum data length to 4MB */ | ||
1708 | mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size; | ||
1709 | } else { | ||
1710 | host->flags &= ~SDHCI_NEEDS_RETUNING; | ||
1711 | /* Reload the new initial value for timer */ | ||
1712 | if (host->tuning_mode == SDHCI_TUNING_MODE_1) | ||
1713 | mod_timer(&host->tuning_timer, jiffies + | ||
1714 | host->tuning_count * HZ); | ||
1715 | } | ||
1716 | |||
1717 | /* | ||
1718 | * In case tuning fails, host controllers which support re-tuning can | ||
1719 | * try tuning again at a later time, when the re-tuning timer expires. | ||
1720 | * So for these controllers, we return 0. Since there might be other | ||
1721 | * controllers who do not have this capability, we return error for | ||
1722 | * them. | ||
1723 | */ | ||
1724 | if (err && host->tuning_count && | ||
1725 | host->tuning_mode == SDHCI_TUNING_MODE_1) | ||
1726 | err = 0; | ||
1727 | |||
1676 | sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); | 1728 | sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); |
1677 | spin_unlock(&host->lock); | 1729 | spin_unlock(&host->lock); |
1678 | enable_irq(host->irq); | 1730 | enable_irq(host->irq); |
@@ -1775,6 +1827,9 @@ static void sdhci_tasklet_finish(unsigned long param) | |||
1775 | 1827 | ||
1776 | del_timer(&host->timer); | 1828 | del_timer(&host->timer); |
1777 | 1829 | ||
1830 | if (host->version >= SDHCI_SPEC_300) | ||
1831 | del_timer(&host->tuning_timer); | ||
1832 | |||
1778 | mrq = host->mrq; | 1833 | mrq = host->mrq; |
1779 | 1834 | ||
1780 | /* | 1835 | /* |
@@ -1848,6 +1903,20 @@ static void sdhci_timeout_timer(unsigned long data) | |||
1848 | spin_unlock_irqrestore(&host->lock, flags); | 1903 | spin_unlock_irqrestore(&host->lock, flags); |
1849 | } | 1904 | } |
1850 | 1905 | ||
1906 | static void sdhci_tuning_timer(unsigned long data) | ||
1907 | { | ||
1908 | struct sdhci_host *host; | ||
1909 | unsigned long flags; | ||
1910 | |||
1911 | host = (struct sdhci_host *)data; | ||
1912 | |||
1913 | spin_lock_irqsave(&host->lock, flags); | ||
1914 | |||
1915 | host->flags |= SDHCI_NEEDS_RETUNING; | ||
1916 | |||
1917 | spin_unlock_irqrestore(&host->lock, flags); | ||
1918 | } | ||
1919 | |||
1851 | /*****************************************************************************\ | 1920 | /*****************************************************************************\ |
1852 | * * | 1921 | * * |
1853 | * Interrupt handling * | 1922 | * Interrupt handling * |
@@ -2122,6 +2191,14 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) | |||
2122 | 2191 | ||
2123 | sdhci_disable_card_detection(host); | 2192 | sdhci_disable_card_detection(host); |
2124 | 2193 | ||
2194 | /* Disable tuning since we are suspending */ | ||
2195 | if (host->version >= SDHCI_SPEC_300 && host->tuning_count && | ||
2196 | host->tuning_mode == SDHCI_TUNING_MODE_1) { | ||
2197 | host->flags &= ~SDHCI_NEEDS_RETUNING; | ||
2198 | mod_timer(&host->tuning_timer, jiffies + | ||
2199 | host->tuning_count * HZ); | ||
2200 | } | ||
2201 | |||
2125 | ret = mmc_suspend_host(host->mmc); | 2202 | ret = mmc_suspend_host(host->mmc); |
2126 | if (ret) | 2203 | if (ret) |
2127 | return ret; | 2204 | return ret; |
@@ -2163,6 +2240,11 @@ int sdhci_resume_host(struct sdhci_host *host) | |||
2163 | ret = mmc_resume_host(host->mmc); | 2240 | ret = mmc_resume_host(host->mmc); |
2164 | sdhci_enable_card_detection(host); | 2241 | sdhci_enable_card_detection(host); |
2165 | 2242 | ||
2243 | /* Set the re-tuning expiration flag */ | ||
2244 | if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && | ||
2245 | (host->tuning_mode == SDHCI_TUNING_MODE_1)) | ||
2246 | host->flags |= SDHCI_NEEDS_RETUNING; | ||
2247 | |||
2166 | return ret; | 2248 | return ret; |
2167 | } | 2249 | } |
2168 | 2250 | ||
@@ -2414,6 +2496,21 @@ int sdhci_add_host(struct sdhci_host *host) | |||
2414 | if (caps[1] & SDHCI_DRIVER_TYPE_D) | 2496 | if (caps[1] & SDHCI_DRIVER_TYPE_D) |
2415 | mmc->caps |= MMC_CAP_DRIVER_TYPE_D; | 2497 | mmc->caps |= MMC_CAP_DRIVER_TYPE_D; |
2416 | 2498 | ||
2499 | /* Initial value for re-tuning timer count */ | ||
2500 | host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> | ||
2501 | SDHCI_RETUNING_TIMER_COUNT_SHIFT; | ||
2502 | |||
2503 | /* | ||
2504 | * In case Re-tuning Timer is not disabled, the actual value of | ||
2505 | * re-tuning timer will be 2 ^ (n - 1). | ||
2506 | */ | ||
2507 | if (host->tuning_count) | ||
2508 | host->tuning_count = 1 << (host->tuning_count - 1); | ||
2509 | |||
2510 | /* Re-tuning mode supported by the Host Controller */ | ||
2511 | host->tuning_mode = (caps[1] & SDHCI_RETUNING_MODE_MASK) >> | ||
2512 | SDHCI_RETUNING_MODE_SHIFT; | ||
2513 | |||
2417 | ocr_avail = 0; | 2514 | ocr_avail = 0; |
2418 | /* | 2515 | /* |
2419 | * According to SD Host Controller spec v3.00, if the Host System | 2516 | * According to SD Host Controller spec v3.00, if the Host System |
@@ -2559,9 +2656,15 @@ int sdhci_add_host(struct sdhci_host *host) | |||
2559 | 2656 | ||
2560 | setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); | 2657 | setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); |
2561 | 2658 | ||
2562 | if (host->version >= SDHCI_SPEC_300) | 2659 | if (host->version >= SDHCI_SPEC_300) { |
2563 | init_waitqueue_head(&host->buf_ready_int); | 2660 | init_waitqueue_head(&host->buf_ready_int); |
2564 | 2661 | ||
2662 | /* Initialize re-tuning timer */ | ||
2663 | init_timer(&host->tuning_timer); | ||
2664 | host->tuning_timer.data = (unsigned long)host; | ||
2665 | host->tuning_timer.function = sdhci_tuning_timer; | ||
2666 | } | ||
2667 | |||
2565 | ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, | 2668 | ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, |
2566 | mmc_hostname(mmc), host); | 2669 | mmc_hostname(mmc), host); |
2567 | if (ret) | 2670 | if (ret) |
@@ -2655,6 +2758,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) | |||
2655 | free_irq(host->irq, host); | 2758 | free_irq(host->irq, host); |
2656 | 2759 | ||
2657 | del_timer_sync(&host->timer); | 2760 | del_timer_sync(&host->timer); |
2761 | if (host->version >= SDHCI_SPEC_300) | ||
2762 | del_timer_sync(&host->tuning_timer); | ||
2658 | 2763 | ||
2659 | tasklet_kill(&host->card_tasklet); | 2764 | tasklet_kill(&host->card_tasklet); |
2660 | tasklet_kill(&host->finish_tasklet); | 2765 | tasklet_kill(&host->finish_tasklet); |
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 6b0a0ee9ac6e..8ea11b7cf89a 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h | |||
@@ -191,7 +191,11 @@ | |||
191 | #define SDHCI_DRIVER_TYPE_A 0x00000010 | 191 | #define SDHCI_DRIVER_TYPE_A 0x00000010 |
192 | #define SDHCI_DRIVER_TYPE_C 0x00000020 | 192 | #define SDHCI_DRIVER_TYPE_C 0x00000020 |
193 | #define SDHCI_DRIVER_TYPE_D 0x00000040 | 193 | #define SDHCI_DRIVER_TYPE_D 0x00000040 |
194 | #define SDHCI_USE_SDR50_TUNING 0x00002000 | 194 | #define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00 |
195 | #define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8 | ||
196 | #define SDHCI_USE_SDR50_TUNING 0x00002000 | ||
197 | #define SDHCI_RETUNING_MODE_MASK 0x0000C000 | ||
198 | #define SDHCI_RETUNING_MODE_SHIFT 14 | ||
195 | #define SDHCI_CLOCK_MUL_MASK 0x00FF0000 | 199 | #define SDHCI_CLOCK_MUL_MASK 0x00FF0000 |
196 | #define SDHCI_CLOCK_MUL_SHIFT 16 | 200 | #define SDHCI_CLOCK_MUL_SHIFT 16 |
197 | 201 | ||