All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v2] Nand driver for Nomadik SoC
Date: Sat, 7 Feb 2009 22:08:53 +0100	[thread overview]
Message-ID: <20090207210853.GC17280@game.jcrosoft.org> (raw)
In-Reply-To: <20090207150450.GA19180@mail.gnudd.com>

On 16:04 Sat 07 Feb     , Alessandro Rubini wrote:
> From: Alessandro Rubini <rubini@unipv.it>
> 
> This driver implements the ECC algorithm described in
> the CPU data sheet and uses the OOB layout chosen in
> already-released development systems (shipped with a custom-made
> u-boot 1.3.1).
> 
> Signed-off-by: Alessandro Rubini <rubini@unipv.it>
> Acked-by: Andrea Gallo <andrea.gallo@stnwireless.com>
> ---
> 
> About V2: I noticed I forgot to run through checkpatch, I fixed all
> whitespace warnings.I left 2 of them (space after open parens) as I
> think the bit fiddling is more readable this way.
> 
>  cpu/arm926ejs/nomadik/Makefile |    6 +-
>  cpu/arm926ejs/nomadik/nand.c   |  240 ++++++++++++++++++++++++++++++++++++++++
>  include/configs/nmdk8815.h     |   14 +--
>  3 files changed, 250 insertions(+), 10 deletions(-)
>  create mode 100644 cpu/arm926ejs/nomadik/nand.c
> 
> diff --git a/cpu/arm926ejs/nomadik/Makefile b/cpu/arm926ejs/nomadik/Makefile
> index e3bd2ee..9c73327 100644
> --- a/cpu/arm926ejs/nomadik/Makefile
> +++ b/cpu/arm926ejs/nomadik/Makefile
> @@ -25,11 +25,13 @@ include $(TOPDIR)/config.mk
>  
>  LIB	= $(obj)lib$(SOC).a
>  
> -COBJS	= timer.o
>  SOBJS	= reset.o
>  
> +COBJS-y	= timer.o
> +COBJS-$(CONFIG_CMD_NAND) += nand.o
> +
>  SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
> -OBJS	:= $(addprefix $(obj),$(COBJS)) $(addprefix $(obj),$(SOBJS))
> +OBJS	:= $(addprefix $(obj),$(COBJS-y)) $(addprefix $(obj),$(SOBJS))
please use this

SRCS	:= $(SOBJS:.o=.S) $(COBJS-y:.o=.c)
OBJS	:= $(addprefix $(obj),$(COBJS-y))
SOBJS	:= $(addprefix $(obj),$(SOBJS))
>  
>  all:	$(obj).depend $(LIB)
>  
> diff --git a/cpu/arm926ejs/nomadik/nand.c b/cpu/arm926ejs/nomadik/nand.c
> new file mode 100644
> index 0000000..22ee591
> --- /dev/null
> +++ b/cpu/arm926ejs/nomadik/nand.c
> @@ -0,0 +1,240 @@
> +/*
> + * (C) Copyright 2007 STMicroelectronics, <www.st.com>
> + * (C) Copyright 2009 Alessandro Rubini <rubini@unipv.it>
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <common.h>
> +#include <nand.h>
> +#include <asm/io.h>
> +
> +#ifdef __arm__ /* Assembly version of the parity-of-byte procedure */
we should need only one
> +
> +static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */
> +{
> +	__asm__ __volatile__(
> +		"eor   %0, %0, %0, lsr #4\n\t"
> +		"eor   %0, %0, %0, lsr #2\n\t"
> +		"eor   %0, %0, %0, lsr #1\n\t"
> +		"ands  %0, %0, #1\n\t"
> +		"subne %0, %0, #2\t"
> +		: "=r" (b) : "0" (b));
> +	return b;
> +}
> +
> +#else /* Equivalent in C, which I used for testing the main procedure */
> +
> +static inline int parity(int b)
> +{
> +	b = b ^ (b >> 4);
> +	b = b ^ (b >> 2);
> +	return (b ^ (b>>1)) & 1
> +		? ~0 : 0;
> +}
> +
> +#endif
> +
> +/*
> + * This is the ECC routine used in hardware, according to the manual.
> + * HW claims to make the calculation but not the correction; so we must
> + * recalculate the bytes for a comparison.
> + */
> +static int ecc512(unsigned char *data, unsigned char *ecc)
> +{
> +	int gpar = 0;
> +	int i, val, par;
> +	int pbits = 0;		/* P8, P16, ... P2048 */
> +	int pprime = 0;		/* P8', P16', ... P2048' */
> +	int lowbits;		/* P1, P2, P4 and primes */
> +
> +	for (i = 0; i < 512; i++) {
> +		par = parity((val = data[i]));
> +		gpar ^= val;
> +		pbits ^= (i & par);
> +	}
> +	/*
> +	 * Ok, now gpar is global parity (xor of all bytes)
> +	 * pbits are all the parity bits (non-prime ones)
> +	 */
> +	par = parity(gpar);
> +	pprime = pbits ^ par;
> +	/* Put low bits in the right position for ecc[2] (bits 7..2) */
> +	lowbits = 0
> +		| (parity(gpar & 0xf0) & 0x80)	/* P4  */
						     ^^
whitespace please fix
> +		| (parity(gpar & 0x0f) & 0x40)	/* P4' */
> +		| (parity(gpar & 0xcc) & 0x20)	/* P2  */
						     ^^
whitespace please fix
> +		| (parity(gpar & 0x33) & 0x10)	/* P2' */
> +		| (parity(gpar & 0xaa) & 0x08)	/* P1  */
						     ^^
whitespace please fix
> +		| (parity(gpar & 0x55) & 0x04);	/* P1' */
> +
> +	ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8));
> +	/* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */
> +	ecc[1] = ~(    (pbits & 0x80) >> 0  | ((pprime & 0x80) >> 1)
> +		    | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2)
> +		    | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3)
> +		    | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4));
> +
> +	ecc[0] = ~(    (pbits & 0x8) << 4  | ((pprime & 0x8) << 3)
> +		    | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2)
> +		    | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1)
> +		    | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0));
> +	return 0;
> +}
> +
> +/* This is the method in the chip->ecc field */
> +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
> +				 uint8_t *ecc_code)
> +{
> +	return ecc512(dat, ecc_code);
> +}
> +
> +static int  nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
	     ^^
whitespace please fix
> +				uint8_t *r_ecc, uint8_t *c_ecc)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	uint32_t r, c, d, diff; /*read, calculated, xor of them */
> +
> +	if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes))
> +		return 0;
> +
> +	/* Reorder the bytes into ascending-order 24 bits -- see manual */
> +	r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2;
> +	c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2;
> +	diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */
> +
> +	/* If 12 bits are different, one per pair, it's correctable */
> +	if (((diff | (diff>>1)) & 0x555555) == 0x555555) {
> +		int bit = ((diff & 2) >> 1)
> +			| ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3);
> +		int byte;
> +
> +		d = diff >> 6; /* remove bit-order info */
> +		byte =  ((d & 2) >> 1)
> +			| ((d & 0x8) >> 2) | ((d & 0x20) >> 3)
> +			| ((d & 0x80) >> 4) | ((d & 0x200) >> 5)
> +			| ((d & 0x800) >> 6) | ((d & 0x2000) >> 7)
> +			| ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9);
> +		/* correct the single bit */
> +		dat[byte] ^= 1<<bit;
> +		return 0;
> +	}
> +	/* If 1 bit only differs, it's one bit error in ECC, ignore */
> +	if ((diff ^ (1 << (ffs(diff) - 1))) == 0)
> +		return 0;
> +	/* Otherwise, uncorrectable */
> +	return -1;
> +}
> +
> +static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode)
> +{ /* mandatory in the structure but not used here */ }
> +
> +
> +/* This is the layout used by older installations, we keep compatible */
> +struct nand_ecclayout nomadik_ecc_layout = {
> +	.eccbytes = 3*4,
please add a space before and after '*'
> +	.eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
> +		0x02, 0x03, 0x04,
> +		0x12, 0x13, 0x14,
> +		0x22, 0x23, 0x24,
> +		0x32, 0x33, 0x34},
> +	.oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
> +};
> +
> +#define REG_FSMC_PCR0	(NOMADIK_FSMC_BASE + 0x40)
> +#define REG_FSMC_PMEM0	(NOMADIK_FSMC_BASE + 0x48)
> +#define REG_FSMC_PATT0	(NOMADIK_FSMC_BASE + 0x4c)
> +#define REG_FSMC_ECCR0	(NOMADIK_FSMC_BASE + 0x54)
please move this to a header
> +
> +#define MASK_ALE	(1 << 24)	/* our ALE is AD21 */
> +#define MASK_CLE	(1 << 23)	/* our CLE is AD22 */
> +
> +/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */
> +static void nomadik_nand_hwcontrol(struct mtd_info *mtd,
> +				   int cmd, unsigned int ctrl)
> +{
> +	struct nand_chip *this = mtd->priv;
> +	u32 pcr0 = readl(REG_FSMC_PCR0);
> +
> +	if (ctrl & NAND_CTRL_CHANGE) {
> +		ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
> +		IO_ADDR_W &= ~(MASK_ALE | MASK_CLE);
> +
> +		if (ctrl & NAND_CLE)
> +			IO_ADDR_W |= MASK_CLE;
> +		if (ctrl & NAND_ALE)
> +			IO_ADDR_W |= MASK_ALE;
> +
> +		if (ctrl & NAND_NCE)
> +			writel(pcr0 |  0x4, REG_FSMC_PCR0);
				     ^^
whitespace please fix
> +		else
> +			writel(pcr0 & ~0x4, REG_FSMC_PCR0);
> +
> +		this->IO_ADDR_W = (void *) IO_ADDR_W;
> +		this->IO_ADDR_R = (void *) IO_ADDR_W;
> +	}
> +
> +	if (cmd != NAND_CMD_NONE)
> +		writeb(cmd, this->IO_ADDR_W);
> +}
> +
> +/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */
> +static int nomadik_nand_ready(struct mtd_info *mtd)
> +{
> +	return 1; /* The ready bit is handled in hardware */
> +}
> +
> +/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */
> +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> +	int i;
> +	struct nand_chip *chip = mtd->priv;
> +	u32 *p = (u32 *) buf;
please add an empty line
> +	len >>= 2;
> +
> +	writel(0, REG_FSMC_ECCR0);
> +	for (i = 0; i < len; i++)
> +		p[i] = readl(chip->IO_ADDR_R);
> +}
> +
> +int board_nand_init(struct nand_chip *chip)
> +{
> +	/* Set up the FSMC_PCR0 for nand access*/
> +	writel(0x0000004a, REG_FSMC_PCR0);
> +	/* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */
> +	writel(0x00020401, REG_FSMC_PMEM0);
> +	writel(0x00020404, REG_FSMC_PATT0);
> +
> +	chip->options = NAND_COPYBACK |	NAND_CACHEPRG | NAND_NO_PADDING;
> +	chip->cmd_ctrl = nomadik_nand_hwcontrol;
> +	chip->dev_ready = nomadik_nand_ready;
> +	/* The chip allows 32bit reads, so avoid the default 8bit copy */
> +	chip->read_buf = nomadik_nand_read_buf;
> +
> +	/* ECC: follow the hardware-defined rulse, but do it in sw */
> +	chip->ecc.mode = NAND_ECC_HW;
> +	chip->ecc.bytes = 3;
> +	chip->ecc.size = 512;
> +	chip->ecc.layout = &nomadik_ecc_layout;
> +	chip->ecc.calculate = nomadik_ecc_calculate;
> +	chip->ecc.hwctl = nomadik_ecc_hwctl;
> +	chip->ecc.correct = nomadik_ecc_correct;
> +
> +	return 0;
> +}
Best Regards,
J.

  reply	other threads:[~2009-02-07 21:08 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-07  4:43 [U-Boot] [PATCH] Nand driver for Nomadik SoC Alessandro Rubini
2009-02-07 15:04 ` [U-Boot] [PATCH v2] " Alessandro Rubini
2009-02-07 21:08   ` Jean-Christophe PLAGNIOL-VILLARD [this message]
2009-02-07 23:19   ` [U-Boot] [PATCH v3] " Alessandro Rubini
2009-02-09  7:34     ` Stefan Roese
2009-02-09  8:40       ` Alessandro Rubini
2009-02-09  8:48         ` Stefan Roese
2009-02-09 17:16     ` Scott Wood
2009-02-09 17:56       ` Alessandro Rubini
2009-02-09 18:10         ` Scott Wood
  -- strict thread matches above, loose matches on Subject: below --
2009-02-09 14:53 [U-Boot] [PATCH 0/3] Drivers for nomadik 8815 board Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 1/3] Added nomadik.h header Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC Alessandro Rubini
2009-02-09 21:48   ` Alessandro Rubini
2009-02-09 22:28     ` Scott Wood
2009-02-09 14:53 ` [U-Boot] [PATCH 3/3] Enable Ethernet for Nomadik 8815 Evaluation Kit Alessandro Rubini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090207210853.GC17280@game.jcrosoft.org \
    --to=plagnioj@jcrosoft.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.