[PATCH 2/2] mtd: nand: driver for Conexant Digicolor NAND Flash Controller

Baruch Siach baruch at tkos.co.il
Sun Feb 22 00:19:23 PST 2015


Hi Boris,

On Thu, Feb 19, 2015 at 11:52:23AM +0100, Boris Brezillon wrote:
> On Thu, 19 Feb 2015 11:43:01 +0200
> Baruch Siach <baruch at tkos.co.il> wrote:
> > On Thu, Feb 19, 2015 at 12:17:04AM +0100, Boris Brezillon wrote:
> > > On Thu, 12 Feb 2015 13:10:19 +0200
> > > Baruch Siach <baruch at tkos.co.il> wrote:
> > I understand you concern. However, since I don't have the needed hardware 
> > to test the multi nand chip setup I want to keep the code simple. These 
> > two fields are separated from the others by a blank line on purpose, and 
> > I'll also add a comment to explain that these are per nand chip files.
> 
> I'm not asking you to support multiple chips right now.
> What I'm asking here is to dispatch NFC and NAND fields in different
> structures:
> 
> struct digicolor_nand {
> 	struct mtd_info		mtd;
> 	struct nand_chip	nand;
> 
> 	u32 nand_cs;
> 	int t_ccs;
> }
> 
> struct digicolor_nfc {
> 	struct nand_hw_control	base;
> 	void __iomem		*regs;
> 	struct device		*dev;
> 
> 	unsigned long		clk_rate;
> 
> 	u32 ale_cmd;
> 	u32 ale_data;
> 	int ale_data_bytes;
> 
> 	/* Only support one NAND for now */
> 	struct digicolor_nand	nand;
> };
> 
> You'll keep the same logic except that this separation will be clearly
> visible.

Sounds reasonable. Will do.

[...]

> > It's just that the INT flag name is quite long, and it make the code using 
> > it less readable IMO. Maybe some macro trickery would do the job here.
> 
> Yep, if that's about avoiding over 80 character lines you could define
> such a macro:
> 
> #define NFC_RDY(flag)		NFC_INT_ ## flag ## _READY

Thanks for the tip. Will do.

[...]

> > I have no control of that. This command goes into a pipe that is managed 
> > at the hardware level. If the nand device does not activate the ready/busy 
> > signal, the pipe will just stall, and the command FIFO will overflow (the 
> > command FIFO has 8 entries). So you will notice the error eventually.
> 
> My point is: you should not return 1 if the NAND is not ready,
> waiting forever is not what I'm suggesting here, but you should find a
> way to return the actual NAND chip R/B status.
> If you don't have any solution to do that from your controller then you
> might consider relying on the default dev_ready implementation which is
> sending a NAND STATUS command to retrieve the R/B status.
> 
> What I'll say is in contradiction with what's done in the sunxi
> driver :-) (actually I'm considering fixing that), but after taking a
> closer look, dev_ready should only return the status of the NAND, and
> not wait for the NAND to be ready.
> The function responsible for waiting is waitfunc, maybe this is the one
> you should overload.

OK. I'll look into overloading waitfunc.

[...]

> > > > +static void digicolor_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd,
> > > > +				   unsigned int ctrl)
> > > > +{
> > > > +	struct nand_chip *chip = mtd->priv;
> > > > +	struct digicolor_nfc *nfc = chip->priv;
> > > > +	u32 cs = nfc->nand_cs;
> > > > +
> > > > +	if (ctrl & NAND_CLE) {
> > > > +		digicolor_nfc_cmd_write(nfc,
> > > > +					CMD_CLE | CMD_CHIP_ENABLE(cs) | cmd);
> > > > +		if (cmd == NAND_CMD_RNDOUTSTART || cmd == NAND_CMD_RNDIN) {
> > > > +			digicolor_nfc_wait_ns(nfc, nfc->t_ccs);
> > > > +		} else if (cmd == NAND_CMD_RESET) {
> > > > +			digicolor_nfc_wait_ns(nfc, 200);
> > > > +			digicolor_nfc_dev_ready(mtd);
> > > > +		}
> > > 
> > > These wait and dev_ready calls are supposed to be part of the generic
> > > cmdfunc implementation, did you encounter any issues when relying on the
> > > default implementation ?
> > 
> > The generic code just waits. This doesn't help here. Hardware does all the 
> > command processing in its own schedule. To make the hardware wait I must 
> > explicitly insert a wait command into the pipe.
> 
> I'm not sure to understand here.
> There's a difference between:
> 1/ waiting for a NAND command to be send on the NAND bus
> 2/ waiting for the NAND to be ready (for DATA read or after a PROGRAM
>    operation)
> 
> NAND core is taking care of the 2/, but 1/ is your responsibility
> (and this is what you have to wait for here).

The generic code in nand_command_lp() sends NAND_CMD_STATUS immediately after 
NAND_CMD_RESET. According to the datasheet you must wait tWB (200ns max) 
before sending any command, including NAND_CMD_STATUS. Setting chip_delay is 
not enough as I explain above. That's way I added digicolor_nfc_wait_ns(). 
That timed wait is what I referred to. You are right that the 
digicolor_nfc_dev_ready() call is not needed here. Will remove.

[...]

> > > > +	while (len >= 4) {
> > > > +		if (digicolor_nfc_wait_ready(nfc, op))
> > > > +			return;
> > > > +		if (op == DATA_READ)
> > > > +			*pr++ = readl_relaxed(nfc->regs + NFC_DATA);
> > > > +		else
> > > > +			writel_relaxed(*pw++, nfc->regs + NFC_DATA);
> > > > +		len -= 4;
> > > > +	}
> > > 
> > > How about using readsl/writesl here (instead of this loop) ?
> > 
> > How can I add the wait condition in readsl/writesl?
> 
> Are you sure you have to wait after each readl access (is NFC_DATA
> interfaced with a FIFO or directly doing PIO access) ?

That's what the datasheet says. There is no mention of data FIFO in the 
datasheet.

[...]

> > > > +	/*
> > > > +	 * Maximum assert/deassert time; asynchronous SDR mode 0
> > > > +	 * Deassert time = max(tWH,tREH) = 30ns
> > > > +	 * Assert time = max(tRC,tRP,tWC,tWP) = 100ns
> > > > +	 * Sample time = 0
> > > > +	 */
> > > > +	timing |= TIMING_DEASSERT(DIV_ROUND_UP(30, ns_per_clk));
> > > > +	timing |= TIMING_ASSERT(DIV_ROUND_UP(100, ns_per_clk));
> > > > +	timing |= TIMING_SAMPLE(0);
> > > > +	writel_relaxed(timing, nfc->regs + NFC_TIMING_CONFIG);
> > > > +	writel_relaxed(NFC_CONTROL_ENABLE, nfc->regs + NFC_CONTROL);
> > > 
> > > Helper functions are provided to extract timings from ONFI timing modes
> > > (either those defined by the chip if it supports ONFI commands, or
> > > those extracted from the datasheet):
> > > 
> > > http://lxr.free-electrons.com/source/include/linux/mtd/nand.h#L976
> > 
> > I wanted to keep that for future improvement. The NAND chip on the EVK board 
> > is actually an old style one, neither ONFI nor JEDEC. I expect to test this 
> > driver on our target hardware that should have something newer.
> 
> Timings are not only related to ONFI chips, and
> onfi_timing_mode_default is extracted from the nand_ids table which is
> describing non-ONFI chips.
> 
> This is not my call to make, but IMHO, dynamic NAND timing
> configuration should be mandatory for new drivers.

I'll look into this for v2.

> > > > +static int digicolor_nfc_ecc_init(struct digicolor_nfc *nfc,
> > > > +				  struct device_node *np)
> > > > +{
> > > > +	struct mtd_info *mtd = &nfc->mtd;
> > > > +	struct nand_chip *chip = &nfc->nand;
> > > > +	int bch_data_range, bch_t, steps, mode, i;
> > > > +	u32 ctrl = NFC_CONTROL_ENABLE | NFC_CONTROL_BCH_KCONFIG;
> > > > +	struct nand_ecclayout *layout;
> > > > +
> > > > +	mode = of_get_nand_ecc_mode(np);
> > > > +	if (mode < 0)
> > > > +		return mode;
> > > > +	if (mode != NAND_ECC_HW_SYNDROME) {
> > > > +		dev_err(nfc->dev, "unsupported ECC mode\n");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	bch_data_range = of_get_nand_ecc_step_size(np);
> > > > +	if (bch_data_range < 0)
> > > > +		return bch_data_range;
> > > > +	if (bch_data_range != 512 && bch_data_range != 1024) {
> > > > +		dev_err(nfc->dev, "unsupported nand-ecc-step-size value\n");
> > > > +		return -EINVAL;
> > > > +	}
> > > > +	if (bch_data_range == 1024)
> > > > +		ctrl |= NFC_CONTROL_BCH_DATA_FIELD_RANGE_1024;
> > > > +	steps = mtd->writesize / bch_data_range;
> > > > +
> > > > +	bch_t = of_get_nand_ecc_strength(np);
> > > > +	if (bch_t < 0)
> > > > +		return bch_t;
> > > 
> > > You should fallback to datasheet values (ecc_strength_ds and
> > > ecc_step_ds) if ECC strength and step are not specified in the DT.
> > 
> > I can only choose from a fix number of strength configuration alternatives. 
> > Should I choose the lowest value that is larger than ecc_strength_ds?
> 
> Yep, this applies to both cases (information extracted from the DT and
> _ds values).

OK. Will do.

Thanks again for your valuable feedback,
baruch

-- 
     http://baruch.siach.name/blog/                  ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch at tkos.co.il - tel: +972.2.679.5364, http://www.tkos.co.il -



More information about the linux-arm-kernel mailing list