aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-tegra.c')
-rw-r--r--drivers/mmc/host/sdhci-tegra.c100
1 files changed, 87 insertions, 13 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index cb348569454b..53b26502f6e2 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -19,6 +19,7 @@
19#include <linux/clk.h> 19#include <linux/clk.h>
20#include <linux/io.h> 20#include <linux/io.h>
21#include <linux/of.h> 21#include <linux/of.h>
22#include <linux/of_device.h>
22#include <linux/of_gpio.h> 23#include <linux/of_gpio.h>
23#include <linux/gpio.h> 24#include <linux/gpio.h>
24#include <linux/mmc/card.h> 25#include <linux/mmc/card.h>
@@ -31,6 +32,19 @@
31 32
32#include "sdhci-pltfm.h" 33#include "sdhci-pltfm.h"
33 34
35#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
36#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
37
38struct sdhci_tegra_soc_data {
39 struct sdhci_pltfm_data *pdata;
40 u32 nvquirks;
41};
42
43struct sdhci_tegra {
44 const struct tegra_sdhci_platform_data *plat;
45 const struct sdhci_tegra_soc_data *soc_data;
46};
47
34static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) 48static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
35{ 49{
36 u32 val; 50 u32 val;
@@ -46,7 +60,12 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
46 60
47static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 61static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
48{ 62{
49 if (unlikely(reg == SDHCI_HOST_VERSION)) { 63 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
64 struct sdhci_tegra *tegra_host = pltfm_host->priv;
65 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
66
67 if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
68 (reg == SDHCI_HOST_VERSION))) {
50 /* Erratum: Version register is invalid in HW. */ 69 /* Erratum: Version register is invalid in HW. */
51 return SDHCI_SPEC_200; 70 return SDHCI_SPEC_200;
52 } 71 }
@@ -56,6 +75,10 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
56 75
57static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 76static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
58{ 77{
78 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
79 struct sdhci_tegra *tegra_host = pltfm_host->priv;
80 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
81
59 /* Seems like we're getting spurious timeout and crc errors, so 82 /* Seems like we're getting spurious timeout and crc errors, so
60 * disable signalling of them. In case of real errors software 83 * disable signalling of them. In case of real errors software
61 * timers should take care of eventually detecting them. 84 * timers should take care of eventually detecting them.
@@ -65,7 +88,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
65 88
66 writel(val, host->ioaddr + reg); 89 writel(val, host->ioaddr + reg);
67 90
68 if (unlikely(reg == SDHCI_INT_ENABLE)) { 91 if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
92 (reg == SDHCI_INT_ENABLE))) {
69 /* Erratum: Must enable block gap interrupt detection */ 93 /* Erratum: Must enable block gap interrupt detection */
70 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 94 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
71 if (val & SDHCI_INT_CARD_INT) 95 if (val & SDHCI_INT_CARD_INT)
@@ -76,10 +100,11 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
76 } 100 }
77} 101}
78 102
79static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) 103static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
80{ 104{
81 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci); 105 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
82 struct tegra_sdhci_platform_data *plat = pltfm_host->priv; 106 struct sdhci_tegra *tegra_host = pltfm_host->priv;
107 const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
83 108
84 if (!gpio_is_valid(plat->wp_gpio)) 109 if (!gpio_is_valid(plat->wp_gpio))
85 return -1; 110 return -1;
@@ -98,7 +123,8 @@ static irqreturn_t carddetect_irq(int irq, void *data)
98static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) 123static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
99{ 124{
100 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 125 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
101 struct tegra_sdhci_platform_data *plat = pltfm_host->priv; 126 struct sdhci_tegra *tegra_host = pltfm_host->priv;
127 const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
102 u32 ctrl; 128 u32 ctrl;
103 129
104 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 130 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
@@ -124,16 +150,44 @@ static struct sdhci_ops tegra_sdhci_ops = {
124 .platform_8bit_width = tegra_sdhci_8bit, 150 .platform_8bit_width = tegra_sdhci_8bit,
125}; 151};
126 152
127static struct sdhci_pltfm_data sdhci_tegra_pdata = { 153#ifdef CONFIG_ARCH_TEGRA_2x_SOC
154static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
155 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
156 SDHCI_QUIRK_SINGLE_POWER_WRITE |
157 SDHCI_QUIRK_NO_HISPD_BIT |
158 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
159 .ops = &tegra_sdhci_ops,
160};
161
162static struct sdhci_tegra_soc_data soc_data_tegra20 = {
163 .pdata = &sdhci_tegra20_pdata,
164 .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
165 NVQUIRK_ENABLE_BLOCK_GAP_DET,
166};
167#endif
168
169#ifdef CONFIG_ARCH_TEGRA_3x_SOC
170static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
128 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 171 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
172 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
129 SDHCI_QUIRK_SINGLE_POWER_WRITE | 173 SDHCI_QUIRK_SINGLE_POWER_WRITE |
130 SDHCI_QUIRK_NO_HISPD_BIT | 174 SDHCI_QUIRK_NO_HISPD_BIT |
131 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 175 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
132 .ops = &tegra_sdhci_ops, 176 .ops = &tegra_sdhci_ops,
133}; 177};
134 178
179static struct sdhci_tegra_soc_data soc_data_tegra30 = {
180 .pdata = &sdhci_tegra30_pdata,
181};
182#endif
183
135static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = { 184static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
136 { .compatible = "nvidia,tegra20-sdhci", }, 185#ifdef CONFIG_ARCH_TEGRA_3x_SOC
186 { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
187#endif
188#ifdef CONFIG_ARCH_TEGRA_2x_SOC
189 { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
190#endif
137 {} 191 {}
138}; 192};
139MODULE_DEVICE_TABLE(of, sdhci_dt_ids); 193MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
@@ -164,13 +218,22 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
164 218
165static int __devinit sdhci_tegra_probe(struct platform_device *pdev) 219static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
166{ 220{
221 const struct of_device_id *match;
222 const struct sdhci_tegra_soc_data *soc_data;
223 struct sdhci_host *host;
167 struct sdhci_pltfm_host *pltfm_host; 224 struct sdhci_pltfm_host *pltfm_host;
168 struct tegra_sdhci_platform_data *plat; 225 struct tegra_sdhci_platform_data *plat;
169 struct sdhci_host *host; 226 struct sdhci_tegra *tegra_host;
170 struct clk *clk; 227 struct clk *clk;
171 int rc; 228 int rc;
172 229
173 host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata); 230 match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
231 if (match)
232 soc_data = match->data;
233 else
234 soc_data = &soc_data_tegra20;
235
236 host = sdhci_pltfm_init(pdev, soc_data->pdata);
174 if (IS_ERR(host)) 237 if (IS_ERR(host))
175 return PTR_ERR(host); 238 return PTR_ERR(host);
176 239
@@ -187,7 +250,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
187 goto err_no_plat; 250 goto err_no_plat;
188 } 251 }
189 252
190 pltfm_host->priv = plat; 253 tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
254 if (!tegra_host) {
255 dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
256 rc = -ENOMEM;
257 goto err_no_plat;
258 }
259
260 tegra_host->plat = plat;
261 tegra_host->soc_data = soc_data;
262
263 pltfm_host->priv = tegra_host;
191 264
192 if (gpio_is_valid(plat->power_gpio)) { 265 if (gpio_is_valid(plat->power_gpio)) {
193 rc = gpio_request(plat->power_gpio, "sdhci_power"); 266 rc = gpio_request(plat->power_gpio, "sdhci_power");
@@ -283,7 +356,8 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
283{ 356{
284 struct sdhci_host *host = platform_get_drvdata(pdev); 357 struct sdhci_host *host = platform_get_drvdata(pdev);
285 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 358 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
286 struct tegra_sdhci_platform_data *plat = pltfm_host->priv; 359 struct sdhci_tegra *tegra_host = pltfm_host->priv;
360 const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
287 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); 361 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
288 362
289 sdhci_remove_host(host, dead); 363 sdhci_remove_host(host, dead);
@@ -326,5 +400,5 @@ static struct platform_driver sdhci_tegra_driver = {
326module_platform_driver(sdhci_tegra_driver); 400module_platform_driver(sdhci_tegra_driver);
327 401
328MODULE_DESCRIPTION("SDHCI driver for Tegra"); 402MODULE_DESCRIPTION("SDHCI driver for Tegra");
329MODULE_AUTHOR(" Google, Inc."); 403MODULE_AUTHOR("Google, Inc.");
330MODULE_LICENSE("GPL v2"); 404MODULE_LICENSE("GPL v2");