|
|
@@ -1,1608 +0,0 @@
|
|
|
---- a/drivers/mtd/nand/Kconfig
|
|
|
-+++ b/drivers/mtd/nand/Kconfig
|
|
|
-@@ -516,4 +516,10 @@ config MTD_NAND_XWAY
|
|
|
- Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
|
|
|
- to the External Bus Unit (EBU).
|
|
|
-
|
|
|
-+config MTD_NAND_BCM
|
|
|
-+ tristate "Support for NAND on some Broadcom SoC"
|
|
|
-+ help
|
|
|
-+ This driver is currently used for the NAND flash controller on the
|
|
|
-+ Broadcom BCM5301X (NorthStar) SoCs.
|
|
|
-+
|
|
|
- endif # MTD_NAND
|
|
|
---- a/drivers/mtd/nand/Makefile
|
|
|
-+++ b/drivers/mtd/nand/Makefile
|
|
|
-@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740
|
|
|
- obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
|
|
|
- obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
|
|
|
- obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
|
|
|
-+obj-$(CONFIG_MTD_NAND_BCM) += bcm_nand.o
|
|
|
-
|
|
|
- nand-objs := nand_base.o nand_bbt.o nand_timings.o
|
|
|
---- /dev/null
|
|
|
-+++ b/drivers/mtd/nand/bcm_nand.c
|
|
|
-@@ -0,0 +1,1583 @@
|
|
|
-+/*
|
|
|
-+ * Nortstar NAND controller driver
|
|
|
-+ *
|
|
|
-+ * (c) Broadcom, Inc. 2012 All Rights Reserved.
|
|
|
-+ * Copyright 2014 Hauke Mehrtens <[email protected]>
|
|
|
-+ *
|
|
|
-+ * Licensed under the GNU/GPL. See COPYING for details.
|
|
|
-+ *
|
|
|
-+ * This module interfaces the NAND controller and hardware ECC capabilities
|
|
|
-+ * tp the generic NAND chip support in the NAND library.
|
|
|
-+ *
|
|
|
-+ * Notes:
|
|
|
-+ * This driver depends on generic NAND driver, but works at the
|
|
|
-+ * page level for operations.
|
|
|
-+ *
|
|
|
-+ * When a page is written, the ECC calculated also protects the OOB
|
|
|
-+ * bytes not taken by ECC, and so the OOB must be combined with any
|
|
|
-+ * OOB data that preceded the page-write operation in order for the
|
|
|
-+ * ECC to be calculated correctly.
|
|
|
-+ * Also, when the page is erased, but OOB data is not, HW ECC will
|
|
|
-+ * indicate an error, because it checks OOB too, which calls for some
|
|
|
-+ * help from the software in this driver.
|
|
|
-+ *
|
|
|
-+ * TBD:
|
|
|
-+ * Block locking/unlocking support, OTP support
|
|
|
-+ */
|
|
|
-+
|
|
|
-+
|
|
|
-+#include <linux/kernel.h>
|
|
|
-+#include <linux/module.h>
|
|
|
-+#include <linux/io.h>
|
|
|
-+#include <linux/ioport.h>
|
|
|
-+#include <linux/interrupt.h>
|
|
|
-+#include <linux/delay.h>
|
|
|
-+#include <linux/err.h>
|
|
|
-+#include <linux/slab.h>
|
|
|
-+#include <linux/bcma/bcma.h>
|
|
|
-+#include <linux/of_irq.h>
|
|
|
-+
|
|
|
-+#include <linux/mtd/mtd.h>
|
|
|
-+#include <linux/mtd/nand.h>
|
|
|
-+#include <linux/mtd/partitions.h>
|
|
|
-+
|
|
|
-+#define NANDC_MAX_CHIPS 2 /* Only 2 CSn supported in NorthStar */
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Driver private control structure
|
|
|
-+ */
|
|
|
-+struct bcmnand_ctrl {
|
|
|
-+ struct mtd_info mtd;
|
|
|
-+ struct nand_chip nand;
|
|
|
-+ struct bcma_device *core;
|
|
|
-+
|
|
|
-+ struct completion op_completion;
|
|
|
-+
|
|
|
-+ struct nand_ecclayout ecclayout;
|
|
|
-+ int cmd_ret; /* saved error code */
|
|
|
-+ unsigned char oob_index;
|
|
|
-+ unsigned char id_byte_index;
|
|
|
-+ unsigned char chip_num;
|
|
|
-+ unsigned char last_cmd;
|
|
|
-+ unsigned char ecc_level;
|
|
|
-+ unsigned char sector_size_shift;
|
|
|
-+ unsigned char sec_per_page_shift;
|
|
|
-+};
|
|
|
-+
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * IRQ numbers - offset from first irq in nandc_irq resource
|
|
|
-+ */
|
|
|
-+#define NANDC_IRQ_RD_MISS 0
|
|
|
-+#define NANDC_IRQ_ERASE_COMPLETE 1
|
|
|
-+#define NANDC_IRQ_COPYBACK_COMPLETE 2
|
|
|
-+#define NANDC_IRQ_PROGRAM_COMPLETE 3
|
|
|
-+#define NANDC_IRQ_CONTROLLER_RDY 4
|
|
|
-+#define NANDC_IRQ_RDBSY_RDY 5
|
|
|
-+#define NANDC_IRQ_ECC_UNCORRECTABLE 6
|
|
|
-+#define NANDC_IRQ_ECC_CORRECTABLE 7
|
|
|
-+#define NANDC_IRQ_NUM 8
|
|
|
-+
|
|
|
-+struct bcmnand_reg_field {
|
|
|
-+ unsigned int reg;
|
|
|
-+ unsigned int pos;
|
|
|
-+ unsigned int width;
|
|
|
-+};
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * REGISTERS
|
|
|
-+ *
|
|
|
-+ * Individual bit-fields aof registers are specificed here
|
|
|
-+ * for clarity, and the rest of the code will access each field
|
|
|
-+ * as if it was its own register.
|
|
|
-+ *
|
|
|
-+ * Following registers are off <reg_base>:
|
|
|
-+ */
|
|
|
-+#define REG_BIT_FIELD(r, p, w) ((struct bcmnand_reg_field){(r), (p), (w)})
|
|
|
-+
|
|
|
-+#define NANDC_8KB_PAGE_SUPPORT REG_BIT_FIELD(0x0, 31, 1)
|
|
|
-+#define NANDC_REV_MAJOR REG_BIT_FIELD(0x0, 8, 8)
|
|
|
-+#define NANDC_REV_MINOR REG_BIT_FIELD(0x0, 0, 8)
|
|
|
-+
|
|
|
-+#define NANDC_CMD_START_OPCODE REG_BIT_FIELD(0x4, 24, 5)
|
|
|
-+
|
|
|
-+#define NANDC_CMD_CS_SEL REG_BIT_FIELD(0x8, 16, 3)
|
|
|
-+#define NANDC_CMD_EXT_ADDR REG_BIT_FIELD(0x8, 0, 16)
|
|
|
-+
|
|
|
-+#define NANDC_CMD_ADDRESS REG_BIT_FIELD(0xc, 0, 32)
|
|
|
-+#define NANDC_CMD_END_ADDRESS REG_BIT_FIELD(0x10, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_INT_STATUS REG_BIT_FIELD(0x14, 0, 32)
|
|
|
-+#define NANDC_INT_STAT_CTLR_RDY REG_BIT_FIELD(0x14, 31, 1)
|
|
|
-+#define NANDC_INT_STAT_FLASH_RDY REG_BIT_FIELD(0x14, 30, 1)
|
|
|
-+#define NANDC_INT_STAT_CACHE_VALID REG_BIT_FIELD(0x14, 29, 1)
|
|
|
-+#define NANDC_INT_STAT_SPARE_VALID REG_BIT_FIELD(0x14, 28, 1)
|
|
|
-+#define NANDC_INT_STAT_ERASED REG_BIT_FIELD(0x14, 27, 1)
|
|
|
-+#define NANDC_INT_STAT_PLANE_RDY REG_BIT_FIELD(0x14, 26, 1)
|
|
|
-+#define NANDC_INT_STAT_FLASH_STATUS REG_BIT_FIELD(0x14, 0, 8)
|
|
|
-+
|
|
|
-+#define NANDC_CS_LOCK REG_BIT_FIELD(0x18, 31, 1)
|
|
|
-+#define NANDC_CS_AUTO_CONFIG REG_BIT_FIELD(0x18, 30, 1)
|
|
|
-+#define NANDC_CS_NAND_WP REG_BIT_FIELD(0x18, 29, 1)
|
|
|
-+#define NANDC_CS_BLK0_WP REG_BIT_FIELD(0x18, 28, 1)
|
|
|
-+#define NANDC_CS_SW_USING_CS(n) REG_BIT_FIELD(0x18, 8+(n), 1)
|
|
|
-+#define NANDC_CS_MAP_SEL_CS(n) REG_BIT_FIELD(0x18, 0+(n), 1)
|
|
|
-+
|
|
|
-+#define NANDC_XOR_ADDR_BLK0_ONLY REG_BIT_FIELD(0x1c, 31, 1)
|
|
|
-+#define NANDC_XOR_ADDR_CS(n) REG_BIT_FIELD(0x1c, 0+(n), 1)
|
|
|
-+
|
|
|
-+#define NANDC_LL_OP_RET_IDLE REG_BIT_FIELD(0x20, 31, 1)
|
|
|
-+#define NANDC_LL_OP_CLE REG_BIT_FIELD(0x20, 19, 1)
|
|
|
-+#define NANDC_LL_OP_ALE REG_BIT_FIELD(0x20, 18, 1)
|
|
|
-+#define NANDC_LL_OP_WE REG_BIT_FIELD(0x20, 17, 1)
|
|
|
-+#define NANDC_LL_OP_RE REG_BIT_FIELD(0x20, 16, 1)
|
|
|
-+#define NANDC_LL_OP_DATA REG_BIT_FIELD(0x20, 0, 16)
|
|
|
-+
|
|
|
-+#define NANDC_MPLANE_ADDR_EXT REG_BIT_FIELD(0x24, 0, 16)
|
|
|
-+#define NANDC_MPLANE_ADDR REG_BIT_FIELD(0x28, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_ACC_CTRL_CS(n) REG_BIT_FIELD(0x50+((n)<<4), 0, 32)
|
|
|
-+#define NANDC_ACC_CTRL_RD_ECC(n) REG_BIT_FIELD(0x50+((n)<<4), 31, 1)
|
|
|
-+#define NANDC_ACC_CTRL_WR_ECC(n) REG_BIT_FIELD(0x50+((n)<<4), 30, 1)
|
|
|
-+#define NANDC_ACC_CTRL_CE_CARE(n) REG_BIT_FIELD(0x50+((n)<<4), 29, 1)
|
|
|
-+#define NANDC_ACC_CTRL_PGM_RDIN(n) REG_BIT_FIELD(0x50+((n)<<4), 28, 1)
|
|
|
-+#define NANDC_ACC_CTRL_ERA_ECC_ERR(n) REG_BIT_FIELD(0x50+((n)<<4), 27, 1)
|
|
|
-+#define NANDC_ACC_CTRL_PGM_PARTIAL(n) REG_BIT_FIELD(0x50+((n)<<4), 26, 1)
|
|
|
-+#define NANDC_ACC_CTRL_WR_PREEMPT(n) REG_BIT_FIELD(0x50+((n)<<4), 25, 1)
|
|
|
-+#define NANDC_ACC_CTRL_PG_HIT(n) REG_BIT_FIELD(0x50+((n)<<4), 24, 1)
|
|
|
-+#define NANDC_ACC_CTRL_PREFETCH(n) REG_BIT_FIELD(0x50+((n)<<4), 23, 1)
|
|
|
-+#define NANDC_ACC_CTRL_CACHE_MODE(n) REG_BIT_FIELD(0x50+((n)<<4), 22, 1)
|
|
|
-+#define NANDC_ACC_CTRL_CACHE_LASTPG(n) REG_BIT_FIELD(0x50+((n)<<4), 21, 1)
|
|
|
-+#define NANDC_ACC_CTRL_ECC_LEVEL(n) REG_BIT_FIELD(0x50+((n)<<4), 16, 5)
|
|
|
-+#define NANDC_ACC_CTRL_SECTOR_1K(n) REG_BIT_FIELD(0x50+((n)<<4), 7, 1)
|
|
|
-+#define NANDC_ACC_CTRL_SPARE_SIZE(n) REG_BIT_FIELD(0x50+((n)<<4), 0, 7)
|
|
|
-+
|
|
|
-+#define NANDC_CONFIG_CS(n) REG_BIT_FIELD(0x54+((n)<<4), 0, 32)
|
|
|
-+#define NANDC_CONFIG_LOCK(n) REG_BIT_FIELD(0x54+((n)<<4), 31, 1)
|
|
|
-+#define NANDC_CONFIG_BLK_SIZE(n) REG_BIT_FIELD(0x54+((n)<<4), 28, 3)
|
|
|
-+#define NANDC_CONFIG_CHIP_SIZE(n) REG_BIT_FIELD(0x54+((n)<<4), 24, 4)
|
|
|
-+#define NANDC_CONFIG_CHIP_WIDTH(n) REG_BIT_FIELD(0x54+((n)<<4), 23, 1)
|
|
|
-+#define NANDC_CONFIG_PAGE_SIZE(n) REG_BIT_FIELD(0x54+((n)<<4), 20, 2)
|
|
|
-+#define NANDC_CONFIG_FUL_ADDR_BYTES(n) REG_BIT_FIELD(0x54+((n)<<4), 16, 3)
|
|
|
-+#define NANDC_CONFIG_COL_ADDR_BYTES(n) REG_BIT_FIELD(0x54+((n)<<4), 12, 3)
|
|
|
-+#define NANDC_CONFIG_BLK_ADDR_BYTES(n) REG_BIT_FIELD(0x54+((n)<<4), 8, 3)
|
|
|
-+
|
|
|
-+#define NANDC_TIMING_1_CS(n) REG_BIT_FIELD(0x58+((n)<<4), 0, 32)
|
|
|
-+#define NANDC_TIMING_2_CS(n) REG_BIT_FIELD(0x5c+((n)<<4), 0, 32)
|
|
|
-+ /* Individual bits for Timing registers - TBD */
|
|
|
-+
|
|
|
-+#define NANDC_CORR_STAT_THRESH_CS(n) REG_BIT_FIELD(0xc0, 6*(n), 6)
|
|
|
-+
|
|
|
-+#define NANDC_BLK_WP_END_ADDR REG_BIT_FIELD(0xc8, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_MPLANE_ERASE_CYC2_OPCODE REG_BIT_FIELD(0xcc, 24, 8)
|
|
|
-+#define NANDC_MPLANE_READ_STAT_OPCODE REG_BIT_FIELD(0xcc, 16, 8)
|
|
|
-+#define NANDC_MPLANE_PROG_ODD_OPCODE REG_BIT_FIELD(0xcc, 8, 8)
|
|
|
-+#define NANDC_MPLANE_PROG_TRL_OPCODE REG_BIT_FIELD(0xcc, 0, 8)
|
|
|
-+
|
|
|
-+#define NANDC_MPLANE_PGCACHE_TRL_OPCODE REG_BIT_FIELD(0xd0, 24, 8)
|
|
|
-+#define NANDC_MPLANE_READ_STAT2_OPCODE REG_BIT_FIELD(0xd0, 16, 8)
|
|
|
-+#define NANDC_MPLANE_READ_EVEN_OPCODE REG_BIT_FIELD(0xd0, 8, 8)
|
|
|
-+#define NANDC_MPLANE_READ_ODD__OPCODE REG_BIT_FIELD(0xd0, 0, 8)
|
|
|
-+
|
|
|
-+#define NANDC_MPLANE_CTRL_ERASE_CYC2_EN REG_BIT_FIELD(0xd4, 31, 1)
|
|
|
-+#define NANDC_MPLANE_CTRL_RD_ADDR_SIZE REG_BIT_FIELD(0xd4, 30, 1)
|
|
|
-+#define NANDC_MPLANE_CTRL_RD_CYC_ADDR REG_BIT_FIELD(0xd4, 29, 1)
|
|
|
-+#define NANDC_MPLANE_CTRL_RD_COL_ADDR REG_BIT_FIELD(0xd4, 28, 1)
|
|
|
-+
|
|
|
-+#define NANDC_UNCORR_ERR_COUNT REG_BIT_FIELD(0xfc, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_CORR_ERR_COUNT REG_BIT_FIELD(0x100, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_READ_CORR_BIT_COUNT REG_BIT_FIELD(0x104, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_BLOCK_LOCK_STATUS REG_BIT_FIELD(0x108, 0, 8)
|
|
|
-+
|
|
|
-+#define NANDC_ECC_CORR_ADDR_CS REG_BIT_FIELD(0x10c, 16, 3)
|
|
|
-+#define NANDC_ECC_CORR_ADDR_EXT REG_BIT_FIELD(0x10c, 0, 16)
|
|
|
-+
|
|
|
-+#define NANDC_ECC_CORR_ADDR REG_BIT_FIELD(0x110, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_ECC_UNC_ADDR_CS REG_BIT_FIELD(0x114, 16, 3)
|
|
|
-+#define NANDC_ECC_UNC_ADDR_EXT REG_BIT_FIELD(0x114, 0, 16)
|
|
|
-+
|
|
|
-+#define NANDC_ECC_UNC_ADDR REG_BIT_FIELD(0x118, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_READ_ADDR_CS REG_BIT_FIELD(0x11c, 16, 3)
|
|
|
-+#define NANDC_READ_ADDR_EXT REG_BIT_FIELD(0x11c, 0, 16)
|
|
|
-+#define NANDC_READ_ADDR REG_BIT_FIELD(0x120, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_PROG_ADDR_CS REG_BIT_FIELD(0x124, 16, 3)
|
|
|
-+#define NANDC_PROG_ADDR_EXT REG_BIT_FIELD(0x124, 0, 16)
|
|
|
-+#define NANDC_PROG_ADDR REG_BIT_FIELD(0x128, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_CPYBK_ADDR_CS REG_BIT_FIELD(0x12c, 16, 3)
|
|
|
-+#define NANDC_CPYBK_ADDR_EXT REG_BIT_FIELD(0x12c, 0, 16)
|
|
|
-+#define NANDC_CPYBK_ADDR REG_BIT_FIELD(0x130, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_ERASE_ADDR_CS REG_BIT_FIELD(0x134, 16, 3)
|
|
|
-+#define NANDC_ERASE_ADDR_EXT REG_BIT_FIELD(0x134, 0, 16)
|
|
|
-+#define NANDC_ERASE_ADDR REG_BIT_FIELD(0x138, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_INV_READ_ADDR_CS REG_BIT_FIELD(0x13c, 16, 3)
|
|
|
-+#define NANDC_INV_READ_ADDR_EXT REG_BIT_FIELD(0x13c, 0, 16)
|
|
|
-+#define NANDC_INV_READ_ADDR REG_BIT_FIELD(0x140, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_INIT_STAT REG_BIT_FIELD(0x144, 0, 32)
|
|
|
-+#define NANDC_INIT_ONFI_DONE REG_BIT_FIELD(0x144, 31, 1)
|
|
|
-+#define NANDC_INIT_DEVID_DONE REG_BIT_FIELD(0x144, 30, 1)
|
|
|
-+#define NANDC_INIT_SUCCESS REG_BIT_FIELD(0x144, 29, 1)
|
|
|
-+#define NANDC_INIT_FAIL REG_BIT_FIELD(0x144, 28, 1)
|
|
|
-+#define NANDC_INIT_BLANK REG_BIT_FIELD(0x144, 27, 1)
|
|
|
-+#define NANDC_INIT_TIMEOUT REG_BIT_FIELD(0x144, 26, 1)
|
|
|
-+#define NANDC_INIT_UNC_ERROR REG_BIT_FIELD(0x144, 25, 1)
|
|
|
-+#define NANDC_INIT_CORR_ERROR REG_BIT_FIELD(0x144, 24, 1)
|
|
|
-+#define NANDC_INIT_PARAM_RDY REG_BIT_FIELD(0x144, 23, 1)
|
|
|
-+#define NANDC_INIT_AUTH_FAIL REG_BIT_FIELD(0x144, 22, 1)
|
|
|
-+
|
|
|
-+#define NANDC_ONFI_STAT REG_BIT_FIELD(0x148, 0, 32)
|
|
|
-+#define NANDC_ONFI_DEBUG REG_BIT_FIELD(0x148, 28, 4)
|
|
|
-+#define NANDC_ONFI_PRESENT REG_BIT_FIELD(0x148, 27, 1)
|
|
|
-+#define NANDC_ONFI_BADID_PG2 REG_BIT_FIELD(0x148, 5, 1)
|
|
|
-+#define NANDC_ONFI_BADID_PG1 REG_BIT_FIELD(0x148, 4, 1)
|
|
|
-+#define NANDC_ONFI_BADID_PG0 REG_BIT_FIELD(0x148, 3, 1)
|
|
|
-+#define NANDC_ONFI_BADCRC_PG2 REG_BIT_FIELD(0x148, 2, 1)
|
|
|
-+#define NANDC_ONFI_BADCRC_PG1 REG_BIT_FIELD(0x148, 1, 1)
|
|
|
-+#define NANDC_ONFI_BADCRC_PG0 REG_BIT_FIELD(0x148, 0, 1)
|
|
|
-+
|
|
|
-+#define NANDC_ONFI_DEBUG_DATA REG_BIT_FIELD(0x14c, 0, 32)
|
|
|
-+
|
|
|
-+#define NANDC_SEMAPHORE REG_BIT_FIELD(0x150, 0, 8)
|
|
|
-+
|
|
|
-+#define NANDC_DEVID_BYTE(b) REG_BIT_FIELD(0x194+((b)&0x4), \
|
|
|
-+ 24-(((b)&3)<<3), 8)
|
|
|
-+
|
|
|
-+#define NANDC_LL_RDDATA REG_BIT_FIELD(0x19c, 0, 16)
|
|
|
-+
|
|
|
-+#define NANDC_INT_N_REG(n) REG_BIT_FIELD(0xf00|((n)<<2), 0, 1)
|
|
|
-+#define NANDC_INT_DIREC_READ_MISS REG_BIT_FIELD(0xf00, 0, 1)
|
|
|
-+#define NANDC_INT_ERASE_DONE REG_BIT_FIELD(0xf04, 0, 1)
|
|
|
-+#define NANDC_INT_CPYBK_DONE REG_BIT_FIELD(0xf08, 0, 1)
|
|
|
-+#define NANDC_INT_PROGRAM_DONE REG_BIT_FIELD(0xf0c, 0, 1)
|
|
|
-+#define NANDC_INT_CONTROLLER_RDY REG_BIT_FIELD(0xf10, 0, 1)
|
|
|
-+#define NANDC_INT_RDBSY_RDY REG_BIT_FIELD(0xf14, 0, 1)
|
|
|
-+#define NANDC_INT_ECC_UNCORRECTABLE REG_BIT_FIELD(0xf18, 0, 1)
|
|
|
-+#define NANDC_INT_ECC_CORRECTABLE REG_BIT_FIELD(0xf1c, 0, 1)
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Following registers are treated as contigous IO memory, offset is from
|
|
|
-+ * <reg_base>, and the data is in big-endian byte order
|
|
|
-+ */
|
|
|
-+#define NANDC_SPARE_AREA_READ_OFF 0x200
|
|
|
-+#define NANDC_SPARE_AREA_WRITE_OFF 0x280
|
|
|
-+#define NANDC_CACHE_OFF 0x400
|
|
|
-+#define NANDC_CACHE_SIZE (128*4)
|
|
|
-+
|
|
|
-+struct bcmnand_areg_field {
|
|
|
-+ unsigned int reg;
|
|
|
-+ unsigned int pos;
|
|
|
-+ unsigned int width;
|
|
|
-+};
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Following are IDM (a.k.a. Slave Wrapper) registers are off <idm_base>:
|
|
|
-+ */
|
|
|
-+#define IDMREG_BIT_FIELD(r, p, w) ((struct bcmnand_areg_field){(r), (p), (w)})
|
|
|
-+
|
|
|
-+#define NANDC_IDM_AXI_BIG_ENDIAN IDMREG_BIT_FIELD(0x408, 28, 1)
|
|
|
-+#define NANDC_IDM_APB_LITTLE_ENDIAN IDMREG_BIT_FIELD(0x408, 24, 1)
|
|
|
-+#define NANDC_IDM_TM IDMREG_BIT_FIELD(0x408, 16, 5)
|
|
|
-+#define NANDC_IDM_IRQ_CORRECABLE_EN IDMREG_BIT_FIELD(0x408, 9, 1)
|
|
|
-+#define NANDC_IDM_IRQ_UNCORRECABLE_EN IDMREG_BIT_FIELD(0x408, 8, 1)
|
|
|
-+#define NANDC_IDM_IRQ_RDYBSY_RDY_EN IDMREG_BIT_FIELD(0x408, 7, 1)
|
|
|
-+#define NANDC_IDM_IRQ_CONTROLLER_RDY_EN IDMREG_BIT_FIELD(0x408, 6, 1)
|
|
|
-+#define NANDC_IDM_IRQ_PRPOGRAM_COMP_EN IDMREG_BIT_FIELD(0x408, 5, 1)
|
|
|
-+#define NANDC_IDM_IRQ_COPYBK_COMP_EN IDMREG_BIT_FIELD(0x408, 4, 1)
|
|
|
-+#define NANDC_IDM_IRQ_ERASE_COMP_EN IDMREG_BIT_FIELD(0x408, 3, 1)
|
|
|
-+#define NANDC_IDM_IRQ_READ_MISS_EN IDMREG_BIT_FIELD(0x408, 2, 1)
|
|
|
-+#define NANDC_IDM_IRQ_N_EN(n) IDMREG_BIT_FIELD(0x408, 2+(n), 1)
|
|
|
-+
|
|
|
-+#define NANDC_IDM_CLOCK_EN IDMREG_BIT_FIELD(0x408, 0, 1)
|
|
|
-+
|
|
|
-+#define NANDC_IDM_IO_ECC_CORR IDMREG_BIT_FIELD(0x500, 3, 1)
|
|
|
-+#define NANDC_IDM_IO_ECC_UNCORR IDMREG_BIT_FIELD(0x500, 2, 1)
|
|
|
-+#define NANDC_IDM_IO_RDYBSY IDMREG_BIT_FIELD(0x500, 1, 1)
|
|
|
-+#define NANDC_IDM_IO_CTRL_RDY IDMREG_BIT_FIELD(0x500, 0, 1)
|
|
|
-+
|
|
|
-+#define NANDC_IDM_RESET IDMREG_BIT_FIELD(0x800, 0, 1)
|
|
|
-+ /* Remaining IDM registers do not seem to be useful, skipped */
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Controller has its own command opcodes
|
|
|
-+ * different from opcodes sent to the actual flash chip
|
|
|
-+ */
|
|
|
-+#define NANDC_CMD_OPCODE_NULL 0
|
|
|
-+#define NANDC_CMD_OPCODE_PAGE_READ 1
|
|
|
-+#define NANDC_CMD_OPCODE_SPARE_READ 2
|
|
|
-+#define NANDC_CMD_OPCODE_STATUS_READ 3
|
|
|
-+#define NANDC_CMD_OPCODE_PAGE_PROG 4
|
|
|
-+#define NANDC_CMD_OPCODE_SPARE_PROG 5
|
|
|
-+#define NANDC_CMD_OPCODE_DEVID_READ 7
|
|
|
-+#define NANDC_CMD_OPCODE_BLOCK_ERASE 8
|
|
|
-+#define NANDC_CMD_OPCODE_FLASH_RESET 9
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Controller hardware ECC data size
|
|
|
-+ *
|
|
|
-+ * The following table contains the number of bytes needed for
|
|
|
-+ * each of the ECC levels, per "sector", which is either 512 or 1024 bytes.
|
|
|
-+ * The actual layout is as follows:
|
|
|
-+ * The entire spare area is equally divided into as many sections as there
|
|
|
-+ * are sectors per page, and the ECC data is located at the end of each
|
|
|
-+ * of these sections.
|
|
|
-+ * For example, given a 2K per page and 64 bytes spare device, configured for
|
|
|
-+ * sector size 1k and ECC level of 4, the spare area will be divided into 2
|
|
|
-+ * sections 32 bytes each, and the last 14 bytes of 32 in each section will
|
|
|
-+ * be filled with ECC data.
|
|
|
-+ * Note: the name of the algorythm and the number of error bits it can correct
|
|
|
-+ * is of no consequence to this driver, therefore omitted.
|
|
|
-+ */
|
|
|
-+struct bcmnand_ecc_size_s {
|
|
|
-+ unsigned char sector_size_shift;
|
|
|
-+ unsigned char ecc_level;
|
|
|
-+ unsigned char ecc_bytes_per_sec;
|
|
|
-+ unsigned char reserved;
|
|
|
-+};
|
|
|
-+
|
|
|
-+static const struct bcmnand_ecc_size_s bcmnand_ecc_sizes[] = {
|
|
|
-+ { 9, 0, 0 },
|
|
|
-+ { 10, 0, 0 },
|
|
|
-+ { 9, 1, 2 },
|
|
|
-+ { 10, 1, 4 },
|
|
|
-+ { 9, 2, 4 },
|
|
|
-+ { 10, 2, 7 },
|
|
|
-+ { 9, 3, 6 },
|
|
|
-+ { 10, 3, 11 },
|
|
|
-+ { 9, 4, 7 },
|
|
|
-+ { 10, 4, 14 },
|
|
|
-+ { 9, 5, 9 },
|
|
|
-+ { 10, 5, 18 },
|
|
|
-+ { 9, 6, 11 },
|
|
|
-+ { 10, 6, 21 },
|
|
|
-+ { 9, 7, 13 },
|
|
|
-+ { 10, 7, 25 },
|
|
|
-+ { 9, 8, 14 },
|
|
|
-+ { 10, 8, 28 },
|
|
|
-+
|
|
|
-+ { 9, 9, 16 },
|
|
|
-+ { 9, 10, 18 },
|
|
|
-+ { 9, 11, 20 },
|
|
|
-+ { 9, 12, 21 },
|
|
|
-+
|
|
|
-+ { 10, 9, 32 },
|
|
|
-+ { 10, 10, 35 },
|
|
|
-+ { 10, 11, 39 },
|
|
|
-+ { 10, 12, 42 },
|
|
|
-+};
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Populate the various fields that depend on how
|
|
|
-+ * the hardware ECC data is located in the spare area
|
|
|
-+ *
|
|
|
-+ * For this controiller, it is easier to fill-in these
|
|
|
-+ * structures at run time.
|
|
|
-+ *
|
|
|
-+ * The bad-block marker is assumed to occupy one byte
|
|
|
-+ * at chip->badblockpos, which must be in the first
|
|
|
-+ * sector of the spare area, namely it is either
|
|
|
-+ * at offset 0 or 5.
|
|
|
-+ * Some chips use both for manufacturer's bad block
|
|
|
-+ * markers, but we ingore that issue here, and assume only
|
|
|
-+ * one byte is used as bad-block marker always.
|
|
|
-+ */
|
|
|
-+static int bcmnand_hw_ecc_layout(struct bcmnand_ctrl *ctrl)
|
|
|
-+{
|
|
|
-+ struct nand_ecclayout *layout;
|
|
|
-+ struct device *dev = &ctrl->core->dev;
|
|
|
-+ unsigned int i, j, k;
|
|
|
-+ unsigned int ecc_per_sec, oob_per_sec;
|
|
|
-+ unsigned int bbm_pos = ctrl->nand.badblockpos;
|
|
|
-+
|
|
|
-+ /* Caclculate spare area per sector size */
|
|
|
-+ oob_per_sec = ctrl->mtd.oobsize >> ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Try to calculate the amount of ECC bytes per sector with a formula */
|
|
|
-+ if (ctrl->sector_size_shift == 9)
|
|
|
-+ ecc_per_sec = ((ctrl->ecc_level * 14) + 7) >> 3;
|
|
|
-+ else if (ctrl->sector_size_shift == 10)
|
|
|
-+ ecc_per_sec = ((ctrl->ecc_level * 14) + 3) >> 2;
|
|
|
-+ else
|
|
|
-+ ecc_per_sec = oob_per_sec + 1; /* cause an error if not in table */
|
|
|
-+
|
|
|
-+ /* Now find out the answer according to the table */
|
|
|
-+ for (i = 0; i < ARRAY_SIZE(bcmnand_ecc_sizes); i++) {
|
|
|
-+ if (bcmnand_ecc_sizes[i].ecc_level == ctrl->ecc_level &&
|
|
|
-+ bcmnand_ecc_sizes[i].sector_size_shift ==
|
|
|
-+ ctrl->sector_size_shift) {
|
|
|
-+ break;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Table match overrides formula */
|
|
|
-+ if (bcmnand_ecc_sizes[i].ecc_level == ctrl->ecc_level &&
|
|
|
-+ bcmnand_ecc_sizes[i].sector_size_shift == ctrl->sector_size_shift)
|
|
|
-+ ecc_per_sec = bcmnand_ecc_sizes[i].ecc_bytes_per_sec;
|
|
|
-+
|
|
|
-+ /* Return an error if calculated ECC leaves no room for OOB */
|
|
|
-+ if ((ctrl->sec_per_page_shift != 0 && ecc_per_sec >= oob_per_sec) ||
|
|
|
-+ (ctrl->sec_per_page_shift == 0 && ecc_per_sec >= (oob_per_sec - 1))) {
|
|
|
-+ dev_err(dev, "ECC level %d too high, leaves no room for OOB data\n",
|
|
|
-+ ctrl->ecc_level);
|
|
|
-+ return -EINVAL;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Fill in the needed fields */
|
|
|
-+ ctrl->nand.ecc.size = ctrl->mtd.writesize >> ctrl->sec_per_page_shift;
|
|
|
-+ ctrl->nand.ecc.bytes = ecc_per_sec;
|
|
|
-+ ctrl->nand.ecc.steps = 1 << ctrl->sec_per_page_shift;
|
|
|
-+ ctrl->nand.ecc.total = ecc_per_sec << ctrl->sec_per_page_shift;
|
|
|
-+ ctrl->nand.ecc.strength = ctrl->ecc_level;
|
|
|
-+
|
|
|
-+ /* Build an ecc layout data structure */
|
|
|
-+ layout = &ctrl->ecclayout;
|
|
|
-+ memset(layout, 0, sizeof(*layout));
|
|
|
-+
|
|
|
-+ /* Total number of bytes used by HW ECC */
|
|
|
-+ layout->eccbytes = ecc_per_sec << ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Location for each of the HW ECC bytes */
|
|
|
-+ for (i = j = 0, k = 1;
|
|
|
-+ i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes;
|
|
|
-+ i++, j++) {
|
|
|
-+ /* switch sector # */
|
|
|
-+ if (j == ecc_per_sec) {
|
|
|
-+ j = 0;
|
|
|
-+ k++;
|
|
|
-+ }
|
|
|
-+ /* save position of each HW-generated ECC byte */
|
|
|
-+ layout->eccpos[i] = (oob_per_sec * k) - ecc_per_sec + j;
|
|
|
-+
|
|
|
-+ /* Check that HW ECC does not overlap bad-block marker */
|
|
|
-+ if (bbm_pos == layout->eccpos[i]) {
|
|
|
-+ dev_err(dev, "ECC level %d too high, HW ECC collides with bad-block marker position\n",
|
|
|
-+ ctrl->ecc_level);
|
|
|
-+ return -EINVAL;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Location of all user-available OOB byte-ranges */
|
|
|
-+ for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++) {
|
|
|
-+ struct nand_oobfree *oobfree = &layout->oobfree[i];
|
|
|
-+
|
|
|
-+ if (i >= (1 << ctrl->sec_per_page_shift))
|
|
|
-+ break;
|
|
|
-+ oobfree->offset = oob_per_sec * i;
|
|
|
-+ oobfree->length = oob_per_sec - ecc_per_sec;
|
|
|
-+
|
|
|
-+ /* Bad-block marker must be in the first sector spare area */
|
|
|
-+ if (WARN_ON(bbm_pos >= (oobfree->offset + oobfree->length)))
|
|
|
-+ return -EINVAL;
|
|
|
-+
|
|
|
-+ if (i != 0)
|
|
|
-+ continue;
|
|
|
-+
|
|
|
-+ /* Remove bad-block marker from available byte range */
|
|
|
-+ if (bbm_pos == oobfree->offset) {
|
|
|
-+ oobfree->offset += 1;
|
|
|
-+ oobfree->length -= 1;
|
|
|
-+ } else if (bbm_pos == (oobfree->offset + oobfree->length - 1)) {
|
|
|
-+ oobfree->length -= 1;
|
|
|
-+ } else {
|
|
|
-+ layout->oobfree[i + 1].offset = bbm_pos + 1;
|
|
|
-+ layout->oobfree[i + 1].length =
|
|
|
-+ oobfree->length - bbm_pos - 1;
|
|
|
-+ oobfree->length = bbm_pos;
|
|
|
-+ i++;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ layout->oobavail = ((oob_per_sec - ecc_per_sec)
|
|
|
-+ << ctrl->sec_per_page_shift) - 1;
|
|
|
-+
|
|
|
-+ ctrl->mtd.oobavail = layout->oobavail;
|
|
|
-+ ctrl->nand.ecc.layout = layout;
|
|
|
-+
|
|
|
-+ /* Output layout for debugging */
|
|
|
-+ dev_dbg(dev, "Spare area=%d eccbytes %d, ecc bytes located at:\n",
|
|
|
-+ ctrl->mtd.oobsize, layout->eccbytes);
|
|
|
-+ for (i = j = 0;
|
|
|
-+ i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes; i++)
|
|
|
-+ pr_debug(" %d", layout->eccpos[i]);
|
|
|
-+ pr_debug("\n");
|
|
|
-+
|
|
|
-+ dev_dbg(dev, "Available %d bytes at (off,len):\n", layout->oobavail);
|
|
|
-+ for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++)
|
|
|
-+ pr_debug("(%d,%d) ", layout->oobfree[i].offset,
|
|
|
-+ layout->oobfree[i].length);
|
|
|
-+ pr_debug("\n");
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Register bit-field manipulation routines
|
|
|
-+ */
|
|
|
-+
|
|
|
-+static inline unsigned int bcmnand_reg_read(struct bcmnand_ctrl *ctrl,
|
|
|
-+ struct bcmnand_reg_field rbf)
|
|
|
-+{
|
|
|
-+ u32 val;
|
|
|
-+
|
|
|
-+ val = bcma_read32(ctrl->core, rbf.reg);
|
|
|
-+ val >>= rbf.pos;
|
|
|
-+ val &= (1 << rbf.width) - 1;
|
|
|
-+
|
|
|
-+ return val;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static inline void bcmnand_reg_write(struct bcmnand_ctrl *ctrl,
|
|
|
-+ struct bcmnand_reg_field rbf,
|
|
|
-+ unsigned newval)
|
|
|
-+{
|
|
|
-+ u32 val, msk;
|
|
|
-+
|
|
|
-+ msk = (1 << rbf.width) - 1;
|
|
|
-+ msk <<= rbf.pos;
|
|
|
-+ newval <<= rbf.pos;
|
|
|
-+ newval &= msk;
|
|
|
-+
|
|
|
-+ val = bcma_read32(ctrl->core, rbf.reg);
|
|
|
-+ val &= ~msk;
|
|
|
-+ val |= newval;
|
|
|
-+ bcma_write32(ctrl->core, rbf.reg, val);
|
|
|
-+}
|
|
|
-+
|
|
|
-+static inline unsigned int bcmnand_reg_aread(struct bcmnand_ctrl *ctrl,
|
|
|
-+ struct bcmnand_areg_field rbf)
|
|
|
-+{
|
|
|
-+ u32 val;
|
|
|
-+
|
|
|
-+ val = bcma_aread32(ctrl->core, rbf.reg);
|
|
|
-+ val >>= rbf.pos;
|
|
|
-+ val &= (1 << rbf.width) - 1;
|
|
|
-+
|
|
|
-+ return val;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static inline void bcmnand_reg_awrite(struct bcmnand_ctrl *ctrl,
|
|
|
-+ struct bcmnand_areg_field rbf,
|
|
|
-+ unsigned int newval)
|
|
|
-+{
|
|
|
-+ u32 val, msk;
|
|
|
-+
|
|
|
-+ msk = (1 << rbf.width) - 1;
|
|
|
-+ msk <<= rbf.pos;
|
|
|
-+ newval <<= rbf.pos;
|
|
|
-+ newval &= msk;
|
|
|
-+
|
|
|
-+ val = bcma_aread32(ctrl->core, rbf.reg);
|
|
|
-+ val &= ~msk;
|
|
|
-+ val |= newval;
|
|
|
-+ bcma_awrite32(ctrl->core, rbf.reg, val);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - dev_ready
|
|
|
-+ *
|
|
|
-+ * Return 1 iff device is ready, 0 otherwise
|
|
|
-+ */
|
|
|
-+static int bcmnand_dev_ready(struct mtd_info *mtd)
|
|
|
-+{
|
|
|
-+ struct nand_chip *chip = mtd->priv;
|
|
|
-+ struct bcmnand_ctrl *ctrl = chip->priv;
|
|
|
-+
|
|
|
-+ return bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Interrupt service routines
|
|
|
-+ */
|
|
|
-+static irqreturn_t bcmnand_isr(int irq, void *dev_id)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = dev_id;
|
|
|
-+ int irq_off;
|
|
|
-+
|
|
|
-+ irq_off = irq - ctrl->core->irq;
|
|
|
-+ WARN_ON(irq_off < 0 || irq_off >= NANDC_IRQ_NUM);
|
|
|
-+
|
|
|
-+ if (!bcmnand_reg_read(ctrl, NANDC_INT_N_REG(irq_off)))
|
|
|
-+ return IRQ_NONE;
|
|
|
-+
|
|
|
-+ /* Acknowledge interrupt */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_INT_N_REG(irq_off), 1);
|
|
|
-+
|
|
|
-+ /* Wake up task */
|
|
|
-+ complete(&ctrl->op_completion);
|
|
|
-+
|
|
|
-+ return IRQ_HANDLED;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static int bcmnand_wait_interrupt(struct bcmnand_ctrl *ctrl,
|
|
|
-+ unsigned int irq_off,
|
|
|
-+ unsigned int timeout_usec)
|
|
|
-+{
|
|
|
-+ long timeout_jiffies;
|
|
|
-+ int ret = 0;
|
|
|
-+
|
|
|
-+ reinit_completion(&ctrl->op_completion);
|
|
|
-+
|
|
|
-+ /* Acknowledge interrupt */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_INT_N_REG(irq_off), 1);
|
|
|
-+
|
|
|
-+ /* Enable IRQ to wait on */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_IRQ_N_EN(irq_off), 1);
|
|
|
-+
|
|
|
-+ timeout_jiffies = 1 + usecs_to_jiffies(timeout_usec);
|
|
|
-+
|
|
|
-+ if (irq_off != NANDC_IRQ_CONTROLLER_RDY ||
|
|
|
-+ 0 == bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY)) {
|
|
|
-+
|
|
|
-+ timeout_jiffies = wait_for_completion_timeout(
|
|
|
-+ &ctrl->op_completion, timeout_jiffies);
|
|
|
-+
|
|
|
-+ if (timeout_jiffies < 0)
|
|
|
-+ ret = timeout_jiffies;
|
|
|
-+ if (timeout_jiffies == 0)
|
|
|
-+ ret = -ETIME;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Disable IRQ, we're done waiting */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_IRQ_N_EN(irq_off), 0);
|
|
|
-+
|
|
|
-+ if (bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY))
|
|
|
-+ ret = 0;
|
|
|
-+
|
|
|
-+ return ret;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * wait for command completion
|
|
|
-+ */
|
|
|
-+static int bcmnand_wait_cmd(struct bcmnand_ctrl *ctrl, unsigned int timeout_usec)
|
|
|
-+{
|
|
|
-+ unsigned int retries;
|
|
|
-+
|
|
|
-+ if (bcmnand_reg_read(ctrl, NANDC_INT_STAT_CTLR_RDY))
|
|
|
-+ return 0;
|
|
|
-+
|
|
|
-+ /* If the timeout is long, wait for interrupt */
|
|
|
-+ if (timeout_usec >= jiffies_to_usecs(1) >> 4)
|
|
|
-+ return bcmnand_wait_interrupt(
|
|
|
-+ ctrl, NANDC_IRQ_CONTROLLER_RDY, timeout_usec);
|
|
|
-+
|
|
|
-+ /* Wait for completion of the prior command */
|
|
|
-+ retries = (timeout_usec >> 3) + 1;
|
|
|
-+
|
|
|
-+ while (retries-- &&
|
|
|
-+ 0 == bcmnand_reg_read(ctrl, NANDC_INT_STAT_CTLR_RDY)) {
|
|
|
-+ cpu_relax();
|
|
|
-+ udelay(6);
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ if (retries == 0)
|
|
|
-+ return -ETIME;
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - waitfunc
|
|
|
-+ */
|
|
|
-+static int bcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = chip->priv;
|
|
|
-+ unsigned int to;
|
|
|
-+ int ret;
|
|
|
-+
|
|
|
-+ /* figure out timeout based on what command is on */
|
|
|
-+ switch (ctrl->last_cmd) {
|
|
|
-+ default:
|
|
|
-+ case NAND_CMD_ERASE1:
|
|
|
-+ case NAND_CMD_ERASE2:
|
|
|
-+ to = 1 << 16;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_STATUS:
|
|
|
-+ case NAND_CMD_RESET:
|
|
|
-+ to = 256;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_READID:
|
|
|
-+ to = 1024;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_READ1:
|
|
|
-+ case NAND_CMD_READ0:
|
|
|
-+ to = 2048;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_PAGEPROG:
|
|
|
-+ to = 4096;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_READOOB:
|
|
|
-+ to = 512;
|
|
|
-+ break;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* deliver deferred error code if any */
|
|
|
-+ ret = ctrl->cmd_ret;
|
|
|
-+ if (ret < 0)
|
|
|
-+ ctrl->cmd_ret = 0;
|
|
|
-+ else
|
|
|
-+ ret = bcmnand_wait_cmd(ctrl, to);
|
|
|
-+
|
|
|
-+ /* Timeout */
|
|
|
-+ if (ret < 0)
|
|
|
-+ return NAND_STATUS_FAIL;
|
|
|
-+
|
|
|
-+ ret = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS);
|
|
|
-+
|
|
|
-+ return ret;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - read_oob
|
|
|
-+ */
|
|
|
-+static int bcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ int page)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = chip->priv;
|
|
|
-+ unsigned int n = ctrl->chip_num;
|
|
|
-+ void __iomem *ctrl_spare;
|
|
|
-+ unsigned int spare_per_sec, sector;
|
|
|
-+ u64 nand_addr;
|
|
|
-+
|
|
|
-+ ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_READ_OFF;
|
|
|
-+
|
|
|
-+ /* Set the page address for the following commands */
|
|
|
-+ nand_addr = ((u64)page << chip->page_shift);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
|
|
|
-+
|
|
|
-+ spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Disable ECC validation for spare area reads */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_RD_ECC(n), 0);
|
|
|
-+
|
|
|
-+ /* Loop all sectors in page */
|
|
|
-+ for (sector = 0; sector < (1<<ctrl->sec_per_page_shift); sector++) {
|
|
|
-+ unsigned int col;
|
|
|
-+
|
|
|
-+ col = (sector << ctrl->sector_size_shift);
|
|
|
-+
|
|
|
-+ /* Issue command to read partial page */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS, nand_addr + col);
|
|
|
-+
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_SPARE_READ);
|
|
|
-+
|
|
|
-+ /* Wait for the command to complete */
|
|
|
-+ if (bcmnand_wait_cmd(ctrl, (sector == 0) ? 10000 : 100))
|
|
|
-+ return -EIO;
|
|
|
-+
|
|
|
-+ if (!bcmnand_reg_read(ctrl, NANDC_INT_STAT_SPARE_VALID))
|
|
|
-+ return -EIO;
|
|
|
-+
|
|
|
-+ /* Set controller to Little Endian mode for copying */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
|
|
|
-+
|
|
|
-+ memcpy(chip->oob_poi + sector * spare_per_sec,
|
|
|
-+ ctrl_spare, spare_per_sec);
|
|
|
-+
|
|
|
-+ /* Return to Big Endian mode for commands etc */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - write_oob
|
|
|
-+ */
|
|
|
-+static int bcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ int page)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = chip->priv;
|
|
|
-+ unsigned int n = ctrl->chip_num;
|
|
|
-+ void __iomem *ctrl_spare;
|
|
|
-+ unsigned int spare_per_sec, sector, num_sec;
|
|
|
-+ u64 nand_addr;
|
|
|
-+ int to, status = 0;
|
|
|
-+
|
|
|
-+ ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_WRITE_OFF;
|
|
|
-+
|
|
|
-+ /* Disable ECC generation for spare area writes */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_WR_ECC(n), 0);
|
|
|
-+
|
|
|
-+ spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Set the page address for the following commands */
|
|
|
-+ nand_addr = ((u64)page << chip->page_shift);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
|
|
|
-+
|
|
|
-+ /* Must allow partial programming to change spare area only */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PGM_PARTIAL(n), 1);
|
|
|
-+
|
|
|
-+ num_sec = 1 << ctrl->sec_per_page_shift;
|
|
|
-+ /* Loop all sectors in page */
|
|
|
-+ for (sector = 0; sector < num_sec; sector++) {
|
|
|
-+ unsigned int col;
|
|
|
-+
|
|
|
-+ /* Spare area accessed by the data sector offset */
|
|
|
-+ col = (sector << ctrl->sector_size_shift);
|
|
|
-+
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS, nand_addr + col);
|
|
|
-+
|
|
|
-+ /* Set controller to Little Endian mode for copying */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
|
|
|
-+
|
|
|
-+ memcpy(ctrl_spare, chip->oob_poi + sector * spare_per_sec,
|
|
|
-+ spare_per_sec);
|
|
|
-+
|
|
|
-+ /* Return to Big Endian mode for commands etc */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
|
|
|
-+
|
|
|
-+ /* Push spare bytes into internal buffer, last goes to flash */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_SPARE_PROG);
|
|
|
-+
|
|
|
-+ if (sector == (num_sec - 1))
|
|
|
-+ to = 1 << 16;
|
|
|
-+ else
|
|
|
-+ to = 1 << 10;
|
|
|
-+
|
|
|
-+ if (bcmnand_wait_cmd(ctrl, to))
|
|
|
-+ return -EIO;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Restore partial programming inhibition */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PGM_PARTIAL(n), 0);
|
|
|
-+
|
|
|
-+ status = bcmnand_waitfunc(mtd, chip);
|
|
|
-+ return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * verify that a buffer is all erased
|
|
|
-+ */
|
|
|
-+static bool bcmnand_buf_erased(const void *buf, unsigned int len)
|
|
|
-+{
|
|
|
-+ unsigned int i;
|
|
|
-+ const u32 *p = buf;
|
|
|
-+
|
|
|
-+ for (i = 0; i < (len >> 2); i++) {
|
|
|
-+ if (p[i] != 0xffffffff)
|
|
|
-+ return false;
|
|
|
-+ }
|
|
|
-+ return true;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * read a page, with or without ECC checking
|
|
|
-+ */
|
|
|
-+static int bcmnand_read_page_do(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ uint8_t *buf, int page, bool ecc)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = chip->priv;
|
|
|
-+ unsigned int n = ctrl->chip_num;
|
|
|
-+ void __iomem *ctrl_cache;
|
|
|
-+ void __iomem *ctrl_spare;
|
|
|
-+ unsigned int data_bytes;
|
|
|
-+ unsigned int spare_per_sec;
|
|
|
-+ unsigned int sector, to = 1 << 16;
|
|
|
-+ u32 err_soft_reg, err_hard_reg;
|
|
|
-+ unsigned int hard_err_count = 0;
|
|
|
-+ int ret;
|
|
|
-+ u64 nand_addr;
|
|
|
-+
|
|
|
-+ ctrl_cache = ctrl->core->io_addr + NANDC_CACHE_OFF;
|
|
|
-+ ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_READ_OFF;
|
|
|
-+
|
|
|
-+ /* Reset ECC error stats */
|
|
|
-+ err_hard_reg = bcmnand_reg_read(ctrl, NANDC_UNCORR_ERR_COUNT);
|
|
|
-+ err_soft_reg = bcmnand_reg_read(ctrl, NANDC_READ_CORR_BIT_COUNT);
|
|
|
-+
|
|
|
-+ spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Set the page address for the following commands */
|
|
|
-+ nand_addr = ((u64)page << chip->page_shift);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
|
|
|
-+
|
|
|
-+ /* Enable ECC validation for ecc page reads */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_RD_ECC(n), ecc);
|
|
|
-+
|
|
|
-+ /* Loop all sectors in page */
|
|
|
-+ for (sector = 0; sector < (1 << ctrl->sec_per_page_shift); sector++) {
|
|
|
-+ data_bytes = 0;
|
|
|
-+
|
|
|
-+ /* Copy partial sectors sized by cache reg */
|
|
|
-+ while (data_bytes < (1<<ctrl->sector_size_shift)) {
|
|
|
-+ unsigned int col;
|
|
|
-+
|
|
|
-+ col = data_bytes + (sector << ctrl->sector_size_shift);
|
|
|
-+
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS,
|
|
|
-+ nand_addr + col);
|
|
|
-+
|
|
|
-+ /* Issue command to read partial page */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_PAGE_READ);
|
|
|
-+
|
|
|
-+ /* Wait for the command to complete */
|
|
|
-+ ret = bcmnand_wait_cmd(ctrl, to);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+
|
|
|
-+ /* Set controller to Little Endian mode for copying */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
|
|
|
-+
|
|
|
-+ if (data_bytes == 0) {
|
|
|
-+ memcpy(chip->oob_poi + sector * spare_per_sec,
|
|
|
-+ ctrl_spare, spare_per_sec);
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ memcpy(buf + col, ctrl_cache, NANDC_CACHE_SIZE);
|
|
|
-+ data_bytes += NANDC_CACHE_SIZE;
|
|
|
-+
|
|
|
-+ /* Return to Big Endian mode for commands etc */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
|
|
|
-+
|
|
|
-+ /* Next iterations should go fast */
|
|
|
-+ to = 1 << 10;
|
|
|
-+
|
|
|
-+ /* capture hard errors for each partial */
|
|
|
-+ if (err_hard_reg != bcmnand_reg_read(ctrl, NANDC_UNCORR_ERR_COUNT)) {
|
|
|
-+ int era = bcmnand_reg_read(ctrl, NANDC_INT_STAT_ERASED);
|
|
|
-+
|
|
|
-+ if (!era &&
|
|
|
-+ !bcmnand_buf_erased(buf + col, NANDC_CACHE_SIZE))
|
|
|
-+ hard_err_count++;
|
|
|
-+
|
|
|
-+ err_hard_reg = bcmnand_reg_read(ctrl,
|
|
|
-+ NANDC_UNCORR_ERR_COUNT);
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ if (!ecc)
|
|
|
-+ return 0;
|
|
|
-+
|
|
|
-+ /* Report hard ECC errors */
|
|
|
-+ if (hard_err_count)
|
|
|
-+ mtd->ecc_stats.failed++;
|
|
|
-+
|
|
|
-+ /* Get ECC soft error stats */
|
|
|
-+ mtd->ecc_stats.corrected += err_soft_reg -
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_READ_CORR_BIT_COUNT);
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - read_page_ecc
|
|
|
-+ */
|
|
|
-+static int bcmnand_read_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ uint8_t *buf, int oob_required, int page)
|
|
|
-+{
|
|
|
-+ return bcmnand_read_page_do(mtd, chip, buf, page, true);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - read_page_raw
|
|
|
-+ */
|
|
|
-+static int bcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ uint8_t *buf, int oob_required, int page)
|
|
|
-+{
|
|
|
-+ return bcmnand_read_page_do(mtd, chip, buf, page, true);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * do page write, with or without ECC generation enabled
|
|
|
-+ */
|
|
|
-+static int bcmnand_write_page_do(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ const uint8_t *buf, bool ecc)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = chip->priv;
|
|
|
-+ unsigned int n = ctrl->chip_num;
|
|
|
-+ void __iomem *ctrl_cache;
|
|
|
-+ void __iomem *ctrl_spare;
|
|
|
-+ unsigned int spare_per_sec, sector, num_sec;
|
|
|
-+ unsigned int data_bytes, spare_bytes;
|
|
|
-+ int i, to;
|
|
|
-+ uint8_t *tmp_poi;
|
|
|
-+ u32 nand_addr;
|
|
|
-+
|
|
|
-+ ctrl_cache = ctrl->core->io_addr + NANDC_CACHE_OFF;
|
|
|
-+ ctrl_spare = ctrl->core->io_addr + NANDC_SPARE_AREA_WRITE_OFF;
|
|
|
-+
|
|
|
-+ /* Get start-of-page address */
|
|
|
-+ nand_addr = bcmnand_reg_read(ctrl, NANDC_CMD_ADDRESS);
|
|
|
-+
|
|
|
-+ tmp_poi = kmalloc(mtd->oobsize, GFP_KERNEL);
|
|
|
-+ if (!tmp_poi)
|
|
|
-+ return -ENOMEM;
|
|
|
-+
|
|
|
-+ /* Retreive pre-existing OOB values */
|
|
|
-+ memcpy(tmp_poi, chip->oob_poi, mtd->oobsize);
|
|
|
-+ ctrl->cmd_ret = bcmnand_read_oob(mtd, chip,
|
|
|
-+ nand_addr >> chip->page_shift);
|
|
|
-+ if (ctrl->cmd_ret < 0) {
|
|
|
-+ kfree(tmp_poi);
|
|
|
-+ return ctrl->cmd_ret;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Apply new OOB data bytes just like they would end up on the chip */
|
|
|
-+ for (i = 0; i < mtd->oobsize; i++)
|
|
|
-+ chip->oob_poi[i] &= tmp_poi[i];
|
|
|
-+ kfree(tmp_poi);
|
|
|
-+
|
|
|
-+ spare_per_sec = mtd->oobsize >> ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Enable ECC generation for ecc page write, if requested */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_WR_ECC(n), ecc);
|
|
|
-+
|
|
|
-+ spare_bytes = 0;
|
|
|
-+ num_sec = 1 << ctrl->sec_per_page_shift;
|
|
|
-+
|
|
|
-+ /* Loop all sectors in page */
|
|
|
-+ for (sector = 0; sector < num_sec; sector++) {
|
|
|
-+ data_bytes = 0;
|
|
|
-+
|
|
|
-+ /* Copy partial sectors sized by cache reg */
|
|
|
-+ while (data_bytes < (1<<ctrl->sector_size_shift)) {
|
|
|
-+ unsigned int col;
|
|
|
-+
|
|
|
-+ col = data_bytes +
|
|
|
-+ (sector << ctrl->sector_size_shift);
|
|
|
-+
|
|
|
-+ /* Set address of 512-byte sub-page */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS,
|
|
|
-+ nand_addr + col);
|
|
|
-+
|
|
|
-+ /* Set controller to Little Endian mode for copying */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN,
|
|
|
-+ 1);
|
|
|
-+
|
|
|
-+ /* Set spare area is written at each sector start */
|
|
|
-+ if (data_bytes == 0) {
|
|
|
-+ memcpy(ctrl_spare,
|
|
|
-+ chip->oob_poi + spare_bytes,
|
|
|
-+ spare_per_sec);
|
|
|
-+ spare_bytes += spare_per_sec;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Copy sub-page data */
|
|
|
-+ memcpy(ctrl_cache, buf + col, NANDC_CACHE_SIZE);
|
|
|
-+ data_bytes += NANDC_CACHE_SIZE;
|
|
|
-+
|
|
|
-+ /* Return to Big Endian mode for commands etc */
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
|
|
|
-+
|
|
|
-+ /* Push data into internal cache */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_PAGE_PROG);
|
|
|
-+
|
|
|
-+ /* Wait for the command to complete */
|
|
|
-+ if (sector == (num_sec - 1))
|
|
|
-+ to = 1 << 16;
|
|
|
-+ else
|
|
|
-+ to = 1 << 10;
|
|
|
-+ ctrl->cmd_ret = bcmnand_wait_cmd(ctrl, to);
|
|
|
-+ if (ctrl->cmd_ret < 0)
|
|
|
-+ return ctrl->cmd_ret;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface = write_page_ecc
|
|
|
-+ */
|
|
|
-+static int bcmnand_write_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ const uint8_t *buf, int oob_required)
|
|
|
-+{
|
|
|
-+ return bcmnand_write_page_do(mtd, chip, buf, true);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface = write_page_raw
|
|
|
-+ */
|
|
|
-+static int bcmnand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
-+ const uint8_t *buf, int oob_required)
|
|
|
-+{
|
|
|
-+ return bcmnand_write_page_do(mtd, chip, buf, false);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * MTD Interface - read_byte
|
|
|
-+ *
|
|
|
-+ * This function emulates simple controllers behavior
|
|
|
-+ * for just a few relevant commands
|
|
|
-+ */
|
|
|
-+static uint8_t bcmnand_read_byte(struct mtd_info *mtd)
|
|
|
-+{
|
|
|
-+ struct nand_chip *nand = mtd->priv;
|
|
|
-+ struct bcmnand_ctrl *ctrl = nand->priv;
|
|
|
-+ struct device *dev = &ctrl->core->dev;
|
|
|
-+ uint8_t b = ~0;
|
|
|
-+
|
|
|
-+ switch (ctrl->last_cmd) {
|
|
|
-+ case NAND_CMD_READID:
|
|
|
-+ if (ctrl->id_byte_index < 8) {
|
|
|
-+ b = bcmnand_reg_read(ctrl, NANDC_DEVID_BYTE(
|
|
|
-+ ctrl->id_byte_index));
|
|
|
-+ ctrl->id_byte_index++;
|
|
|
-+ }
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_READOOB:
|
|
|
-+ if (ctrl->oob_index < mtd->oobsize)
|
|
|
-+ b = nand->oob_poi[ctrl->oob_index++];
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_STATUS:
|
|
|
-+ b = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS);
|
|
|
-+ break;
|
|
|
-+ default:
|
|
|
-+ dev_err(dev, "got unkown command: 0x%x in read_byte\n",
|
|
|
-+ ctrl->last_cmd);
|
|
|
-+ }
|
|
|
-+ return b;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * MTD Interface - read_word
|
|
|
-+ *
|
|
|
-+ * Can not be tested without x16 chip, but the SoC does not support x16 i/f.
|
|
|
-+ */
|
|
|
-+static u16 bcmnand_read_word(struct mtd_info *mtd)
|
|
|
-+{
|
|
|
-+ u16 w = ~0;
|
|
|
-+
|
|
|
-+ w = bcmnand_read_byte(mtd);
|
|
|
-+ barrier();
|
|
|
-+ w |= bcmnand_read_byte(mtd) << 8;
|
|
|
-+
|
|
|
-+ return w;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * MTD Interface - select a chip from an array
|
|
|
-+ */
|
|
|
-+static void bcmnand_select_chip(struct mtd_info *mtd, int chip)
|
|
|
-+{
|
|
|
-+ struct nand_chip *nand = mtd->priv;
|
|
|
-+ struct bcmnand_ctrl *ctrl = nand->priv;
|
|
|
-+
|
|
|
-+ ctrl->chip_num = chip;
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_CS_SEL, chip);
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * NAND Interface - emulate low-level NAND commands
|
|
|
-+ *
|
|
|
-+ * Only a few low-level commands are really needed by generic NAND,
|
|
|
-+ * and they do not call for CMD_LL operations the controller can support.
|
|
|
-+ */
|
|
|
-+static void bcmnand_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
|
|
-+ int column, int page_addr)
|
|
|
-+{
|
|
|
-+ struct nand_chip *nand = mtd->priv;
|
|
|
-+ struct bcmnand_ctrl *ctrl = nand->priv;
|
|
|
-+ struct device *dev = &ctrl->core->dev;
|
|
|
-+ u64 nand_addr;
|
|
|
-+ unsigned int to = 1;
|
|
|
-+
|
|
|
-+ ctrl->last_cmd = command;
|
|
|
-+
|
|
|
-+ /* Set address for some commands */
|
|
|
-+ switch (command) {
|
|
|
-+ case NAND_CMD_ERASE1:
|
|
|
-+ column = 0;
|
|
|
-+ /*FALLTHROUGH*/
|
|
|
-+ case NAND_CMD_SEQIN:
|
|
|
-+ case NAND_CMD_READ0:
|
|
|
-+ case NAND_CMD_READ1:
|
|
|
-+ WARN_ON(column >= mtd->writesize);
|
|
|
-+ nand_addr = (u64) column |
|
|
|
-+ ((u64)page_addr << nand->page_shift);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_EXT_ADDR, nand_addr >> 32);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_ADDRESS, nand_addr);
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_ERASE2:
|
|
|
-+ case NAND_CMD_RESET:
|
|
|
-+ case NAND_CMD_READID:
|
|
|
-+ case NAND_CMD_READOOB:
|
|
|
-+ case NAND_CMD_PAGEPROG:
|
|
|
-+ default:
|
|
|
-+ /* Do nothing, address not used */
|
|
|
-+ break;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Issue appropriate command to controller */
|
|
|
-+ switch (command) {
|
|
|
-+ case NAND_CMD_SEQIN:
|
|
|
-+ /* Only need to load command address, done */
|
|
|
-+ return;
|
|
|
-+
|
|
|
-+ case NAND_CMD_RESET:
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_FLASH_RESET);
|
|
|
-+ to = 1 << 8;
|
|
|
-+ break;
|
|
|
-+
|
|
|
-+ case NAND_CMD_READID:
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_DEVID_READ);
|
|
|
-+ ctrl->id_byte_index = 0;
|
|
|
-+ to = 1 << 8;
|
|
|
-+ break;
|
|
|
-+
|
|
|
-+ case NAND_CMD_READ0:
|
|
|
-+ case NAND_CMD_READ1:
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_PAGE_READ);
|
|
|
-+ to = 1 << 15;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_STATUS:
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_STATUS_READ);
|
|
|
-+ to = 1 << 8;
|
|
|
-+ break;
|
|
|
-+ case NAND_CMD_ERASE1:
|
|
|
-+ return;
|
|
|
-+
|
|
|
-+ case NAND_CMD_ERASE2:
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CMD_START_OPCODE,
|
|
|
-+ NANDC_CMD_OPCODE_BLOCK_ERASE);
|
|
|
-+ to = 1 << 18;
|
|
|
-+ break;
|
|
|
-+
|
|
|
-+ case NAND_CMD_PAGEPROG:
|
|
|
-+ /* Cmd already set from write_page */
|
|
|
-+ return;
|
|
|
-+
|
|
|
-+ case NAND_CMD_READOOB:
|
|
|
-+ /* Emulate simple interface */
|
|
|
-+ bcmnand_read_oob(mtd, nand, page_addr);
|
|
|
-+ ctrl->oob_index = 0;
|
|
|
-+ return;
|
|
|
-+
|
|
|
-+ default:
|
|
|
-+ dev_err(dev, "got unkown command: 0x%x in cmdfunc\n",
|
|
|
-+ ctrl->last_cmd);
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Wait for command to complete */
|
|
|
-+ ctrl->cmd_ret = bcmnand_wait_cmd(ctrl, to);
|
|
|
-+
|
|
|
-+}
|
|
|
-+
|
|
|
-+static int bcmnand_scan(struct mtd_info *mtd)
|
|
|
-+{
|
|
|
-+ struct nand_chip *nand = mtd->priv;
|
|
|
-+ struct bcmnand_ctrl *ctrl = nand->priv;
|
|
|
-+ struct device *dev = &ctrl->core->dev;
|
|
|
-+ bool sector_1k = false;
|
|
|
-+ unsigned int chip_num = 0;
|
|
|
-+ int ecc_level = 0;
|
|
|
-+ int ret;
|
|
|
-+
|
|
|
-+ ret = nand_scan_ident(mtd, NANDC_MAX_CHIPS, NULL);
|
|
|
-+ if (ret)
|
|
|
-+ return ret;
|
|
|
-+
|
|
|
-+ /* Get configuration from first chip */
|
|
|
-+ sector_1k = bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_SECTOR_1K(0));
|
|
|
-+ ecc_level = bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(0));
|
|
|
-+ mtd->writesize_shift = nand->page_shift;
|
|
|
-+
|
|
|
-+ ctrl->ecc_level = ecc_level;
|
|
|
-+ ctrl->sector_size_shift = sector_1k ? 10 : 9;
|
|
|
-+
|
|
|
-+ /* Configure spare area, tweak as needed */
|
|
|
-+ do {
|
|
|
-+ ctrl->sec_per_page_shift =
|
|
|
-+ mtd->writesize_shift - ctrl->sector_size_shift;
|
|
|
-+
|
|
|
-+ /* will return -EINVAL if OOB space exhausted */
|
|
|
-+ ret = bcmnand_hw_ecc_layout(ctrl);
|
|
|
-+
|
|
|
-+ /* First try to bump sector size to 1k, then decrease level */
|
|
|
-+ if (ret && nand->page_shift > 9 && ctrl->sector_size_shift < 10)
|
|
|
-+ ctrl->sector_size_shift = 10;
|
|
|
-+ else if (ret)
|
|
|
-+ ctrl->ecc_level--;
|
|
|
-+
|
|
|
-+ } while (ret && ctrl->ecc_level > 0);
|
|
|
-+
|
|
|
-+ if (WARN_ON(ctrl->ecc_level == 0))
|
|
|
-+ return -ENOENT;
|
|
|
-+
|
|
|
-+ if ((ctrl->sector_size_shift > 9) != (sector_1k == 1)) {
|
|
|
-+ dev_info(dev, "sector size adjusted to 1k\n");
|
|
|
-+ sector_1k = 1;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ if (ecc_level != ctrl->ecc_level) {
|
|
|
-+ dev_info(dev, "ECC level adjusted from %u to %u\n",
|
|
|
-+ ecc_level, ctrl->ecc_level);
|
|
|
-+ ecc_level = ctrl->ecc_level;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* handle the hardware chip config registers */
|
|
|
-+ for (chip_num = 0; chip_num < nand->numchips; chip_num++) {
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_SECTOR_1K(chip_num),
|
|
|
-+ sector_1k);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip_num),
|
|
|
-+ ecc_level);
|
|
|
-+
|
|
|
-+ /* Large pages: no partial page programming */
|
|
|
-+ if (mtd->writesize > 512) {
|
|
|
-+ bcmnand_reg_write(ctrl,
|
|
|
-+ NANDC_ACC_CTRL_PGM_RDIN(chip_num), 0);
|
|
|
-+ bcmnand_reg_write(ctrl,
|
|
|
-+ NANDC_ACC_CTRL_PGM_PARTIAL(chip_num), 0);
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Do not raise ECC error when reading erased pages */
|
|
|
-+ /* This bit has only partial effect, driver needs to help */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_ERA_ECC_ERR(chip_num),
|
|
|
-+ 0);
|
|
|
-+
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PG_HIT(chip_num), 0);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_PREFETCH(chip_num), 0);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_CACHE_MODE(chip_num), 0);
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_ACC_CTRL_CACHE_LASTPG(chip_num),
|
|
|
-+ 0);
|
|
|
-+
|
|
|
-+ /* TBD: consolidate or at least verify the s/w and h/w geometries agree */
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ /* Allow writing on device */
|
|
|
-+ if (!(nand->options & NAND_ROM))
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CS_NAND_WP, 0);
|
|
|
-+
|
|
|
-+ dev_dbg(dev, "layout.oobavail=%d\n", nand->ecc.layout->oobavail);
|
|
|
-+
|
|
|
-+ ret = nand_scan_tail(mtd);
|
|
|
-+
|
|
|
-+ if (nand->badblockbits == 0)
|
|
|
-+ nand->badblockbits = 8;
|
|
|
-+ if (WARN_ON((1 << nand->page_shift) != mtd->writesize))
|
|
|
-+ return -EIO;
|
|
|
-+
|
|
|
-+ /* Spit out some key chip parameters as detected by nand_base */
|
|
|
-+ dev_dbg(dev, "erasesize=%d writesize=%d oobsize=%d page_shift=%d badblockpos=%d badblockbits=%d\n",
|
|
|
-+ mtd->erasesize, mtd->writesize, mtd->oobsize,
|
|
|
-+ nand->page_shift, nand->badblockpos, nand->badblockbits);
|
|
|
-+
|
|
|
-+ return ret;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * main intiailization function
|
|
|
-+ */
|
|
|
-+static int bcmnand_ctrl_init(struct bcmnand_ctrl *ctrl)
|
|
|
-+{
|
|
|
-+ unsigned int chip;
|
|
|
-+ struct nand_chip *nand;
|
|
|
-+ struct mtd_info *mtd;
|
|
|
-+ struct device *dev = &ctrl->core->dev;
|
|
|
-+ int ret;
|
|
|
-+
|
|
|
-+ /* Software variables init */
|
|
|
-+ nand = &ctrl->nand;
|
|
|
-+ mtd = &ctrl->mtd;
|
|
|
-+
|
|
|
-+ init_completion(&ctrl->op_completion);
|
|
|
-+
|
|
|
-+ mtd->priv = nand;
|
|
|
-+ mtd->owner = THIS_MODULE;
|
|
|
-+ mtd->name = KBUILD_MODNAME;
|
|
|
-+
|
|
|
-+ nand->priv = ctrl;
|
|
|
-+
|
|
|
-+ nand->chip_delay = 5; /* not used */
|
|
|
-+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)~0L;
|
|
|
-+
|
|
|
-+ if (bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_WIDTH(0)))
|
|
|
-+ nand->options |= NAND_BUSWIDTH_16;
|
|
|
-+ nand->options |= NAND_SKIP_BBTSCAN; /* Dont need BBTs */
|
|
|
-+
|
|
|
-+ nand->options |= NAND_NO_SUBPAGE_WRITE; /* Subpages unsupported */
|
|
|
-+
|
|
|
-+ nand->dev_ready = bcmnand_dev_ready;
|
|
|
-+ nand->read_byte = bcmnand_read_byte;
|
|
|
-+ nand->read_word = bcmnand_read_word;
|
|
|
-+ nand->select_chip = bcmnand_select_chip;
|
|
|
-+ nand->cmdfunc = bcmnand_cmdfunc;
|
|
|
-+ nand->waitfunc = bcmnand_waitfunc;
|
|
|
-+
|
|
|
-+ nand->ecc.mode = NAND_ECC_HW;
|
|
|
-+ nand->ecc.read_page_raw = bcmnand_read_page_raw;
|
|
|
-+ nand->ecc.write_page_raw = bcmnand_write_page_raw;
|
|
|
-+ nand->ecc.read_page = bcmnand_read_page_ecc;
|
|
|
-+ nand->ecc.write_page = bcmnand_write_page_ecc;
|
|
|
-+ nand->ecc.read_oob = bcmnand_read_oob;
|
|
|
-+ nand->ecc.write_oob = bcmnand_write_oob;
|
|
|
-+
|
|
|
-+ /* Set AUTO_CNFIG bit - try to auto-detect chips */
|
|
|
-+ bcmnand_reg_write(ctrl, NANDC_CS_AUTO_CONFIG, 1);
|
|
|
-+
|
|
|
-+ usleep_range(1000, 1500);
|
|
|
-+
|
|
|
-+ /* Print out current chip config */
|
|
|
-+ for (chip = 0; chip < NANDC_MAX_CHIPS; chip++) {
|
|
|
-+ dev_dbg(dev, "chip[%d]: size=%#x block=%#x page=%#x ecc_level=%#x\n",
|
|
|
-+ chip,
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_SIZE(chip)),
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_CONFIG_BLK_SIZE(chip)),
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_CONFIG_PAGE_SIZE(chip)),
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip)));
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ dev_dbg(dev, "Nand controller is reads=%d\n",
|
|
|
-+ bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY));
|
|
|
-+
|
|
|
-+ ret = bcmnand_scan(mtd);
|
|
|
-+ if (ret) {
|
|
|
-+ dev_err(dev, "scanning the nand flash chip failed with %i\n",
|
|
|
-+ ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static int bcmnand_idm_init(struct bcmnand_ctrl *ctrl)
|
|
|
-+{
|
|
|
-+ int irq_off;
|
|
|
-+ unsigned int retries = 0x1000;
|
|
|
-+ struct device *dev = &ctrl->core->dev;
|
|
|
-+
|
|
|
-+ if (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET))
|
|
|
-+ dev_info(dev, "stuck in reset\n");
|
|
|
-+
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 1);
|
|
|
-+ if (!bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) {
|
|
|
-+ dev_err(dev, "reset of failed\n");
|
|
|
-+ return -EIO;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ while (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) {
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 0);
|
|
|
-+ cpu_relax();
|
|
|
-+ usleep_range(100, 150);
|
|
|
-+ if (!(retries--)) {
|
|
|
-+ dev_err(dev, "did not came back from reset\n");
|
|
|
-+ return -ETIMEDOUT;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_CLOCK_EN, 1);
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
|
|
|
-+ udelay(10);
|
|
|
-+
|
|
|
-+ dev_info(dev, "NAND Controller rev %d.%02d\n",
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_REV_MAJOR),
|
|
|
-+ bcmnand_reg_read(ctrl, NANDC_REV_MINOR));
|
|
|
-+
|
|
|
-+ usleep_range(250, 350);
|
|
|
-+
|
|
|
-+ /* Disable all IRQs */
|
|
|
-+ for (irq_off = 0; irq_off < NANDC_IRQ_NUM; irq_off++)
|
|
|
-+ bcmnand_reg_awrite(ctrl, NANDC_IDM_IRQ_N_EN(irq_off), 0);
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static const char * const part_probes[] = { "ofpart", "bcm47xxpart", NULL };
|
|
|
-+
|
|
|
-+/*
|
|
|
-+ * Top-level init function
|
|
|
-+ */
|
|
|
-+static int bcmnand_probe(struct bcma_device *core)
|
|
|
-+{
|
|
|
-+ struct mtd_part_parser_data parser_data;
|
|
|
-+ struct device *dev = &core->dev;
|
|
|
-+ struct bcmnand_ctrl *ctrl;
|
|
|
-+ int res, i, irq;
|
|
|
-+
|
|
|
-+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
|
|
-+ if (!ctrl)
|
|
|
-+ return -ENOMEM;
|
|
|
-+
|
|
|
-+ bcma_set_drvdata(core, ctrl);
|
|
|
-+
|
|
|
-+ ctrl->mtd.dev.parent = &core->dev;
|
|
|
-+ ctrl->core = core;
|
|
|
-+
|
|
|
-+ /* Acquire all interrupt lines */
|
|
|
-+ for (i = 0; i < NANDC_IRQ_NUM; i++) {
|
|
|
-+ irq = bcma_core_irq(core, i);
|
|
|
-+ if (!irq) {
|
|
|
-+ dev_err(dev, "IRQ idx %i not available\n", i);
|
|
|
-+ return -ENOENT;
|
|
|
-+ }
|
|
|
-+ res = devm_request_irq(dev, irq, bcmnand_isr, 0,
|
|
|
-+ KBUILD_MODNAME, ctrl);
|
|
|
-+ if (res < 0) {
|
|
|
-+ dev_err(dev, "problem requesting irq: %i (idx: %i)\n",
|
|
|
-+ irq, i);
|
|
|
-+ return res;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ res = bcmnand_idm_init(ctrl);
|
|
|
-+ if (res)
|
|
|
-+ return res;
|
|
|
-+
|
|
|
-+ res = bcmnand_ctrl_init(ctrl);
|
|
|
-+ if (res)
|
|
|
-+ return res;
|
|
|
-+
|
|
|
-+ parser_data.of_node = dev->of_node;
|
|
|
-+ res = mtd_device_parse_register(&ctrl->mtd, part_probes, &parser_data, NULL, 0);
|
|
|
-+ if (res) {
|
|
|
-+ dev_err(dev, "Failed to register MTD device: %d\n", res);
|
|
|
-+ return res;
|
|
|
-+ }
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static void bcmnand_remove(struct bcma_device *core)
|
|
|
-+{
|
|
|
-+ struct bcmnand_ctrl *ctrl = bcma_get_drvdata(core);
|
|
|
-+
|
|
|
-+ mtd_device_unregister(&ctrl->mtd);
|
|
|
-+}
|
|
|
-+
|
|
|
-+static const struct bcma_device_id bcmnand_bcma_tbl[] = {
|
|
|
-+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_NAND, BCMA_ANY_REV, BCMA_ANY_CLASS),
|
|
|
-+ BCMA_CORETABLE_END
|
|
|
-+};
|
|
|
-+MODULE_DEVICE_TABLE(bcma, bgmac_bcma_tbl);
|
|
|
-+
|
|
|
-+static struct bcma_driver bcmnand_bcma_driver = {
|
|
|
-+ .name = KBUILD_MODNAME,
|
|
|
-+ .id_table = bcmnand_bcma_tbl,
|
|
|
-+ .probe = bcmnand_probe,
|
|
|
-+ .remove = bcmnand_remove,
|
|
|
-+};
|
|
|
-+
|
|
|
-+static int __init bcmnand_init(void)
|
|
|
-+{
|
|
|
-+ return bcma_driver_register(&bcmnand_bcma_driver);
|
|
|
-+}
|
|
|
-+
|
|
|
-+static void __exit bcmnand_exit(void)
|
|
|
-+{
|
|
|
-+ bcma_driver_unregister(&bcmnand_bcma_driver);
|
|
|
-+}
|
|
|
-+
|
|
|
-+module_init(bcmnand_init)
|
|
|
-+module_exit(bcmnand_exit)
|
|
|
-+
|
|
|
-+MODULE_LICENSE("GPL");
|
|
|
-+MODULE_AUTHOR("Hauke Mehrtens");
|
|
|
-+MODULE_DESCRIPTION("Northstar on-chip NAND Flash Controller driver");
|