[PATCH linux dev-5.3 13/13] mtd: spi-nor: aspeed: add support for AST2600 training

Cédric Le Goater clg at kaod.org
Thu Sep 26 16:59:51 AEST 2019


On 26/09/2019 04:14, Andrew Jeffery wrote:
> 
> 
> On Wed, 25 Sep 2019, at 22:12, Cédric Le Goater wrote:
> 
> Would prefer some description here, this patch gets complex. At least for me,
> probably because I'm not familiar with the flash training routine.

Joel, Could you please squeeze in this in the commit log ? I can resend
just that patch if you prefer, once you have pushed the series. This is 
just an optimization.

Thanks,

C.  


The training consists in finding the appropriate read timing delays for 
the HCLK dividers 2, 3, 4, and 5 and store the results in the Read Timing 
Compensation register. The previous SoC AST2500 and AST2400 were covering 
a broader HCLK range [ 1 - 5 ] because the AHB frequency was lower.

The algo first reads a golden buffer at low speed and then performs reads 
with different clocks and delay cycles settings to find a breaking point.
This selects the default clock frequency for the CEx control register.
The current settings are 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. We might change the approach depending on the 
results on other systems.

Only CS0 is taken into account for the moment.

> 
>> Signed-off-by: Cédric Le Goater <clg at kaod.org>
>> ---
>>  drivers/mtd/spi-nor/aspeed-smc.c | 68 ++++++++++++++++++++++++++++++++
>>  1 file changed, 68 insertions(+)
>>
>> diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
>> index 48164d819a37..e9bc89755912 100644
>> --- a/drivers/mtd/spi-nor/aspeed-smc.c
>> +++ b/drivers/mtd/spi-nor/aspeed-smc.c
>> @@ -142,6 +142,9 @@ static u32 aspeed_smc_segment_end_ast2600(
>>  static u32 aspeed_smc_segment_reg_ast2600(
>>  	struct aspeed_smc_controller *controller, u32 start, u32 end);
>>  
>> +static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip 
>> *chip,
>> +	      u32 hdiv, const u8 *golden_buf, u8 *test_buf);
>> +
>>  static const struct aspeed_smc_info fmc_2600_info = {
>>  	.maxsize = 256 * 1024 * 1024,
>>  	.nce = 3,
>> @@ -149,7 +152,11 @@ static const struct aspeed_smc_info fmc_2600_info 
>> = {
>>  	.we0 = 16,
>>  	.ctl0 = 0x10,
>>  	.timing = 0x94,
>> +	.hclk_mask = 0xf0fff0ff,
>> +	.hdiv_max = 2,
>>  	.set_4b = aspeed_smc_chip_set_4b,
>> +	.optimize_read = aspeed_smc_optimize_read,
>> +	.calibrate = aspeed_smc_calibrate_reads_ast2600,
>>  	.segment_start = aspeed_smc_segment_start_ast2600,
>>  	.segment_end = aspeed_smc_segment_end_ast2600,
>>  	.segment_reg = aspeed_smc_segment_reg_ast2600,
>> @@ -162,7 +169,11 @@ static const struct aspeed_smc_info spi_2600_info 
>> = {
>>  	.we0 = 16,
>>  	.ctl0 = 0x10,
>>  	.timing = 0x94,
>> +	.hclk_mask = 0xf0fff0ff,
>> +	.hdiv_max = 2,
>>  	.set_4b = aspeed_smc_chip_set_4b,
>> +	.optimize_read = aspeed_smc_optimize_read,
>> +	.calibrate = aspeed_smc_calibrate_reads_ast2600,
>>  	.segment_start = aspeed_smc_segment_start_ast2600,
>>  	.segment_end = aspeed_smc_segment_end_ast2600,
>>  	.segment_reg = aspeed_smc_segment_reg_ast2600,
>> @@ -1101,6 +1112,63 @@ static int aspeed_smc_optimize_read(struct 
>> aspeed_smc_chip *chip,
>>  	return 0;
>>  }
>>  
>> +#define TIMING_DELAY_DI         BIT(3)
>> +#define TIMING_DELAY_HCYCLE_MAX     5
>> +
>> +static int aspeed_smc_calibrate_reads_ast2600(struct aspeed_smc_chip 
>> *chip, u32 hdiv,
>> +					      const u8 *golden_buf, u8 *test_buf)
>> +{
>> +	struct aspeed_smc_controller *controller = chip->controller;
>> +	const struct aspeed_smc_info *info = controller->info;
>> +	int hcycle;
>> +	u32 shift = (hdiv - 2) << 3;
>> +	u32 mask = ~(0xfu << shift);
>> +	u32 fread_timing_val = 0;
>> +
>> +	for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) {
>> +		int delay_ns;
>> +		bool pass = false;
>> +
>> +		fread_timing_val &= mask;
>> +		fread_timing_val |= hcycle << shift;
>> +
>> +		/* no DI input delay first  */
>> +		writel(fread_timing_val, controller->regs + info->timing);
>> +		pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
>> +		dev_dbg(chip->nor.dev,
>> +			"  * [%08x] %d HCLK delay, DI delay none : %s",
>> +			fread_timing_val, hcycle, pass ? "PASS" : "FAIL");
>> +		if (pass)
>> +			return 0;
>> +
>> +		/* 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));
>> +			fread_timing_val |= delay_ns << (4 + shift);
>> +
>> +			writel(fread_timing_val, controller->regs + info->timing);
>> +			pass = aspeed_smc_check_reads(chip, golden_buf, test_buf);
>> +			dev_dbg(chip->nor.dev,
>> +				"  * [%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;
>> +		}
>> +	}
>> +
>> +	/* No good setting for this frequency */
>> +	return -1;
>> +}
>> +
>>  static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
>>  {
>>  	struct aspeed_smc_controller *controller = chip->controller;
>> -- 
>> 2.21.0
>>
>>



More information about the openbmc mailing list