#define TIMING_DELAY_DI BIT(3)
#define TIMING_DELAY_HCYCLE_MAX 5
+#define TIMING_DELAY_INPUT_MAX 16
#define TIMING_REG_AST2600(chip) \
((chip)->aspi->regs + (chip)->aspi->data->timing + \
(chip)->cs * 4)
+/*
+ * This function returns the center point of the longest
+ * continuous "pass" interval within the buffer. The interval
+ * must contains the highest number of consecutive "pass"
+ * results and not span across multiple rows.
+ */
+static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols,
+ u8 buf[rows][cols])
+{
+ int r = 0, c = 0;
+ int max = 0;
+ int i, j;
+
+ for (i = 0; i < rows; i++) {
+ for (j = 0; j < cols;) {
+ int k = j;
+
+ while (k < cols && buf[i][k])
+ k++;
+
+ if (k - j > max) {
+ max = k - j;
+ r = i;
+ c = j + (k - j) / 2;
+ }
+
+ j = k + 1;
+ }
+ }
+
+ return max > 4 ? r * cols + c : 0;
+}
+
static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv,
const u8 *golden_buf, u8 *test_buf)
{
struct aspeed_spi *aspi = chip->aspi;
int hcycle;
+ int delay_ns;
u32 shift = (hdiv - 2) << 3;
- u32 mask = ~(0xfu << shift);
+ u32 mask = ~(0xffu << shift);
u32 fread_timing_val = 0;
+ u8 calib_res[6][17] = {0};
+ u32 calib_point;
for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
- int delay_ns;
bool pass = false;
fread_timing_val &= mask;
" * [%08x] %d HCLK delay, DI delay none : %s",
fread_timing_val, hcycle, pass ? "PASS" : "FAIL");
if (pass)
- return 0;
+ calib_res[hcycle][0] = 1;
/* Add DI input delays */
fread_timing_val &= mask;
fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift;
- for (delay_ns = 0; delay_ns < 0x10; delay_ns++) {
- fread_timing_val &= ~(0xf << (4 + shift));
+ for (delay_ns = 0; delay_ns < TIMING_DELAY_INPUT_MAX; delay_ns++) {
+ fread_timing_val &= ~(0xfu << (4 + shift));
fread_timing_val |= delay_ns << (4 + shift);
writel(fread_timing_val, TIMING_REG_AST2600(chip));
" * [%08x] %d HCLK delay, DI delay %d.%dns : %s",
fread_timing_val, hcycle, (delay_ns + 1) / 2,
(delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL");
- /*
- * TODO: This is optimistic. We should look
- * for a working interval and save the middle
- * value in the read timing register.
- */
+
if (pass)
- return 0;
+ calib_res[hcycle][delay_ns + 1] = 1;
}
}
+ calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, calib_res);
/* No good setting for this frequency */
- return -1;
+ if (calib_point == 0)
+ return -1;
+
+ hcycle = calib_point / 17;
+ delay_ns = calib_point % 17;
+
+ fread_timing_val = (TIMING_DELAY_DI | hcycle | (delay_ns << 4)) << shift;
+
+ dev_dbg(aspi->dev, "timing val: %08x, final hcycle: %d, delay_ns: %d\n",
+ fread_timing_val, hcycle, delay_ns);
+
+ writel(fread_timing_val, TIMING_REG_AST2600(chip));
+
+ return 0;
}
/*