[U-Boot] [PATCH 01/10] arm: marvell: Increase MAXARGS to 32

This makes more complex U-Boot scripts possible.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- include/configs/mv-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/configs/mv-common.h b/include/configs/mv-common.h index b654fff..b90de14 100644 --- a/include/configs/mv-common.h +++ b/include/configs/mv-common.h @@ -95,7 +95,7 @@ #define CONFIG_SYS_MEMTEST_START 0x00800000 /* 8M */ #define CONFIG_SYS_MEMTEST_END 0x00ffffff /*(_16M -1) */ #define CONFIG_SYS_RESET_ADDRESS 0xffff0000 /* Rst Vector Adr */ -#define CONFIG_SYS_MAXARGS 16 /* max number of command args */ +#define CONFIG_SYS_MAXARGS 32 /* max number of command args */
/* ====> Include platform Common Definitions */ #include <asm/arch/config.h>

This patch changes the MBUS base addresses and sizes to use more generic names and also adds defines for the sizes. It also moves the base address to higher addresses.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/cpu.c | 14 ++++++++------ arch/arm/mach-mvebu/include/mach/cpu.h | 12 ++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index 9496d5f..7335620 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -19,18 +19,20 @@
static struct mbus_win windows[] = { /* PCIE MEM address space */ - { DEFADR_PCI_MEM, 256 << 20, CPU_TARGET_PCIE13, CPU_ATTR_PCIE_MEM }, + { MBUS_PCI_MEM_BASE, MBUS_PCI_MEM_SIZE, + CPU_TARGET_PCIE13, CPU_ATTR_PCIE_MEM },
/* PCIE IO address space */ - { DEFADR_PCI_IO, 64 << 10, CPU_TARGET_PCIE13, CPU_ATTR_PCIE_IO }, + { MBUS_PCI_IO_BASE, MBUS_PCI_IO_SIZE, + CPU_TARGET_PCIE13, CPU_ATTR_PCIE_IO },
/* SPI */ - { DEFADR_SPIF, 8 << 20, CPU_TARGET_DEVICEBUS_BOOTROM_SPI, - CPU_ATTR_SPIFLASH }, + { MBUS_SPI_BASE, MBUS_SPI_SIZE, + CPU_TARGET_DEVICEBUS_BOOTROM_SPI, CPU_ATTR_SPIFLASH },
/* NOR */ - { DEFADR_BOOTROM, 8 << 20, CPU_TARGET_DEVICEBUS_BOOTROM_SPI, - CPU_ATTR_BOOTROM }, + { MBUS_BOOTROM_BASE, MBUS_BOOTROM_SIZE, + CPU_TARGET_DEVICEBUS_BOOTROM_SPI, CPU_ATTR_BOOTROM }, };
void reset_cpu(unsigned long ignored) diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h index 8bcdef6..674fdaa 100644 --- a/arch/arm/mach-mvebu/include/mach/cpu.h +++ b/arch/arm/mach-mvebu/include/mach/cpu.h @@ -65,10 +65,14 @@ enum { /* * Default Device Address MAP BAR values */ -#define DEFADR_PCI_MEM 0x90000000 -#define DEFADR_PCI_IO 0xC0000000 -#define DEFADR_SPIF 0xF4000000 -#define DEFADR_BOOTROM 0xF8000000 +#define MBUS_PCI_MEM_BASE 0xE8000000 +#define MBUS_PCI_MEM_SIZE (128 << 20) +#define MBUS_PCI_IO_BASE 0xF1100000 +#define MBUS_PCI_IO_SIZE (64 << 10) +#define MBUS_SPI_BASE 0xF4000000 +#define MBUS_SPI_SIZE (8 << 20) +#define MBUS_BOOTROM_BASE 0xF8000000 +#define MBUS_BOOTROM_SIZE (8 << 20)
struct mbus_win { u32 base;

With this patch, the MBUS bridge registers (base and size) are configured upon each call to mbus_dt_setup_win(). This is needed, since the board code can also call this function in later boot stages. As done in the maxbcm board.
This is needed to fix a problem with the secondary CPU's not booting in Linux on AXP.
Signed-off-by: Stefan Roese sr@denx.de Cc: Peter Morrow peter@senient.com Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/include/mach/soc.h | 3 ++ arch/arm/mach-mvebu/mbus.c | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+)
diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index 125b5f2..71254c5 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -66,6 +66,9 @@ #define MVEBU_SATA0_BASE (MVEBU_REGISTER(0xa8000)) #define MVEBU_SDIO_BASE (MVEBU_REGISTER(0xd8000))
+#define MBUS_BRIDGE_WIN_CTRL_REG (MVEBU_REGISTER(0x20250)) +#define MBUS_BRIDGE_WIN_BASE_REG (MVEBU_REGISTER(0x20254)) + #define SDRAM_MAX_CS 4 #define SDRAM_ADDR_MASK 0xFF000000
diff --git a/arch/arm/mach-mvebu/mbus.c b/arch/arm/mach-mvebu/mbus.c index 9b76bce..71fa693 100644 --- a/arch/arm/mach-mvebu/mbus.c +++ b/arch/arm/mach-mvebu/mbus.c @@ -52,6 +52,7 @@ #include <asm/io.h> #include <asm/arch/cpu.h> #include <asm/arch/soc.h> +#include <linux/compat.h> #include <linux/mbus.h>
#define BIT(nr) (1UL << (nr)) @@ -407,6 +408,53 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size) return 0; }
+static void mvebu_mbus_get_lowest_base(struct mvebu_mbus_state *mbus, + phys_addr_t *base) +{ + int win; + *base = 0xffffffff; + + for (win = 0; win < mbus->soc->num_wins; win++) { + u64 wbase; + u32 wsize; + u8 wtarget, wattr; + int enabled; + + mvebu_mbus_read_window(mbus, win, + &enabled, &wbase, &wsize, + &wtarget, &wattr, NULL); + + if (!enabled) + continue; + + if (wbase < *base) + *base = wbase; + } +} + +static void mvebu_config_mbus_bridge(struct mvebu_mbus_state *mbus) +{ + phys_addr_t base; + u32 val; + u32 size; + + /* Set MBUS bridge base/ctrl */ + mvebu_mbus_get_lowest_base(&mbus_state, &base); + + size = 0xffffffff - base + 1; + if (!is_power_of_2(size)) { + /* Round up to next power of 2 */ + size = 1 << (ffs(base) + 1); + base = 0xffffffff - size + 1; + } + + /* Now write base and size */ + writel(base, MBUS_BRIDGE_WIN_BASE_REG); + /* Align window size to 64KiB */ + val = (size / (64 << 10)) - 1; + writel((val << 16) | 0x1, MBUS_BRIDGE_WIN_CTRL_REG); +} + int mbus_dt_setup_win(struct mvebu_mbus_state *mbus, u32 base, u32 size, u8 target, u8 attr) { @@ -426,6 +474,13 @@ int mbus_dt_setup_win(struct mvebu_mbus_state *mbus, return -ENOMEM; }
+ /* + * Re-configure the mbus bridge registers each time this function + * is called. Since it may get called from the board code in + * later boot stages as well. + */ + mvebu_config_mbus_bridge(mbus); + return 0; }

Only with disabled MMU its possible to switch the base register address on Armada 38x. Without this the SDRAM located at >= 0x4000.0000 is also not accessible, as its still locked to cache.
So to fully release / unlock this area from cache, we need to first flush all caches, then disable the MMU and disable the L2 cache.
On Armada XP this does not seem to be needed. Even worse, with this code added, I sometimes see strange input charactes loss from the console.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/cpu.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index 7335620..23c2a97 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -183,13 +183,25 @@ static void set_cbar(u32 addr) int arch_cpu_init(void) { #ifndef CONFIG_SPL_BUILD - /* - * Only with disabled MMU its possible to switch the base - * register address on Armada 38x. Without this the SDRAM - * located at >= 0x4000.0000 is also not accessible, as its - * still locked to cache. - */ - mmu_disable(); + if (mvebu_soc_family() == MVEBU_SOC_A38X) { + struct pl310_regs *const pl310 = + (struct pl310_regs *)CONFIG_SYS_PL310_BASE; + + /* + * Only with disabled MMU its possible to switch the base + * register address on Armada 38x. Without this the SDRAM + * located at >= 0x4000.0000 is also not accessible, as its + * still locked to cache. + * + * So to fully release / unlock this area from cache, we need + * to first flush all caches, then disable the MMU and + * disable the L2 cache. + */ + icache_disable(); + dcache_disable(); + mmu_disable(); + clrbits_le32(&pl310->pl310_ctrl, L2X0_CTRL_EN); + } #endif
/* Linux expects the internal registers to be at 0xf1000000 */

Accessing MBUS windows not backed-up by e.g. PCIe devices will hang the SoC. Disable MBUS error propagation back to CPU allows to read 0xffffffff instead of hanging the SoC.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/cpu.c | 7 +++++++ arch/arm/mach-mvebu/include/mach/soc.h | 3 +++ 2 files changed, 10 insertions(+)
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index 23c2a97..451f308 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -182,6 +182,8 @@ static void set_cbar(u32 addr)
int arch_cpu_init(void) { + u32 reg; + #ifndef CONFIG_SPL_BUILD if (mvebu_soc_family() == MVEBU_SOC_A38X) { struct pl310_regs *const pl310 = @@ -241,6 +243,11 @@ int arch_cpu_init(void) */ mvebu_mbus_probe(windows, ARRAY_SIZE(windows));
+ /* Disable MBUS error propagation */ + reg = readl(SOC_COHERENCY_FABRIC_CTRL_REG); + reg &= ~MBUS_ERR_PROP_EN; + writel(reg, SOC_COHERENCY_FABRIC_CTRL_REG); + return 0; } #endif /* CONFIG_ARCH_CPU_INIT */ diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index 71254c5..a29e5dc 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -66,6 +66,9 @@ #define MVEBU_SATA0_BASE (MVEBU_REGISTER(0xa8000)) #define MVEBU_SDIO_BASE (MVEBU_REGISTER(0xd8000))
+#define SOC_COHERENCY_FABRIC_CTRL_REG (MVEBU_REGISTER(0x20200)) +#define MBUS_ERR_PROP_EN (1 << 8) + #define MBUS_BRIDGE_WIN_CTRL_REG (MVEBU_REGISTER(0x20250)) #define MBUS_BRIDGE_WIN_BASE_REG (MVEBU_REGISTER(0x20254))

Cloned from the Linux driver v4.2.0-rc2. Plus some patches from Antoine Tenart enabling controller initialization and ONFI timing support:
http://lists.infradead.org/pipermail/linux-mtd/2015-July/060197.html
Please note that this driver needs the Linux NAND subsystem sync to v4.1 from Scott to be applied:
https://www.mail-archive.com/u-boot@lists.denx.de/msg175762.html
Otherwise it will not compile.
Tested on the Marvell Armada XP DB-MV784MP-GP eval board.
Signed-off-by: Stefan Roese sr@denx.de Cc: Antoine Tenart antoine.tenart@free-electrons.com Cc: Ezeguil Garcia ezequiel.garcia@free-electrons.com Cc: Luka Perkov luka.perkov@sartura.hr Cc: Scott Wood scottwood@freescale.com --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/pxa3xx_nand.c | 1621 ++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/pxa3xx_nand.h | 64 ++ 4 files changed, 1693 insertions(+) create mode 100644 drivers/mtd/nand/pxa3xx_nand.c create mode 100644 drivers/mtd/nand/pxa3xx_nand.h
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3024357..6fd2a34 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -56,6 +56,13 @@ config SYS_NAND_VF610_NFC_60_ECC_BYTES
endchoice
+config NAND_PXA3XX + bool "Support for NAND on PXA3xx and Armada 370/XP/38x" + select SYS_NAND_SELF_INIT + help + This enables the driver for the NAND flash device found on + PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). + comment "Generic NAND options"
# Enhance depends when converting drivers to Kconfig which use this config diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 347ea62..c777a58 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_NAND_MXC) += mxc_nand.o obj-$(CONFIG_NAND_MXS) += mxs_nand.o obj-$(CONFIG_NAND_NDFC) += ndfc.o obj-$(CONFIG_NAND_NOMADIK) += nomadik.o +obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o obj-$(CONFIG_NAND_SPEAR) += spr_nand.o obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c new file mode 100644 index 0000000..1565a9a --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -0,0 +1,1621 @@ +/* + * drivers/mtd/nand/pxa3xx_nand.c + * + * Copyright © 2005 Intel Corporation + * Copyright © 2006 Marvell International Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <malloc.h> +#include <nand.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/types.h> + +#include "pxa3xx_nand.h" + +/* Some U-Boot compatibility macros */ +#define writesl(a, d, s) __raw_writesl((unsigned long)a, d, s) +#define readsl(a, d, s) __raw_readsl((unsigned long)a, d, s) +#define writesw(a, d, s) __raw_writesw((unsigned long)a, d, s) +#define readsw(a, d, s) __raw_readsw((unsigned long)a, d, s) +#define writesb(a, d, s) __raw_writesb((unsigned long)a, d, s) +#define readsb(a, d, s) __raw_readsb((unsigned long)a, d, s) + +#define TIMEOUT_DRAIN_FIFO 5 /* in ms */ +#define CHIP_DELAY_TIMEOUT 200 +#define NAND_STOP_DELAY 40 +#define PAGE_CHUNK_SIZE (2048) + +/* + * Define a buffer size for the initial command that detects the flash device: + * STATUS, READID and PARAM. The largest of these is the PARAM command, + * needing 256 bytes. + */ +#define INIT_BUFFER_SIZE 256 + +/* registers and bit definitions */ +#define NDCR (0x00) /* Control register */ +#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ +#define NDSR (0x14) /* Status Register */ +#define NDPCR (0x18) /* Page Count Register */ +#define NDBDR0 (0x1C) /* Bad Block Register 0 */ +#define NDBDR1 (0x20) /* Bad Block Register 1 */ +#define NDECCCTRL (0x28) /* ECC control */ +#define NDDB (0x40) /* Data Buffer */ +#define NDCB0 (0x48) /* Command Buffer0 */ +#define NDCB1 (0x4C) /* Command Buffer1 */ +#define NDCB2 (0x50) /* Command Buffer2 */ + +#define NDCR_SPARE_EN (0x1 << 31) +#define NDCR_ECC_EN (0x1 << 30) +#define NDCR_DMA_EN (0x1 << 29) +#define NDCR_ND_RUN (0x1 << 28) +#define NDCR_DWIDTH_C (0x1 << 27) +#define NDCR_DWIDTH_M (0x1 << 26) +#define NDCR_PAGE_SZ (0x1 << 24) +#define NDCR_NCSX (0x1 << 23) +#define NDCR_ND_MODE (0x3 << 21) +#define NDCR_NAND_MODE (0x0) +#define NDCR_CLR_PG_CNT (0x1 << 20) +#define NDCR_STOP_ON_UNCOR (0x1 << 19) +#define NDCR_RD_ID_CNT_MASK (0x7 << 16) +#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) + +#define NDCR_RA_START (0x1 << 15) +#define NDCR_PG_PER_BLK (0x1 << 14) +#define NDCR_ND_ARB_EN (0x1 << 12) +#define NDCR_INT_MASK (0xFFF) + +#define NDSR_MASK (0xfff) +#define NDSR_ERR_CNT_OFF (16) +#define NDSR_ERR_CNT_MASK (0x1f) +#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) +#define NDSR_RDY (0x1 << 12) +#define NDSR_FLASH_RDY (0x1 << 11) +#define NDSR_CS0_PAGED (0x1 << 10) +#define NDSR_CS1_PAGED (0x1 << 9) +#define NDSR_CS0_CMDD (0x1 << 8) +#define NDSR_CS1_CMDD (0x1 << 7) +#define NDSR_CS0_BBD (0x1 << 6) +#define NDSR_CS1_BBD (0x1 << 5) +#define NDSR_UNCORERR (0x1 << 4) +#define NDSR_CORERR (0x1 << 3) +#define NDSR_WRDREQ (0x1 << 2) +#define NDSR_RDDREQ (0x1 << 1) +#define NDSR_WRCMDREQ (0x1) + +#define NDCB0_LEN_OVRD (0x1 << 28) +#define NDCB0_ST_ROW_EN (0x1 << 26) +#define NDCB0_AUTO_RS (0x1 << 25) +#define NDCB0_CSEL (0x1 << 24) +#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) +#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_NC (0x1 << 20) +#define NDCB0_DBC (0x1 << 19) +#define NDCB0_ADDR_CYC_MASK (0x7 << 16) +#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) +#define NDCB0_CMD2_MASK (0xff << 8) +#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 */ + +/* macros for registers read/write */ +#define nand_writel(info, off, val) \ + writel((val), (info)->mmio_base + (off)) + +#define nand_readl(info, off) \ + readl((info)->mmio_base + (off)) + +/* error code and state */ +enum { + ERR_NONE = 0, + ERR_DMABUSERR = -1, + ERR_SENDCMD = -2, + ERR_UNCORERR = -3, + ERR_BBERR = -4, + ERR_CORERR = -5, +}; + +enum { + STATE_IDLE = 0, + STATE_PREPARED, + STATE_CMD_HANDLE, + STATE_DMA_READING, + STATE_DMA_WRITING, + STATE_DMA_DONE, + STATE_PIO_READING, + STATE_PIO_WRITING, + STATE_CMD_DONE, + STATE_READY, +}; + +enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, +}; + +struct pxa3xx_nand_host { + struct nand_chip chip; + struct mtd_info *mtd; + void *info_data; + + /* page size of attached chip */ + int use_ecc; + int cs; + + /* calculated from pxa3xx_nand_flash data */ + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; + size_t read_id_bytes; + +}; + +struct pxa3xx_nand_info { + struct nand_hw_control controller; + struct pxa3xx_nand_platform_data *pdata; + + struct clk *clk; + void __iomem *mmio_base; + unsigned long mmio_phys; + int cmd_complete, dev_ready; + + unsigned int buf_start; + unsigned int buf_count; + unsigned int buf_size; + unsigned int data_buff_pos; + unsigned int oob_buff_pos; + + unsigned char *data_buff; + unsigned char *oob_buff; + + struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; + unsigned int state; + + /* + * This driver supports NFCv1 (as found in PXA SoC) + * and NFCv2 (as found in Armada 370/XP SoC). + */ + enum pxa3xx_nand_variant variant; + + int cs; + int use_ecc; /* use HW ECC ? */ + int ecc_bch; /* using BCH ECC? */ + int use_spare; /* use spare ? */ + int need_wait; + + unsigned int data_size; /* data to be read from FIFO */ + unsigned int chunk_size; /* split commands chunk size */ + unsigned int oob_size; + unsigned int spare_size; + unsigned int ecc_size; + unsigned int ecc_err_cnt; + unsigned int max_bitflips; + int retcode; + + /* cached register value */ + uint32_t reg_ndcr; + uint32_t ndtr0cs0; + uint32_t ndtr1cs0; + + /* generated NDCBx register values */ + uint32_t ndcb0; + uint32_t ndcb1; + uint32_t ndcb2; + uint32_t ndcb3; +}; + +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, }, +}; + +static struct pxa3xx_nand_flash builtin_flash_types[] = { + { 0x46ec, 16, 16, &timing[1] }, + { 0xdaec, 8, 8, &timing[1] }, + { 0xd7ec, 8, 8, &timing[1] }, + { 0xa12c, 8, 8, &timing[2] }, + { 0xb12c, 16, 16, &timing[2] }, + { 0xdc2c, 8, 8, &timing[2] }, + { 0xcc2c, 16, 16, &timing[2] }, + { 0xba20, 16, 16, &timing[3] }, +}; + +static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; +static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_mirror_pattern +}; + +static struct nand_ecclayout ecc_layout_2KB_bch4bit = { + .eccbytes = 32, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 30} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch4bit = { + .eccbytes = 64, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 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}, + /* Bootrom looks in bytes 0 & 5 for bad blocks */ + .oobfree = { {6, 26}, { 64, 32} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch8bit = { + .eccbytes = 128, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { } +}; + +#define NDTR0_tCH(c) (min((c), 7) << 19) +#define NDTR0_tCS(c) (min((c), 7) << 16) +#define NDTR0_tWH(c) (min((c), 7) << 11) +#define NDTR0_tWP(c) (min((c), 7) << 8) +#define NDTR0_tRH(c) (min((c), 7) << 3) +#define NDTR0_tRP(c) (min((c), 7) << 0) + +#define NDTR1_tR(c) (min((c), 65535) << 16) +#define NDTR1_tWHR(c) (min((c), 15) << 4) +#define NDTR1_tAR(c) (min((c), 15) << 0) + +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) + +static enum pxa3xx_nand_variant pxa3xx_nand_get_variant(void) +{ + /* We only support the Armada 370/XP/38x for now */ + return PXA3XX_NAND_VARIANT_ARMADA370; +} + +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, + const struct pxa3xx_nand_timing *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + unsigned long nand_clk = mvebu_get_nand_clock(); + uint32_t ndtr0, ndtr1; + + ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | + NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | + NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | + NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | + NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | + NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | + NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, + const struct nand_sdr_timings *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + struct nand_chip *chip = &host->chip; + unsigned long nand_clk = mvebu_get_nand_clock(); + uint32_t ndtr0, ndtr1; + + u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); + u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); + u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); + u32 tWP_min = DIV_ROUND_UP(t->tWC_min - tWH_min, 1000); + u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); + u32 tRP_min = DIV_ROUND_UP(t->tRC_min - tREH_min, 1000); + u32 tR = chip->chip_delay * 1000; + u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); + u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); + + /* fallback to a default value if tR = 0 */ + if (!tR) + tR = 20000; + + ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | + NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | + NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | + NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | + NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | + NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | + NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host) +{ + const struct nand_sdr_timings *timings; + struct nand_chip *chip = &host->chip; + struct pxa3xx_nand_info *info = host->info_data; + const struct pxa3xx_nand_flash *f = NULL; + int mode, id, ntypes, i; + + mode = onfi_get_async_timing_mode(chip); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + ntypes = ARRAY_SIZE(builtin_flash_types); + + chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1); + + id = chip->read_byte(host->mtd); + id |= chip->read_byte(host->mtd) << 0x8; + + for (i = 0; i < ntypes; i++) { + f = &builtin_flash_types[i]; + + if (f->chip_id == id) + break; + } + + if (i == ntypes) { + dev_err(&info->pdev->dev, "Error: timings not found\n"); + return -EINVAL; + } + + pxa3xx_nand_set_timing(host, f->timing); + + if (f->flash_width == 16) { + info->reg_ndcr |= NDCR_DWIDTH_M; + chip->options |= NAND_BUSWIDTH_16; + } + + info->reg_ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; + } else { + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + } + + return 0; +} + +/* + * Set the data and OOB size, depending on the selected + * spare and ECC configuration. + * Only applicable to READ0, READOOB and PAGEPROG commands. + */ +static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info, + struct mtd_info *mtd) +{ + int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; + + info->data_size = mtd->writesize; + if (!oob_enable) + return; + + info->oob_size = info->spare_size; + if (!info->use_ecc) + info->oob_size += info->ecc_size; +} + +/** + * NOTE: it is a must to set ND_RUN firstly, then write + * command buffer, otherwise, it does not work. + * We enable all the interrupt at the same time, and + * let pxa3xx_nand_irq to handle all logic. + */ +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + + ndcr = info->reg_ndcr; + + if (info->use_ecc) { + ndcr |= NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x1); + } else { + ndcr &= ~NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x0); + } + + ndcr &= ~NDCR_DMA_EN; + + if (info->use_spare) + ndcr |= NDCR_SPARE_EN; + else + ndcr &= ~NDCR_SPARE_EN; + + ndcr |= NDCR_ND_RUN; + + /* clear status bits and run */ + nand_writel(info, NDCR, 0); + nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, ndcr); +} + +static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr | int_mask); +} + +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) +{ + if (info->ecc_bch) { + u32 ts; + + /* + * According to the datasheet, when reading from NDDB + * with BCH enabled, after each 32 bytes reads, we + * have to make sure that the NDSR.RDDREQ bit is set. + * + * Drain the FIFO 8 32 bits reads at a time, and skip + * the polling on the last read. + */ + while (len > 8) { + readsl(info->mmio_base + NDDB, data, 8); + + ts = get_timer(0); + while (!(nand_readl(info, NDSR) & NDSR_RDDREQ)) { + if (get_timer(ts) > TIMEOUT_DRAIN_FIFO) { + dev_err(&info->pdev->dev, + "Timeout on RDDREQ while draining the FIFO\n"); + return; + } + } + + data += 32; + len -= 8; + } + } + + readsl(info->mmio_base + NDDB, data, len); +} + +static void handle_data_pio(struct pxa3xx_nand_info *info) +{ + unsigned int do_bytes = min(info->data_size, info->chunk_size); + + switch (info->state) { + case STATE_PIO_WRITING: + writesl(info->mmio_base + NDDB, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(do_bytes, 4)); + + if (info->oob_size > 0) + writesl(info->mmio_base + NDDB, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->oob_size, 4)); + break; + case STATE_PIO_READING: + drain_fifo(info, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(do_bytes, 4)); + + if (info->oob_size > 0) + drain_fifo(info, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->oob_size, 4)); + break; + default: + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, + info->state); + BUG(); + } + + /* Update buffer pointers for multi-page read/write */ + info->data_buff_pos += do_bytes; + info->oob_buff_pos += info->oob_size; + info->data_size -= do_bytes; +} + +static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info) +{ + handle_data_pio(info); + + info->state = STATE_CMD_DONE; + nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); +} + +static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info) +{ + unsigned int status, is_completed = 0, is_ready = 0; + unsigned int ready, cmd_done; + irqreturn_t ret = IRQ_HANDLED; + + if (info->cs == 0) { + ready = NDSR_FLASH_RDY; + cmd_done = NDSR_CS0_CMDD; + } else { + ready = NDSR_RDY; + cmd_done = NDSR_CS1_CMDD; + } + + status = nand_readl(info, NDSR); + + if (status & NDSR_UNCORERR) + info->retcode = ERR_UNCORERR; + if (status & NDSR_CORERR) { + info->retcode = ERR_CORERR; + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 && + info->ecc_bch) + info->ecc_err_cnt = NDSR_ERR_CNT(status); + else + info->ecc_err_cnt = 1; + + /* + * Each chunk composing a page is corrected independently, + * and we need to store maximum number of corrected bitflips + * to return it to the MTD layer in ecc.read_page(). + */ + info->max_bitflips = max_t(unsigned int, + info->max_bitflips, + info->ecc_err_cnt); + } + if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { + info->state = (status & NDSR_RDDREQ) ? + STATE_PIO_READING : STATE_PIO_WRITING; + /* Call the IRQ thread in U-Boot directly */ + pxa3xx_nand_irq_thread(info); + return 0; + } + if (status & cmd_done) { + info->state = STATE_CMD_DONE; + is_completed = 1; + } + if (status & ready) { + info->state = STATE_READY; + is_ready = 1; + } + + if (status & NDSR_WRCMDREQ) { + nand_writel(info, NDSR, NDSR_WRCMDREQ); + status &= ~NDSR_WRCMDREQ; + info->state = STATE_CMD_HANDLE; + + /* + * Command buffer registers NDCB{0-2} (and optionally NDCB3) + * must be loaded by writing directly either 12 or 16 + * bytes directly to NDCB0, four bytes at a time. + * + * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored + * but each NDCBx register can be read. + */ + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); + + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDCB0, info->ndcb3); + } + + /* clear NDSR to let the controller exit the IRQ */ + nand_writel(info, NDSR, status); + if (is_completed) + info->cmd_complete = 1; + if (is_ready) + info->dev_ready = 1; + + return ret; +} + +static inline int is_buf_blank(uint8_t *buf, size_t len) +{ + for (; len > 0; len--) + if (*buf++ != 0xff) + return 0; + return 1; +} + +static void set_command_address(struct pxa3xx_nand_info *info, + unsigned int page_size, uint16_t column, int page_addr) +{ + /* small page addr setting */ + if (page_size < PAGE_CHUNK_SIZE) { + info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) + | (column & 0xFF); + + info->ndcb2 = 0; + } else { + info->ndcb1 = ((page_addr & 0xFFFF) << 16) + | (column & 0xFFFF); + + if (page_addr & 0xFF0000) + info->ndcb2 = (page_addr & 0xFF0000) >> 16; + else + info->ndcb2 = 0; + } +} + +static void prepare_start_command(struct pxa3xx_nand_info *info, int command) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct mtd_info *mtd = host->mtd; + + /* reset data and oob column point to handle data */ + info->buf_start = 0; + info->buf_count = 0; + info->oob_size = 0; + info->data_buff_pos = 0; + info->oob_buff_pos = 0; + info->use_ecc = 0; + info->use_spare = 1; + info->retcode = ERR_NONE; + info->ecc_err_cnt = 0; + info->ndcb3 = 0; + info->need_wait = 0; + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_PAGEPROG: + info->use_ecc = 1; + case NAND_CMD_READOOB: + pxa3xx_set_datasize(info, mtd); + break; + case NAND_CMD_PARAM: + info->use_spare = 0; + break; + default: + info->ndcb1 = 0; + info->ndcb2 = 0; + break; + } + + /* + * If we are about to issue a read command, or about to set + * the write address, then clean the data buffer. + */ + if (command == NAND_CMD_READ0 || + command == NAND_CMD_READOOB || + command == NAND_CMD_SEQIN) { + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xFF, info->buf_count); + } +} + +static int prepare_set_command(struct pxa3xx_nand_info *info, int command, + int ext_cmd_type, uint16_t column, int page_addr) +{ + int addr_cycle, exec_cmd; + struct pxa3xx_nand_host *host; + struct mtd_info *mtd; + + host = info->host[info->cs]; + mtd = host->mtd; + addr_cycle = 0; + exec_cmd = 1; + + if (info->cs != 0) + info->ndcb0 = NDCB0_CSEL; + else + info->ndcb0 = 0; + + if (command == NAND_CMD_SEQIN) + exec_cmd = 0; + + addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles + + host->col_addr_cycles); + + switch (command) { + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + info->buf_start = column; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | addr_cycle + | NAND_CMD_READ0; + + if (command == NAND_CMD_READOOB) + info->buf_start += mtd->writesize; + + /* + * Multiple page read needs an 'extended command type' field, + * which is either naked-read or last-read according to the + * state. + */ + if (mtd->writesize == PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); + } else if (mtd->writesize > PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->chunk_size + + info->oob_size; + } + + set_command_address(info, mtd->writesize, column, page_addr); + break; + + case NAND_CMD_SEQIN: + + info->buf_start = column; + set_command_address(info, mtd->writesize, 0, page_addr); + + /* + * Multiple page programming needs to execute the initial + * SEQIN command that sets the page address. + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | addr_cycle + | command; + /* No data transfer in this case */ + info->data_size = 0; + exec_cmd = 1; + } + break; + + case NAND_CMD_PAGEPROG: + if (is_buf_blank(info->data_buff, + (mtd->writesize + mtd->oobsize))) { + exec_cmd = 0; + break; + } + + /* Second command setting for large pages */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + /* + * Multiple page write uses the 'extended command' + * field. This can be used to issue a command dispatch + * or a naked-write depending on the current stage. + */ + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->chunk_size + + info->oob_size; + + /* + * This is the command dispatch that completes a chunked + * page program operation. + */ + if (info->data_size == 0) { + info->ndcb0 = NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | command; + info->ndcb1 = 0; + info->ndcb2 = 0; + info->ndcb3 = 0; + } + } else { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_AUTO_RS + | NDCB0_ST_ROW_EN + | NDCB0_DBC + | (NAND_CMD_PAGEPROG << 8) + | NAND_CMD_SEQIN + | addr_cycle; + } + break; + + case NAND_CMD_PARAM: + info->buf_count = 256; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | NDCB0_LEN_OVRD + | command; + info->ndcb1 = (column & 0xFF); + info->ndcb3 = 256; + info->data_size = 256; + break; + + case NAND_CMD_READID: + info->buf_count = host->read_id_bytes; + info->ndcb0 |= NDCB0_CMD_TYPE(3) + | NDCB0_ADDR_CYC(1) + | command; + info->ndcb1 = (column & 0xFF); + + info->data_size = 8; + break; + case NAND_CMD_STATUS: + info->buf_count = 1; + info->ndcb0 |= NDCB0_CMD_TYPE(4) + | NDCB0_ADDR_CYC(1) + | command; + + info->data_size = 8; + break; + + case 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; + info->ndcb1 = page_addr; + info->ndcb2 = 0; + + break; + case NAND_CMD_RESET: + info->ndcb0 |= NDCB0_CMD_TYPE(5) + | command; + + break; + + case NAND_CMD_ERASE2: + exec_cmd = 0; + break; + + default: + exec_cmd = 0; + dev_err(&info->pdev->dev, "non-supported command %x\n", + command); + break; + } + + return exec_cmd; +} + +static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int exec_cmd; + + /* + * if this is a x16 device ,then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + prepare_start_command(info, command); + + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, 0, column, page_addr); + + if (exec_cmd) { + u32 ts; + + info->cmd_complete = 0; + info->dev_ready = 0; + info->need_wait = 1; + pxa3xx_nand_start(info); + + ts = get_timer(0); + while (1) { + u32 status; + + status = nand_readl(info, NDSR); + if (status) + pxa3xx_nand_irq(info); + + if (info->cmd_complete) + break; + + if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { + dev_err(&info->pdev->dev, "Wait timeout!!!\n"); + return; + } + } + } + info->state = STATE_IDLE; +} + +static void nand_cmdfunc_extended(struct mtd_info *mtd, + const unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int exec_cmd, ext_cmd_type; + + /* + * if this is a x16 device then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + /* Select the extended command for the first command */ + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + ext_cmd_type = EXT_CMD_TYPE_MONO; + break; + case NAND_CMD_SEQIN: + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + break; + case NAND_CMD_PAGEPROG: + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + break; + default: + ext_cmd_type = 0; + break; + } + + prepare_start_command(info, command); + + /* + * Prepare the "is ready" completion before starting a command + * transaction sequence. If the command is not executed the + * completion will be completed, see below. + * + * We can do that inside the loop because the command variable + * is invariant and thus so is the exec_cmd. + */ + info->need_wait = 1; + info->dev_ready = 0; + + do { + u32 ts; + + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, ext_cmd_type, + column, page_addr); + if (!exec_cmd) { + info->need_wait = 0; + info->dev_ready = 1; + break; + } + + info->cmd_complete = 0; + pxa3xx_nand_start(info); + + ts = get_timer(0); + while (1) { + u32 status; + + status = nand_readl(info, NDSR); + if (status) + pxa3xx_nand_irq(info); + + if (info->cmd_complete) + break; + + if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { + dev_err(&info->pdev->dev, "Wait timeout!!!\n"); + return; + } + } + + /* Check if the sequence is complete */ + if (info->data_size == 0 && command != NAND_CMD_PAGEPROG) + break; + + /* + * After a splitted program command sequence has issued + * the command dispatch, the command sequence is complete. + */ + if (info->data_size == 0 && + command == NAND_CMD_PAGEPROG && + ext_cmd_type == EXT_CMD_TYPE_DISPATCH) + break; + + if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { + /* Last read: issue a 'last naked read' */ + if (info->data_size == info->chunk_size) + ext_cmd_type = EXT_CMD_TYPE_LAST_RW; + else + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + + /* + * If a splitted program command has no more data to transfer, + * the command dispatch must be issued to complete. + */ + } else if (command == NAND_CMD_PAGEPROG && + info->data_size == 0) { + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + } + } while (1); + + info->state = STATE_IDLE; +} + +static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (info->retcode == ERR_CORERR && info->use_ecc) { + mtd->ecc_stats.corrected += info->ecc_err_cnt; + + } else if (info->retcode == ERR_UNCORERR) { + /* + * for blank page (all 0xff), HW will calculate its ECC as + * 0, which is different from the ECC information within + * OOB, ignore such uncorrectable errors + */ + if (is_buf_blank(buf, mtd->writesize)) + info->retcode = ERR_NONE; + else + mtd->ecc_stats.failed++; + } + + return info->max_bitflips; +} + +static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + char retval = 0xFF; + + if (info->buf_start < info->buf_count) + /* Has just send a new command? */ + retval = info->data_buff[info->buf_start++]; + + return retval; +} + +static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + u16 retval = 0xFFFF; + + if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { + retval = *((u16 *)(info->data_buff+info->buf_start)); + info->buf_start += 2; + } + return retval; +} + +static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(buf, info->data_buff + info->buf_start, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(info->data_buff + info->buf_start, buf, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) +{ + return; +} + +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + + if (info->need_wait) { + u32 ts; + + info->need_wait = 0; + + ts = get_timer(0); + while (1) { + u32 status; + + status = nand_readl(info, NDSR); + if (status) + pxa3xx_nand_irq(info); + + if (info->dev_ready) + break; + + if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { + dev_err(&info->pdev->dev, "Ready timeout!!!\n"); + return NAND_STATUS_FAIL; + } + } + } + + /* pxa3xx_nand_send_command has waited for command complete */ + if (this->state == FL_WRITING || this->state == FL_ERASING) { + if (info->retcode == ERR_NONE) + return 0; + else + return NAND_STATUS_FAIL; + } + + return NAND_STATUS_READY; +} + +static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct mtd_info *mtd = host->mtd; + struct nand_chip *chip = mtd->priv; + + info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; + info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; + info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; + + return 0; +} + +static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +{ + /* + * We set 0 by hard coding here, for we don't support keep_config + * when there is more than one chip attached to the controller + */ + struct pxa3xx_nand_host *host = info->host[0]; + uint32_t ndcr = nand_readl(info, NDCR); + + if (ndcr & NDCR_PAGE_SZ) { + /* Controller's FIFO size */ + info->chunk_size = 2048; + host->read_id_bytes = 4; + } else { + info->chunk_size = 512; + host->read_id_bytes = 2; + } + + /* Set an initial chunk size */ + info->reg_ndcr = ndcr & ~NDCR_INT_MASK; + info->ndtr0cs0 = nand_readl(info, NDTR0CS0); + info->ndtr1cs0 = nand_readl(info, NDTR1CS0); + return 0; +} + +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + return 0; +} + +static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host) +{ + struct pxa3xx_nand_info *info = host->info_data; + struct pxa3xx_nand_platform_data *pdata = info->pdata; + struct mtd_info *mtd; + struct nand_chip *chip; + const struct nand_sdr_timings *timings; + int ret; + + mtd = info->host[info->cs]->mtd; + chip = mtd->priv; + + /* configure default flash values */ + info->reg_ndcr = 0x0; /* enable all interrupts */ + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->reg_ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); + info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + + /* use the common timing to make a try */ + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + + chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); + ret = chip->waitfunc(mtd, chip); + if (ret & NAND_STATUS_FAIL) + return -ENODEV; + + return 0; +} + +static int pxa_ecc_init(struct pxa3xx_nand_info *info, + struct nand_ecc_ctrl *ecc, + int strength, int ecc_stepsize, int page_size) +{ + if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { + info->chunk_size = 2048; + info->spare_size = 40; + info->ecc_size = 24; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { + info->chunk_size = 512; + info->spare_size = 8; + info->ecc_size = 8; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + /* + * Required ECC: 4-bit correction per 512 bytes + * Select: 16-bit correction per 2048 bytes + */ + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { + info->ecc_bch = 1; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_2KB_bch4bit; + ecc->strength = 16; + + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_4KB_bch4bit; + ecc->strength = 16; + + /* + * Required ECC: 8-bit correction per 512 bytes + * Select: 16-bit correction per 1024 bytes + */ + } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->chunk_size = 1024; + info->spare_size = 0; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_4KB_bch8bit; + ecc->strength = 16; + } else { + dev_err(&info->pdev->dev, + "ECC strength %d at page size %d is not supported\n", + strength, page_size); + return -ENODEV; + } + + return 0; +} + +static int pxa3xx_nand_scan(struct mtd_info *mtd) +{ + struct pxa3xx_nand_host *host = mtd->priv; + struct pxa3xx_nand_info *info = host->info_data; + struct pxa3xx_nand_platform_data *pdata = info->pdata; + struct nand_chip *chip = mtd->priv; + int ret; + uint16_t ecc_strength, ecc_step; + + if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) + goto KEEP_CONFIG; + + /* Set a default chunk size */ + info->chunk_size = 512; + + ret = pxa3xx_nand_sensing(host); + if (ret) { + dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", + info->cs); + + return ret; + } + +KEEP_CONFIG: + /* Device detection must be done with ECC disabled */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDECCCTRL, 0x0); + + if (nand_scan_ident(mtd, 1, NULL)) + return -ENODEV; + + if (!pdata->keep_config) { + ret = pxa3xx_nand_init_timings(host); + if (ret) { + dev_err(&info->pdev->dev, + "Failed to set timings: %d\n", ret); + return ret; + } + } + + ret = pxa3xx_nand_config_flash(info); + if (ret) + return ret; + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; +#endif + + /* + * If the page size is bigger than the FIFO size, let's check + * we are given the right variant and then switch to the extended + * (aka splitted) command handling, + */ + if (mtd->writesize > PAGE_CHUNK_SIZE) { + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) { + chip->cmdfunc = nand_cmdfunc_extended; + } else { + dev_err(&info->pdev->dev, + "unsupported page size on this variant\n"); + return -ENODEV; + } + } + + if (pdata->ecc_strength && pdata->ecc_step_size) { + ecc_strength = pdata->ecc_strength; + ecc_step = pdata->ecc_step_size; + } else { + ecc_strength = chip->ecc_strength_ds; + ecc_step = chip->ecc_step_ds; + } + + /* Set default ECC strength requirements on non-ONFI devices */ + if (ecc_strength < 1 && ecc_step < 1) { + ecc_strength = 1; + ecc_step = 512; + } + + ret = pxa_ecc_init(info, &chip->ecc, ecc_strength, + ecc_step, mtd->writesize); + if (ret) + return ret; + + /* calculate addressing information */ + if (mtd->writesize >= 2048) + host->col_addr_cycles = 2; + else + host->col_addr_cycles = 1; + + /* release the initial buffer */ + kfree(info->data_buff); + + /* allocate the real data + oob buffer */ + info->buf_size = mtd->writesize + mtd->oobsize; + ret = pxa3xx_nand_init_buff(info); + if (ret) + return ret; + info->oob_buff = info->data_buff + mtd->writesize; + + if ((mtd->size >> chip->page_shift) > 65536) + host->row_addr_cycles = 3; + else + host->row_addr_cycles = 2; + return nand_scan_tail(mtd); +} + +static int alloc_nand_resource(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_host *host; + struct nand_chip *chip = NULL; + struct mtd_info *mtd; + int ret, cs; + + pdata = info->pdata; + if (pdata->num_cs <= 0) + return -ENODEV; + + info->variant = pxa3xx_nand_get_variant(); + for (cs = 0; cs < pdata->num_cs; cs++) { + mtd = &nand_info[cs]; + chip = (struct nand_chip *)info + + sizeof(struct pxa3xx_nand_host); + host = (struct pxa3xx_nand_host *)chip; + info->host[cs] = host; + host->mtd = mtd; + host->cs = cs; + host->info_data = info; + host->read_id_bytes = 4; + mtd->priv = host; + mtd->owner = THIS_MODULE; + + chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; + chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; + chip->controller = &info->controller; + chip->waitfunc = pxa3xx_nand_waitfunc; + chip->select_chip = pxa3xx_nand_select_chip; + chip->read_word = pxa3xx_nand_read_word; + chip->read_byte = pxa3xx_nand_read_byte; + chip->read_buf = pxa3xx_nand_read_buf; + chip->write_buf = pxa3xx_nand_write_buf; + chip->options |= NAND_NO_SUBPAGE_WRITE; + chip->cmdfunc = nand_cmdfunc; + } + + info->mmio_base = (void __iomem *)MVEBU_NAND_BASE; + + /* Allocate a buffer to allow flash detection */ + info->buf_size = INIT_BUFFER_SIZE; + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) { + ret = -ENOMEM; + goto fail_disable_clk; + } + + /* initialize all interrupts to be disabled */ + disable_int(info, NDSR_MASK); + + return 0; + + kfree(info->data_buff); +fail_disable_clk: + return ret; +} + +static int pxa3xx_nand_probe_dt(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->enable_arbiter = 1; + pdata->num_cs = 1; + + info->pdata = pdata; + + return 0; +} + +static int pxa3xx_nand_probe(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata; + int ret, cs, probe_success; + + ret = pxa3xx_nand_probe_dt(info); + if (ret) + return ret; + + pdata = info->pdata; + + ret = alloc_nand_resource(info); + if (ret) { + dev_err(&pdev->dev, "alloc nand resource failed\n"); + return ret; + } + + probe_success = 0; + for (cs = 0; cs < pdata->num_cs; cs++) { + struct mtd_info *mtd = info->host[cs]->mtd; + + /* + * The mtd name matches the one used in 'mtdparts' kernel + * parameter. This name cannot be changed or otherwise + * user's mtd partitions configuration would get broken. + */ + mtd->name = "pxa3xx_nand-0"; + info->cs = cs; + ret = pxa3xx_nand_scan(mtd); + if (ret) { + dev_info(&pdev->dev, "failed to scan nand at cs %d\n", + cs); + continue; + } + + if (!ret) + probe_success = 1; + } + + if (!probe_success) + return -ENODEV; + + return 0; +} + +/* + * Main initialization routine + */ +void board_nand_init(void) +{ + struct pxa3xx_nand_info *info; + struct pxa3xx_nand_host *host; + int ret; + + info = kzalloc(sizeof(*info) + (sizeof(struct mtd_info) + + sizeof(*host)) * + CONFIG_SYS_MAX_NAND_DEVICE, GFP_KERNEL); + if (!info) + return; + + /* + * If CONFIG_SYS_NAND_SELF_INIT is defined, each driver is responsible + * for instantiating struct nand_chip, while drivers/mtd/nand/nand.c + * still provides a "struct mtd_info nand_info" instance. + */ + info->host[0]->mtd = &nand_info[0]; + + ret = pxa3xx_nand_probe(info); + if (ret) + return; + + nand_register(0); +} diff --git a/drivers/mtd/nand/pxa3xx_nand.h b/drivers/mtd/nand/pxa3xx_nand.h new file mode 100644 index 0000000..8f24ae6 --- /dev/null +++ b/drivers/mtd/nand/pxa3xx_nand.h @@ -0,0 +1,64 @@ +#ifndef __ASM_ARCH_PXA3XX_NAND_H +#define __ASM_ARCH_PXA3XX_NAND_H + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +struct pxa3xx_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_flash { + uint32_t chip_id; + unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ + unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ +}; + +/* + * Current pxa3xx_nand controller has two chip select which + * both be workable. + * + * Notice should be taken that: + * When you want to use this feature, you should not enable the + * keep configuration feature, for two chip select could be + * attached with different nand chip. The different page size + * and timing requirement make the keep configuration impossible. + */ + +/* The max num of chip select current support */ +#define NUM_CHIP_SELECT (2) +struct pxa3xx_nand_platform_data { + /* the data flash bus is shared between the Static Memory + * Controller and the Data Flash Controller, the arbiter + * controls the ownership of the bus + */ + int enable_arbiter; + + /* allow platform code to keep OBM/bootloader defined NFC config */ + int keep_config; + + /* indicate how many chip selects will be used */ + int num_cs; + + /* use an flash-based bad block table */ + bool flash_bbt; + + /* requested ECC strength and ECC step size */ + int ecc_strength, ecc_step_size; + + const struct mtd_partition *parts[NUM_CHIP_SELECT]; + unsigned int nr_parts[NUM_CHIP_SELECT]; + + const struct pxa3xx_nand_flash *flash; + size_t num_flash; +}; +#endif /* __ASM_ARCH_PXA3XX_NAND_H */

This patch enables the NAND controller on the Armada XP/38x and provides a new function that returns the NAND controller input clock. This function will be used by the MVEBU NAND driver.
As part of this patch, the multiple BIT macro definitions are moved to a common place in soc.h.
Signed-off-by: Stefan Roese sr@denx.de Cc: Peter Morrow peter@senient.com Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/cpu.c | 20 ++++++++++++++++++++ arch/arm/mach-mvebu/include/mach/cpu.h | 1 + arch/arm/mach-mvebu/include/mach/soc.h | 22 ++++++++++++++++++++++ arch/arm/mach-mvebu/mbus.c | 2 -- board/Marvell/db-88f6820-gp/db-88f6820-gp.c | 2 -- board/Marvell/db-mv784mp-gp/db-mv784mp-gp.c | 2 -- drivers/net/mvneta.c | 1 - 7 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index 451f308..bde3990 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -243,6 +243,19 @@ int arch_cpu_init(void) */ mvebu_mbus_probe(windows, ARRAY_SIZE(windows));
+ if (mvebu_soc_family() == MVEBU_SOC_AXP) { + /* Enable GBE0, GBE1, LCD and NFC PUP */ + reg = readl(ARMADA_XP_PUP_ENABLE); + reg |= GE0_PUP_EN | GE1_PUP_EN | LCD_PUP_EN | + NAND_PUP_EN | SPI_PUP_EN; + writel(reg, ARMADA_XP_PUP_ENABLE); + } + + /* Enable NAND and NAND arbiter */ + reg = readl(MVEBU_SOC_DEV_MUX_REG); + reg |= NAND_EN | NAND_ARBITER_EN; + writel(reg, MVEBU_SOC_DEV_MUX_REG); + /* Disable MBUS error propagation */ reg = readl(SOC_COHERENCY_FABRIC_CTRL_REG); reg &= ~MBUS_ERR_PROP_EN; @@ -252,6 +265,13 @@ int arch_cpu_init(void) } #endif /* CONFIG_ARCH_CPU_INIT */
+u32 mvebu_get_nand_clock(void) +{ + return CONFIG_SYS_MVEBU_PLL_CLOCK / + ((readl(MVEBU_CORE_DIV_CLK_CTRL(1)) & + NAND_ECC_DIVCKL_RATIO_MASK) >> NAND_ECC_DIVCKL_RATIO_OFFS); +} + /* * SOC specific misc init */ diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h index 674fdaa..6fa4173 100644 --- a/arch/arm/mach-mvebu/include/mach/cpu.h +++ b/arch/arm/mach-mvebu/include/mach/cpu.h @@ -117,6 +117,7 @@ unsigned int mvebu_sdram_bs(enum memory_bank bank); void mvebu_sdram_size_adjust(enum memory_bank bank); int mvebu_mbus_probe(struct mbus_win windows[], int count); int mvebu_soc_family(void); +u32 mvebu_get_nand_clock(void);
int mv_sdh_init(unsigned long regbase, u32 max_clk, u32 min_clk, u32 quirks);
diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index a29e5dc..6115d6e 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -11,6 +11,8 @@ #ifndef _MVEBU_SOC_H #define _MVEBU_SOC_H
+#define BIT(x) (1 << (x)) + #define SOC_MV78460_ID 0x7846 #define SOC_88F6810_ID 0x6810 #define SOC_88F6820_ID 0x6820 @@ -25,6 +27,9 @@ #define CONFIG_SYS_TCLK 250000000 /* 250MHz */ #endif
+/* Armada XP PLL frequency (used for NAND clock generation) */ +#define CONFIG_SYS_MVEBU_PLL_CLOCK 2000000000 + /* SOC specific definations */ #define INTREG_BASE 0xd0000000 #define INTREG_BASE_ADDR_REG (INTREG_BASE + 0x20080) @@ -53,6 +58,7 @@ #define MVEBU_GPIO1_BASE (MVEBU_REGISTER(0x18140)) #define MVEBU_GPIO2_BASE (MVEBU_REGISTER(0x18180)) #define MVEBU_SYSTEM_REG_BASE (MVEBU_REGISTER(0x18200)) +#define MVEBU_CLOCK_BASE (MVEBU_REGISTER(0x18700)) #define MVEBU_CPU_WIN_BASE (MVEBU_REGISTER(0x20000)) #define MVEBU_SDRAM_BASE (MVEBU_REGISTER(0x20180)) #define MVEBU_TIMER_BASE (MVEBU_REGISTER(0x20300)) @@ -64,6 +70,7 @@ #define MVEBU_EGIGA1_BASE (MVEBU_REGISTER(0x74000)) #define MVEBU_AXP_SATA_BASE (MVEBU_REGISTER(0xa0000)) #define MVEBU_SATA0_BASE (MVEBU_REGISTER(0xa8000)) +#define MVEBU_NAND_BASE (MVEBU_REGISTER(0xd0000)) #define MVEBU_SDIO_BASE (MVEBU_REGISTER(0xd8000))
#define SOC_COHERENCY_FABRIC_CTRL_REG (MVEBU_REGISTER(0x20200)) @@ -72,6 +79,21 @@ #define MBUS_BRIDGE_WIN_CTRL_REG (MVEBU_REGISTER(0x20250)) #define MBUS_BRIDGE_WIN_BASE_REG (MVEBU_REGISTER(0x20254))
+#define MVEBU_SOC_DEV_MUX_REG (MVEBU_SYSTEM_REG_BASE + 0x08) +#define NAND_EN BIT(0) +#define NAND_ARBITER_EN BIT(27) + +#define ARMADA_XP_PUP_ENABLE (MVEBU_SYSTEM_REG_BASE + 0x44c) +#define GE0_PUP_EN BIT(0) +#define GE1_PUP_EN BIT(1) +#define LCD_PUP_EN BIT(2) +#define NAND_PUP_EN BIT(4) +#define SPI_PUP_EN BIT(5) + +#define MVEBU_CORE_DIV_CLK_CTRL(i) (MVEBU_CLOCK_BASE + ((i) * 0x8)) +#define NAND_ECC_DIVCKL_RATIO_OFFS 8 +#define NAND_ECC_DIVCKL_RATIO_MASK (0x3F << NAND_ECC_DIVCKL_RATIO_OFFS) + #define SDRAM_MAX_CS 4 #define SDRAM_ADDR_MASK 0xFF000000
diff --git a/arch/arm/mach-mvebu/mbus.c b/arch/arm/mach-mvebu/mbus.c index 71fa693..771cce6 100644 --- a/arch/arm/mach-mvebu/mbus.c +++ b/arch/arm/mach-mvebu/mbus.c @@ -55,8 +55,6 @@ #include <linux/compat.h> #include <linux/mbus.h>
-#define BIT(nr) (1UL << (nr)) - /* DDR target is the same on all platforms */ #define TARGET_DDR 0
diff --git a/board/Marvell/db-88f6820-gp/db-88f6820-gp.c b/board/Marvell/db-88f6820-gp/db-88f6820-gp.c index e661fa1..0eb17f6 100644 --- a/board/Marvell/db-88f6820-gp/db-88f6820-gp.c +++ b/board/Marvell/db-88f6820-gp/db-88f6820-gp.c @@ -15,8 +15,6 @@
DECLARE_GLOBAL_DATA_PTR;
-#define BIT(nr) (1UL << (nr)) - #define ETH_PHY_CTRL_REG 0 #define ETH_PHY_CTRL_POWER_DOWN_BIT 11 #define ETH_PHY_CTRL_POWER_DOWN_MASK (1 << ETH_PHY_CTRL_POWER_DOWN_BIT) diff --git a/board/Marvell/db-mv784mp-gp/db-mv784mp-gp.c b/board/Marvell/db-mv784mp-gp/db-mv784mp-gp.c index 00ca878..78c7b7e 100644 --- a/board/Marvell/db-mv784mp-gp/db-mv784mp-gp.c +++ b/board/Marvell/db-mv784mp-gp/db-mv784mp-gp.c @@ -12,8 +12,6 @@
DECLARE_GLOBAL_DATA_PTR;
-#define BIT(nr) (1UL << (nr)) - #define ETH_PHY_CTRL_REG 0 #define ETH_PHY_CTRL_POWER_DOWN_BIT 11 #define ETH_PHY_CTRL_POWER_DOWN_MASK (1 << ETH_PHY_CTRL_POWER_DOWN_BIT) diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c index efaae16..38ad14e 100644 --- a/drivers/net/mvneta.c +++ b/drivers/net/mvneta.c @@ -41,7 +41,6 @@ printf(fmt, ##args)
#define CONFIG_NR_CPUS 1 -#define BIT(nr) (1UL << (nr)) #define ETH_HLEN 14 /* Total octets in header */
/* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(extra for cache prefetch) */

This patch enables NAND support on the Marvell Armada XP DB-MV784MP-GP eval board.
Signed-off-by: Stefan Roese sr@denx.de Cc: Peter Morrow peter@senient.com Cc: Luka Perkov luka.perkov@sartura.hr --- configs/db-mv784mp-gp_defconfig | 1 + include/configs/db-mv784mp-gp.h | 5 +++++ 2 files changed, 6 insertions(+)
diff --git a/configs/db-mv784mp-gp_defconfig b/configs/db-mv784mp-gp_defconfig index d11377f..c9859a8 100644 --- a/configs/db-mv784mp-gp_defconfig +++ b/configs/db-mv784mp-gp_defconfig @@ -4,4 +4,5 @@ CONFIG_SPL=y # CONFIG_CMD_IMLS is not set # CONFIG_CMD_FLASH is not set # CONFIG_CMD_SETEXPR is not set +CONFIG_NAND_PXA3XX=y CONFIG_SPI_FLASH=y diff --git a/include/configs/db-mv784mp-gp.h b/include/configs/db-mv784mp-gp.h index 41e6fdc..6629fae 100644 --- a/include/configs/db-mv784mp-gp.h +++ b/include/configs/db-mv784mp-gp.h @@ -28,6 +28,7 @@ #define CONFIG_CMD_ENV #define CONFIG_CMD_I2C #define CONFIG_CMD_IDE +#define CONFIG_CMD_NAND #define CONFIG_CMD_PING #define CONFIG_CMD_SF #define CONFIG_CMD_SPI @@ -89,6 +90,10 @@ #define CONFIG_DOS_PARTITION #endif /* CONFIG_CMD_IDE */
+/* NAND */ +#define CONFIG_SYS_NAND_USE_FLASH_BBT +#define CONFIG_SYS_NAND_ONFI_DETECTION + /* * mv-common.h should be defined after CMD configs since it used them * to enable certain macros

This patch enables the USB EHCI support for the Marvell Armada XP (AXP) SoCs. In compatism to the Armada 38x (A38x), the AXP needs to configure the USB PLL and the USB PHY's specifically in U-Boot. The A38x has done this already in the bin_hdr (SPL U-Boot). Without this, accessing the controller registers in U-Boot or Linux will hang the CPU.
Additionally, the AXP uses a different USB EHCI base address. This patch also takes care of this by runtime SoC detection in the Marvell EHCI driver.
Signed-off-by: Stefan Roese sr@denx.de Signed-off-by: Anton Schubert anton.schubert@gmx.de Cc: Marek Vasut marex@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/cpu.c | 58 ++++++++++++++++++++++++++++++++++ arch/arm/mach-mvebu/include/mach/soc.h | 1 + drivers/usb/host/ehci-marvell.c | 9 +++++- 3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index bde3990..0a31b85 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -179,6 +179,61 @@ static void set_cbar(u32 addr) asm("mcr p15, 4, %0, c15, c0" : : "r" (addr)); }
+#define MV_USB_PHY_BASE (MVEBU_AXP_USB_BASE + 0x800) +#define MV_USB_PHY_PLL_REG(reg) (MV_USB_PHY_BASE | ((reg & 0xF) << 2)) +#define MV_USB_X3_BASE(addr) (MVEBU_AXP_USB_BASE | BIT(11) | \ + (((addr) & 0xF) << 6)) +#define MV_USB_X3_PHY_CHANNEL(dev, reg) (MV_USB_X3_BASE(dev + 1) | \ + ((reg & 0xF) << 2)) + +static void setup_usb_phys(void) +{ + u32 val; + int dev; + + /* + * USB PLL init + */ + + /* Setup PLL frequency */ + val = readl(MV_USB_PHY_PLL_REG(1)); + val &= ~(0x3FF); + val |= 0x605; /* USB REF frequency = 25 MHz */ + writel(val, MV_USB_PHY_PLL_REG(1)); + + /* Power up PLL and PHY channel */ + val = readl(MV_USB_PHY_PLL_REG(2)); + val |= BIT(9); + writel(val, MV_USB_PHY_PLL_REG(2)); + + /* Assert VCOCAL_START */ + val = readl(MV_USB_PHY_PLL_REG(1)); + val |= BIT(21); + writel(val, MV_USB_PHY_PLL_REG(1)); + + mdelay(1); + + /* + * USB PHY init (change from defaults) specific for 40nm (78X30 78X60) + */ + + for (dev = 0; dev < 3; dev++) { + val = readl(MV_USB_X3_PHY_CHANNEL(dev, 3)); + val |= BIT(15); + writel(val, MV_USB_X3_PHY_CHANNEL(dev, 3)); + + /* Assert REG_RCAL_START in Channel REG 1 */ + val = readl(MV_USB_X3_PHY_CHANNEL(dev, 1)); + val |= BIT(12); + writel(val, MV_USB_X3_PHY_CHANNEL(dev, 1)); + + udelay(40); + + val = readl(MV_USB_X3_PHY_CHANNEL(dev, 1)); + val &= ~BIT(12); + writel(val, MV_USB_X3_PHY_CHANNEL(dev, 1)); + } +}
int arch_cpu_init(void) { @@ -249,6 +304,9 @@ int arch_cpu_init(void) reg |= GE0_PUP_EN | GE1_PUP_EN | LCD_PUP_EN | NAND_PUP_EN | SPI_PUP_EN; writel(reg, ARMADA_XP_PUP_ENABLE); + + /* Configure USB PLL and PHYs on AXP */ + setup_usb_phys(); }
/* Enable NAND and NAND arbiter */ diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index 6115d6e..a8a6b27 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -65,6 +65,7 @@ #define MVEBU_EGIGA2_BASE (MVEBU_REGISTER(0x30000)) #define MVEBU_EGIGA3_BASE (MVEBU_REGISTER(0x34000)) #define MVEBU_REG_PCIE_BASE (MVEBU_REGISTER(0x40000)) +#define MVEBU_AXP_USB_BASE (MVEBU_REGISTER(0x50000)) #define MVEBU_USB20_BASE (MVEBU_REGISTER(0x58000)) #define MVEBU_EGIGA0_BASE (MVEBU_REGISTER(0x70000)) #define MVEBU_EGIGA1_BASE (MVEBU_REGISTER(0x74000)) diff --git a/drivers/usb/host/ehci-marvell.c b/drivers/usb/host/ehci-marvell.c index 03c489c..826e7f7 100644 --- a/drivers/usb/host/ehci-marvell.c +++ b/drivers/usb/host/ehci-marvell.c @@ -33,7 +33,14 @@ DECLARE_GLOBAL_DATA_PTR; */ #ifdef CONFIG_ARMADA_XP
-#define MVUSB0_BASE MVEBU_USB20_BASE +/* + * Armada XP and Armada 38x have different base addresses for + * the USB 2.0 EHCI host controller. So we need to provide + * a mechnism to support both here. + */ +#define MVUSB0_BASE \ + (mvebu_soc_family() == MVEBU_SOC_A38X ? \ + MVEBU_USB20_BASE : MVEBU_AXP_USB_BASE)
/* * Once all the older Marvell SoC's (Orion, Kirkwood) are converted

On Thursday, July 23, 2015 at 10:26:19 AM, Stefan Roese wrote:
This patch enables the USB EHCI support for the Marvell Armada XP (AXP) SoCs. In compatism to the Armada 38x (A38x), the AXP needs to configure the USB PLL and the USB PHY's specifically in U-Boot. The A38x has done this already in the bin_hdr (SPL U-Boot). Without this, accessing the controller registers in U-Boot or Linux will hang the CPU.
Additionally, the AXP uses a different USB EHCI base address. This patch also takes care of this by runtime SoC detection in the Marvell EHCI driver.
Signed-off-by: Stefan Roese sr@denx.de Signed-off-by: Anton Schubert anton.schubert@gmx.de Cc: Marek Vasut marex@denx.de Cc: Luka Perkov luka.perkov@sartura.hr
arch/arm/mach-mvebu/cpu.c | 58 ++++++++++++++++++++++++++++++++++ arch/arm/mach-mvebu/include/mach/soc.h | 1 + drivers/usb/host/ehci-marvell.c | 9 +++++- 3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index bde3990..0a31b85 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -179,6 +179,61 @@ static void set_cbar(u32 addr) asm("mcr p15, 4, %0, c15, c0" : : "r" (addr)); }
+#define MV_USB_PHY_BASE (MVEBU_AXP_USB_BASE + 0x800) +#define MV_USB_PHY_PLL_REG(reg) (MV_USB_PHY_BASE | ((reg & 0xF)
<< 2))
(reg) in the macro please.
+#define MV_USB_X3_BASE(addr) (MVEBU_AXP_USB_BASE | BIT(11) | \
(((addr) & 0xF) << 6))
+#define MV_USB_X3_PHY_CHANNEL(dev, reg) (MV_USB_X3_BASE(dev + 1) | \
((reg & 0xF) << 2))
Same here.
+static void setup_usb_phys(void) +{
- u32 val;
- int dev;
- /*
* USB PLL init
*/
- /* Setup PLL frequency */
- val = readl(MV_USB_PHY_PLL_REG(1));
- val &= ~(0x3FF);
- val |= 0x605; /* USB REF frequency = 25 MHz */
- writel(val, MV_USB_PHY_PLL_REG(1));
Please use clrsetbits_le32() and friends :)
- /* Power up PLL and PHY channel */
- val = readl(MV_USB_PHY_PLL_REG(2));
- val |= BIT(9);
- writel(val, MV_USB_PHY_PLL_REG(2));
- /* Assert VCOCAL_START */
- val = readl(MV_USB_PHY_PLL_REG(1));
- val |= BIT(21);
- writel(val, MV_USB_PHY_PLL_REG(1));
- mdelay(1);
- /*
* USB PHY init (change from defaults) specific for 40nm (78X30 78X60)
*/
- for (dev = 0; dev < 3; dev++) {
val = readl(MV_USB_X3_PHY_CHANNEL(dev, 3));
val |= BIT(15);
writel(val, MV_USB_X3_PHY_CHANNEL(dev, 3));
/* Assert REG_RCAL_START in Channel REG 1 */
val = readl(MV_USB_X3_PHY_CHANNEL(dev, 1));
val |= BIT(12);
writel(val, MV_USB_X3_PHY_CHANNEL(dev, 1));
udelay(40);
val = readl(MV_USB_X3_PHY_CHANNEL(dev, 1));
val &= ~BIT(12);
writel(val, MV_USB_X3_PHY_CHANNEL(dev, 1));
- }
+}
int arch_cpu_init(void) { @@ -249,6 +304,9 @@ int arch_cpu_init(void) reg |= GE0_PUP_EN | GE1_PUP_EN | LCD_PUP_EN | NAND_PUP_EN | SPI_PUP_EN; writel(reg, ARMADA_XP_PUP_ENABLE);
/* Configure USB PLL and PHYs on AXP */
setup_usb_phys();
}
/* Enable NAND and NAND arbiter */
diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index 6115d6e..a8a6b27 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -65,6 +65,7 @@ #define MVEBU_EGIGA2_BASE (MVEBU_REGISTER(0x30000)) #define MVEBU_EGIGA3_BASE (MVEBU_REGISTER(0x34000)) #define MVEBU_REG_PCIE_BASE (MVEBU_REGISTER(0x40000)) +#define MVEBU_AXP_USB_BASE (MVEBU_REGISTER(0x50000)) #define MVEBU_USB20_BASE (MVEBU_REGISTER(0x58000)) #define MVEBU_EGIGA0_BASE (MVEBU_REGISTER(0x70000)) #define MVEBU_EGIGA1_BASE (MVEBU_REGISTER(0x74000))
The () around MVEBU_REGISTER() shouldn't be needed btw ;-)

This patch enabled the USB/EHCI support for the Marvell DB-MV784MP-GP Armada XP eval board.
Signed-off-by: Stefan Roese sr@denx.de Cc: Anton Schubert anton.schubert@gmx.de Cc: Marek Vasut marex@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- include/configs/db-mv784mp-gp.h | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/include/configs/db-mv784mp-gp.h b/include/configs/db-mv784mp-gp.h index 6629fae..6cddbda 100644 --- a/include/configs/db-mv784mp-gp.h +++ b/include/configs/db-mv784mp-gp.h @@ -34,6 +34,7 @@ #define CONFIG_CMD_SPI #define CONFIG_CMD_TFTPPUT #define CONFIG_CMD_TIME +#define CONFIG_CMD_USB
/* I2C */ #define CONFIG_SYS_I2C @@ -42,6 +43,12 @@ #define CONFIG_SYS_I2C_SLAVE 0x0 #define CONFIG_SYS_I2C_SPEED 100000
+/* USB/EHCI configuration */ +#define CONFIG_USB_EHCI +#define CONFIG_USB_STORAGE +#define CONFIG_USB_EHCI_MARVELL +#define CONFIG_EHCI_IS_TDI + /* SPI NOR flash default params, used by sf commands */ #define CONFIG_SF_DEFAULT_SPEED 1000000 #define CONFIG_SF_DEFAULT_MODE SPI_MODE_3
participants (2)
-
Marek Vasut
-
Stefan Roese