diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-tegra.c')
-rw-r--r-- | drivers/mmc/host/sdhci-tegra.c | 105 |
1 files changed, 36 insertions, 69 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index f3778d58d1cd..ad28b49f0203 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c | |||
@@ -20,11 +20,10 @@ | |||
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_device.h> |
23 | #include <linux/of_gpio.h> | ||
24 | #include <linux/gpio.h> | ||
25 | #include <linux/mmc/card.h> | 23 | #include <linux/mmc/card.h> |
26 | #include <linux/mmc/host.h> | 24 | #include <linux/mmc/host.h> |
27 | #include <linux/mmc/slot-gpio.h> | 25 | #include <linux/mmc/slot-gpio.h> |
26 | #include <linux/gpio/consumer.h> | ||
28 | 27 | ||
29 | #include "sdhci-pltfm.h" | 28 | #include "sdhci-pltfm.h" |
30 | 29 | ||
@@ -41,7 +40,6 @@ | |||
41 | #define NVQUIRK_DISABLE_SDR50 BIT(3) | 40 | #define NVQUIRK_DISABLE_SDR50 BIT(3) |
42 | #define NVQUIRK_DISABLE_SDR104 BIT(4) | 41 | #define NVQUIRK_DISABLE_SDR104 BIT(4) |
43 | #define NVQUIRK_DISABLE_DDR50 BIT(5) | 42 | #define NVQUIRK_DISABLE_DDR50 BIT(5) |
44 | #define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6) | ||
45 | 43 | ||
46 | struct sdhci_tegra_soc_data { | 44 | struct sdhci_tegra_soc_data { |
47 | const struct sdhci_pltfm_data *pdata; | 45 | const struct sdhci_pltfm_data *pdata; |
@@ -50,7 +48,7 @@ struct sdhci_tegra_soc_data { | |||
50 | 48 | ||
51 | struct sdhci_tegra { | 49 | struct sdhci_tegra { |
52 | const struct sdhci_tegra_soc_data *soc_data; | 50 | const struct sdhci_tegra_soc_data *soc_data; |
53 | int power_gpio; | 51 | struct gpio_desc *power_gpio; |
54 | }; | 52 | }; |
55 | 53 | ||
56 | static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) | 54 | static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) |
@@ -71,23 +69,19 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) | |||
71 | static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) | 69 | static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) |
72 | { | 70 | { |
73 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | 71 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
74 | struct sdhci_tegra *tegra_host = pltfm_host->priv; | ||
75 | const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; | ||
76 | 72 | ||
77 | if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) { | 73 | switch (reg) { |
78 | switch (reg) { | 74 | case SDHCI_TRANSFER_MODE: |
79 | case SDHCI_TRANSFER_MODE: | 75 | /* |
80 | /* | 76 | * Postpone this write, we must do it together with a |
81 | * Postpone this write, we must do it together with a | 77 | * command write that is down below. |
82 | * command write that is down below. | 78 | */ |
83 | */ | 79 | pltfm_host->xfer_mode_shadow = val; |
84 | pltfm_host->xfer_mode_shadow = val; | 80 | return; |
85 | return; | 81 | case SDHCI_COMMAND: |
86 | case SDHCI_COMMAND: | 82 | writel((val << 16) | pltfm_host->xfer_mode_shadow, |
87 | writel((val << 16) | pltfm_host->xfer_mode_shadow, | 83 | host->ioaddr + SDHCI_TRANSFER_MODE); |
88 | host->ioaddr + SDHCI_TRANSFER_MODE); | 84 | return; |
89 | return; | ||
90 | } | ||
91 | } | 85 | } |
92 | 86 | ||
93 | writew(val, host->ioaddr + reg); | 87 | writew(val, host->ioaddr + reg); |
@@ -173,7 +167,6 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) | |||
173 | static const struct sdhci_ops tegra_sdhci_ops = { | 167 | static const struct sdhci_ops tegra_sdhci_ops = { |
174 | .get_ro = tegra_sdhci_get_ro, | 168 | .get_ro = tegra_sdhci_get_ro, |
175 | .read_w = tegra_sdhci_readw, | 169 | .read_w = tegra_sdhci_readw, |
176 | .write_w = tegra_sdhci_writew, | ||
177 | .write_l = tegra_sdhci_writel, | 170 | .write_l = tegra_sdhci_writel, |
178 | .set_clock = sdhci_set_clock, | 171 | .set_clock = sdhci_set_clock, |
179 | .set_bus_width = tegra_sdhci_set_bus_width, | 172 | .set_bus_width = tegra_sdhci_set_bus_width, |
@@ -214,6 +207,18 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = { | |||
214 | NVQUIRK_DISABLE_SDR104, | 207 | NVQUIRK_DISABLE_SDR104, |
215 | }; | 208 | }; |
216 | 209 | ||
210 | static const struct sdhci_ops tegra114_sdhci_ops = { | ||
211 | .get_ro = tegra_sdhci_get_ro, | ||
212 | .read_w = tegra_sdhci_readw, | ||
213 | .write_w = tegra_sdhci_writew, | ||
214 | .write_l = tegra_sdhci_writel, | ||
215 | .set_clock = sdhci_set_clock, | ||
216 | .set_bus_width = tegra_sdhci_set_bus_width, | ||
217 | .reset = tegra_sdhci_reset, | ||
218 | .set_uhs_signaling = sdhci_set_uhs_signaling, | ||
219 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, | ||
220 | }; | ||
221 | |||
217 | static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { | 222 | static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { |
218 | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | | 223 | .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | |
219 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | | 224 | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | |
@@ -221,15 +226,14 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { | |||
221 | SDHCI_QUIRK_NO_HISPD_BIT | | 226 | SDHCI_QUIRK_NO_HISPD_BIT | |
222 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | | 227 | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | |
223 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, | 228 | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, |
224 | .ops = &tegra_sdhci_ops, | 229 | .ops = &tegra114_sdhci_ops, |
225 | }; | 230 | }; |
226 | 231 | ||
227 | static struct sdhci_tegra_soc_data soc_data_tegra114 = { | 232 | static struct sdhci_tegra_soc_data soc_data_tegra114 = { |
228 | .pdata = &sdhci_tegra114_pdata, | 233 | .pdata = &sdhci_tegra114_pdata, |
229 | .nvquirks = NVQUIRK_DISABLE_SDR50 | | 234 | .nvquirks = NVQUIRK_DISABLE_SDR50 | |
230 | NVQUIRK_DISABLE_DDR50 | | 235 | NVQUIRK_DISABLE_DDR50 | |
231 | NVQUIRK_DISABLE_SDR104 | | 236 | NVQUIRK_DISABLE_SDR104, |
232 | NVQUIRK_SHADOW_XFER_MODE_REG, | ||
233 | }; | 237 | }; |
234 | 238 | ||
235 | static const struct of_device_id sdhci_tegra_dt_match[] = { | 239 | static const struct of_device_id sdhci_tegra_dt_match[] = { |
@@ -241,17 +245,6 @@ static const struct of_device_id sdhci_tegra_dt_match[] = { | |||
241 | }; | 245 | }; |
242 | MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); | 246 | MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); |
243 | 247 | ||
244 | static int sdhci_tegra_parse_dt(struct device *dev) | ||
245 | { | ||
246 | struct device_node *np = dev->of_node; | ||
247 | struct sdhci_host *host = dev_get_drvdata(dev); | ||
248 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||
249 | struct sdhci_tegra *tegra_host = pltfm_host->priv; | ||
250 | |||
251 | tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0); | ||
252 | return mmc_of_parse(host->mmc); | ||
253 | } | ||
254 | |||
255 | static int sdhci_tegra_probe(struct platform_device *pdev) | 248 | static int sdhci_tegra_probe(struct platform_device *pdev) |
256 | { | 249 | { |
257 | const struct of_device_id *match; | 250 | const struct of_device_id *match; |
@@ -281,21 +274,18 @@ static int sdhci_tegra_probe(struct platform_device *pdev) | |||
281 | tegra_host->soc_data = soc_data; | 274 | tegra_host->soc_data = soc_data; |
282 | pltfm_host->priv = tegra_host; | 275 | pltfm_host->priv = tegra_host; |
283 | 276 | ||
284 | rc = sdhci_tegra_parse_dt(&pdev->dev); | 277 | rc = mmc_of_parse(host->mmc); |
285 | if (rc) | 278 | if (rc) |
286 | goto err_parse_dt; | 279 | goto err_parse_dt; |
287 | 280 | ||
288 | if (gpio_is_valid(tegra_host->power_gpio)) { | 281 | tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", |
289 | rc = gpio_request(tegra_host->power_gpio, "sdhci_power"); | 282 | GPIOD_OUT_HIGH); |
290 | if (rc) { | 283 | if (IS_ERR(tegra_host->power_gpio)) { |
291 | dev_err(mmc_dev(host->mmc), | 284 | rc = PTR_ERR(tegra_host->power_gpio); |
292 | "failed to allocate power gpio\n"); | 285 | goto err_power_req; |
293 | goto err_power_req; | ||
294 | } | ||
295 | gpio_direction_output(tegra_host->power_gpio, 1); | ||
296 | } | 286 | } |
297 | 287 | ||
298 | clk = clk_get(mmc_dev(host->mmc), NULL); | 288 | clk = devm_clk_get(mmc_dev(host->mmc), NULL); |
299 | if (IS_ERR(clk)) { | 289 | if (IS_ERR(clk)) { |
300 | dev_err(mmc_dev(host->mmc), "clk err\n"); | 290 | dev_err(mmc_dev(host->mmc), "clk err\n"); |
301 | rc = PTR_ERR(clk); | 291 | rc = PTR_ERR(clk); |
@@ -312,10 +302,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) | |||
312 | 302 | ||
313 | err_add_host: | 303 | err_add_host: |
314 | clk_disable_unprepare(pltfm_host->clk); | 304 | clk_disable_unprepare(pltfm_host->clk); |
315 | clk_put(pltfm_host->clk); | ||
316 | err_clk_get: | 305 | err_clk_get: |
317 | if (gpio_is_valid(tegra_host->power_gpio)) | ||
318 | gpio_free(tegra_host->power_gpio); | ||
319 | err_power_req: | 306 | err_power_req: |
320 | err_parse_dt: | 307 | err_parse_dt: |
321 | err_alloc_tegra_host: | 308 | err_alloc_tegra_host: |
@@ -323,26 +310,6 @@ err_alloc_tegra_host: | |||
323 | return rc; | 310 | return rc; |
324 | } | 311 | } |
325 | 312 | ||
326 | static int sdhci_tegra_remove(struct platform_device *pdev) | ||
327 | { | ||
328 | struct sdhci_host *host = platform_get_drvdata(pdev); | ||
329 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||
330 | struct sdhci_tegra *tegra_host = pltfm_host->priv; | ||
331 | int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); | ||
332 | |||
333 | sdhci_remove_host(host, dead); | ||
334 | |||
335 | if (gpio_is_valid(tegra_host->power_gpio)) | ||
336 | gpio_free(tegra_host->power_gpio); | ||
337 | |||
338 | clk_disable_unprepare(pltfm_host->clk); | ||
339 | clk_put(pltfm_host->clk); | ||
340 | |||
341 | sdhci_pltfm_free(pdev); | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static struct platform_driver sdhci_tegra_driver = { | 313 | static struct platform_driver sdhci_tegra_driver = { |
347 | .driver = { | 314 | .driver = { |
348 | .name = "sdhci-tegra", | 315 | .name = "sdhci-tegra", |
@@ -350,7 +317,7 @@ static struct platform_driver sdhci_tegra_driver = { | |||
350 | .pm = SDHCI_PLTFM_PMOPS, | 317 | .pm = SDHCI_PLTFM_PMOPS, |
351 | }, | 318 | }, |
352 | .probe = sdhci_tegra_probe, | 319 | .probe = sdhci_tegra_probe, |
353 | .remove = sdhci_tegra_remove, | 320 | .remove = sdhci_pltfm_unregister, |
354 | }; | 321 | }; |
355 | 322 | ||
356 | module_platform_driver(sdhci_tegra_driver); | 323 | module_platform_driver(sdhci_tegra_driver); |