Re: [PATCH v3 14/37] mtd: nand: denali: support "nand-ecc-strength" DT property

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On Fri, 14 Apr 2017 16:57:23 +0900
Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx> wrote:

> Hi Boris,
> 
> 
> 2017-04-11 16:56 GMT+09:00 Boris Brezillon <boris.brezillon@xxxxxxxxxxxxxxxxxx>:
> > Hi Masahiro,
> >
> > On Tue, 11 Apr 2017 15:19:21 +0900
> > Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx> wrote:
> >  
> >> Hi Boris,
> >>
> >>
> >>
> >> 2017-04-10 1:33 GMT+09:00 Boris Brezillon <boris.brezillon@xxxxxxxxxxxxxxxxxx>:  
> >> > On Mon, 3 Apr 2017 12:16:34 +0900
> >> > Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx> wrote:
> >> >  
> >> >> Hi Boris,
> >> >>
> >> >>
> >> >>
> >> >> 2017-03-31 18:46 GMT+09:00 Boris Brezillon <boris.brezillon@xxxxxxxxxxxxxxxxxx>:
> >> >>  
> >> >> > You can try something like that when no explicit ecc.strength and
> >> >> > ecc.size has been set in the DT and when ECC_MAXIMIZE was not passed.
> >> >> >
> >> >> > static int
> >> >> > denali_get_closest_ecc_strength(struct denali_nand_info *denali,
> >> >> >                                 int strength)
> >> >> > {
> >> >> >         /*
> >> >> >          * Whatever you need to select a strength that is greater than
> >> >> >          * or equal to strength.
> >> >> >          */
> >> >> >
> >> >> >         return X;
> >> >> > }  
> >> >>
> >> >>
> >> >> Is here anything specific to Denali?  
> >> >
> >> > Well, only the denali driver knows what the hardware supports, though
> >> > having a generic function that takes a table of supported strengths
> >> > would work.
> >> >  
> >> >>
> >> >>  
> >> >> > static int denali_try_to_match_ecc_req(struct denali_nand_info *denali)
> >> >> > {
> >> >> >         struct nand_chip *chip = &denali->nand;
> >> >> >         struct mtd_info *mtd = nand_to_mtd(chip);
> >> >> >         int max_ecc_bytes = mtd->oobsize - denali->bbtskipbytes;
> >> >> >         int ecc_steps, ecc_strength, ecc_bytes;
> >> >> >         int ecc_size = chip->ecc_step_ds;
> >> >> >         int ecc_strength = chip->ecc_strength_ds;
> >> >> >
> >> >> >         /*
> >> >> >          * No information provided by the NAND chip, let the core
> >> >> >          * maximize the strength.
> >> >> >          */
> >> >> >         if (!ecc_size || !ecc_strength)
> >> >> >                 return -ENOTSUPP;
> >> >> >
> >> >> >         if (ecc_size > 512)
> >> >> >                 ecc_size = 1024;
> >> >> >         else
> >> >> >                 ecc_size = 512;
> >> >> >
> >> >> >         /* Adjust ECC step size based on hardware support. */
> >> >> >         if (ecc_size == 1024 &&
> >> >> >             !(denali->caps & DENALI_CAP_ECC_SIZE_1024))
> >> >> >                 ecc_size = 512;
> >> >> >         else if(ecc_size == 512 &&
> >> >> >                 !(denali->caps & DENALI_CAP_ECC_SIZE_512))
> >> >> >                 ecc_size = 1024;
> >> >> >
> >> >> >         if (ecc_size < chip->ecc_size_ds) {
> >> >> >                 /*
> >> >> >                  * When the selected size if smaller than the expected
> >> >> >                  * one we try to use the same strength but on 512 blocks
> >> >> >                  * so that we can still fix the same number of errors
> >> >> >                  * even if they are concentrated in the first 512bytes
> >> >> >                  * of a 1024bytes portion.
> >> >> >                  */
> >> >> >                 ecc_strength = chip->ecc_strength_ds;
> >> >> >                 ecc_strength = denali_get_closest_ecc_strength(denali,
> >> >> >                                                                ecc_strength);
> >> >> >         } else {
> >> >> >                 /* Always prefer 1024bytes ECC blocks when possible. */
> >> >> >                 if (ecc_size != 1024 &&
> >> >> >                     (denali->caps & DENALI_CAP_ECC_SIZE_1024) &&
> >> >> >                     mtd->writesize > 1024)
> >> >> >                         ecc_size = 1024;
> >> >> >
> >> >> >                 /*
> >> >> >                  * Adjust the strength based on the selected ECC step
> >> >> >                  * size.
> >> >> >                  */
> >> >> >                 ecc_strength = DIV_ROUND_UP(ecc_size,
> >> >> >                                             chip->ecc_step_ds) *
> >> >> >                                chip->ecc_strength_ds;
> >> >> >         }
> >> >> >
> >> >> >         ecc_bytes = denali_calc_ecc_bytes(ecc_size,
> >> >> >                                           ecc_strength);
> >> >> >         ecc_bytes *= mtd->writesize / ecc_size;
> >> >> >
> >> >> >         /*
> >> >> >          * If we don't have enough space, let the core maximize
> >> >> >          * the strength.
> >> >> >          */
> >> >> >         if (ecc_bytes > max_ecc_bytes)
> >> >> >                 return -ENOTSUPP;
> >> >> >
> >> >> >         chip->ecc.strength = ecc_strength;
> >> >> >         chip->ecc.size = ecc_size;
> >> >> >
> >> >> >         return 0;
> >> >> > }  
> >> >>
> >> >>
> >> >> As a whole, this does not seem to driver-specific.  
> >> >
> >> > It's almost controller-agnostic, except for the denali_calc_ecc_bytes()
> >> > function, but I guess we could ask drivers to implement a hook that is
> >> > passed the ECC step size and strength and returns the associated
> >> > number of ECC bytes.
> >> >  
> >> >>
> >> >>
> >> >> [1] A driver provides some pairs of (ecc_strength, ecc_size)
> >> >>     it can support.
> >> >>
> >> >> [2] The core framework knows the chip's requirement
> >> >>     (ecc_strength_ds, ecc_size_ds).
> >> >>
> >> >>
> >> >> Then, the core framework provides a function
> >> >> to return a most recommended (ecc_strength, ecc_size).
> >> >>
> >> >>
> >> >>
> >> >> struct nand_ecc_spec {
> >> >>        int ecc_strength;
> >> >>        int ecc_size;
> >> >> };
> >> >>
> >> >> /*
> >> >>  * This function choose the most recommented (ecc_str, ecc_size)
> >> >>  * "recommended" means: minimum ecc stregth that meets
> >> >>  * the chip's requirment.
> >> >>  *
> >> >>  *
> >> >>  * @chip   - nand_chip
> >> >>  * @controller_ecc_spec - Array of (ecc_str, ecc_size) supported by the
> >> >>                           controller. (terminated by NULL as sentinel)
> >> >>  */
> >> >> struct nand_ecc_spec * nand_try_to_match_ecc_req(struct nand_chip *chip,
> >> >>                                                  struct nand_ecc_spec
> >> >> *controller_ecc_spec)
> >> >> {
> >> >>       /*
> >> >>        * Return the pointer to the most recommended
> >> >>        * struct nand_ecc_spec.
> >> >>        * If nothing suitable found, return NULL.
> >> >>        */
> >> >> }
> >> >>  
> >> >
> >> > I like the idea, except I would do this slightly differently to avoid
> >> > declaring all combinations of stepsize and strengths
> >> >
> >> > struct nand_ecc_stepsize_info {
> >> >         int stepsize;
> >> >         int nstrengths;
> >> >         int *strengths;
> >> > };
> >> >
> >> > struct nand_ecc_engine_caps {
> >> >         int nstepsizes;
> >> >         struct nand_ecc_stepsize_info *stepsizes;
> >> >         int (*calc_ecc_bytes)(int stepsize, int strength);
> >> > };
> >> >
> >> > int nand_try_to_match_ecc_req(struct nand_chip *chip,
> >> >                               const struct nand_ecc_engine_caps *caps,
> >> >                               struct nand_ecc_spec *spec)
> >> > {
> >> >         /*
> >> >          * Find the most appropriate setting based on the ECC engine
> >> >          * caps and fill the spec object accordingly.
> >> >          * Returns 0 in case of success and a negative error code
> >> >          * otherwise.
> >> >          */
> >> > }
> >> >
> >> > Note that nand_try_to_match_ecc_req() has to be more generic than
> >> > denali_try_to_match_ecc_req() WRT step sizes, which will probably
> >> > complexify the logic.  
> >>
> >>
> >> After I fiddle with this generic approach for a while,
> >> I started to feel like giving up.  
> >
> > I don't get it. What was the problem with my initial suggestion (the
> > denali specific one, not the generic approach)? You proposed to make it
> > generic, which, I agree, is a bit more complicated.
> >  
> >>
> >> I wonder if we really want over-implementation
> >> for covering _theoretically_ possible cases.  
> >
> > Okay, one more theoretical case I'd like to expose: you have board
> > design with different NAND parts which have different ECC requirements.
> > If you were about to describe the exact ECC strength you want for each
> > board you'll have to have different DTs.  
> 
> In this case, fixed ecc-strength in DT is not feasible.
> 
> > Maximizing the ECC strength
> > would still work, but what if the MTD user needs some OOB bytes (like
> > is the case with JFFS2) and ECC maximization reserved all of the
> > available bytes?  
> 
> JFFS2 needs some bytes in oob-free area for the clean marker.
> You are right.
> This implies NAND_ECC_MAXIMIZE is not very useful.
> We do not know whether we have enough space left in oob, or not.
> 
> 
> 
> > The other reason I prefer to have the drivers automatically guessing
> > what's appropriate is because then you don't have to care when writing
> > your DT.
> >  
> >>
> >> In practice, there are not so many ECC settings possible
> >> on a single controller.
> >>
> >> As for Denali IP, it would be theoretically possible to instantiate
> >> multiple ECC engines.  However, in practice, there is no sensible
> >> reason to do so.  At least, I do not know any real chip to support that.
> >>
> >> So, I'd like to simplify the logic for Denali.
> >>
> >>   - Support either 512 or 1024 ECC size.
> >>     If there is (ever) a controller that supports both,
> >>     1024 should be chosen.
> >>
> >>   - ECC strength is not specified via DT, it is simply maximized.
> >>
> >> This simplifies the logic much and I believe this is enough.
> >>
> >> One more reason is, as we talked before,
> >> we need to match ECC setting between Linux and firmware (boot-loader),  
> >
> > If the bootloader implements the same logic it should match.
> >  
> >> so anyway we end up with using a fixed setting specified by DT.
> >>  
> >
> > Really, I don't see what's the problem with the function I proposed,
> > but I'm willing to make a concession.
> > Make the nand-ecc-strength+nand-ecc-step-size or nand-ecc-maximize
> > mandatory so that if someone ever needs to support the 'match NAND
> > requirements' feature we won't have to add a vendor specific property
> > like this one [1].
> >
> > Are you fine with that?  
> 
> No.  This requirement seems too strong.

Hm, can you give more details? All I want is a solution where we can
later support the feature I'm asking without adding a extra DT
property, and, in order to do that we must make sure the case you want
to support as a first step are explicitly requested in the DT.

It's as simple as:

	if ((!ecc->strength || !ecc->size) &&
	    !(ecc->options & NAND_ECC_MAXIMIZE))
		return -ENOTSUPP;

> At least, it is a problem for non-DT platforms.

Well, for non-DT platforms you have to keep ECC maximization anyway,
otherwise you're not backward compatible.

> 
> 
> If a driver provides ECC engine caps info,
> perhaps ECC maximizing could be a generalized helper function as well.

I don't get it. I thought the generic helper was too hard to implement.
Now you want to add a new functionality.

I'm not against this idea, but maybe it's easier to provide a denali
specific implementation before tackling the generic one.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux