diff options
author | Sowjanya Komatineni <skomatineni@nvidia.com> | 2019-01-23 14:30:54 -0500 |
---|---|---|
committer | Ulf Hansson <ulf.hansson@linaro.org> | 2019-02-25 02:40:58 -0500 |
commit | 3c4019f979783575c50db35eae80f30b382e9e49 (patch) | |
tree | e61f3beadf0a2e0326e776e7b32be6d70e815e2e /drivers/mmc | |
parent | 4c4faff62bf59e64c5175b7704727e6b9db361f2 (diff) |
mmc: tegra: HW Command Queue Support for Tegra SDMMC
This patch adds HW Command Queue for supported Tegra SDMMC
controllers.
Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Acked-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/Kconfig | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-tegra.c | 117 |
2 files changed, 114 insertions, 4 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d1769f1d060d..28fcd8f580a1 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig | |||
@@ -251,6 +251,7 @@ config MMC_SDHCI_TEGRA | |||
251 | depends on ARCH_TEGRA | 251 | depends on ARCH_TEGRA |
252 | depends on MMC_SDHCI_PLTFM | 252 | depends on MMC_SDHCI_PLTFM |
253 | select MMC_SDHCI_IO_ACCESSORS | 253 | select MMC_SDHCI_IO_ACCESSORS |
254 | select MMC_CQHCI | ||
254 | help | 255 | help |
255 | This selects the Tegra SD/MMC controller. If you have a Tegra | 256 | This selects the Tegra SD/MMC controller. If you have a Tegra |
256 | platform with SD or MMC devices, say Y or M here. | 257 | platform with SD or MMC devices, say Y or M here. |
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 7d681a8fa4ba..31d7ae4f1e20 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/ktime.h> | 33 | #include <linux/ktime.h> |
34 | 34 | ||
35 | #include "sdhci-pltfm.h" | 35 | #include "sdhci-pltfm.h" |
36 | #include "cqhci.h" | ||
36 | 37 | ||
37 | /* Tegra SDHOST controller vendor register definitions */ | 38 | /* Tegra SDHOST controller vendor register definitions */ |
38 | #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 | 39 | #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 |
@@ -90,6 +91,9 @@ | |||
90 | #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) | 91 | #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) |
91 | #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) | 92 | #define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8) |
92 | 93 | ||
94 | /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ | ||
95 | #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 | ||
96 | |||
93 | struct sdhci_tegra_soc_data { | 97 | struct sdhci_tegra_soc_data { |
94 | const struct sdhci_pltfm_data *pdata; | 98 | const struct sdhci_pltfm_data *pdata; |
95 | u32 nvquirks; | 99 | u32 nvquirks; |
@@ -131,6 +135,7 @@ struct sdhci_tegra { | |||
131 | u32 default_tap; | 135 | u32 default_tap; |
132 | u32 default_trim; | 136 | u32 default_trim; |
133 | u32 dqs_trim; | 137 | u32 dqs_trim; |
138 | bool enable_hwcq; | ||
134 | }; | 139 | }; |
135 | 140 | ||
136 | static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) | 141 | static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) |
@@ -685,6 +690,20 @@ static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host) | |||
685 | tegra_host->dqs_trim = 0x11; | 690 | tegra_host->dqs_trim = 0x11; |
686 | } | 691 | } |
687 | 692 | ||
693 | static void tegra_sdhci_parse_dt(struct sdhci_host *host) | ||
694 | { | ||
695 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||
696 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); | ||
697 | |||
698 | if (device_property_read_bool(host->mmc->parent, "supports-cqe")) | ||
699 | tegra_host->enable_hwcq = true; | ||
700 | else | ||
701 | tegra_host->enable_hwcq = false; | ||
702 | |||
703 | tegra_sdhci_parse_pad_autocal_dt(host); | ||
704 | tegra_sdhci_parse_tap_and_trim(host); | ||
705 | } | ||
706 | |||
688 | static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) | 707 | static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) |
689 | { | 708 | { |
690 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | 709 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); |
@@ -914,6 +933,49 @@ static void tegra_sdhci_voltage_switch(struct sdhci_host *host) | |||
914 | tegra_host->pad_calib_required = true; | 933 | tegra_host->pad_calib_required = true; |
915 | } | 934 | } |
916 | 935 | ||
936 | static void sdhci_tegra_cqe_enable(struct mmc_host *mmc) | ||
937 | { | ||
938 | struct cqhci_host *cq_host = mmc->cqe_private; | ||
939 | u32 cqcfg = 0; | ||
940 | |||
941 | /* | ||
942 | * Tegra SDMMC Controller design prevents write access to BLOCK_COUNT | ||
943 | * registers when CQE is enabled. | ||
944 | */ | ||
945 | cqcfg = cqhci_readl(cq_host, CQHCI_CFG); | ||
946 | if (cqcfg & CQHCI_ENABLE) | ||
947 | cqhci_writel(cq_host, (cqcfg & ~CQHCI_ENABLE), CQHCI_CFG); | ||
948 | |||
949 | sdhci_cqe_enable(mmc); | ||
950 | |||
951 | if (cqcfg & CQHCI_ENABLE) | ||
952 | cqhci_writel(cq_host, cqcfg, CQHCI_CFG); | ||
953 | } | ||
954 | |||
955 | static void sdhci_tegra_dumpregs(struct mmc_host *mmc) | ||
956 | { | ||
957 | sdhci_dumpregs(mmc_priv(mmc)); | ||
958 | } | ||
959 | |||
960 | static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask) | ||
961 | { | ||
962 | int cmd_error = 0; | ||
963 | int data_error = 0; | ||
964 | |||
965 | if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) | ||
966 | return intmask; | ||
967 | |||
968 | cqhci_irq(host->mmc, intmask, cmd_error, data_error); | ||
969 | |||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { | ||
974 | .enable = sdhci_tegra_cqe_enable, | ||
975 | .disable = sdhci_cqe_disable, | ||
976 | .dumpregs = sdhci_tegra_dumpregs, | ||
977 | }; | ||
978 | |||
917 | static const struct sdhci_ops tegra_sdhci_ops = { | 979 | static const struct sdhci_ops tegra_sdhci_ops = { |
918 | .get_ro = tegra_sdhci_get_ro, | 980 | .get_ro = tegra_sdhci_get_ro, |
919 | .read_w = tegra_sdhci_readw, | 981 | .read_w = tegra_sdhci_readw, |
@@ -1067,6 +1129,7 @@ static const struct sdhci_ops tegra186_sdhci_ops = { | |||
1067 | .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, | 1129 | .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, |
1068 | .voltage_switch = tegra_sdhci_voltage_switch, | 1130 | .voltage_switch = tegra_sdhci_voltage_switch, |
1069 | .get_max_clock = tegra_sdhci_get_max_clock, | 1131 | .get_max_clock = tegra_sdhci_get_max_clock, |
1132 | .irq = sdhci_tegra_cqhci_irq, | ||
1070 | }; | 1133 | }; |
1071 | 1134 | ||
1072 | static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { | 1135 | static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { |
@@ -1108,6 +1171,54 @@ static const struct of_device_id sdhci_tegra_dt_match[] = { | |||
1108 | }; | 1171 | }; |
1109 | MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); | 1172 | MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match); |
1110 | 1173 | ||
1174 | static int sdhci_tegra_add_host(struct sdhci_host *host) | ||
1175 | { | ||
1176 | struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); | ||
1177 | struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); | ||
1178 | struct cqhci_host *cq_host; | ||
1179 | bool dma64; | ||
1180 | int ret; | ||
1181 | |||
1182 | if (!tegra_host->enable_hwcq) | ||
1183 | return sdhci_add_host(host); | ||
1184 | |||
1185 | sdhci_enable_v4_mode(host); | ||
1186 | |||
1187 | ret = sdhci_setup_host(host); | ||
1188 | if (ret) | ||
1189 | return ret; | ||
1190 | |||
1191 | host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; | ||
1192 | |||
1193 | cq_host = devm_kzalloc(host->mmc->parent, | ||
1194 | sizeof(*cq_host), GFP_KERNEL); | ||
1195 | if (!cq_host) { | ||
1196 | ret = -ENOMEM; | ||
1197 | goto cleanup; | ||
1198 | } | ||
1199 | |||
1200 | cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR; | ||
1201 | cq_host->ops = &sdhci_tegra_cqhci_ops; | ||
1202 | |||
1203 | dma64 = host->flags & SDHCI_USE_64_BIT_DMA; | ||
1204 | if (dma64) | ||
1205 | cq_host->caps |= CQHCI_TASK_DESC_SZ_128; | ||
1206 | |||
1207 | ret = cqhci_init(cq_host, host->mmc, dma64); | ||
1208 | if (ret) | ||
1209 | goto cleanup; | ||
1210 | |||
1211 | ret = __sdhci_add_host(host); | ||
1212 | if (ret) | ||
1213 | goto cleanup; | ||
1214 | |||
1215 | return 0; | ||
1216 | |||
1217 | cleanup: | ||
1218 | sdhci_cleanup_host(host); | ||
1219 | return ret; | ||
1220 | } | ||
1221 | |||
1111 | static int sdhci_tegra_probe(struct platform_device *pdev) | 1222 | static int sdhci_tegra_probe(struct platform_device *pdev) |
1112 | { | 1223 | { |
1113 | const struct of_device_id *match; | 1224 | const struct of_device_id *match; |
@@ -1155,9 +1266,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) | |||
1155 | if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) | 1266 | if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) |
1156 | host->mmc->caps |= MMC_CAP_1_8V_DDR; | 1267 | host->mmc->caps |= MMC_CAP_1_8V_DDR; |
1157 | 1268 | ||
1158 | tegra_sdhci_parse_pad_autocal_dt(host); | 1269 | tegra_sdhci_parse_dt(host); |
1159 | |||
1160 | tegra_sdhci_parse_tap_and_trim(host); | ||
1161 | 1270 | ||
1162 | tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", | 1271 | tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", |
1163 | GPIOD_OUT_HIGH); | 1272 | GPIOD_OUT_HIGH); |
@@ -1195,7 +1304,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) | |||
1195 | 1304 | ||
1196 | usleep_range(2000, 4000); | 1305 | usleep_range(2000, 4000); |
1197 | 1306 | ||
1198 | rc = sdhci_add_host(host); | 1307 | rc = sdhci_tegra_add_host(host); |
1199 | if (rc) | 1308 | if (rc) |
1200 | goto err_add_host; | 1309 | goto err_add_host; |
1201 | 1310 | ||