Enhance SDIO tuning algorithm

Improve the SDIO tuning algorithm to increase stability and
performance under low-temperature conditions.

Signed-off-by: Swee Aun Khor <sakhor@efinixinc.com>
This commit is contained in:
Swee Aun Khor
2026-04-10 13:52:28 +08:00
committed by Byron Lathi
parent 334eb3ba15
commit 9074d7744f

View File

@@ -286,81 +286,164 @@ int efx_sdio_find_optimal_timing(struct efx_sdio_host *host,
{
int max_consecutive_length, row_length, optimal_sample_count,
optimal_pll_shift;
int *optimal_rows;
int *optimal_rows = NULL;
int optimal_row_count, center_row, i;
char optimal_row_str[32];
optimal_rows = kmalloc(max_sample_count * sizeof(int), GFP_KERNEL);
if (!optimal_rows) {
return -ENOMEM;
}
// Max sample count should be less than 8 With system clock is 200Mhz and target SDIO clock is 25Mhz.
// Shall perform basic tuning if the max sample count more than 8.
if (max_sample_count > 8) {
optimal_rows = kmalloc(max_sample_count * sizeof(int), GFP_KERNEL);
if (!optimal_rows) {
return -ENOMEM;
}
/* Find rows with longest consecutive 1's */
dev_dbg(&host->pdev->dev, "Analyzing timing results\n");
max_consecutive_length = 0;
for (i = 0; i < max_sample_count; i++) {
row_length = efx_sdio_find_longest_consecutive_ones(result_map[i],
EFX_SDIO_MAX_PLL_SHIFT);
dev_dbg(&host->pdev->dev, "Sample[%u]: consecutive_length=%d\n",
i, row_length);
if (row_length > max_consecutive_length) {
dev_dbg(&host->pdev->dev,
"New best: Sample[%u] length=%d\n",
i, row_length);
max_consecutive_length = row_length;
}
}
dev_dbg(&host->pdev->dev, "Best consecutive length: %d\n",
max_consecutive_length);
/* Find rows with longest consecutive 1's */
dev_dbg(&host->pdev->dev, "Analyzing timing results\n");
max_consecutive_length = 0;
for (i = 0; i < max_sample_count; i++) {
row_length = efx_sdio_find_longest_consecutive_ones(result_map[i],
EFX_SDIO_MAX_PLL_SHIFT);
dev_dbg(&host->pdev->dev, "Sample[%u]: consecutive_length=%d\n",
i, row_length);
if (row_length > max_consecutive_length) {
dev_dbg(&host->pdev->dev,
"New best: Sample[%u] length=%d\n",
i, row_length);
max_consecutive_length = row_length;
}
}
dev_dbg(&host->pdev->dev, "Best consecutive length: %d\n",
max_consecutive_length);
if (max_consecutive_length < EFX_SDIO_MIN_TIMING_MARGIN) {
dev_warn(&host->pdev->dev,
"Insufficient timing margin: %d (minimum %d)\n",
max_consecutive_length, EFX_SDIO_MIN_TIMING_MARGIN);
}
if (max_consecutive_length < EFX_SDIO_MIN_TIMING_MARGIN) {
dev_warn(&host->pdev->dev,
"Insufficient timing margin: %d (minimum %d)\n",
max_consecutive_length, EFX_SDIO_MIN_TIMING_MARGIN);
}
/* Collect all rows with maximum consecutive length */
optimal_row_count = 0;
for (i = 0; i < max_sample_count; i++) {
if (efx_sdio_find_longest_consecutive_ones(result_map[i],
EFX_SDIO_MAX_PLL_SHIFT) ==
max_consecutive_length) {
optimal_rows[optimal_row_count++] = i;
}
}
/* Collect all rows with maximum consecutive length */
optimal_row_count = 0;
for (i = 0; i < max_sample_count; i++) {
if (efx_sdio_find_longest_consecutive_ones(result_map[i],
EFX_SDIO_MAX_PLL_SHIFT) ==
max_consecutive_length) {
optimal_rows[optimal_row_count++] = i;
}
}
if (optimal_row_count == 0) {
kfree(optimal_rows);
return -ENODEV; /* No valid configurations found */
}
if (optimal_row_count == 0) {
kfree(optimal_rows);
return -ENODEV; /* No valid configurations found */
}
/* Find center row */
center_row = optimal_row_count / 2;
optimal_sample_count = optimal_rows[center_row];
/* Find center row */
center_row = optimal_row_count / 2;
optimal_sample_count = optimal_rows[center_row];
/* Find center column within optimal row */
dev_dbg(&host->pdev->dev,
"Selected sample_count=%d from %d optimal rows\n",
optimal_sample_count, optimal_row_count);
/* Find center column within optimal row */
dev_dbg(&host->pdev->dev,
"Selected sample_count=%d from %d optimal rows\n",
optimal_sample_count, optimal_row_count);
/* Debug: Show the row being analyzed for center calculation */
optimal_row_str[0] = '\0';
for (i = 0; i < EFX_SDIO_MAX_PLL_SHIFT; i++) {
sprintf(optimal_row_str + strlen(optimal_row_str), "%d",
result_map[optimal_sample_count][i]);
}
dev_dbg(&host->pdev->dev,
"Analyzing row[%d]: [%s] for center calculation\n",
optimal_sample_count, optimal_row_str);
/* Debug: Show the row being analyzed for center calculation */
optimal_row_str[0] = '\0';
for (i = 0; i < EFX_SDIO_MAX_PLL_SHIFT; i++) {
sprintf(optimal_row_str + strlen(optimal_row_str), "%d",
result_map[optimal_sample_count][i]);
}
dev_dbg(&host->pdev->dev,
"Analyzing row[%d]: [%s] for center calculation\n",
optimal_sample_count, optimal_row_str);
optimal_pll_shift =
efx_sdio_find_center_of_consecutive_ones(result_map[optimal_sample_count],
EFX_SDIO_MAX_PLL_SHIFT);
optimal_pll_shift =
efx_sdio_find_center_of_consecutive_ones(result_map[optimal_sample_count],
EFX_SDIO_MAX_PLL_SHIFT);
dev_dbg(&host->pdev->dev,
"Center PLL calculation result: pll_shift=%d\n",
optimal_pll_shift);
dev_dbg(&host->pdev->dev,
"Center PLL calculation result: pll_shift=%d\n",
optimal_pll_shift);
// Check before free
if (optimal_rows) {
kfree(optimal_rows);
}
} else {
u8 *flat_map;
int flat_length;
// Append the same result_map to make it look like 1D for better center calculation
flat_length = max_sample_count * 2 * EFX_SDIO_MAX_PLL_SHIFT;
flat_map = kmalloc(flat_length, GFP_KERNEL);
if (!flat_map) {
return -ENOMEM;
}
int idx = 0;
for (i = 0; i < max_sample_count; i++) {
memcpy(&flat_map[idx], result_map[i], EFX_SDIO_MAX_PLL_SHIFT);
idx += EFX_SDIO_MAX_PLL_SHIFT;
}
for (i = 0; i < max_sample_count; i++) {
memcpy(&flat_map[idx], result_map[i], EFX_SDIO_MAX_PLL_SHIFT);
idx += EFX_SDIO_MAX_PLL_SHIFT;
}
//Debug: Print the flat map for analysis
dev_dbg(&host->pdev->dev, "Flat map:\n");
for (i = 0; i < flat_length; i++) {
dev_dbg(&host->pdev->dev, "flat_map[%d] = %d\n", i, flat_map[i]);
}
max_consecutive_length = efx_sdio_find_longest_consecutive_ones(flat_map, flat_length);
dev_dbg(&host->pdev->dev, "Best consecutive length in flat map: %d\n", max_consecutive_length);
if (max_consecutive_length < EFX_SDIO_MIN_TIMING_MARGIN) {
dev_warn(&host->pdev->dev,
"Insufficient timing margin in flat map: %d (minimum %d)\n",
max_consecutive_length, EFX_SDIO_MIN_TIMING_MARGIN);
}
// DDR and SDR modes using different selection strategy for better tuning results
// DDR mode: select center of longest consecutive 1's for better stability as DDR is more sensitive to timing
// SDR mode: select first occurrence of longest consecutive 1's for better performance as SDR is less sensitive to timing and can benefit from more aggressive settings
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) {
int center_idx = efx_sdio_find_center_of_consecutive_ones(flat_map, flat_length);
optimal_sample_count = (center_idx%(EFX_SDIO_MAX_PLL_SHIFT * max_sample_count)) / EFX_SDIO_MAX_PLL_SHIFT;
optimal_pll_shift = center_idx % EFX_SDIO_MAX_PLL_SHIFT;
dev_info(&host->pdev->dev,
"DDR mode: Selected center of longest sequence at index %d (sample_count=%d, pll_shift=%d)\n",
center_idx, optimal_sample_count, optimal_pll_shift);
} else {
// For SDR modes, find the first occurrence of the longest consecutive 1's
int i, j;
optimal_sample_count = 0;
optimal_pll_shift = 0;
bool found = false;
for (i = 0; i < flat_length; i++) {
if (flat_map[i] == 1) {
int current_length = 1;
for (j = i + 1; j < flat_length && flat_map[j] == 1; j++) {
current_length++;
}
if (current_length == max_consecutive_length) {
optimal_sample_count = (i%(EFX_SDIO_MAX_PLL_SHIFT * max_sample_count))/EFX_SDIO_MAX_PLL_SHIFT;
optimal_pll_shift = i % EFX_SDIO_MAX_PLL_SHIFT;
found = true;
break;
}
}
}
dev_info(&host->pdev->dev,
"SDR mode: Selected first occurrence of longest sequence at index %d (sample_count=%d, pll_shift=%d)\n",
optimal_sample_count * EFX_SDIO_MAX_PLL_SHIFT + optimal_pll_shift,
optimal_sample_count, optimal_pll_shift);
}
kfree(flat_map);
}
/* Store optimal configuration */
host->optimal_sample_count = optimal_sample_count;
@@ -371,7 +454,6 @@ int efx_sdio_find_optimal_timing(struct efx_sdio_host *host,
"Optimal timing found: sample_count=%u, pll_shift=%u, margin=%d\n",
optimal_sample_count, optimal_pll_shift, max_consecutive_length);
kfree(optimal_rows);
return 0;
}
@@ -477,6 +559,18 @@ int efx_sdio_execute_tuning(struct mmc_host *mmc, u32 opcode)
success = efx_sdio_execute_custom_tuning_command(host, bus_width);
}
// for non-DDR modes, additional checking needed to ensure result is successful
// success only 1 if bit 8 and bit 9 of EFX_SDIO_HOST_ADJUSTMENT register are both 1
if (success && mmc->ios.timing != MMC_TIMING_UHS_DDR50) {
u32 host_adjustment = efx_sdio_readl(host, EFX_SDIO_HOST_ADJUSTMENT);
if ((host_adjustment & 0x300) != 0x300) {
success = 0; // Mark as failure if either bit 8 or bit 9 is not set
dev_dbg(&host->pdev->dev,
"Additional check failed: host_adjustment=0x%08x\n",
host_adjustment);
}
}
/* Update result map */
if (!success) {
dev_dbg(&host->pdev->dev,
@@ -487,7 +581,6 @@ int efx_sdio_execute_tuning(struct mmc_host *mmc, u32 opcode)
tuning_result_map[sample_count][pll_shift] = 0;
consecutive_passes = 0;
} else {
//SA tuning_result_map[sample_count][pll_shift] = 1;
consecutive_passes++;
}