|
|
@@ -27,6 +27,7 @@
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
#include <linux/pinctrl/consumer.h>
|
|
|
#include <linux/sys_soc.h>
|
|
|
+#include <linux/thermal.h>
|
|
|
|
|
|
#include "sdhci-pltfm.h"
|
|
|
|
|
|
@@ -286,15 +287,19 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
struct sdhci_host *host = mmc_priv(mmc);
|
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
|
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
|
|
+ struct thermal_zone_device *thermal_dev;
|
|
|
struct device *dev = omap_host->dev;
|
|
|
struct mmc_ios *ios = &mmc->ios;
|
|
|
u32 start_window = 0, max_window = 0;
|
|
|
+ bool single_point_failure = false;
|
|
|
bool dcrc_was_enabled = false;
|
|
|
u8 cur_match, prev_match = 0;
|
|
|
u32 length = 0, max_len = 0;
|
|
|
u32 phase_delay = 0;
|
|
|
+ int temperature;
|
|
|
int ret = 0;
|
|
|
u32 reg;
|
|
|
+ int i;
|
|
|
|
|
|
/* clock tuning is not needed for upto 52MHz */
|
|
|
if (ios->clock <= 52000000)
|
|
|
@@ -304,6 +309,16 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
if (ios->timing == MMC_TIMING_UHS_SDR50 && !(reg & CAPA2_TSDR50))
|
|
|
return 0;
|
|
|
|
|
|
+ thermal_dev = thermal_zone_get_zone_by_name("cpu_thermal");
|
|
|
+ if (IS_ERR(thermal_dev)) {
|
|
|
+ dev_err(dev, "Unable to get thermal zone for tuning\n");
|
|
|
+ return PTR_ERR(thermal_dev);
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = thermal_zone_get_temp(thermal_dev, &temperature);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_DLL);
|
|
|
reg |= DLL_SWT;
|
|
|
sdhci_omap_writel(omap_host, SDHCI_OMAP_DLL, reg);
|
|
|
@@ -321,6 +336,11 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
|
|
|
omap_host->is_tuning = true;
|
|
|
|
|
|
+ /*
|
|
|
+ * Stage 1: Search for a maximum pass window ignoring any
|
|
|
+ * any single point failures. If the tuning value ends up
|
|
|
+ * near it, move away from it in stage 2 below
|
|
|
+ */
|
|
|
while (phase_delay <= MAX_PHASE_DELAY) {
|
|
|
sdhci_omap_set_dll(omap_host, phase_delay);
|
|
|
|
|
|
@@ -328,10 +348,15 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
if (cur_match) {
|
|
|
if (prev_match) {
|
|
|
length++;
|
|
|
+ } else if (single_point_failure) {
|
|
|
+ /* ignore single point failure */
|
|
|
+ length++;
|
|
|
} else {
|
|
|
start_window = phase_delay;
|
|
|
length = 1;
|
|
|
}
|
|
|
+ } else {
|
|
|
+ single_point_failure = prev_match;
|
|
|
}
|
|
|
|
|
|
if (length > max_len) {
|
|
|
@@ -349,13 +374,76 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
goto tuning_error;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * Assign tuning value as a ratio of maximum pass window based
|
|
|
+ * on temperature
|
|
|
+ */
|
|
|
+ if (temperature < -20000)
|
|
|
+ phase_delay = min(max_window + 4 * max_len - 24,
|
|
|
+ max_window +
|
|
|
+ DIV_ROUND_UP(13 * max_len, 16) * 4);
|
|
|
+ else if (temperature < 20000)
|
|
|
+ phase_delay = max_window + DIV_ROUND_UP(9 * max_len, 16) * 4;
|
|
|
+ else if (temperature < 40000)
|
|
|
+ phase_delay = max_window + DIV_ROUND_UP(8 * max_len, 16) * 4;
|
|
|
+ else if (temperature < 70000)
|
|
|
+ phase_delay = max_window + DIV_ROUND_UP(7 * max_len, 16) * 4;
|
|
|
+ else if (temperature < 90000)
|
|
|
+ phase_delay = max_window + DIV_ROUND_UP(5 * max_len, 16) * 4;
|
|
|
+ else if (temperature < 120000)
|
|
|
+ phase_delay = max_window + DIV_ROUND_UP(4 * max_len, 16) * 4;
|
|
|
+ else
|
|
|
+ phase_delay = max_window + DIV_ROUND_UP(3 * max_len, 16) * 4;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Stage 2: Search for a single point failure near the chosen tuning
|
|
|
+ * value in two steps. First in the +3 to +10 range and then in the
|
|
|
+ * +2 to -10 range. If found, move away from it in the appropriate
|
|
|
+ * direction by the appropriate amount depending on the temperature.
|
|
|
+ */
|
|
|
+ for (i = 3; i <= 10; i++) {
|
|
|
+ sdhci_omap_set_dll(omap_host, phase_delay + i);
|
|
|
+
|
|
|
+ if (mmc_send_tuning(mmc, opcode, NULL)) {
|
|
|
+ if (temperature < 10000)
|
|
|
+ phase_delay += i + 6;
|
|
|
+ else if (temperature < 20000)
|
|
|
+ phase_delay += i - 12;
|
|
|
+ else if (temperature < 70000)
|
|
|
+ phase_delay += i - 8;
|
|
|
+ else
|
|
|
+ phase_delay += i - 6;
|
|
|
+
|
|
|
+ goto single_failure_found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 2; i >= -10; i--) {
|
|
|
+ sdhci_omap_set_dll(omap_host, phase_delay + i);
|
|
|
+
|
|
|
+ if (mmc_send_tuning(mmc, opcode, NULL)) {
|
|
|
+ if (temperature < 10000)
|
|
|
+ phase_delay += i + 12;
|
|
|
+ else if (temperature < 20000)
|
|
|
+ phase_delay += i + 8;
|
|
|
+ else if (temperature < 70000)
|
|
|
+ phase_delay += i + 8;
|
|
|
+ else if (temperature < 90000)
|
|
|
+ phase_delay += i + 10;
|
|
|
+ else
|
|
|
+ phase_delay += i + 12;
|
|
|
+
|
|
|
+ goto single_failure_found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+single_failure_found:
|
|
|
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
|
|
|
if (!(reg & AC12_SCLK_SEL)) {
|
|
|
ret = -EIO;
|
|
|
goto tuning_error;
|
|
|
}
|
|
|
|
|
|
- phase_delay = max_window + 4 * (max_len >> 1);
|
|
|
sdhci_omap_set_dll(omap_host, phase_delay);
|
|
|
|
|
|
omap_host->is_tuning = false;
|