[PATCH 5/9] mtd: pxa3xx_nand: add support for the Marvell Berlin nand controller

Boris Brezillon boris.brezillon at free-electrons.com
Sun Feb 8 13:06:33 PST 2015


Hi Antoine,

I didn't review the whole patch yet, but I'll come back to this review
soon.

On Tue, 27 Jan 2015 15:10:12 +0100
Antoine Tenart <antoine.tenart at free-electrons.com> wrote:

> The nand controller on Marvell Berlin SoC reuse the pxa3xx nand driver
> as it quite close. The process of sending commands can be compared to
> the one of the Marvell armada 370: read and write commands are done in
> chunks.
> 
> But the Berlin nand controller has some other specificities which
> require some modifications of the pxa3xx nand driver:
> - there are no IRQ available so we need to poll the status register: we
>   have to use our own cmdfunc Berlin function, and early on the probing
>   function.
> - PAGEPROG are very different from the one used in the pxa3xx driver,
>   so we're using a specific process for this one
> - the SEQIN command is equivalent to a READ0 command
> - the RNDOUT command must be used to perform a read operation, and the
>   command is not NAND_CMD_RNDOUT

Really ?
RNDOUT is only used when you only did a standard page read (READ0 +
READSTART), and you want to move the data pointer inside the currently
loaded page (this command only require 2 address cycles).
I wonder how this can work for a regular read page command (where 5
address cycles are required).
Anyway, I trust you if you say you have to do that, but I'm really
curious (how is the controller hiding this standard read phase).

> - the ERASE1 command is specific (0xd060)

As stated in the following comments, I'm pretty sure it's a standard
erase command.

> 
> Signed-off-by: Antoine Tenart <antoine.tenart at free-electrons.com>
> ---
>  drivers/mtd/nand/pxa3xx_nand.c | 287 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 261 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index b2783b1f663c..62ea369dc524 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -109,6 +109,8 @@
>  #define NDCB0_EXT_CMD_TYPE(x)	(((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK)
>  #define NDCB0_CMD_TYPE_MASK	(0x7 << 21)
>  #define NDCB0_CMD_TYPE(x)	(((x) << 21) & NDCB0_CMD_TYPE_MASK)
> +#define NDCB0_CMD_XTYPE_MASK	(0x7 << 29)
> +#define NDCB0_CMD_XTYPE(x)	(((x) << 29) & NDCB0_CMD_XTYPE_MASK)
>  #define NDCB0_NC		(0x1 << 20)
>  #define NDCB0_DBC		(0x1 << 19)
>  #define NDCB0_ADDR_CYC_MASK	(0x7 << 16)
> @@ -117,13 +119,20 @@
>  #define NDCB0_CMD1_MASK		(0xff)
>  #define NDCB0_ADDR_CYC_SHIFT	(16)
>  
> -#define EXT_CMD_TYPE_DISPATCH	6 /* Command dispatch */
> -#define EXT_CMD_TYPE_NAKED_RW	5 /* Naked read or Naked write */
> -#define EXT_CMD_TYPE_READ	4 /* Read */
> -#define EXT_CMD_TYPE_DISP_WR	4 /* Command dispatch with write */
> -#define EXT_CMD_TYPE_FINAL	3 /* Final command */
> -#define EXT_CMD_TYPE_LAST_RW	1 /* Last naked read/write */
> -#define EXT_CMD_TYPE_MONO	0 /* Monolithic read/write */
> +#define EXT_CMD_TYPE_LAST_PAGEPROG	10
> +#define EXT_CMD_TYPE_CHUNK_PAGEPROG	9
> +#define EXT_CMD_TYPE_LAST_RNDOUT	8
> +#define EXT_CMD_TYPE_CHUNK_RNDOUT	7
> +#define EXT_CMD_TYPE_DISPATCH		6 /* Command dispatch */
> +#define EXT_CMD_TYPE_NAKED_RW		5 /* Naked read or Naked write */
> +#define EXT_CMD_TYPE_READ		4 /* Read */
> +#define EXT_CMD_TYPE_DISP_WR		4 /* Command dispatch with write */
> +#define EXT_CMD_TYPE_FINAL		3 /* Final command */
> +#define EXT_CMD_TYPE_LAST_RW		1 /* Last naked read/write */
> +#define EXT_CMD_TYPE_MONO		0 /* Monolithic read/write */
> +
> +#define BERLIN_NAND_CMD_RNDOUT		0x3000

Your specific RNDOUT command looks like a regular READ0 (0x0) +
READSTART (0x30) sequence, I think this is why you need to do a RNDOUT
to read your page ;-).

> +#define BERLIN_NAND_CMD_ERASE1		0xd060

Ditto, it's a regular ERASE command sequence: ERASE1 (0x60) + ERASE2
(0xd0).



>  
>  /* macros for registers read/write */
>  #define nand_writel(info, off, val)	\
> @@ -158,6 +167,7 @@ enum {
>  enum pxa3xx_nand_variant {
>  	PXA3XX_NAND_VARIANT_PXA,
>  	PXA3XX_NAND_VARIANT_ARMADA370,
> +	PXA3XX_NAND_VARIANT_BERLIN2,
>  };
>  
>  struct pxa3xx_nand_host {
> @@ -244,10 +254,13 @@ module_param(use_dma, bool, 0444);
>  MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW");
>  
>  static struct pxa3xx_nand_timing timing[] = {
> -	{ 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
> -	{ 10,  0, 20,  40, 30,  40, 11123, 110, 10, },
> -	{ 10, 25, 15,  25, 15,  30, 25000,  60, 10, },
> -	{ 10, 35, 15,  25, 15,  25, 25000,  60, 10, },
> +	{ 40, 80, 60, 100, 80, 100,  90000, 400, 40, },
> +	{ 10,  0, 20,  40, 30,  40,  11123, 110, 10, },
> +	{ 10, 25, 15,  25, 15,  30,  25000,  60, 10, },
> +	{ 10, 35, 15,  25, 15,  25,  25000,  60, 10, },
> +	{  5, 20, 10,  12, 10,  12,  60000,  60, 10, },
> +	{  5, 20, 10,  12, 10,  12, 200000, 120, 10, },
> +	{  5, 15, 10,  15, 10,  15,  60000,  60, 10, },
>  };
>  
>  static struct pxa3xx_nand_flash builtin_flash_types[] = {
> @@ -260,6 +273,20 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {
>  { "512MiB 8-bit",  0xdc2c,  64, 2048,  8,  8, 4096, &timing[2] },
>  { "512MiB 16-bit", 0xcc2c,  64, 2048, 16, 16, 4096, &timing[2] },
>  { "256MiB 16-bit", 0xba20,  64, 2048, 16, 16, 2048, &timing[3] },
> +{ },
> +};
> +
> +static struct pxa3xx_nand_flash berlin_builtin_flash_types[] = {
> +{ "2GiB 8-bit",    0xd5ec, 128, 8192,  8,  8, 2048, &timing[4] },
> +{ "2GiB 8-bit",    0xd598, 128, 8192,  8,  8, 2048, &timing[5] },
> +{ "2GiB 8-bit",    0x482c, 256, 4096,  8,  8, 2048, &timing[6] },
> +{ "4GiB 8-bit",    0xd7ec, 128, 8192,  8,  8, 4096, &timing[5] },
> +{ "8GiB 8-bit",    0xdeec, 128, 8192,  8,  8, 4096, &timing[5] },
> +{ "4GiB 8-bit",    0xd7ad, 256, 8192,  8,  8, 2048, &timing[5] },
> +{ "4GiB 8-bit",    0x682c, 256, 4096,  8,  8, 4096, &timing[6] },
> +{ "8GiB 8-bit",    0x882c, 256, 8192,  8,  8, 4096, &timing[6] },
> +{ "8GiB 8-bit",    0xdead, 256, 8192,  8,  8, 4096, &timing[6] },
> +{ },
>  };

As already stated by Brian and Ezequiel, this should be handled with
the new API for timing retrieval.
However, IMHO, this should be part of another series reworking the pxa
driver, and I know you plan to do that, so I don't mind if this
table is added to support your IP (as long as you're taking care of
reworking that ;-)).

>  
>  static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' };
> @@ -320,6 +347,18 @@ static struct nand_ecclayout ecc_layout_4KB_bch8bit = {
>  	.oobfree = { }
>  };
>  
> +static struct nand_ecclayout ecc_layout_oob_128 = {
> +        .eccbytes = 48,
> +        .eccpos = {
> +                   80, 81, 82, 83, 84, 85, 86, 87,
> +                   88, 89, 90, 91, 92, 93, 94, 95,
> +                   96, 97, 98, 99, 100, 101, 102, 103,
> +                   104, 105, 106, 107, 108, 109, 110, 111,
> +                   112, 113, 114, 115, 116, 117, 118, 119,
> +                   120, 121, 122, 123, 124, 125, 126, 127},
> +        .oobfree = { {.offset = 2, .length = 78} }
> +};
> +

Okay, this is only my opinion, but I think all the ECC layouts defined
in this driver should be built dynamically (depending on the requested
ECC strength/block-size).
Take a look at the sunxi driver if you need an example.

Again, these are mid term changes, and I won't ask you to change that
in this series.

>  /* Define a default flash type setting serve as flash detecting only */
>  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
>  
> @@ -346,6 +385,10 @@ static const struct of_device_id pxa3xx_nand_dt_ids[] = {
>  		.compatible = "marvell,armada370-nand",
>  		.data       = (void *)PXA3XX_NAND_VARIANT_ARMADA370,
>  	},
> +	{
> +		.compatible = "marvell,berlin2-nand",
> +		.data       = (void *)PXA3XX_NAND_VARIANT_BERLIN2,
> +	},
>  	{}
>  };
>  MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids);
> @@ -378,6 +421,11 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,
>  		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
>  		NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
>  
> +	if (info->variant == PXA3XX_NAND_VARIANT_BERLIN2) {
> +		ndtr0 = 0x84840A12;
> +		ndtr1 = 0x00208662;
> +	}
> +
>  	info->ndtr0cs0 = ndtr0;
>  	info->ndtr1cs0 = ndtr1;
>  	nand_writel(info, NDTR0CS0, ndtr0);
> @@ -644,6 +692,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
>  		nand_writel(info, NDCB0, info->ndcb1);
>  		nand_writel(info, NDCB0, info->ndcb2);
>  
> +		if (info->variant == PXA3XX_NAND_VARIANT_BERLIN2 &&
> +				info->ndcb0 & NDCB0_LEN_OVRD)
> +			nand_writel(info, NDCB0,
> +					info->chunk_size + info->oob_size);
> +
>  		/* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */
>  		if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370)
>  			nand_writel(info, NDCB0, info->ndcb3);
> @@ -755,6 +808,19 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
>  	if (command == NAND_CMD_SEQIN)
>  		exec_cmd = 0;
>  
> +	/* Berlin specific */
> +	if (info->variant == PXA3XX_NAND_VARIANT_BERLIN2) {
> +		if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB)
> +			exec_cmd = 0;
> +
> +		if (command == NAND_CMD_SEQIN)
> +			command = NAND_CMD_READ0;
> +		else if (command == NAND_CMD_RNDOUT)
> +			command = BERLIN_NAND_CMD_RNDOUT;

As stated above your BERLIN_NAND_CMD_RNDOUT is just a regular READ0 +
READSTART command, are there any differences between a RNDOUT and a
regular READ in your driver ?

> +		else if (command == NAND_CMD_ERASE1)
> +			command = BERLIN_NAND_CMD_ERASE1;
> +	}
> +
>  	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles
>  				    + host->col_addr_cycles);
>  
> @@ -814,6 +880,34 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
>  			break;
>  		}
>  
> +		if (info->variant == PXA3XX_NAND_VARIANT_BERLIN2) {
> +			if (ext_cmd_type == EXT_CMD_TYPE_LAST_PAGEPROG) {
> +				info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> +						| NDCB0_CMD_XTYPE(0x3)
> +						| NDCB0_ST_ROW_EN
> +						| NDCB0_DBC
> +						| 0xff
> +						| (0x1080 & (0xff << 8));
> +			} else if (ext_cmd_type == EXT_CMD_TYPE_CHUNK_PAGEPROG) {
> +				info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> +						| NDCB0_CMD_XTYPE(0x5)
> +						| NDCB0_NC
> +						| NDCB0_AUTO_RS
> +						| NDCB0_LEN_OVRD
> +						| (0x1080 & 0xff);
> +			} else {
> +				info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
> +						| NDCB0_CMD_XTYPE(0x4)
> +						| NDCB0_NC
> +						| NDCB0_AUTO_RS
> +						| NDCB0_LEN_OVRD
> +						| addr_cycle
> +						| (0x1080 & 0xff);
> +			}
> +
> +			break;
> +		}
> +
>  		/* Second command setting for large pages */
>  		if (mtd->writesize > PAGE_CHUNK_SIZE) {
>  			/*
> @@ -870,6 +964,27 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
>  
>  		info->data_size = 8;
>  		break;
> +
> +	case BERLIN_NAND_CMD_RNDOUT:
> +		info->buf_start = column;
> +
> +		if (ext_cmd_type == EXT_CMD_TYPE_LAST_RNDOUT)
> +			info->ndcb0 = NDCB0_CMD_XTYPE(0x5)
> +					| NDCB0_LEN_OVRD;
> +		else if (ext_cmd_type == EXT_CMD_TYPE_CHUNK_RNDOUT)
> +			info->ndcb0 = NDCB0_CMD_XTYPE(0x5)
> +					| NDCB0_LEN_OVRD
> +					| NDCB0_NC;
> +		else
> +			info->ndcb0 |= NDCB0_CMD_TYPE(0)
> +					| NDCB0_CMD_XTYPE(0x6)
> +					| NDCB0_DBC
> +					| NDCB0_NC
> +					| addr_cycle
> +					| command;
> +
> +		break;
> +
>  	case NAND_CMD_STATUS:
>  		info->buf_count = 1;
>  		info->ndcb0 |= NDCB0_CMD_TYPE(4)
> @@ -880,15 +995,18 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
>  		break;
>  
>  	case NAND_CMD_ERASE1:
> +	case BERLIN_NAND_CMD_ERASE1:
>  		info->ndcb0 |= NDCB0_CMD_TYPE(2)
>  				| NDCB0_AUTO_RS
>  				| NDCB0_ADDR_CYC(3)
>  				| NDCB0_DBC
> -				| (NAND_CMD_ERASE2 << 8)
> -				| NAND_CMD_ERASE1;
> +				| command;
>  		info->ndcb1 = page_addr;
>  		info->ndcb2 = 0;
>  
> +		if (command == NAND_CMD_ERASE1)
> +			info->ndcb0 |= (NAND_CMD_ERASE2 << 8);
> +

I don't see any difference with the old version:
you previously set command to BERLIN_NAND_CMD_ERASE1, which, according
to your definition, is equal to
(NAND_CMD_ERASE1 | (NAND_CMD_ERASE2 << 8))

Right ?

Am I missing something obvious ?


That's all for now :-).

Best Regards,

Boris


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com



More information about the linux-arm-kernel mailing list