Skip to content

Commit eeaec1e

Browse files
legoaterbroonie
authored andcommitted
spi: aspeed: Calibrate read timings
To accommodate the different response time of SPI transfers on different boards and different SPI NOR devices, the Aspeed controllers provide a set of Read Timing Compensation registers to tune the timing delays depending on the frequency being used. The AST2600 SoC has one of these registers per device. On the AST2500 and AST2400 SoCs, the timing register is shared by all devices which is problematic to get good results other than for one device. The algorithm first reads a golden buffer at low speed and then performs reads with different clocks and delay cycle settings to find a breaking point. This selects a default good frequency for the CEx control register. The current settings are a bit optimistic as we pick the first delay giving good results. A safer approach would be to determine an interval and choose the middle value. Calibration is performed when the direct mapping for reads is created. Since the underlying spi-nor object needs to be initialized to create the spi_mem operation for direct mapping, we should be fine. Having a specific API would clarify the requirements though. Cc: Pratyush Yadav <[email protected]> Reviewed-by: Joel Stanley <[email protected]> Tested-by: Joel Stanley <[email protected]> Tested-by: Tao Ren <[email protected]> Tested-by: Jae Hyun Yoo <[email protected]> Signed-off-by: Cédric Le Goater <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 53526ab commit eeaec1e

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

drivers/spi/spi-aspeed-smc.c

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#define CTRL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI only */
3434
#define CTRL_IO_DUMMY_SET(dummy) \
3535
(((((dummy) >> 2) & 0x1) << 14) | (((dummy) & 0x3) << 6))
36+
#define CTRL_FREQ_SEL_SHIFT 8
37+
#define CTRL_FREQ_SEL_MASK GENMASK(11, CTRL_FREQ_SEL_SHIFT)
3638
#define CTRL_CE_STOP_ACTIVE BIT(2)
3739
#define CTRL_IO_MODE_CMD_MASK GENMASK(1, 0)
3840
#define CTRL_IO_MODE_NORMAL 0x0
@@ -45,6 +47,9 @@
4547
/* CEx Address Decoding Range Register */
4648
#define CE0_SEGMENT_ADDR_REG 0x30
4749

50+
/* CEx Read timing compensation register */
51+
#define CE0_TIMING_COMPENSATION_REG 0x94
52+
4853
enum aspeed_spi_ctl_reg_value {
4954
ASPEED_SPI_BASE,
5055
ASPEED_SPI_READ,
@@ -70,10 +75,15 @@ struct aspeed_spi_data {
7075
bool hastype;
7176
u32 mode_bits;
7277
u32 we0;
78+
u32 timing;
79+
u32 hclk_mask;
80+
u32 hdiv_max;
7381

7482
u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg);
7583
u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg);
7684
u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end);
85+
int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv,
86+
const u8 *golden_buf, u8 *test_buf);
7787
};
7888

7989
#define ASPEED_SPI_MAX_NUM_CS 5
@@ -525,6 +535,8 @@ static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip,
525535
return 0;
526536
}
527537

538+
static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip);
539+
528540
static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
529541
{
530542
struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->master);
@@ -573,6 +585,8 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
573585
chip->ctl_val[ASPEED_SPI_READ] = ctl_val;
574586
writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl);
575587

588+
ret = aspeed_spi_do_calibration(chip);
589+
576590
dev_info(aspi->dev, "CE%d read buswidth:%d [0x%08x]\n",
577591
chip->cs, op->data.buswidth, chip->ctl_val[ASPEED_SPI_READ]);
578592

@@ -825,6 +839,249 @@ static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi,
825839
((end - 1) & AST2600_SEG_ADDR_MASK);
826840
}
827841

842+
/*
843+
* Read timing compensation sequences
844+
*/
845+
846+
#define CALIBRATE_BUF_SIZE SZ_16K
847+
848+
static bool aspeed_spi_check_reads(struct aspeed_spi_chip *chip,
849+
const u8 *golden_buf, u8 *test_buf)
850+
{
851+
int i;
852+
853+
for (i = 0; i < 10; i++) {
854+
memcpy_fromio(test_buf, chip->ahb_base, CALIBRATE_BUF_SIZE);
855+
if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0) {
856+
#if defined(VERBOSE_DEBUG)
857+
print_hex_dump_bytes(DEVICE_NAME " fail: ", DUMP_PREFIX_NONE,
858+
test_buf, 0x100);
859+
#endif
860+
return false;
861+
}
862+
}
863+
return true;
864+
}
865+
866+
#define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8))
867+
868+
/*
869+
* The timing register is shared by all devices. Only update for CE0.
870+
*/
871+
static int aspeed_spi_calibrate(struct aspeed_spi_chip *chip, u32 hdiv,
872+
const u8 *golden_buf, u8 *test_buf)
873+
{
874+
struct aspeed_spi *aspi = chip->aspi;
875+
const struct aspeed_spi_data *data = aspi->data;
876+
int i;
877+
int good_pass = -1, pass_count = 0;
878+
u32 shift = (hdiv - 1) << 2;
879+
u32 mask = ~(0xfu << shift);
880+
u32 fread_timing_val = 0;
881+
882+
/* Try HCLK delay 0..5, each one with/without delay and look for a
883+
* good pair.
884+
*/
885+
for (i = 0; i < 12; i++) {
886+
bool pass;
887+
888+
if (chip->cs == 0) {
889+
fread_timing_val &= mask;
890+
fread_timing_val |= FREAD_TPASS(i) << shift;
891+
writel(fread_timing_val, aspi->regs + data->timing);
892+
}
893+
pass = aspeed_spi_check_reads(chip, golden_buf, test_buf);
894+
dev_dbg(aspi->dev,
895+
" * [%08x] %d HCLK delay, %dns DI delay : %s",
896+
fread_timing_val, i / 2, (i & 1) ? 0 : 4,
897+
pass ? "PASS" : "FAIL");
898+
if (pass) {
899+
pass_count++;
900+
if (pass_count == 3) {
901+
good_pass = i - 1;
902+
break;
903+
}
904+
} else {
905+
pass_count = 0;
906+
}
907+
}
908+
909+
/* No good setting for this frequency */
910+
if (good_pass < 0)
911+
return -1;
912+
913+
/* We have at least one pass of margin, let's use first pass */
914+
if (chip->cs == 0) {
915+
fread_timing_val &= mask;
916+
fread_timing_val |= FREAD_TPASS(good_pass) << shift;
917+
writel(fread_timing_val, aspi->regs + data->timing);
918+
}
919+
dev_dbg(aspi->dev, " * -> good is pass %d [0x%08x]",
920+
good_pass, fread_timing_val);
921+
return 0;
922+
}
923+
924+
static bool aspeed_spi_check_calib_data(const u8 *test_buf, u32 size)
925+
{
926+
const u32 *tb32 = (const u32 *)test_buf;
927+
u32 i, cnt = 0;
928+
929+
/* We check if we have enough words that are neither all 0
930+
* nor all 1's so the calibration can be considered valid.
931+
*
932+
* I use an arbitrary threshold for now of 64
933+
*/
934+
size >>= 2;
935+
for (i = 0; i < size; i++) {
936+
if (tb32[i] != 0 && tb32[i] != 0xffffffff)
937+
cnt++;
938+
}
939+
return cnt >= 64;
940+
}
941+
942+
static const u32 aspeed_spi_hclk_divs[] = {
943+
0xf, /* HCLK */
944+
0x7, /* HCLK/2 */
945+
0xe, /* HCLK/3 */
946+
0x6, /* HCLK/4 */
947+
0xd, /* HCLK/5 */
948+
};
949+
950+
#define ASPEED_SPI_HCLK_DIV(i) \
951+
(aspeed_spi_hclk_divs[(i) - 1] << CTRL_FREQ_SEL_SHIFT)
952+
953+
static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip)
954+
{
955+
struct aspeed_spi *aspi = chip->aspi;
956+
const struct aspeed_spi_data *data = aspi->data;
957+
u32 ahb_freq = aspi->clk_freq;
958+
u32 max_freq = chip->clk_freq;
959+
u32 ctl_val;
960+
u8 *golden_buf = NULL;
961+
u8 *test_buf = NULL;
962+
int i, rc, best_div = -1;
963+
964+
dev_dbg(aspi->dev, "calculate timing compensation - AHB freq: %d MHz",
965+
ahb_freq / 1000000);
966+
967+
/*
968+
* use the related low frequency to get check calibration data
969+
* and get golden data.
970+
*/
971+
ctl_val = chip->ctl_val[ASPEED_SPI_READ] & data->hclk_mask;
972+
writel(ctl_val, chip->ctl);
973+
974+
test_buf = kzalloc(CALIBRATE_BUF_SIZE * 2, GFP_KERNEL);
975+
if (!test_buf)
976+
return -ENOMEM;
977+
978+
golden_buf = test_buf + CALIBRATE_BUF_SIZE;
979+
980+
memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE);
981+
if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) {
982+
dev_info(aspi->dev, "Calibration area too uniform, using low speed");
983+
goto no_calib;
984+
}
985+
986+
#if defined(VERBOSE_DEBUG)
987+
print_hex_dump_bytes(DEVICE_NAME " good: ", DUMP_PREFIX_NONE,
988+
golden_buf, 0x100);
989+
#endif
990+
991+
/* Now we iterate the HCLK dividers until we find our breaking point */
992+
for (i = ARRAY_SIZE(aspeed_spi_hclk_divs); i > data->hdiv_max - 1; i--) {
993+
u32 tv, freq;
994+
995+
freq = ahb_freq / i;
996+
if (freq > max_freq)
997+
continue;
998+
999+
/* Set the timing */
1000+
tv = chip->ctl_val[ASPEED_SPI_READ] | ASPEED_SPI_HCLK_DIV(i);
1001+
writel(tv, chip->ctl);
1002+
dev_dbg(aspi->dev, "Trying HCLK/%d [%08x] ...", i, tv);
1003+
rc = data->calibrate(chip, i, golden_buf, test_buf);
1004+
if (rc == 0)
1005+
best_div = i;
1006+
}
1007+
1008+
/* Nothing found ? */
1009+
if (best_div < 0) {
1010+
dev_warn(aspi->dev, "No good frequency, using dumb slow");
1011+
} else {
1012+
dev_dbg(aspi->dev, "Found good read timings at HCLK/%d", best_div);
1013+
1014+
/* Record the freq */
1015+
for (i = 0; i < ASPEED_SPI_MAX; i++)
1016+
chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) |
1017+
ASPEED_SPI_HCLK_DIV(best_div);
1018+
}
1019+
1020+
no_calib:
1021+
writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl);
1022+
kfree(test_buf);
1023+
return 0;
1024+
}
1025+
1026+
#define TIMING_DELAY_DI BIT(3)
1027+
#define TIMING_DELAY_HCYCLE_MAX 5
1028+
#define TIMING_REG_AST2600(chip) \
1029+
((chip)->aspi->regs + (chip)->aspi->data->timing + \
1030+
(chip)->cs * 4)
1031+
1032+
static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv,
1033+
const u8 *golden_buf, u8 *test_buf)
1034+
{
1035+
struct aspeed_spi *aspi = chip->aspi;
1036+
int hcycle;
1037+
u32 shift = (hdiv - 2) << 3;
1038+
u32 mask = ~(0xfu << shift);
1039+
u32 fread_timing_val = 0;
1040+
1041+
for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
1042+
int delay_ns;
1043+
bool pass = false;
1044+
1045+
fread_timing_val &= mask;
1046+
fread_timing_val |= hcycle << shift;
1047+
1048+
/* no DI input delay first */
1049+
writel(fread_timing_val, TIMING_REG_AST2600(chip));
1050+
pass = aspeed_spi_check_reads(chip, golden_buf, test_buf);
1051+
dev_dbg(aspi->dev,
1052+
" * [%08x] %d HCLK delay, DI delay none : %s",
1053+
fread_timing_val, hcycle, pass ? "PASS" : "FAIL");
1054+
if (pass)
1055+
return 0;
1056+
1057+
/* Add DI input delays */
1058+
fread_timing_val &= mask;
1059+
fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift;
1060+
1061+
for (delay_ns = 0; delay_ns < 0x10; delay_ns++) {
1062+
fread_timing_val &= ~(0xf << (4 + shift));
1063+
fread_timing_val |= delay_ns << (4 + shift);
1064+
1065+
writel(fread_timing_val, TIMING_REG_AST2600(chip));
1066+
pass = aspeed_spi_check_reads(chip, golden_buf, test_buf);
1067+
dev_dbg(aspi->dev,
1068+
" * [%08x] %d HCLK delay, DI delay %d.%dns : %s",
1069+
fread_timing_val, hcycle, (delay_ns + 1) / 2,
1070+
(delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL");
1071+
/*
1072+
* TODO: This is optimistic. We should look
1073+
* for a working interval and save the middle
1074+
* value in the read timing register.
1075+
*/
1076+
if (pass)
1077+
return 0;
1078+
}
1079+
}
1080+
1081+
/* No good setting for this frequency */
1082+
return -1;
1083+
}
1084+
8281085
/*
8291086
* Platform definitions
8301087
*/
@@ -833,6 +1090,10 @@ static const struct aspeed_spi_data ast2400_fmc_data = {
8331090
.hastype = true,
8341091
.we0 = 16,
8351092
.ctl0 = CE0_CTRL_REG,
1093+
.timing = CE0_TIMING_COMPENSATION_REG,
1094+
.hclk_mask = 0xfffff0ff,
1095+
.hdiv_max = 1,
1096+
.calibrate = aspeed_spi_calibrate,
8361097
.segment_start = aspeed_spi_segment_start,
8371098
.segment_end = aspeed_spi_segment_end,
8381099
.segment_reg = aspeed_spi_segment_reg,
@@ -843,6 +1104,10 @@ static const struct aspeed_spi_data ast2400_spi_data = {
8431104
.hastype = false,
8441105
.we0 = 0,
8451106
.ctl0 = 0x04,
1107+
.timing = 0x14,
1108+
.hclk_mask = 0xfffff0ff,
1109+
.hdiv_max = 1,
1110+
.calibrate = aspeed_spi_calibrate,
8461111
/* No segment registers */
8471112
};
8481113

@@ -851,6 +1116,10 @@ static const struct aspeed_spi_data ast2500_fmc_data = {
8511116
.hastype = true,
8521117
.we0 = 16,
8531118
.ctl0 = CE0_CTRL_REG,
1119+
.timing = CE0_TIMING_COMPENSATION_REG,
1120+
.hclk_mask = 0xffffd0ff,
1121+
.hdiv_max = 1,
1122+
.calibrate = aspeed_spi_calibrate,
8541123
.segment_start = aspeed_spi_segment_start,
8551124
.segment_end = aspeed_spi_segment_end,
8561125
.segment_reg = aspeed_spi_segment_reg,
@@ -861,6 +1130,10 @@ static const struct aspeed_spi_data ast2500_spi_data = {
8611130
.hastype = false,
8621131
.we0 = 16,
8631132
.ctl0 = CE0_CTRL_REG,
1133+
.timing = CE0_TIMING_COMPENSATION_REG,
1134+
.hclk_mask = 0xffffd0ff,
1135+
.hdiv_max = 1,
1136+
.calibrate = aspeed_spi_calibrate,
8641137
.segment_start = aspeed_spi_segment_start,
8651138
.segment_end = aspeed_spi_segment_end,
8661139
.segment_reg = aspeed_spi_segment_reg,
@@ -872,6 +1145,10 @@ static const struct aspeed_spi_data ast2600_fmc_data = {
8721145
.mode_bits = SPI_RX_QUAD | SPI_RX_QUAD,
8731146
.we0 = 16,
8741147
.ctl0 = CE0_CTRL_REG,
1148+
.timing = CE0_TIMING_COMPENSATION_REG,
1149+
.hclk_mask = 0xf0fff0ff,
1150+
.hdiv_max = 2,
1151+
.calibrate = aspeed_spi_ast2600_calibrate,
8751152
.segment_start = aspeed_spi_segment_ast2600_start,
8761153
.segment_end = aspeed_spi_segment_ast2600_end,
8771154
.segment_reg = aspeed_spi_segment_ast2600_reg,
@@ -883,6 +1160,10 @@ static const struct aspeed_spi_data ast2600_spi_data = {
8831160
.mode_bits = SPI_RX_QUAD | SPI_RX_QUAD,
8841161
.we0 = 16,
8851162
.ctl0 = CE0_CTRL_REG,
1163+
.timing = CE0_TIMING_COMPENSATION_REG,
1164+
.hclk_mask = 0xf0fff0ff,
1165+
.hdiv_max = 2,
1166+
.calibrate = aspeed_spi_ast2600_calibrate,
8861167
.segment_start = aspeed_spi_segment_ast2600_start,
8871168
.segment_end = aspeed_spi_segment_ast2600_end,
8881169
.segment_reg = aspeed_spi_segment_ast2600_reg,

0 commit comments

Comments
 (0)