|
33 | 33 | #include <linux/ktime.h> |
34 | 34 |
|
35 | 35 | #include "sdhci-pltfm.h" |
| 36 | +#include "cqhci.h" |
36 | 37 |
|
37 | 38 | /* Tegra SDHOST controller vendor register definitions */ |
38 | 39 | #define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 |
|
90 | 91 | #define NVQUIRK_NEEDS_PAD_CONTROL BIT(7) |
91 | 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 | 97 | struct sdhci_tegra_soc_data { |
94 | 98 | const struct sdhci_pltfm_data *pdata; |
95 | 99 | u32 nvquirks; |
@@ -131,6 +135,7 @@ struct sdhci_tegra { |
131 | 135 | u32 default_tap; |
132 | 136 | u32 default_trim; |
133 | 137 | u32 dqs_trim; |
| 138 | + bool enable_hwcq; |
134 | 139 | }; |
135 | 140 |
|
136 | 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 | 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 | 707 | static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) |
689 | 708 | { |
690 | 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 | 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 | 979 | static const struct sdhci_ops tegra_sdhci_ops = { |
918 | 980 | .get_ro = tegra_sdhci_get_ro, |
919 | 981 | .read_w = tegra_sdhci_readw, |
@@ -1067,6 +1129,7 @@ static const struct sdhci_ops tegra186_sdhci_ops = { |
1067 | 1129 | .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, |
1068 | 1130 | .voltage_switch = tegra_sdhci_voltage_switch, |
1069 | 1131 | .get_max_clock = tegra_sdhci_get_max_clock, |
| 1132 | + .irq = sdhci_tegra_cqhci_irq, |
1070 | 1133 | }; |
1071 | 1134 |
|
1072 | 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 | 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 | 1222 | static int sdhci_tegra_probe(struct platform_device *pdev) |
1112 | 1223 | { |
1113 | 1224 | const struct of_device_id *match; |
@@ -1155,9 +1266,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) |
1155 | 1266 | if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) |
1156 | 1267 | host->mmc->caps |= MMC_CAP_1_8V_DDR; |
1157 | 1268 |
|
1158 | | - tegra_sdhci_parse_pad_autocal_dt(host); |
1159 | | - |
1160 | | - tegra_sdhci_parse_tap_and_trim(host); |
| 1269 | + tegra_sdhci_parse_dt(host); |
1161 | 1270 |
|
1162 | 1271 | tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", |
1163 | 1272 | GPIOD_OUT_HIGH); |
@@ -1195,7 +1304,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) |
1195 | 1304 |
|
1196 | 1305 | usleep_range(2000, 4000); |
1197 | 1306 |
|
1198 | | - rc = sdhci_add_host(host); |
| 1307 | + rc = sdhci_tegra_add_host(host); |
1199 | 1308 | if (rc) |
1200 | 1309 | goto err_add_host; |
1201 | 1310 |
|
|
0 commit comments