diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c03f7dc..9f4d8e9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -152,6 +152,7 @@ bool "Intel/Marvell PXA based" select GENERIC_GPIO select HAS_POWEROFF + select HAVE_CLK config ARCH_ROCKCHIP bool "Rockchip RX3xxx" diff --git a/arch/arm/boards/zylonite/board.c b/arch/arm/boards/zylonite/board.c index 2caadbc..2ff08b7 100644 --- a/arch/arm/boards/zylonite/board.c +++ b/arch/arm/boards/zylonite/board.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -60,11 +62,16 @@ static int zylonite_devices_init(void) { + struct clk *clk; + armlinux_set_architecture(MACH_TYPE_ZYLONITE); pxa_add_uart((void *)0x40100000, 0); add_generic_device("smc91c111", DEVICE_ID_DYNAMIC, NULL, 0x14000300, 0x100000, IORESOURCE_MEM, &smsc91x_pdata); + clk = clk_get_sys("nand", NULL); + if (!IS_ERR(clk)) + clkdev_add_physbase(clk, 0x43100000, NULL); add_generic_device("mrvl_nand", DEVICE_ID_DYNAMIC, NULL, 0x43100000, 0x1000, IORESOURCE_MEM, &nand_pdata); devfs_add_partition("nand0", SZ_1M, SZ_256K, DEVFS_PARTITION_FIXED, diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 54094f4..1c08948 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -17,6 +17,8 @@ config ARCH_PXA3XX bool select CPU_XSC3 + select CLKDEV_LOOKUP + select COMMON_CLK config ARCH_PXA310 bool diff --git a/arch/arm/mach-pxa/include/mach/clock.h b/arch/arm/mach-pxa/include/mach/clock.h index 40f6223..f86152f 100644 --- a/arch/arm/mach-pxa/include/mach/clock.h +++ b/arch/arm/mach-pxa/include/mach/clock.h @@ -14,7 +14,6 @@ unsigned long pxa_get_uartclk(void); unsigned long pxa_get_mmcclk(void); unsigned long pxa_get_lcdclk(void); -unsigned long pxa_get_nandclk(void); unsigned long pxa_get_pwmclk(void); #endif /* !__MACH_CLOCK_H */ diff --git a/arch/arm/mach-pxa/speed-pxa3xx.c b/arch/arm/mach-pxa/speed-pxa3xx.c index 6a08ea7..b24b7a8 100644 --- a/arch/arm/mach-pxa/speed-pxa3xx.c +++ b/arch/arm/mach-pxa/speed-pxa3xx.c @@ -8,6 +8,9 @@ */ #include +#include +#include +#include #include #include @@ -24,10 +27,17 @@ return BASE_CLK; } -unsigned long pxa_get_nandclk(void) +static int pxa3xx_clock_init(void) { - if (cpu_is_pxa320()) - return 104000000; - else - return 156000000; + unsigned long nand_rate = (cpu_is_pxa320()) ? 104000000 : 156000000; + struct clk *clk; + int ret; + + clk = clk_fixed("nand", nand_rate); + ret = clk_register_clkdev(clk, NULL, "nand"); + if (ret) + return ret; + + return 0; } +postcore_initcall(pxa3xx_clock_init); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ff26584..2b4a478 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -92,17 +92,18 @@ config NAND_ORION bool - prompt "Orion NAND driver" + prompt "Marvell Orion NAND driver" depends on ARCH_KIRKWOOD help Support for the Orion NAND controller, present in Kirkwood SoCs. config NAND_MRVL_NFC bool - prompt "Marvell NAND driver" - depends on ARCH_PXA3XX + prompt "Marvell PXA3xx NAND driver" + depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_PXA3XX help - Support for the PXA3xx NAND controller, present in pxa3xx SoCs. + Support for the PXA3xx NAND controller, present in Armada 370/XP and + PXA3xx SoCs. config NAND_ATMEL bool diff --git a/drivers/mtd/nand/nand_mrvl_nfc.c b/drivers/mtd/nand/nand_mrvl_nfc.c index 1ec48cc..f160d15 100644 --- a/drivers/mtd/nand/nand_mrvl_nfc.c +++ b/drivers/mtd/nand/nand_mrvl_nfc.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -54,6 +53,7 @@ #define NDCB0 (0x48) /* Command Buffer0 */ #define NDCB1 (0x4C) /* Command Buffer1 */ #define NDCB2 (0x50) /* Command Buffer2 */ +#define NDCB3 (0x54) /* Command Buffer3 */ #define NDCR_SPARE_EN (0x1 << 31) #define NDCR_ECC_EN (0x1 << 30) @@ -93,6 +93,8 @@ #define NDSR_RDDREQ (0x1 << 1) #define NDSR_WRCMDREQ (0x1) +#define NDECCCTRL_BCH_EN BIT(0) + #define NDCB0_LEN_OVRD (0x1 << 28) #define NDCB0_ST_ROW_EN (0x1 << 26) #define NDCB0_AUTO_RS (0x1 << 25) @@ -130,11 +132,16 @@ #define nand_readsl(host, off, buf, nbbytes) \ readsl((host)->mmio_base + (off), buf, nbbytes) +struct mrvl_nand_variant { + unsigned int hwflags; +}; + struct mrvl_nand_host { struct mtd_info mtd; struct nand_chip chip; struct mtd_partition *parts; struct device_d *dev; + struct clk *core_clk; /* calculated from mrvl_nand_flash data */ unsigned int col_addr_cycles; @@ -142,6 +149,9 @@ size_t read_id_bytes; void __iomem *mmio_base; + unsigned int hwflags; +#define HWFLAGS_ECC_BCH BIT(0) +#define HWFLAGS_HAS_NDCB3 BIT(1) unsigned int buf_start; unsigned int buf_count; @@ -153,8 +163,10 @@ int ecc_strength; int ecc_step; + int num_cs; /* avaiable CS signals */ int cs; /* selected chip 0/1 */ int use_ecc; /* use HW ECC ? */ + int ecc_bch; /* HW ECC is BCH */ int use_spare; /* use spare ? */ int flash_bbt; @@ -219,6 +231,42 @@ .oobfree = { {0, 40} } }; +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 = { {1, 4}, {6, 26}, {64, 32} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch8bit = { + .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}, + /* Bootrom looks in bytes 0 & 5 for bad blocks */ + .oobfree = { {1, 4}, {6, 26} } +}; + #define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCS(c) (min((c), 7) << 16) #define NDTR0_tWH(c) (min((c), 7) << 11) @@ -233,9 +281,22 @@ #define mtd_info_to_host(mtd) ((struct mrvl_nand_host *) \ (((struct nand_chip *)((mtd)->priv))->priv)) +static const struct mrvl_nand_variant pxa3xx_variant = { + .hwflags = 0, +}; + +static const struct mrvl_nand_variant armada370_variant = { + .hwflags = HWFLAGS_ECC_BCH | HWFLAGS_HAS_NDCB3, +}; + static struct of_device_id mrvl_nand_dt_ids[] = { { .compatible = "marvell,pxa3xx-nand", + .data = &pxa3xx_variant, + }, + { + .compatible = "marvell,armada370-nand", + .data = &armada370_variant, }, {} }; @@ -270,6 +331,7 @@ { 0x46ec, 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, { 0xdaec, 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, { 0xd7ec, 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, + { 0xd3ec, 5, 20, 10, 12, 10, 12, 25000, 60, 10, }, { 0xa12c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, { 0xb12c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, { 0xdc2c, 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, @@ -281,10 +343,10 @@ static void mrvl_nand_set_timing(struct mrvl_nand_host *host, bool use_default) { struct mtd_info *mtd = &host->mtd; + unsigned long nand_clk = clk_get_rate(host->core_clk); struct mrvl_nand_timing *t; uint32_t ndtr0, ndtr1; u16 id; - unsigned long nand_clk = pxa_get_nandclk(); if (use_default) { id = 0; @@ -377,6 +439,17 @@ { uint32_t ndcr; + if (host->hwflags & HWFLAGS_ECC_BCH) { + uint32_t reg = nand_readl(host, NDECCCTRL); + + if (host->use_ecc && host->ecc_bch) + reg |= NDECCCTRL_BCH_EN; + else + reg &= ~NDECCCTRL_BCH_EN; + + nand_writel(host, NDECCCTRL, reg); + } + ndcr = host->reg_ndcr; if (host->use_ecc) ndcr |= NDCR_ECC_EN; @@ -403,12 +476,19 @@ dev_err(host->dev, "Waiting for command request failed\n"); } else { /* - * Writing 12 bytes to NDBC0 sets NDBC0, NDBC1 and NDBC2 ! + * Command buffer registers NDCB{0-2,3} + * 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(host, NDSR, NDSR_WRCMDREQ); nand_writel(host, NDCB0, host->ndcb0); nand_writel(host, NDCB0, host->ndcb1); nand_writel(host, NDCB0, host->ndcb2); + if (host->hwflags & HWFLAGS_HAS_NDCB3) + nand_writel(host, NDCB0, host->ndcb3); } } @@ -562,6 +642,7 @@ case NAND_CMD_PAGEPROG: host->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_AUTO_RS | NDCB0_DBC | (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN @@ -599,6 +680,7 @@ case NAND_CMD_ERASE1: host->ndcb0 |= NDCB0_CMD_TYPE(2) + | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3) | NDCB0_DBC | (NAND_CMD_ERASE2 << 8) @@ -719,7 +801,7 @@ memcpy(host->data_buff + mtd->writesize, chip->oob_poi, mtd->oobsize); else - memset(host->data_buff + mtd->writesize, 0, mtd->oobsize); + memset(host->data_buff + mtd->writesize, 0xff, mtd->oobsize); dev_dbg(host->dev, "%s(buf=%p, oob_required=%d) => 0\n", __func__, buf, oob_required); return 0; @@ -743,8 +825,14 @@ else ret = -EBADMSG; } - if (ndsr & NDSR_CORERR) + if (ndsr & NDSR_CORERR) { ret = 1; + if ((host->hwflags & HWFLAGS_ECC_BCH) && host->ecc_bch) { + ret = NDSR_ERR_CNT(ndsr); + ndsr &= ~(NDSR_ERR_CNT_MASK << NDSR_ERR_CNT_OFF); + nand_writel(host, NDSR, ndsr); + } + } dev_dbg(host->dev, "%s(buf=%p, page=%d, oob_required=%d) => %d\n", __func__, buf, page, oob_required, ret); return ret; @@ -821,32 +909,124 @@ host->reg_ndcr = ndcr; } -static int pxa_ecc_init(struct mrvl_nand_host *host, - struct nand_ecc_ctrl *ecc, - int strength, int ecc_stepsize, int page_size) +static int pxa_ecc_strength1(struct mrvl_nand_host *host, + struct nand_ecc_ctrl *ecc, int ecc_stepsize, int page_size) { - if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { + if (ecc_stepsize == 512 && page_size == 2048) { host->chunk_size = 2048; host->spare_size = 40; host->ecc_size = 24; + host->ecc_bch = 0; ecc->mode = NAND_ECC_HW; ecc->size = 512; ecc->strength = 1; ecc->layout = &ecc_layout_2KB_hwecc; + return 0; + } - } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { + if (ecc_stepsize == 512 && page_size == 512) { host->chunk_size = 512; host->spare_size = 8; host->ecc_size = 8; + host->ecc_bch = 0; ecc->mode = NAND_ECC_HW; ecc->size = 512; ecc->layout = &ecc_layout_512B_hwecc; ecc->strength = 1; - } else { + return 0; + } + + return -ENODEV; +} + +static int pxa_ecc_strength4(struct mrvl_nand_host *host, + struct nand_ecc_ctrl *ecc, int ecc_stepsize, int page_size) +{ + if (!(host->hwflags & HWFLAGS_ECC_BCH)) + return -ENODEV; + + /* + * Required ECC: 4-bit correction per 512 bytes + * Select: 16-bit correction per 2048 bytes + */ + if (ecc_stepsize == 512 && page_size == 2048) { + host->chunk_size = 2048; + host->spare_size = 32; + host->ecc_size = 32; + host->ecc_bch = 1; + ecc->mode = NAND_ECC_HW; + ecc->size = 2048; + ecc->layout = &ecc_layout_2KB_bch4bit; + ecc->strength = 16; + return 0; + } + + if (ecc_stepsize == 512 && page_size == 4096) { + host->chunk_size = 2048; + host->spare_size = 32; + host->ecc_size = 32; + host->ecc_bch = 1; + ecc->mode = NAND_ECC_HW; + ecc->size = 2048; + ecc->layout = &ecc_layout_4KB_bch4bit; + ecc->strength = 16; + return 0; + } + + return -ENODEV; +} + +static int pxa_ecc_strength8(struct mrvl_nand_host *host, + struct nand_ecc_ctrl *ecc, int ecc_stepsize, int page_size) +{ + if (!(host->hwflags & HWFLAGS_ECC_BCH)) + return -ENODEV; + + /* + * Required ECC: 8-bit correction per 512 bytes + * Select: 16-bit correction per 1024 bytes + */ + if (ecc_stepsize == 512 && page_size == 4096) { + host->chunk_size = 1024; + host->spare_size = 0; + host->ecc_size = 32; + host->ecc_bch = 1; + ecc->mode = NAND_ECC_HW; + ecc->size = 1024; + ecc->layout = &ecc_layout_4KB_bch8bit; + ecc->strength = 16; + return 0; + } + + return -ENODEV; +} + +static int pxa_ecc_init(struct mrvl_nand_host *host, + struct nand_ecc_ctrl *ecc, + int strength, int ecc_stepsize, int page_size) +{ + int ret; + + switch (strength) { + case 1: + ret = pxa_ecc_strength1(host, ecc, ecc_stepsize, page_size); + break; + case 4: + ret = pxa_ecc_strength4(host, ecc, ecc_stepsize, page_size); + break; + case 8: + ret = pxa_ecc_strength8(host, ecc, ecc_stepsize, page_size); + break; + default: + ret = -ENODEV; + break; + } + + if (ret) { dev_err(host->dev, "ECC strength %d at page size %d is not supported\n", strength, page_size); - return -ENODEV; + return ret; } dev_info(host->dev, "ECC strength %d, ECC step size %d\n", @@ -868,6 +1048,11 @@ ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); host->reg_ndcr = ndcr; + /* Device detection must be done with BCH ECC disabled */ + if (host->hwflags & HWFLAGS_ECC_BCH) + nand_writel(host, NDECCCTRL, + nand_readl(host, NDECCCTRL) & ~NDECCCTRL_BCH_EN); + mrvl_nand_set_timing(host, true); if (nand_scan_ident(mtd, 1, NULL)) { host->reg_ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C; @@ -927,6 +1112,7 @@ pdata = dev->platform_data; host = xzalloc(sizeof(*host)); + host->num_cs = 1; host->cs = 0; mtd = &host->mtd; mtd->priv = &host->chip; @@ -954,6 +1140,13 @@ free(host); return host->mmio_base; } + host->core_clk = clk_get(dev, NULL); + if (IS_ERR(host->core_clk)) { + free(host); + return (void *)host->core_clk; + } + clk_enable(host->core_clk); + if (pdata) { host->keep_config = pdata->keep_config; host->flash_bbt = pdata->flash_bbt; @@ -973,13 +1166,33 @@ static int mrvl_nand_probe_dt(struct mrvl_nand_host *host) { struct device_node *np = host->dev->device_node; + const struct of_device_id *match; + const struct mrvl_nand_variant *variant; + + if (!IS_ENABLED(CONFIG_OFTREE) || host->dev->platform_data) + return 0; + + match = of_match_node(mrvl_nand_dt_ids, np); + if (!match) + return -EINVAL; + variant = match->data; if (of_get_property(np, "marvell,nand-keep-config", NULL)) host->keep_config = 1; - of_property_read_u32(np, "num-cs", &host->cs); + of_property_read_u32(np, "num-cs", &host->num_cs); if (of_get_nand_on_flash_bbt(np)) host->flash_bbt = 1; + host->ecc_strength = of_get_nand_ecc_strength(np); + if (host->ecc_strength < 0) + host->ecc_strength = 0; + + host->ecc_step = of_get_nand_ecc_step_size(np); + if (host->ecc_step < 0) + host->ecc_step = 0; + + host->hwflags = variant->hwflags; + return 0; } diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index 97f3095..0956ee1 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -49,6 +49,40 @@ EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode); /** + * of_get_nand_ecc_step_size - Get ECC step size associated to + * the required ECC strength (see below). + * @np: Pointer to the given device_node + * + * return the ECC step size, or errno in error case. + */ +int of_get_nand_ecc_step_size(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-step-size", &val); + return ret ? ret : val; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size); + +/** + * of_get_nand_ecc_strength - Get required ECC strength over the + * correspnding step size as defined by 'nand-ecc-size' + * @np: Pointer to the given device_node + * + * return the ECC strength, or errno in error case. + */ +int of_get_nand_ecc_strength(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-strength", &val); + return ret ? ret : val; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength); + +/** * of_get_nand_bus_width - Get nand bus witdh for given device_node * @np: Pointer to the given device_node * diff --git a/include/of_mtd.h b/include/of_mtd.h index a5a8f20..9f5b8a2 100644 --- a/include/of_mtd.h +++ b/include/of_mtd.h @@ -12,6 +12,8 @@ #include int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_ecc_step_size(struct device_node *np); +int of_get_nand_ecc_strength(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); bool of_get_nand_on_flash_bbt(struct device_node *np);