aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorLucas Stach <dev@lynxeye.de>2015-12-22 13:41:00 -0500
committerUlf Hansson <ulf.hansson@linaro.org>2015-12-28 08:14:56 -0500
commita8e326a911d3ca1b7480aca936956a4e89c4add5 (patch)
treec8dec8b1743b0bc51d0d42173632e18d3a560acf /drivers/mmc
parent918f4cbd4340ddd1eb389cd8efa3b07ac74ec4c0 (diff)
mmc: tegra: implement module external clock change
Allow the the driver to change the clock supplied from the CAR directly, minimizing the need to divide the clock inside the SDMMC module itself. This allows for higher clock speeds than the default 48MHz supplied to the module and is a prerequisite to support DDR signaling modes, where the Tegra host needs to be run with a fixed internal divider of 2 for data to be sampled correctly. (Tegra K1 TRM v03p chapter 29.7.1.1) Also enable the broken preset value quirk as the preset values need to be adapted to the changed clocking. While Tegra114+ allows this through vendor registers, there is no such way for Tegra30. Takes the easy way out and keep things consistent between the different SoC generations by flagging the preset registers as unusable. Signed-off-by: Lucas Stach <dev@lynxeye.de> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c61
1 files changed, 54 insertions, 7 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 368f1b74a525..f11db8337cce 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -49,6 +49,7 @@ struct sdhci_tegra_soc_data {
49struct sdhci_tegra { 49struct sdhci_tegra {
50 const struct sdhci_tegra_soc_data *soc_data; 50 const struct sdhci_tegra_soc_data *soc_data;
51 struct gpio_desc *power_gpio; 51 struct gpio_desc *power_gpio;
52 bool ddr_signaling;
52}; 53};
53 54
54static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 55static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
@@ -143,6 +144,8 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
143 if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104) 144 if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104)
144 misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104; 145 misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
145 sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); 146 sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
147
148 tegra_host->ddr_signaling = false;
146} 149}
147 150
148static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) 151static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
@@ -164,15 +167,54 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
164 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 167 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
165} 168}
166 169
170static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
171{
172 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
173 struct sdhci_tegra *tegra_host = pltfm_host->priv;
174 unsigned long host_clk;
175
176 if (!clock)
177 return;
178
179 host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
180 clk_set_rate(pltfm_host->clk, host_clk);
181 host->max_clk = clk_get_rate(pltfm_host->clk);
182
183 return sdhci_set_clock(host, clock);
184}
185
186static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
187 unsigned timing)
188{
189 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
190 struct sdhci_tegra *tegra_host = pltfm_host->priv;
191
192 if (timing == MMC_TIMING_UHS_DDR50)
193 tegra_host->ddr_signaling = true;
194
195 return sdhci_set_uhs_signaling(host, timing);
196}
197
198static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
199{
200 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
201
202 /*
203 * DDR modes require the host to run at double the card frequency, so
204 * the maximum rate we can support is half of the module input clock.
205 */
206 return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
207}
208
167static const struct sdhci_ops tegra_sdhci_ops = { 209static const struct sdhci_ops tegra_sdhci_ops = {
168 .get_ro = tegra_sdhci_get_ro, 210 .get_ro = tegra_sdhci_get_ro,
169 .read_w = tegra_sdhci_readw, 211 .read_w = tegra_sdhci_readw,
170 .write_l = tegra_sdhci_writel, 212 .write_l = tegra_sdhci_writel,
171 .set_clock = sdhci_set_clock, 213 .set_clock = tegra_sdhci_set_clock,
172 .set_bus_width = tegra_sdhci_set_bus_width, 214 .set_bus_width = tegra_sdhci_set_bus_width,
173 .reset = tegra_sdhci_reset, 215 .reset = tegra_sdhci_reset,
174 .set_uhs_signaling = sdhci_set_uhs_signaling, 216 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
175 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 217 .get_max_clock = tegra_sdhci_get_max_clock,
176}; 218};
177 219
178static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { 220static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
@@ -197,6 +239,7 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
197 SDHCI_QUIRK_NO_HISPD_BIT | 239 SDHCI_QUIRK_NO_HISPD_BIT |
198 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 240 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
199 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 241 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
242 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
200 .ops = &tegra_sdhci_ops, 243 .ops = &tegra_sdhci_ops,
201}; 244};
202 245
@@ -212,11 +255,11 @@ static const struct sdhci_ops tegra114_sdhci_ops = {
212 .read_w = tegra_sdhci_readw, 255 .read_w = tegra_sdhci_readw,
213 .write_w = tegra_sdhci_writew, 256 .write_w = tegra_sdhci_writew,
214 .write_l = tegra_sdhci_writel, 257 .write_l = tegra_sdhci_writel,
215 .set_clock = sdhci_set_clock, 258 .set_clock = tegra_sdhci_set_clock,
216 .set_bus_width = tegra_sdhci_set_bus_width, 259 .set_bus_width = tegra_sdhci_set_bus_width,
217 .reset = tegra_sdhci_reset, 260 .reset = tegra_sdhci_reset,
218 .set_uhs_signaling = sdhci_set_uhs_signaling, 261 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
219 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 262 .get_max_clock = tegra_sdhci_get_max_clock,
220}; 263};
221 264
222static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { 265static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
@@ -226,6 +269,7 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
226 SDHCI_QUIRK_NO_HISPD_BIT | 269 SDHCI_QUIRK_NO_HISPD_BIT |
227 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | 270 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
228 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 271 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
272 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
229 .ops = &tegra114_sdhci_ops, 273 .ops = &tegra114_sdhci_ops,
230}; 274};
231 275
@@ -241,7 +285,9 @@ static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
241 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | 285 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
242 SDHCI_QUIRK_SINGLE_POWER_WRITE | 286 SDHCI_QUIRK_SINGLE_POWER_WRITE |
243 SDHCI_QUIRK_NO_HISPD_BIT | 287 SDHCI_QUIRK_NO_HISPD_BIT |
244 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 288 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
289 SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
290 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
245 .ops = &tegra114_sdhci_ops, 291 .ops = &tegra114_sdhci_ops,
246}; 292};
247 293
@@ -288,6 +334,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
288 rc = -ENOMEM; 334 rc = -ENOMEM;
289 goto err_alloc_tegra_host; 335 goto err_alloc_tegra_host;
290 } 336 }
337 tegra_host->ddr_signaling = false;
291 tegra_host->soc_data = soc_data; 338 tegra_host->soc_data = soc_data;
292 pltfm_host->priv = tegra_host; 339 pltfm_host->priv = tegra_host;
293 340