|
3 | 3 | * Copyright (C) 2010 Google, Inc. |
4 | 4 | */ |
5 | 5 |
|
| 6 | +#include <linux/bitfield.h> |
6 | 7 | #include <linux/clk.h> |
7 | 8 | #include <linux/delay.h> |
8 | 9 | #include <linux/dma-mapping.h> |
9 | 10 | #include <linux/err.h> |
10 | 11 | #include <linux/gpio/consumer.h> |
11 | 12 | #include <linux/init.h> |
12 | 13 | #include <linux/io.h> |
| 14 | +#include <linux/iommu.h> |
13 | 15 | #include <linux/iopoll.h> |
14 | 16 | #include <linux/ktime.h> |
15 | 17 | #include <linux/mmc/card.h> |
|
95 | 97 | #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec |
96 | 98 | #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31) |
97 | 99 |
|
| 100 | +#define SDHCI_TEGRA_CIF2AXI_CTRL_0 0x1fc |
| 101 | + |
98 | 102 | #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) |
99 | 103 | #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) |
100 | 104 | #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) |
|
122 | 126 | #define NVQUIRK_HAS_TMCLK BIT(10) |
123 | 127 |
|
124 | 128 | #define NVQUIRK_HAS_ANDROID_GPT_SECTOR BIT(11) |
| 129 | +#define NVQUIRK_PROGRAM_STREAMID BIT(12) |
125 | 130 |
|
126 | 131 | /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ |
127 | 132 | #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 |
@@ -178,6 +183,7 @@ struct sdhci_tegra { |
178 | 183 | bool enable_hwcq; |
179 | 184 | unsigned long curr_clk_rate; |
180 | 185 | u8 tuned_tap_delay; |
| 186 | + u32 stream_id; |
181 | 187 | }; |
182 | 188 |
|
183 | 189 | static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) |
@@ -1565,6 +1571,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra234 = { |
1565 | 1571 | NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | |
1566 | 1572 | NVQUIRK_ENABLE_SDR50 | |
1567 | 1573 | NVQUIRK_ENABLE_SDR104 | |
| 1574 | + NVQUIRK_PROGRAM_STREAMID | |
1568 | 1575 | NVQUIRK_HAS_TMCLK, |
1569 | 1576 | .min_tap_delay = 95, |
1570 | 1577 | .max_tap_delay = 111, |
@@ -1631,6 +1638,19 @@ static int sdhci_tegra_add_host(struct sdhci_host *host) |
1631 | 1638 | return ret; |
1632 | 1639 | } |
1633 | 1640 |
|
| 1641 | +/* Program MC streamID for DMA transfers */ |
| 1642 | +static void sdhci_tegra_program_stream_id(struct sdhci_host *host) |
| 1643 | +{ |
| 1644 | + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
| 1645 | + struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); |
| 1646 | + |
| 1647 | + if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID) { |
| 1648 | + tegra_sdhci_writel(host, FIELD_PREP(GENMASK(15, 8), tegra_host->stream_id) | |
| 1649 | + FIELD_PREP(GENMASK(7, 0), tegra_host->stream_id), |
| 1650 | + SDHCI_TEGRA_CIF2AXI_CTRL_0); |
| 1651 | + } |
| 1652 | +} |
| 1653 | + |
1634 | 1654 | static int sdhci_tegra_probe(struct platform_device *pdev) |
1635 | 1655 | { |
1636 | 1656 | const struct sdhci_tegra_soc_data *soc_data; |
@@ -1691,6 +1711,12 @@ static int sdhci_tegra_probe(struct platform_device *pdev) |
1691 | 1711 |
|
1692 | 1712 | tegra_sdhci_parse_dt(host); |
1693 | 1713 |
|
| 1714 | + if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID && |
| 1715 | + !tegra_dev_iommu_get_stream_id(&pdev->dev, &tegra_host->stream_id)) { |
| 1716 | + dev_warn(mmc_dev(host->mmc), "missing IOMMU stream ID\n"); |
| 1717 | + tegra_host->stream_id = 0x7f; |
| 1718 | + } |
| 1719 | + |
1694 | 1720 | tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", |
1695 | 1721 | GPIOD_OUT_HIGH); |
1696 | 1722 | if (IS_ERR(tegra_host->power_gpio)) { |
@@ -1776,6 +1802,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev) |
1776 | 1802 | if (rc) |
1777 | 1803 | goto err_add_host; |
1778 | 1804 |
|
| 1805 | + sdhci_tegra_program_stream_id(host); |
| 1806 | + |
1779 | 1807 | return 0; |
1780 | 1808 |
|
1781 | 1809 | err_add_host: |
@@ -1872,6 +1900,8 @@ static int sdhci_tegra_resume(struct device *dev) |
1872 | 1900 | if (ret) |
1873 | 1901 | return ret; |
1874 | 1902 |
|
| 1903 | + sdhci_tegra_program_stream_id(host); |
| 1904 | + |
1875 | 1905 | ret = sdhci_resume_host(host); |
1876 | 1906 | if (ret) |
1877 | 1907 | goto disable_clk; |
|
0 commit comments