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 | |
| 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>
| -rw-r--r-- | drivers/mmc/host/sdhci.c | 109 | ||||
| -rw-r--r-- | drivers/mmc/host/sdhci.h | 6 | ||||
| -rw-r--r-- | include/linux/mmc/sdhci.h | 6 |
3 files changed, 118 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 | ||
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index a88a799ed7c3..e902618d68f4 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h | |||
| @@ -112,6 +112,7 @@ struct sdhci_host { | |||
| 112 | #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ | 112 | #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ |
| 113 | #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ | 113 | #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ |
| 114 | #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */ | 114 | #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */ |
| 115 | #define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */ | ||
| 115 | 116 | ||
| 116 | unsigned int version; /* SDHCI spec. version */ | 117 | unsigned int version; /* SDHCI spec. version */ |
| 117 | 118 | ||
| @@ -152,6 +153,11 @@ struct sdhci_host { | |||
| 152 | wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ | 153 | wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ |
| 153 | unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ | 154 | unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ |
| 154 | 155 | ||
| 156 | unsigned int tuning_count; /* Timer count for re-tuning */ | ||
| 157 | unsigned int tuning_mode; /* Re-tuning mode supported by host */ | ||
| 158 | #define SDHCI_TUNING_MODE_1 0 | ||
| 159 | struct timer_list tuning_timer; /* Timer for tuning */ | ||
| 160 | |||
| 155 | unsigned long private[0] ____cacheline_aligned; | 161 | unsigned long private[0] ____cacheline_aligned; |
| 156 | }; | 162 | }; |
| 157 | #endif /* __SDHCI_H */ | 163 | #endif /* __SDHCI_H */ |
