summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2018-06-27 13:09:55 -0400
committerTom Rini <trini@konsulko.com>2018-06-27 13:09:55 -0400
commitde76610545f4350f8e3eac7c0c4ff6349106a9bf (patch)
tree7a8df8ce032c6b53ac64864c91c213c8a93492da /drivers
parent3925b2ac97b50b1facab096ac98243615683c295 (diff)
parentb4cb809289235eb39dc2a3e521d8254da0ad752e (diff)
Merge git://git.denx.de/u-boot-imx
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/mtd/nand/Kconfig16
-rw-r--r--drivers/mtd/nand/Makefile2
-rw-r--r--drivers/mtd/nand/mxs_nand.c405
-rw-r--r--drivers/mtd/nand/mxs_nand.h73
-rw-r--r--drivers/mtd/nand/mxs_nand_dt.c86
-rw-r--r--drivers/mtd/nand/mxs_nand_spl.c40
-rw-r--r--drivers/mtd/nand/nand_base.c3
-rw-r--r--drivers/power/pmic/pfuze100.c3
-rw-r--r--drivers/power/pmic/pmic_pfuze3000.c2
10 files changed, 466 insertions, 166 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b7e4ffb09d..29af22ecc7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -109,7 +109,7 @@ config MSM_GPIO
- MSM8916
config MXC_GPIO
- bool "Freescale/NXP MXC UART driver"
+ bool "Freescale/NXP MXC GPIO driver"
help
Support GPIO controllers on various i.MX platforms
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 94fbf89e4b..bdc272142e 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -143,6 +143,7 @@ config NAND_MXC
config NAND_MXS
bool "MXS NAND support"
depends on MX23 || MX28 || MX6 || MX7
+ select SYS_NAND_SELF_INIT
imply CMD_NAND
select APBH_DMA
select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7
@@ -151,6 +152,21 @@ config NAND_MXS
This enables NAND driver for the NAND flash controller on the
MXS processors.
+if NAND_MXS
+
+config NAND_MXS_DT
+ bool "Support MXS NAND controller as a DT device"
+ depends on OF_CONTROL && MTD
+ help
+ Enable the driver for MXS NAND flash on platforms using
+ device tree.
+
+config NAND_MXS_USE_MINIMUM_ECC
+ bool "Use minimum ECC strength supported by the controller"
+ default false
+
+endif
+
config NAND_ZYNQ
bool "Support for Zynq Nand controller"
select SYS_NAND_SELF_INIT
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index e20ef07773..c61e3f3839 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o
obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o
obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o
obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o
+obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o
obj-$(CONFIG_SPL_NAND_INIT) += nand.o
ifeq ($(CONFIG_SPL_ENV_SUPPORT),y)
obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o
obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o
obj-$(CONFIG_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_NAND_MXS) += mxs_nand.o
+obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o
obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o
obj-$(CONFIG_NAND_SPEAR) += spr_nand.o
obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o
diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c
index b9ffa7cbae..e3341812a2 100644
--- a/drivers/mtd/nand/mxs_nand.c
+++ b/drivers/mtd/nand/mxs_nand.c
@@ -13,8 +13,9 @@
*/
#include <common.h>
-#include <linux/mtd/mtd.h>
+#include <dm.h>
#include <linux/mtd/rawnand.h>
+#include <linux/sizes.h>
#include <linux/types.h>
#include <malloc.h>
#include <linux/errno.h>
@@ -24,11 +25,10 @@
#include <asm/mach-imx/regs-bch.h>
#include <asm/mach-imx/regs-gpmi.h>
#include <asm/arch/sys_proto.h>
-#include <asm/mach-imx/dma.h>
+#include "mxs_nand.h"
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
-#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
#if (defined(CONFIG_MX6) || defined(CONFIG_MX7))
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2
#else
@@ -45,35 +45,7 @@
#define MXS_NAND_BCH_TIMEOUT 10000
-struct mxs_nand_info {
- int cur_chip;
-
- uint32_t cmd_queue_len;
- uint32_t data_buf_size;
-
- uint8_t *cmd_buf;
- uint8_t *data_buf;
- uint8_t *oob_buf;
-
- uint8_t marking_block_bad;
- uint8_t raw_oob_mode;
-
- /* Functions with altered behaviour */
- int (*hooked_read_oob)(struct mtd_info *mtd,
- loff_t from, struct mtd_oob_ops *ops);
- int (*hooked_write_oob)(struct mtd_info *mtd,
- loff_t to, struct mtd_oob_ops *ops);
- int (*hooked_block_markbad)(struct mtd_info *mtd,
- loff_t ofs);
-
- /* DMA descriptors */
- struct mxs_dma_desc **desc;
- uint32_t desc_index;
-};
-
struct nand_ecclayout fake_ecc_layout;
-static int chunk_data_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
-static int galois_field = 13;
/*
* Cache management functions
@@ -134,61 +106,21 @@ static void mxs_nand_return_dma_descs(struct mxs_nand_info *info)
info->desc_index = 0;
}
-static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size)
-{
- return page_data_size / chunk_data_size;
-}
-
-static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength)
-{
- return ecc_strength * galois_field;
-}
-
static uint32_t mxs_nand_aux_status_offset(void)
{
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
- uint32_t page_oob_size)
-{
- int ecc_strength;
- int max_ecc_strength_supported;
-
- /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */
- if (is_mx6sx() || is_mx7())
- max_ecc_strength_supported = 62;
- else
- max_ecc_strength_supported = 40;
-
- /*
- * Determine the ECC layout with the formula:
- * ECC bits per chunk = (total page spare data bits) /
- * (bits per ECC level) / (chunks per page)
- * where:
- * total page spare data bits =
- * (page oob size - meta data size) * (bits per byte)
- */
- ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8)
- / (galois_field *
- mxs_nand_ecc_chunk_cnt(page_data_size));
-
- return min(round_down(ecc_strength, 2), max_ecc_strength_supported);
-}
-
-static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
- uint32_t ecc_strength)
+static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo,
+ uint32_t page_data_size)
{
- uint32_t chunk_data_size_in_bits;
- uint32_t chunk_ecc_size_in_bits;
+ uint32_t chunk_data_size_in_bits = geo->ecc_chunk_size * 8;
+ uint32_t chunk_ecc_size_in_bits = geo->ecc_strength * geo->gf_len;
uint32_t chunk_total_size_in_bits;
uint32_t block_mark_chunk_number;
uint32_t block_mark_chunk_bit_offset;
uint32_t block_mark_bit_offset;
- chunk_data_size_in_bits = chunk_data_size * 8;
- chunk_ecc_size_in_bits = mxs_nand_ecc_size_in_bits(ecc_strength);
-
chunk_total_size_in_bits =
chunk_data_size_in_bits + chunk_ecc_size_in_bits;
@@ -213,7 +145,7 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
(block_mark_chunk_number * chunk_total_size_in_bits);
if (block_mark_chunk_bit_offset > chunk_data_size_in_bits)
- return 1;
+ return -EINVAL;
/*
* Now that we know the chunk number in which the block mark appears,
@@ -222,36 +154,100 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,
block_mark_bit_offset -=
block_mark_chunk_number * chunk_ecc_size_in_bits;
- return block_mark_bit_offset;
+ geo->block_mark_byte_offset = block_mark_bit_offset >> 3;
+ geo->block_mark_bit_offset = block_mark_bit_offset & 0x7;
+
+ return 0;
}
-static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd)
+static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo,
+ struct mtd_info *mtd,
+ unsigned int ecc_strength,
+ unsigned int ecc_step)
{
- uint32_t ecc_strength;
- ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
- return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+
+ switch (ecc_step) {
+ case SZ_512:
+ geo->gf_len = 13;
+ break;
+ case SZ_1K:
+ geo->gf_len = 14;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ geo->ecc_chunk_size = ecc_step;
+ geo->ecc_strength = round_up(ecc_strength, 2);
+
+ /* Keep the C >= O */
+ if (geo->ecc_chunk_size < mtd->oobsize)
+ return -EINVAL;
+
+ if (geo->ecc_strength > nand_info->max_ecc_strength_supported)
+ return -EINVAL;
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ return 0;
}
-static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd)
+static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo,
+ struct mtd_info *mtd)
{
- uint32_t ecc_strength;
- ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize);
- return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) & 0x7;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+
+ /* The default for the length of Galois Field. */
+ geo->gf_len = 13;
+
+ /* The default for chunk size. */
+ geo->ecc_chunk_size = 512;
+
+ if (geo->ecc_chunk_size < mtd->oobsize) {
+ geo->gf_len = 14;
+ geo->ecc_chunk_size *= 2;
+ }
+
+ if (mtd->oobsize > geo->ecc_chunk_size) {
+ printf("Not support the NAND chips whose oob size is larger then %d bytes!\n",
+ geo->ecc_chunk_size);
+ return -EINVAL;
+ }
+
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size;
+
+ /*
+ * Determine the ECC layout with the formula:
+ * ECC bits per chunk = (total page spare data bits) /
+ * (bits per ECC level) / (chunks per page)
+ * where:
+ * total page spare data bits =
+ * (page oob size - meta data size) * (bits per byte)
+ */
+ geo->ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8)
+ / (geo->gf_len * geo->ecc_chunk_count);
+
+ geo->ecc_strength = min(round_down(geo->ecc_strength, 2),
+ nand_info->max_ecc_strength_supported);
+
+ return 0;
}
/*
* Wait for BCH complete IRQ and clear the IRQ
*/
-static int mxs_nand_wait_for_bch_complete(void)
+static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info)
{
- struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
int timeout = MXS_NAND_BCH_TIMEOUT;
int ret;
- ret = mxs_wait_mask_set(&bch_regs->hw_bch_ctrl_reg,
+ ret = mxs_wait_mask_set(&nand_info->bch_regs->hw_bch_ctrl_reg,
BCH_CTRL_COMPLETE_IRQ, timeout);
- writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr);
+ writel(BCH_CTRL_COMPLETE_IRQ, &nand_info->bch_regs->hw_bch_ctrl_clr);
return ret;
}
@@ -349,11 +345,9 @@ static int mxs_nand_device_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
- struct mxs_gpmi_regs *gpmi_regs =
- (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
uint32_t tmp;
- tmp = readl(&gpmi_regs->hw_gpmi_stat);
+ tmp = readl(&nand_info->gpmi_regs->hw_gpmi_stat);
tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip);
return tmp & 1;
@@ -377,18 +371,15 @@ static void mxs_nand_select_chip(struct mtd_info *mtd, int chip)
* swapping the block mark, or swapping it *back* -- but it doesn't matter
* because the the operation is the same.
*/
-static void mxs_nand_swap_block_mark(struct mtd_info *mtd,
- uint8_t *data_buf, uint8_t *oob_buf)
+static void mxs_nand_swap_block_mark(struct bch_geometry *geo,
+ uint8_t *data_buf, uint8_t *oob_buf)
{
- uint32_t bit_offset;
- uint32_t buf_offset;
+ uint32_t bit_offset = geo->block_mark_bit_offset;
+ uint32_t buf_offset = geo->block_mark_byte_offset;
uint32_t src;
uint32_t dst;
- bit_offset = mxs_nand_mark_bit_offset(mtd);
- buf_offset = mxs_nand_mark_byte_offset(mtd);
-
/*
* Get the byte from the data area that overlays the block mark. Since
* the ECC engine applies its own view to the bits in the page, the
@@ -564,6 +555,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
int page)
{
struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct bch_geometry *geo = &nand_info->bch_geometry;
struct mxs_dma_desc *d;
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
uint32_t corrected = 0, failed = 0;
@@ -652,7 +644,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
goto rtn;
}
- ret = mxs_nand_wait_for_bch_complete();
+ ret = mxs_nand_wait_for_bch_complete(nand_info);
if (ret) {
printf("MXS NAND: BCH read timeout\n");
goto rtn;
@@ -662,11 +654,11 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
mxs_nand_inval_data_buf(nand_info);
/* Read DMA completed, now do the mark swapping. */
- mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf);
+ mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf);
/* Loop over status bytes, accumulating ECC status. */
status = nand_info->oob_buf + mxs_nand_aux_status_offset();
- for (i = 0; i < mxs_nand_ecc_chunk_cnt(mtd->writesize); i++) {
+ for (i = 0; i < geo->ecc_chunk_count; i++) {
if (status[i] == 0x00)
continue;
@@ -714,6 +706,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
int oob_required, int page)
{
struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct bch_geometry *geo = &nand_info->bch_geometry;
struct mxs_dma_desc *d;
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
int ret;
@@ -722,7 +715,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize);
/* Handle block mark swapping. */
- mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf);
+ mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf);
/* Compile the DMA descriptor - write data. */
d = mxs_nand_get_dma_desc(nand_info);
@@ -759,7 +752,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
goto rtn;
}
- ret = mxs_nand_wait_for_bch_complete();
+ ret = mxs_nand_wait_for_bch_complete(nand_info);
if (ret) {
printf("MXS NAND: BCH write timeout\n");
goto rtn;
@@ -970,57 +963,69 @@ static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
return 0;
}
+static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+ if (chip->ecc.strength > 0 && chip->ecc.size > 0)
+ return mxs_nand_calc_ecc_layout_by_info(geo, mtd,
+ chip->ecc.strength, chip->ecc.size);
+
+ if (nand_info->use_minimum_ecc ||
+ mxs_nand_calc_ecc_layout(geo, mtd)) {
+ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+ return -EINVAL;
+
+ return mxs_nand_calc_ecc_layout_by_info(geo, mtd,
+ chip->ecc_strength_ds, chip->ecc_step_ds);
+ }
+
+ return 0;
+}
+
/*
- * Nominally, the purpose of this function is to look for or create the bad
- * block table. In fact, since the we call this function at the very end of
- * the initialization process started by nand_scan(), and we doesn't have a
- * more formal mechanism, we "hook" this function to continue init process.
- *
* At this point, the physical NAND Flash chips have been identified and
* counted, so we know the physical geometry. This enables us to make some
* important configuration decisions.
*
* The return value of this function propagates directly back to this driver's
- * call to nand_scan(). Anything other than zero will cause this driver to
+ * board_nand_init(). Anything other than zero will cause this driver to
* tear everything down and declare failure.
*/
-static int mxs_nand_scan_bbt(struct mtd_info *mtd)
+int mxs_nand_setup_ecc(struct mtd_info *mtd)
{
struct nand_chip *nand = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
- struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+ struct bch_geometry *geo = &nand_info->bch_geometry;
+ struct mxs_bch_regs *bch_regs = nand_info->bch_regs;
uint32_t tmp;
+ int ret;
- if (mtd->oobsize > MXS_NAND_CHUNK_DATA_CHUNK_SIZE) {
- galois_field = 14;
- chunk_data_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 2;
- }
+ ret = mxs_nand_set_geometry(mtd, geo);
+ if (ret)
+ return ret;
- if (mtd->oobsize > chunk_data_size) {
- printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", chunk_data_size);
- return -EINVAL;
- }
+ mxs_nand_calc_mark_offset(geo, mtd->writesize);
/* Configure BCH and set NFC geometry */
mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);
/* Configure layout 0 */
- tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
- << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
+ tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
- tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
- << BCH_FLASHLAYOUT0_ECC0_OFFSET;
- tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
- tmp |= (14 == galois_field ? 1 : 0) <<
+ tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET;
+ tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
+ tmp |= (geo->gf_len == 14 ? 1 : 0) <<
BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
writel(tmp, &bch_regs->hw_bch_flash0layout0);
tmp = (mtd->writesize + mtd->oobsize)
<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
- tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
- << BCH_FLASHLAYOUT1_ECCN_OFFSET;
- tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
- tmp |= (14 == galois_field ? 1 : 0) <<
+ tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET;
+ tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
+ tmp |= (geo->gf_len == 14 ? 1 : 0) <<
BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
writel(tmp, &bch_regs->hw_bch_flash0layout1);
@@ -1046,8 +1051,7 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
mtd->_block_markbad = mxs_nand_hook_block_markbad;
}
- /* We use the reference implementation for bad block management. */
- return nand_default_bbt(mtd);
+ return 0;
}
/*
@@ -1088,12 +1092,8 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info)
/*
* Initializes the NFC hardware.
*/
-int mxs_nand_init(struct mxs_nand_info *info)
+int mxs_nand_init_dma(struct mxs_nand_info *info)
{
- struct mxs_gpmi_regs *gpmi_regs =
- (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
- struct mxs_bch_regs *bch_regs =
- (struct mxs_bch_regs *)MXS_BCH_BASE;
int i = 0, j, ret = 0;
info->desc = malloc(sizeof(struct mxs_dma_desc *) *
@@ -1122,14 +1122,14 @@ int mxs_nand_init(struct mxs_nand_info *info)
}
/* Reset the GPMI block. */
- mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg);
- mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);
+ mxs_reset_block(&info->gpmi_regs->hw_gpmi_ctrl0_reg);
+ mxs_reset_block(&info->bch_regs->hw_bch_ctrl_reg);
/*
* Choose NAND mode, set IRQ polarity, disable write protection and
* select BCH ECC.
*/
- clrsetbits_le32(&gpmi_regs->hw_gpmi_ctrl1,
+ clrsetbits_le32(&info->gpmi_regs->hw_gpmi_ctrl1,
GPMI_CTRL1_GPMI_MODE,
GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET |
GPMI_CTRL1_BCH_MODE);
@@ -1149,16 +1149,7 @@ err1:
return ret;
}
-/*!
- * This function is called during the driver binding process.
- *
- * @param pdev the device structure used to store device specific
- * information that is used by the suspend, resume and
- * remove functions
- *
- * @return The function always returns 0.
- */
-int board_nand_init(struct nand_chip *nand)
+int mxs_nand_init_spl(struct nand_chip *nand)
{
struct mxs_nand_info *nand_info;
int err;
@@ -1170,31 +1161,83 @@ int board_nand_init(struct nand_chip *nand)
}
memset(nand_info, 0, sizeof(struct mxs_nand_info));
+ nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
+ nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
err = mxs_nand_alloc_buffers(nand_info);
if (err)
- goto err1;
+ return err;
+
+ err = mxs_nand_init_dma(nand_info);
+ if (err)
+ return err;
+
+ nand_set_controller_data(nand, nand_info);
+
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->cmd_ctrl = mxs_nand_cmd_ctrl;
+ nand->dev_ready = mxs_nand_device_ready;
+ nand->select_chip = mxs_nand_select_chip;
+
+ nand->read_byte = mxs_nand_read_byte;
+ nand->read_buf = mxs_nand_read_buf;
+
+ nand->ecc.read_page = mxs_nand_ecc_read_page;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.bytes = 9;
+ nand->ecc.size = 512;
+ nand->ecc.strength = 8;
+
+ return 0;
+}
+
+int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ int err;
+
+ nand = &nand_info->chip;
+ mtd = nand_to_mtd(nand);
+ err = mxs_nand_alloc_buffers(nand_info);
+ if (err)
+ return err;
- err = mxs_nand_init(nand_info);
+ err = mxs_nand_init_dma(nand_info);
if (err)
- goto err2;
+ goto err_free_buffers;
memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout));
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+#endif
+
nand_set_controller_data(nand, nand_info);
nand->options |= NAND_NO_SUBPAGE_WRITE;
+ if (nand_info->dev)
+ nand->flash_node = dev_of_offset(nand_info->dev);
+
nand->cmd_ctrl = mxs_nand_cmd_ctrl;
nand->dev_ready = mxs_nand_device_ready;
nand->select_chip = mxs_nand_select_chip;
nand->block_bad = mxs_nand_block_bad;
- nand->scan_bbt = mxs_nand_scan_bbt;
nand->read_byte = mxs_nand_read_byte;
nand->read_buf = mxs_nand_read_buf;
nand->write_buf = mxs_nand_write_buf;
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL))
+ goto err_free_buffers;
+
+ if (mxs_nand_setup_ecc(mtd))
+ goto err_free_buffers;
+
nand->ecc.read_page = mxs_nand_ecc_read_page;
nand->ecc.write_page = mxs_nand_ecc_write_page;
nand->ecc.read_oob = mxs_nand_ecc_read_oob;
@@ -1202,16 +1245,58 @@ int board_nand_init(struct nand_chip *nand)
nand->ecc.layout = &fake_ecc_layout;
nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.bytes = 9;
- nand->ecc.size = 512;
- nand->ecc.strength = 8;
+ nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size;
+ nand->ecc.strength = nand_info->bch_geometry.ecc_strength;
+
+ /* second phase scan */
+ err = nand_scan_tail(mtd);
+ if (err)
+ goto err_free_buffers;
+
+ err = nand_register(0, mtd);
+ if (err)
+ goto err_free_buffers;
return 0;
-err2:
+err_free_buffers:
free(nand_info->data_buf);
free(nand_info->cmd_buf);
-err1:
- free(nand_info);
+
return err;
}
+
+#ifndef CONFIG_NAND_MXS_DT
+void board_nand_init(void)
+{
+ struct mxs_nand_info *nand_info;
+
+ nand_info = malloc(sizeof(struct mxs_nand_info));
+ if (!nand_info) {
+ printf("MXS NAND: Failed to allocate private data\n");
+ return;
+ }
+ memset(nand_info, 0, sizeof(struct mxs_nand_info));
+
+ nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
+ nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+
+ /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */
+ if (is_mx6sx() || is_mx7())
+ nand_info->max_ecc_strength_supported = 62;
+ else
+ nand_info->max_ecc_strength_supported = 40;
+
+#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC
+ nand_info->use_minimum_ecc = true;
+#endif
+
+ if (mxs_nand_init_ctrl(nand_info) < 0)
+ goto err;
+
+ return;
+
+err:
+ free(nand_info);
+}
+#endif
diff --git a/drivers/mtd/nand/mxs_nand.h b/drivers/mtd/nand/mxs_nand.h
new file mode 100644
index 0000000000..4bd65cded9
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NXP GPMI NAND flash driver
+ *
+ * Copyright (C) 2018 Toradex
+ * Authors:
+ * Stefan Agner <stefan.agner@toradex.com>
+ */
+
+#include <linux/mtd/mtd.h>
+#include <asm/cache.h>
+#include <nand.h>
+#include <asm/mach-imx/dma.h>
+
+/**
+ * @gf_len: The length of Galois Field. (e.g., 13 or 14)
+ * @ecc_strength: A number that describes the strength of the ECC
+ * algorithm.
+ * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note
+ * the first chunk in the page includes both data and
+ * metadata, so it's a bit larger than this value.
+ * @ecc_chunk_count: The number of ECC chunks in the page,
+ * @block_mark_byte_offset: The byte offset in the ECC-based page view at
+ * which the underlying physical block mark appears.
+ * @block_mark_bit_offset: The bit offset into the ECC-based page view at
+ * which the underlying physical block mark appears.
+ */
+struct bch_geometry {
+ unsigned int gf_len;
+ unsigned int ecc_strength;
+ unsigned int ecc_chunk_size;
+ unsigned int ecc_chunk_count;
+ unsigned int block_mark_byte_offset;
+ unsigned int block_mark_bit_offset;
+};
+
+struct mxs_nand_info {
+ struct nand_chip chip;
+ struct udevice *dev;
+ unsigned int max_ecc_strength_supported;
+ bool use_minimum_ecc;
+ int cur_chip;
+
+ uint32_t cmd_queue_len;
+ uint32_t data_buf_size;
+ struct bch_geometry bch_geometry;
+
+ uint8_t *cmd_buf;
+ uint8_t *data_buf;
+ uint8_t *oob_buf;
+
+ uint8_t marking_block_bad;
+ uint8_t raw_oob_mode;
+
+ struct mxs_gpmi_regs *gpmi_regs;
+ struct mxs_bch_regs *bch_regs;
+
+ /* Functions with altered behaviour */
+ int (*hooked_read_oob)(struct mtd_info *mtd,
+ loff_t from, struct mtd_oob_ops *ops);
+ int (*hooked_write_oob)(struct mtd_info *mtd,
+ loff_t to, struct mtd_oob_ops *ops);
+ int (*hooked_block_markbad)(struct mtd_info *mtd,
+ loff_t ofs);
+
+ /* DMA descriptors */
+ struct mxs_dma_desc **desc;
+ uint32_t desc_index;
+};
+
+int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info);
+int mxs_nand_init_spl(struct nand_chip *nand);
+int mxs_nand_setup_ecc(struct mtd_info *mtd);
diff --git a/drivers/mtd/nand/mxs_nand_dt.c b/drivers/mtd/nand/mxs_nand_dt.c
new file mode 100644
index 0000000000..f89eb091a9
--- /dev/null
+++ b/drivers/mtd/nand/mxs_nand_dt.c
@@ -0,0 +1,86 @@
+/*
+ * NXP GPMI NAND flash driver (DT initialization)
+ *
+ * Copyright (C) 2018 Toradex
+ * Authors:
+ * Stefan Agner <stefan.agner@toradex.com>
+ *
+ * Based on denali_dt.c
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/printk.h>
+
+#include "mxs_nand.h"
+
+struct mxs_nand_dt_data {
+ unsigned int max_ecc_strength_supported;
+};
+
+static const struct mxs_nand_dt_data mxs_nand_imx7d_data = {
+ .max_ecc_strength_supported = 62,
+};
+
+static const struct udevice_id mxs_nand_dt_ids[] = {
+ {
+ .compatible = "fsl,imx7d-gpmi-nand",
+ .data = (unsigned long)&mxs_nand_imx7d_data,
+ },
+ { /* sentinel */ }
+};
+
+static int mxs_nand_dt_probe(struct udevice *dev)
+{
+ struct mxs_nand_info *info = dev_get_priv(dev);
+ const struct mxs_nand_dt_data *data;
+ struct resource res;
+ int ret;
+
+ data = (void *)dev_get_driver_data(dev);
+ if (data)
+ info->max_ecc_strength_supported = data->max_ecc_strength_supported;
+
+ info->dev = dev;
+
+ ret = dev_read_resource_byname(dev, "gpmi-nand", &res);
+ if (ret)
+ return ret;
+
+ info->gpmi_regs = devm_ioremap(dev, res.start, resource_size(&res));
+
+
+ ret = dev_read_resource_byname(dev, "bch", &res);
+ if (ret)
+ return ret;
+
+ info->bch_regs = devm_ioremap(dev, res.start, resource_size(&res));
+
+ info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc");
+
+ return mxs_nand_init_ctrl(info);
+}
+
+U_BOOT_DRIVER(mxs_nand_dt) = {
+ .name = "mxs-nand-dt",
+ .id = UCLASS_MTD,
+ .of_match = mxs_nand_dt_ids,
+ .probe = mxs_nand_dt_probe,
+ .priv_auto_alloc_size = sizeof(struct mxs_nand_info),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_GET_DRIVER(mxs_nand_dt),
+ &dev);
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize MXS NAND controller. (error %d)\n",
+ ret);
+}
diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c
index 3e8b35f04a..2d7bbe83cc 100644
--- a/drivers/mtd/nand/mxs_nand_spl.c
+++ b/drivers/mtd/nand/mxs_nand_spl.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <nand.h>
#include <malloc.h>
+#include "mxs_nand.h"
static struct mtd_info *mtd;
static struct nand_chip nand_chip;
@@ -48,7 +49,29 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
}
}
-static int mxs_flash_ident(struct mtd_info *mtd)
+#if defined (CONFIG_SPL_NAND_IDENT)
+
+/* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */
+static int mxs_flash_full_ident(struct mtd_info *mtd)
+{
+ int nand_maf_id, nand_dev_id;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_flash_dev *type;
+
+ type = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, NULL);
+
+ if (IS_ERR(type)) {
+ chip->select_chip(mtd, -1);
+ return PTR_ERR(type);
+ }
+
+ return 0;
+}
+
+#else
+
+/* Trying to detect the NAND flash using ONFi only */
+static int mxs_flash_onfi_ident(struct mtd_info *mtd)
{
register struct nand_chip *chip = mtd_to_nand(mtd);
int i;
@@ -108,6 +131,19 @@ static int mxs_flash_ident(struct mtd_info *mtd)
return 0;
}
+#endif /* CONFIG_SPL_NAND_IDENT */
+
+static int mxs_flash_ident(struct mtd_info *mtd)
+{
+ int ret;
+#if defined (CONFIG_SPL_NAND_IDENT)
+ ret = mxs_flash_full_ident(mtd);
+#else
+ ret = mxs_flash_onfi_ident(mtd);
+#endif
+ return ret;
+}
+
static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page)
{
register struct nand_chip *chip = mtd_to_nand(mtd);
@@ -145,7 +181,7 @@ static int mxs_nand_init(void)
return 0;
/* init mxs nand driver */
- board_nand_init(&nand_chip);
+ mxs_nand_init_spl(&nand_chip);
mtd = nand_to_mtd(&nand_chip);
/* set mtd functions */
nand_chip.cmdfunc = mxs_nand_command;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index eb9f121f81..64e4621aaa 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3755,7 +3755,7 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
/*
* Get the flash and manufacturer id and lookup if the type is supported.
*/
-static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
+struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int *maf_id, int *dev_id,
struct nand_flash_dev *type)
@@ -3927,6 +3927,7 @@ ident_done:
mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
return type;
}
+EXPORT_SYMBOL(nand_get_flash_type);
#if CONFIG_IS_ENABLED(OF_CONTROL)
DECLARE_GLOBAL_DATA_PTR;
diff --git a/drivers/power/pmic/pfuze100.c b/drivers/power/pmic/pfuze100.c
index 4670a84560..8a5a8996b4 100644
--- a/drivers/power/pmic/pfuze100.c
+++ b/drivers/power/pmic/pfuze100.c
@@ -12,6 +12,7 @@
#include <power/pmic.h>
#include <power/regulator.h>
#include <power/pfuze100_pmic.h>
+#include <power/pfuze3000_pmic.h>
static const struct pmic_child_info pmic_children_info[] = {
/* sw[x], swbst */
@@ -23,7 +24,7 @@ static const struct pmic_child_info pmic_children_info[] = {
static int pfuze100_reg_count(struct udevice *dev)
{
- return PFUZE100_NUM_OF_REGS;
+ return dev->driver_data == PFUZE3000 ? PFUZE3000_NUM_OF_REGS : PFUZE100_NUM_OF_REGS;
}
static int pfuze100_write(struct udevice *dev, uint reg, const uint8_t *buff,
diff --git a/drivers/power/pmic/pmic_pfuze3000.c b/drivers/power/pmic/pmic_pfuze3000.c
index f2a51fe4a4..1077fa5e9d 100644
--- a/drivers/power/pmic/pmic_pfuze3000.c
+++ b/drivers/power/pmic/pmic_pfuze3000.c
@@ -22,7 +22,7 @@ int power_pfuze3000_init(unsigned char bus)
p->name = name;
p->interface = PMIC_I2C;
- p->number_of_regs = PMIC_NUM_OF_REGS;
+ p->number_of_regs = PFUZE3000_NUM_OF_REGS;
p->hw.i2c.addr = CONFIG_POWER_PFUZE3000_I2C_ADDR;
p->hw.i2c.tx_num = 1;
p->bus = bus;