[PATCH] mtd: brcmnand: set initial ECC params based on info from HW

Florian Fainelli f.fainelli at gmail.com
Mon Feb 1 10:09:28 PST 2016


On 01/02/16 09:53, Brian Norris wrote:
> On Wed, Jan 27, 2016 at 08:58:20AM +0100, Rafał Miłecki wrote:
>> So far we were depending on nand_dt_init getting ECC info from DT and
>> setting it in ECC struct. It is possible to simply read this info from
>> hardware registers which makes adding support for new hardware way
>> easier (no more guessing & trying all combinations).
>> Please note it still gives a precedence to DT which was overwrite
>> whatever was initially set by the driver.
> 
> Precedence of DT is extremely important, but even with that, I'm not
> sure it's a great idea to support this by default. It's very error prone
> to rely on HW defaults at boot time; they might be left at their reset
> values.

I second that, that are platforms which are pretty much guaranteed to be
broken.

> 
> Anyway, if we are really making the DT properties optional, we'd need to
> modify Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt to
> reflect this.

The Marvell PXA3XX NAND controller has a "marvell,nand-keep-config"
which seems to be in par with what Rafal is after here, maybe it would
be worth standardizing/mimicing that kind of property for the brcmnand
driver?

> 
>> Signed-off-by: Rafał Miłecki <zajec5 at gmail.com>
>> ---
>> It took me hours to figure out how to setup NAND on my D-Link DIR-885L.
>> This should be very helpful for ppl adding new devices support.
> 
> What if we just print out the hardware defaults when we bail out due to
> missing ECC DT properties? Then developers can choose to set up their DT
> with these properties, if those are actually proven correct. Would that
> save you the hours of setup you mention?
> 
> Another option: maybe a commandline boot flag?

One could argue that the bootloader is patf of the platform, and so, if
the bootloader is known to provide good defaults, then a DT property
could be appropriate. The commandline boot flag just sounds a little
impractical, in case you have more than one NAND controller, but how
common is that?

> 
>> ---
>>  drivers/mtd/nand/brcmnand/brcmnand.c | 14 ++++++++++++++
>>  1 file changed, 14 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
>> index 844fc07..f358cda 100644
>> --- a/drivers/mtd/nand/brcmnand/brcmnand.c
>> +++ b/drivers/mtd/nand/brcmnand/brcmnand.c
>> @@ -615,6 +615,17 @@ static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
>>  	return mask << NAND_ACC_CONTROL_ECC_SHIFT;
>>  }
>>  
>> +static int brcmnand_get_ecc_strength(struct brcmnand_host *host)
>> +{
>> +	struct brcmnand_controller *ctrl = host->ctrl;
>> +	u32 mask = brcmnand_ecc_level_mask(ctrl);
>> +	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
>> +						  BRCMNAND_CS_ACC_CONTROL);
>> +
>> +	return (nand_readreg(ctrl, acc_control_offs) & mask) >>
>> +		NAND_ACC_CONTROL_ECC_SHIFT;
> 
> This is wrong. You ignore Hamming ECC and 1K sector sizes. With Hamming
> ECC, you need to translate a value of '15' to 'strength = 1'. And with
> 1K sector sizes, you need to translate a value of 'N' to 'strength = N *
> 2'.
> 
>> +}
>> +
>>  static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
>>  {
>>  	struct brcmnand_controller *ctrl = host->ctrl;
>> @@ -1960,6 +1971,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
>>  	nand_writereg(ctrl, cfg_offs,
>>  		      nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
>>  
>> +	chip->ecc.strength = brcmnand_get_ecc_strength(host);
>> +	chip->ecc.size = brcmnand_get_sector_size_1k(host) ? 1024 : 512;
>> +
>>  	if (nand_scan_ident(mtd, 1, NULL))
>>  		return -ENXIO;
>>  
> 
> Brian
> 


-- 
Florian



More information about the linux-mtd mailing list