summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx')
-rw-r--r--arch/arm/mach-imx/Kconfig11
-rw-r--r--arch/arm/mach-imx/Makefile1
-rw-r--r--arch/arm/mach-imx/cmd_nandbcb.c369
-rw-r--r--arch/arm/mach-imx/cpu.c6
-rw-r--r--arch/arm/mach-imx/imx8/Makefile2
-rw-r--r--arch/arm/mach-imx/imx8/misc.c39
-rw-r--r--arch/arm/mach-imx/mx6/soc.c5
-rw-r--r--arch/arm/mach-imx/spl.c8
8 files changed, 435 insertions, 6 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index b6fd1595f0..aeb5493488 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -71,6 +71,17 @@ config CMD_HDMIDETECT
This enables the 'hdmidet' command which detects if an HDMI monitor
is connected.
+config CMD_NANDBCB
+ bool "i.MX6 NAND Boot Control Block(BCB) command"
+ depends on NAND && CMD_MTDPARTS
+ default y if ARCH_MX6 && NAND_MXS
+ help
+ Unlike normal 'nand write/erase' commands, this command update
+ Boot Control Block(BCB) for i.MX6 platform NAND IP's.
+
+ This is similar to kobs-ng, which is used in Linux as separate
+ rootfs package.
+
config NXP_BOARD_REVISION
bool "Read NXP board revision from fuses"
depends on ARCH_MX6 || ARCH_MX7
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 898478fc4a..08ee52edbf 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -59,6 +59,7 @@ ifneq ($(CONFIG_SPL_BUILD),y)
obj-$(CONFIG_CMD_BMODE) += cmd_bmode.o
obj-$(CONFIG_CMD_HDMIDETECT) += cmd_hdmidet.o
obj-$(CONFIG_CMD_DEKBLOB) += cmd_dek.o
+obj-$(CONFIG_CMD_NANDBCB) += cmd_nandbcb.o
endif
PLUGIN = board/$(BOARDDIR)/plugin
diff --git a/arch/arm/mach-imx/cmd_nandbcb.c b/arch/arm/mach-imx/cmd_nandbcb.c
new file mode 100644
index 0000000000..065b814b2e
--- /dev/null
+++ b/arch/arm/mach-imx/cmd_nandbcb.c
@@ -0,0 +1,369 @@
+/*
+ * i.MX6 nand boot control block(bcb).
+ *
+ * Based on the common/imx-bbu-nand-fcb.c from barebox and imx kobs-ng
+ *
+ * Copyright (C) 2017 Jagan Teki <jagan@amarulasolutions.com>
+ * Copyright (C) 2016 Sergey Kubushyn <ksi@koi8.net>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <nand.h>
+
+#include <asm/io.h>
+#include <jffs2/jffs2.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/mach-imx/imx-nandbcb.h>
+#include <asm/mach-imx/imximage.cfg>
+#include <mxs_nand.h>
+#include <linux/mtd/mtd.h>
+#include <nand.h>
+
+#define BF_VAL(v, bf) (((v) & bf##_MASK) >> bf##_OFFSET)
+#define GETBIT(v, n) (((v) >> (n)) & 0x1)
+
+static u8 calculate_parity_13_8(u8 d)
+{
+ u8 p = 0;
+
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 3) ^ GETBIT(d, 2)) << 0;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 5) ^ GETBIT(d, 4) ^ GETBIT(d, 2) ^
+ GETBIT(d, 1)) << 1;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 6) ^ GETBIT(d, 5) ^ GETBIT(d, 1) ^
+ GETBIT(d, 0)) << 2;
+ p |= (GETBIT(d, 7) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 0)) << 3;
+ p |= (GETBIT(d, 6) ^ GETBIT(d, 4) ^ GETBIT(d, 3) ^ GETBIT(d, 2) ^
+ GETBIT(d, 1) ^ GETBIT(d, 0)) << 4;
+
+ return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+ int i;
+ u8 *src = _src;
+ u8 *ecc = _ecc;
+
+ for (i = 0; i < size; i++)
+ ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static u32 calc_chksum(void *buf, size_t size)
+{
+ u32 chksum = 0;
+ u8 *bp = buf;
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ chksum += bp[i];
+
+ return ~chksum;
+}
+
+static void fill_fcb(struct fcb_block *fcb, struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+
+ fcb->fingerprint = FCB_FINGERPRINT;
+ fcb->version = FCB_VERSION_1;
+ fcb->pagesize = mtd->writesize;
+ fcb->oob_pagesize = mtd->writesize + mtd->oobsize;
+ fcb->sectors = mtd->erasesize / mtd->writesize;
+
+ /* Divide ECC strength by two and save the value into FCB structure. */
+ fcb->ecc_level = nand_info->bch_geometry.ecc_strength >> 1;
+
+ fcb->ecc_type = fcb->ecc_level;
+
+ /* Also hardcoded in kobs-ng */
+ fcb->ecc_nr = 0x00000200;
+ fcb->ecc_size = 0x00000200;
+ fcb->datasetup = 80;
+ fcb->datahold = 60;
+ fcb->addr_setup = 25;
+ fcb->dsample_time = 6;
+ fcb->meta_size = 10;
+
+ /* DBBT search area starts at second page on first block */
+ fcb->dbbt_start = 1;
+
+ fcb->bb_byte = nand_info->bch_geometry.block_mark_byte_offset;
+ fcb->bb_start_bit = nand_info->bch_geometry.block_mark_bit_offset;
+
+ fcb->phy_offset = mtd->writesize;
+
+ fcb->nr_blocks = mtd->writesize / fcb->ecc_nr - 1;
+
+ fcb->checksum = calc_chksum((void *)fcb + 4, sizeof(*fcb) - 4);
+}
+
+static int dbbt_fill_data(struct mtd_info *mtd, void *buf, int num_blocks)
+{
+ int n, n_bad_blocks = 0;
+ u32 *bb = buf + 0x8;
+ u32 *n_bad_blocksp = buf + 0x4;
+
+ for (n = 0; n < num_blocks; n++) {
+ loff_t offset = n * mtd->erasesize;
+ if (mtd_block_isbad(mtd, offset)) {
+ n_bad_blocks++;
+ *bb = n;
+ bb++;
+ }
+ }
+
+ *n_bad_blocksp = n_bad_blocks;
+
+ return n_bad_blocks;
+}
+
+static int nandbcb_update(struct mtd_info *mtd, loff_t off, size_t size,
+ size_t maxsize, const u_char *buf)
+{
+ nand_erase_options_t opts;
+ struct fcb_block *fcb;
+ struct dbbt_block *dbbt;
+ loff_t fw1_off;
+ void *fwbuf, *fcb_raw_page, *dbbt_page, *dbbt_data_page;
+ int nr_blks, nr_blks_fcb, fw1_blk;
+ size_t fwsize, dummy;
+ int i, ret;
+
+ /* erase */
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = off;
+ opts.length = maxsize - 1;
+ ret = nand_erase_opts(mtd, &opts);
+ if (ret) {
+ printf("%s: erase failed (ret = %d)\n", __func__, ret);
+ return ret;
+ }
+
+ /*
+ * Reference documentation from i.MX6DQRM section 8.5.2.2
+ *
+ * Nand Boot Control Block(BCB) contains two data structures,
+ * - Firmware Configuration Block(FCB)
+ * - Discovered Bad Block Table(DBBT)
+ *
+ * FCB contains,
+ * - nand timings
+ * - DBBT search page address,
+ * - start page address of primary firmware
+ * - start page address of secondary firmware
+ *
+ * setup fcb:
+ * - number of blocks = mtd partition size / mtd erasesize
+ * - two firmware blocks, primary and secondary
+ * - first 4 block for FCB/DBBT
+ * - rest split in half for primary and secondary firmware
+ * - same firmware will write two times
+ */
+ nr_blks_fcb = 2;
+ nr_blks = maxsize / mtd->erasesize;
+ fw1_blk = nr_blks_fcb;
+
+ /* write fw */
+ fwsize = ALIGN(size + FLASH_OFFSET_STANDARD + mtd->writesize,
+ mtd->writesize);
+ fwbuf = kzalloc(fwsize, GFP_KERNEL);
+ if (!fwbuf) {
+ debug("failed to allocate fwbuf\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ memcpy(fwbuf + FLASH_OFFSET_STANDARD, buf, size);
+ fw1_off = fw1_blk * mtd->erasesize;
+ ret = nand_write_skip_bad(mtd, fw1_off, &fwsize, NULL, maxsize,
+ (u_char *)fwbuf, WITH_WR_VERIFY);
+ printf("NAND fw write: 0x%llx offset, 0x%x bytes written: %s\n",
+ fw1_off, fwsize, ret ? "ERROR" : "OK");
+ if (ret)
+ goto fwbuf_err;
+
+ /* fill fcb */
+ fcb = kzalloc(sizeof(*fcb), GFP_KERNEL);
+ if (!fcb) {
+ debug("failed to allocate fcb\n");
+ ret = -ENOMEM;
+ goto fwbuf_err;
+ }
+
+ fcb->fw1_start = (fw1_blk * mtd->erasesize) / mtd->writesize;
+ fcb->fw1_pages = size / mtd->writesize + 1;
+ fill_fcb(fcb, mtd);
+
+ /* fill dbbt */
+ dbbt_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!dbbt_page) {
+ debug("failed to allocate dbbt_page\n");
+ ret = -ENOMEM;
+ goto fcb_err;
+ }
+
+ dbbt_data_page = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!dbbt_data_page) {
+ debug("failed to allocate dbbt_data_page\n");
+ ret = -ENOMEM;
+ goto dbbt_page_err;
+ }
+
+ dbbt = dbbt_page;
+ dbbt->checksum = 0;
+ dbbt->fingerprint = DBBT_FINGERPRINT2;
+ dbbt->version = DBBT_VERSION_1;
+ ret = dbbt_fill_data(mtd, dbbt_data_page, nr_blks);
+ if (ret < 0)
+ goto dbbt_data_page_err;
+ else if (ret > 0)
+ dbbt->dbbtpages = 1;
+
+ /* write fcb/dbbt */
+ fcb_raw_page = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!fcb_raw_page) {
+ debug("failed to allocate fcb_raw_page\n");
+ ret = -ENOMEM;
+ goto dbbt_data_page_err;
+ }
+
+ memcpy(fcb_raw_page + 12, fcb, sizeof(struct fcb_block));
+ encode_hamming_13_8(fcb_raw_page + 12, fcb_raw_page + 12 + 512, 512);
+ /*
+ * Set the first and second byte of OOB data to 0xFF, not 0x00. These
+ * bytes are used as the Manufacturers Bad Block Marker (MBBM). Since
+ * the FCB is mostly written to the first page in a block, a scan for
+ * factory bad blocks will detect these blocks as bad, e.g. when
+ * function nand_scan_bbt() is executed to build a new bad block table.
+ */
+ memset(fcb_raw_page + mtd->writesize, 0xFF, 2);
+
+ for (i = 0; i < nr_blks_fcb; i++) {
+ if (mtd_block_isbad(mtd, off)) {
+ printf("Block %d is bad, skipped\n", i);
+ continue;
+ }
+
+ /* raw write */
+ mtd_oob_ops_t ops = {
+ .datbuf = (u8 *)fcb_raw_page,
+ .oobbuf = ((u8 *)fcb_raw_page) + mtd->writesize,
+ .len = mtd->writesize,
+ .ooblen = mtd->oobsize,
+ .mode = MTD_OPS_RAW
+ };
+
+ ret = mtd_write_oob(mtd, mtd->erasesize * i, &ops);
+ if (ret)
+ goto fcb_raw_page_err;
+ debug("NAND fcb write: 0x%x offset, 0x%x bytes written: %s\n",
+ mtd->erasesize * i, ops.len, ret ? "ERROR" : "OK");
+
+ ret = mtd_write(mtd, mtd->erasesize * i + mtd->writesize,
+ mtd->writesize, &dummy, dbbt_page);
+ if (ret)
+ goto fcb_raw_page_err;
+ debug("NAND dbbt write: 0x%x offset, 0x%x bytes written: %s\n",
+ mtd->erasesize * i + mtd->writesize, dummy,
+ ret ? "ERROR" : "OK");
+
+ /* dbbtpages == 0 if no bad blocks */
+ if (dbbt->dbbtpages > 0) {
+ loff_t to = (mtd->erasesize * i + mtd->writesize * 5);
+
+ ret = mtd_write(mtd, to, mtd->writesize, &dummy,
+ dbbt_data_page);
+ if (ret)
+ goto fcb_raw_page_err;
+ }
+ }
+
+fcb_raw_page_err:
+ kfree(fcb_raw_page);
+dbbt_data_page_err:
+ kfree(dbbt_data_page);
+dbbt_page_err:
+ kfree(dbbt_page);
+fcb_err:
+ kfree(fcb);
+fwbuf_err:
+ kfree(fwbuf);
+err:
+ return ret;
+}
+
+static int do_nandbcb_update(int argc, char * const argv[])
+{
+ struct mtd_info *mtd;
+ loff_t addr, offset, size, maxsize;
+ char *endp;
+ u_char *buf;
+ int dev;
+ int ret;
+
+ if (argc != 4)
+ return CMD_RET_USAGE;
+
+ dev = nand_curr_device;
+ if (dev < 0) {
+ printf("failed to get nand_curr_device, run nand device");
+ return CMD_RET_FAILURE;
+ }
+
+ addr = simple_strtoul(argv[1], &endp, 16);
+ if (*argv[1] == 0 || *endp != 0)
+ return CMD_RET_FAILURE;
+
+ mtd = get_nand_dev_by_index(dev);
+ if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &offset, &size,
+ &maxsize, MTD_DEV_TYPE_NAND, mtd->size))
+ return CMD_RET_FAILURE;
+
+ buf = map_physmem(addr, size, MAP_WRBACK);
+ if (!buf) {
+ puts("failed to map physical memory\n");
+ return CMD_RET_FAILURE;
+ }
+
+ ret = nandbcb_update(mtd, offset, size, maxsize, buf);
+
+ return ret == 0 ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+
+static int do_nandbcb(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ const char *cmd;
+ int ret = 0;
+
+ if (argc < 5)
+ goto usage;
+
+ cmd = argv[1];
+ --argc;
+ ++argv;
+
+ if (strcmp(cmd, "update") == 0) {
+ ret = do_nandbcb_update(argc, argv);
+ goto done;
+ }
+
+done:
+ if (ret != -1)
+ return ret;
+usage:
+ return CMD_RET_USAGE;
+}
+
+static char nandbcb_help_text[] =
+ "update addr off|partition len - update 'len' bytes starting at\n"
+ " 'off|part' to memory address 'addr', skipping bad blocks";
+
+U_BOOT_CMD(nandbcb, 5, 1, do_nandbcb,
+ "i.MX6 Nand BCB",
+ nandbcb_help_text
+);
diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c
index 3a8cf30c06..6e9a175210 100644
--- a/arch/arm/mach-imx/cpu.c
+++ b/arch/arm/mach-imx/cpu.c
@@ -289,10 +289,12 @@ void arch_preboot_os(void)
imx_pcie_remove();
#endif
#if defined(CONFIG_SATA)
- sata_remove(0);
+ if (!is_mx6sdl()) {
+ sata_remove(0);
#if defined(CONFIG_MX6)
- disable_sata_clock();
+ disable_sata_clock();
#endif
+ }
#endif
#if defined(CONFIG_VIDEO_IPUV3)
/* disable video before launching O/S */
diff --git a/arch/arm/mach-imx/imx8/Makefile b/arch/arm/mach-imx/imx8/Makefile
index 31ad169ccf..92b5c56acb 100644
--- a/arch/arm/mach-imx/imx8/Makefile
+++ b/arch/arm/mach-imx/imx8/Makefile
@@ -4,4 +4,4 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-y += cpu.o iomux.o
+obj-y += cpu.o iomux.o misc.o
diff --git a/arch/arm/mach-imx/imx8/misc.c b/arch/arm/mach-imx/imx8/misc.c
new file mode 100644
index 0000000000..fe73e29eee
--- /dev/null
+++ b/arch/arm/mach-imx/imx8/misc.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <common.h>
+#include <asm/arch/sci/sci.h>
+
+int sc_pm_setup_uart(sc_rsrc_t uart_rsrc, sc_pm_clock_rate_t clk_rate)
+{
+ sc_pm_clock_rate_t rate = clk_rate;
+ int ret;
+
+ /* Power up UARTn */
+ ret = sc_pm_set_resource_power_mode(-1, uart_rsrc, SC_PM_PW_MODE_ON);
+ if (ret)
+ return ret;
+
+ /* Set UARTn clock root to 'rate' MHz */
+ ret = sc_pm_set_clock_rate(-1, uart_rsrc, SC_PM_CLK_PER, &rate);
+ if (ret)
+ return ret;
+
+ /* Enable UARTn clock root */
+ ret = sc_pm_clock_enable(-1, uart_rsrc, SC_PM_CLK_PER, true, false);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void build_info(void)
+{
+ u32 sc_build = 0, sc_commit = 0;
+
+ /* Get SCFW build and commit id */
+ sc_misc_build_info(-1, &sc_build, &sc_commit);
+ if (!sc_build) {
+ printf("SCFW does not support build info\n");
+ sc_commit = 0; /* Display 0 if build info not supported */
+ }
+ printf("Build: SCFW %x\n", sc_commit);
+}
diff --git a/arch/arm/mach-imx/mx6/soc.c b/arch/arm/mach-imx/mx6/soc.c
index e80f1d484b..4084ab7672 100644
--- a/arch/arm/mach-imx/mx6/soc.c
+++ b/arch/arm/mach-imx/mx6/soc.c
@@ -95,6 +95,11 @@ u32 get_cpu_rev(void)
type = MXC_CPU_MX6DP;
}
reg &= 0xff; /* mx6 silicon revision */
+
+ /* For 6DQ, the value 0x00630005 is Silicon revision 1.3*/
+ if (((type == MXC_CPU_MX6Q) || (type == MXC_CPU_MX6D)) && (reg == 0x5))
+ reg = 0x3;
+
return (type << 12) | (reg + (0x10 * (major + 1)));
}
diff --git a/arch/arm/mach-imx/spl.c b/arch/arm/mach-imx/spl.c
index 9f1e0f6a72..1f230aca33 100644
--- a/arch/arm/mach-imx/spl.c
+++ b/arch/arm/mach-imx/spl.c
@@ -24,6 +24,7 @@ u32 spl_boot_device(void)
{
unsigned int bmode = readl(&src_base->sbmr2);
u32 reg = imx6_src_get_boot_mode();
+ u32 mmc_index = ((reg >> 11) & 0x03);
/*
* Check for BMODE if serial downloader is enabled
@@ -84,11 +85,12 @@ u32 spl_boot_device(void)
/* SD/eSD: 8.5.3, Table 8-15 */
case IMX6_BMODE_SD:
case IMX6_BMODE_ESD:
- return BOOT_DEVICE_MMC1;
- /* MMC/eMMC: 8.5.3 */
case IMX6_BMODE_MMC:
case IMX6_BMODE_EMMC:
- return BOOT_DEVICE_MMC1;
+ if (mmc_index == 1)
+ return BOOT_DEVICE_MMC2;
+ else
+ return BOOT_DEVICE_MMC1;
/* NAND Flash: 8.5.2, Table 8-10 */
case IMX6_BMODE_NAND_MIN ... IMX6_BMODE_NAND_MAX:
return BOOT_DEVICE_NAND;