[PATCH 1/4] mtd: nand: add NVIDIA Tegra NAND Flash controller driver

Lucas Stach dev at lynxeye.de
Tue Jan 6 16:17:07 PST 2015


Am Dienstag, den 06.01.2015, 00:41 +0100 schrieb Stefan Agner:
> Hi Lucas,
> 
> Thanks for picking that up!
> 
> I did some short benchmarks on Colibri T20 V1.2, L4T. Write/read speeds
> I measured on the YAFFS2 based file system:
> 
> # dd if=/dev/zero of=test bs=50M count=1 conv=fdatasync
> 1+0 records in
> 1+0 records out
> 52428800 bytes (52 MB) copied, 9.88293 s, 5.3 MB/s
> 
> echo 3 > /proc/sys/vm/drop_caches
> # dd if=test of=/dev/zero bs=50M count=1
> 1+0 records in
> 1+0 records out
> 52428800 bytes (52 MB) copied, 5.97056 s, 8.8 MB/s
> 
Thanks, this puts things into perspective.

> So your values look quite realistic then!
> 
> Some comments below...
> 
> On 2015-01-04 21:39, Lucas Stach wrote:
> > Add support for the NAND flash controller found on NVIDIA
> > Tegra 2/3 SoCs. This is a largely reworked version of the driver
> > started by Thierry.
> > 
> > Signed-off-by: Thierry Reding <thierry.reding at avionic-design.de>
> > Signed-off-by: Lucas Stach <dev at lynxeye.de>
> > ---
> > I've tested this driver with the in-kernel mtd-tests and some
> > realworld workloads on a Colibri T20 module.
> > ---
> >  .../bindings/mtd/nvidia,tegra20-nand.txt           |  30 +
> >  MAINTAINERS                                        |   6 +
> >  drivers/mtd/nand/Kconfig                           |   6 +
> >  drivers/mtd/nand/Makefile                          |   1 +
> >  drivers/mtd/nand/tegra_nand.c                      | 794 +++++++++++++++++++++
> >  5 files changed, 837 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/mtd/nvidia,tegra20-nand.txt
> >  create mode 100644 drivers/mtd/nand/tegra_nand.c
> > 

[...]

> > +static irqreturn_t tegra_nand_irq(int irq, void *data)
> > +{
> > +	struct tegra_nand *nand = data;
> > +	irqreturn_t ret = IRQ_HANDLED;
> > +	u32 isr, dma;
> > +
> > +	isr = readl(nand->regs + ISR);
> > +	dma = readl(nand->regs + DMA_CTRL);
> > +
> > +	if (!isr && !(dma & DMA_CTRL_IS_DONE)) {
> > +		ret = IRQ_NONE;
> > +		goto out;
> 
> The out label doesn't do anything more than just return. Why not just
> return IRQ_NONE here and return IRQ_HANDLED at the end, saves the local
> variable and helps readability...
> 
Yes, this function looked a bit more complicated when I started to clean
this. So not removing the out label was just an oversight.

> Why is this needed anyway, is the IRQ shared with other peripherals?
> 
> In the L4T driver, there is a warning message about spurious interrupts,
> does this works around this interrupts?
> 
I haven't seen any spurious IRQs during my testing. So not doing
anything special should be okay I think.

[...]

> > +}
> > +
> > +static void tegra_nand_setup_timing(struct tegra_nand *nand, int mode)
> > +{
> > +	unsigned long rate = clk_get_rate(nand->clk) / 1000000;
> > +	unsigned long period = 1000000 / rate;
> 
> Hm, period of a clock in ns... Sounds like a common use case. I searched
> for a macro/helper, but did not found anything. Well then.
> 
> > +	const struct nand_sdr_timings *timings;
> > +	u32 val, reg = 0;
> > +
> > +	timings = onfi_async_timing_mode_to_sdr_timings(mode);
> > +
> > +	val = max3(timings->tAR_min, timings->tRR_min,
> > +		   timings->tRC_min) / period;
> > +	if (val > 2)
> > +		val -= 2;
> 
> According to my TRM this is:
> Generated timing = (n+3) * NAND_CLK_PERIOD ns.
> 
> Shouldn't this look like
> 	if (val >= 3)
> 		val -= 3;
> then?
> 
> I see that all the calculations of the timings below are different then
> in TRM. I think we need to take the whole offset into account, or do I
> miss something here?
> 
> 
> > +	reg |= TIMING_TCR_TAR_TRR(val);
> > +
> > +	val = max(max(timings->tCS_min, timings->tCH_min),
> > +		  max(timings->tALS_min, timings->tALH_min)) / period;
> > +	if (val > 1)
> > +		val -= 1;
> > +	reg |= TIMING_TCS(val);
> 
> See mask error in macro definition.
> 
> > +
> > +	val = max(timings->tRP_min, timings->tREA_max) + 6000;
> > +	reg |= TIMING_TRP(val / 1000);
> > +	reg |= TIMING_TRP_RESP(val / period);
> > +
> > +	reg |= TIMING_TWB(timings->tWB_max / period);
> > +	reg |= TIMING_TWHR(timings->tWHR_min / period);
> > +	reg |= TIMING_TWH(timings->tWH_min / 1000);
> > +	reg |= TIMING_TWP(timings->tWP_min / 1000);
> > +	reg |= TIMING_TRH(timings->tRHW_min / 1000);
> 
> Why 1000 for those three values? In my TRM, those values are in
> NAND_CLK_PERIOD too.
> 

Thanks for noticing. I wrote that part half a year ago and don't know
the details anymore. I will look this up again (and fix if necessary)
and come back to you.

Regards,
Lucas




More information about the linux-arm-kernel mailing list