aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2012-02-01 18:30:55 -0500
committerChris Ball <cjb@laptop.org>2012-03-25 19:33:45 -0400
commit3e44a1a7d22cdc44c98569fedbd993985bfd64d3 (patch)
treed6dac36ff0d819e5b2b436acb804f320f8e995aa /drivers
parent192b5372421766f62fce20d2db2deb19a58e2cfc (diff)
mmc: sdhci-tegra: Explicitly support Tegra30
Tegra30 differs from Tegra20 in a number of ways. This patch implements a minimal set of differences in order to get the Cardhu board's SD slot and eMMC working. Given the diffs between the mainline sdhci-tegra.c and our downstream versions, I expect we'll eventually need to add many more differences, hence the seemingly heavy-weight addition of the soc_data structure. * Tegra30 doesn't need to override register access to SDHCI_HOST_VERSION or SDHCI_INT_ENABLE. * Tegra30 needs quirk SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers')
-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 78a36eba4df0..ccbca0ba8c39 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>
@@ -32,6 +33,19 @@
32 33
33#include "sdhci-pltfm.h" 34#include "sdhci-pltfm.h"
34 35
36#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
37#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
38
39struct sdhci_tegra_soc_data {
40 struct sdhci_pltfm_data *pdata;
41 u32 nvquirks;
42};
43
44struct sdhci_tegra {
45 const struct tegra_sdhci_platform_data *plat;
46 const struct sdhci_tegra_soc_data *soc_data;
47};
48
35static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) 49static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
36{ 50{
37 u32 val; 51 u32 val;
@@ -47,7 +61,12 @@ static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
47 61
48static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 62static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
49{ 63{
50 if (unlikely(reg == SDHCI_HOST_VERSION)) { 64 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
65 struct sdhci_tegra *tegra_host = pltfm_host->priv;
66 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
67
68 if (unlikely((soc_data->nvquirks & NVQUIRK_FORCE_SDHCI_SPEC_200) &&
69 (reg == SDHCI_HOST_VERSION))) {
51 /* Erratum: Version register is invalid in HW. */ 70 /* Erratum: Version register is invalid in HW. */
52 return SDHCI_SPEC_200; 71 return SDHCI_SPEC_200;
53 } 72 }
@@ -57,6 +76,10 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
57 76
58static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 77static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
59{ 78{
79 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
80 struct sdhci_tegra *tegra_host = pltfm_host->priv;
81 const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
82
60 /* Seems like we're getting spurious timeout and crc errors, so 83 /* Seems like we're getting spurious timeout and crc errors, so
61 * disable signalling of them. In case of real errors software 84 * disable signalling of them. In case of real errors software
62 * timers should take care of eventually detecting them. 85 * timers should take care of eventually detecting them.
@@ -66,7 +89,8 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
66 89
67 writel(val, host->ioaddr + reg); 90 writel(val, host->ioaddr + reg);
68 91
69 if (unlikely(reg == SDHCI_INT_ENABLE)) { 92 if (unlikely((soc_data->nvquirks & NVQUIRK_ENABLE_BLOCK_GAP_DET) &&
93 (reg == SDHCI_INT_ENABLE))) {
70 /* Erratum: Must enable block gap interrupt detection */ 94 /* Erratum: Must enable block gap interrupt detection */
71 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 95 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL);
72 if (val & SDHCI_INT_CARD_INT) 96 if (val & SDHCI_INT_CARD_INT)
@@ -77,10 +101,11 @@ static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
77 } 101 }
78} 102}
79 103
80static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) 104static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
81{ 105{
82 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci); 106 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
83 struct tegra_sdhci_platform_data *plat = pltfm_host->priv; 107 struct sdhci_tegra *tegra_host = pltfm_host->priv;
108 const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
84 109
85 if (!gpio_is_valid(plat->wp_gpio)) 110 if (!gpio_is_valid(plat->wp_gpio))
86 return -1; 111 return -1;
@@ -99,7 +124,8 @@ static irqreturn_t carddetect_irq(int irq, void *data)
99static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) 124static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width)
100{ 125{
101 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 126 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
102 struct tegra_sdhci_platform_data *plat = pltfm_host->priv; 127 struct sdhci_tegra *tegra_host = pltfm_host->priv;
128 const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
103 u32 ctrl; 129 u32 ctrl;
104 130
105 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 131 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
@@ -125,16 +151,44 @@ static struct sdhci_ops tegra_sdhci_ops = {
125 .platform_8bit_width = tegra_sdhci_8bit, 151 .platform_8bit_width = tegra_sdhci_8bit,
126}; 152};
127 153
128static struct sdhci_pltfm_data sdhci_tegra_pdata = { 154#ifdef CONFIG_ARCH_TEGRA_2x_SOC
155static struct sdhci_pltfm_data sdhci_tegra20_pdata = {
156 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
157 SDHCI_QUIRK_SINGLE_POWER_WRITE |
158 SDHCI_QUIRK_NO_HISPD_BIT |
159 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
160 .ops = &tegra_sdhci_ops,
161};
162
163static struct sdhci_tegra_soc_data soc_data_tegra20 = {
164 .pdata = &sdhci_tegra20_pdata,
165 .nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
166 NVQUIRK_ENABLE_BLOCK_GAP_DET,
167};
168#endif
169
170#ifdef CONFIG_ARCH_TEGRA_3x_SOC
171static struct sdhci_pltfm_data sdhci_tegra30_pdata = {
129 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 172 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
173 SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
130 SDHCI_QUIRK_SINGLE_POWER_WRITE | 174 SDHCI_QUIRK_SINGLE_POWER_WRITE |
131 SDHCI_QUIRK_NO_HISPD_BIT | 175 SDHCI_QUIRK_NO_HISPD_BIT |
132 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 176 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
133 .ops = &tegra_sdhci_ops, 177 .ops = &tegra_sdhci_ops,
134}; 178};
135 179
180static struct sdhci_tegra_soc_data soc_data_tegra30 = {
181 .pdata = &sdhci_tegra30_pdata,
182};
183#endif
184
136static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = { 185static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = {
137 { .compatible = "nvidia,tegra20-sdhci", }, 186#ifdef CONFIG_ARCH_TEGRA_3x_SOC
187 { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
188#endif
189#ifdef CONFIG_ARCH_TEGRA_2x_SOC
190 { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },
191#endif
138 {} 192 {}
139}; 193};
140MODULE_DEVICE_TABLE(of, sdhci_dt_ids); 194MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
@@ -165,13 +219,22 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata(
165 219
166static int __devinit sdhci_tegra_probe(struct platform_device *pdev) 220static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
167{ 221{
222 const struct of_device_id *match;
223 const struct sdhci_tegra_soc_data *soc_data;
224 struct sdhci_host *host;
168 struct sdhci_pltfm_host *pltfm_host; 225 struct sdhci_pltfm_host *pltfm_host;
169 struct tegra_sdhci_platform_data *plat; 226 struct tegra_sdhci_platform_data *plat;
170 struct sdhci_host *host; 227 struct sdhci_tegra *tegra_host;
171 struct clk *clk; 228 struct clk *clk;
172 int rc; 229 int rc;
173 230
174 host = sdhci_pltfm_init(pdev, &sdhci_tegra_pdata); 231 match = of_match_device(sdhci_tegra_dt_match, &pdev->dev);
232 if (match)
233 soc_data = match->data;
234 else
235 soc_data = &soc_data_tegra20;
236
237 host = sdhci_pltfm_init(pdev, soc_data->pdata);
175 if (IS_ERR(host)) 238 if (IS_ERR(host))
176 return PTR_ERR(host); 239 return PTR_ERR(host);
177 240
@@ -188,7 +251,17 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
188 goto err_no_plat; 251 goto err_no_plat;
189 } 252 }
190 253
191 pltfm_host->priv = plat; 254 tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
255 if (!tegra_host) {
256 dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
257 rc = -ENOMEM;
258 goto err_no_plat;
259 }
260
261 tegra_host->plat = plat;
262 tegra_host->soc_data = soc_data;
263
264 pltfm_host->priv = tegra_host;
192 265
193 if (gpio_is_valid(plat->power_gpio)) { 266 if (gpio_is_valid(plat->power_gpio)) {
194 rc = gpio_request(plat->power_gpio, "sdhci_power"); 267 rc = gpio_request(plat->power_gpio, "sdhci_power");
@@ -284,7 +357,8 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev)
284{ 357{
285 struct sdhci_host *host = platform_get_drvdata(pdev); 358 struct sdhci_host *host = platform_get_drvdata(pdev);
286 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 359 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
287 struct tegra_sdhci_platform_data *plat = pltfm_host->priv; 360 struct sdhci_tegra *tegra_host = pltfm_host->priv;
361 const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
288 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); 362 int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
289 363
290 sdhci_remove_host(host, dead); 364 sdhci_remove_host(host, dead);
@@ -327,5 +401,5 @@ static struct platform_driver sdhci_tegra_driver = {
327module_platform_driver(sdhci_tegra_driver); 401module_platform_driver(sdhci_tegra_driver);
328 402
329MODULE_DESCRIPTION("SDHCI driver for Tegra"); 403MODULE_DESCRIPTION("SDHCI driver for Tegra");
330MODULE_AUTHOR(" Google, Inc."); 404MODULE_AUTHOR("Google, Inc.");
331MODULE_LICENSE("GPL v2"); 405MODULE_LICENSE("GPL v2");