aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-05-11 09:46:43 -0400
committerTom Rini <trini@konsulko.com>2020-05-11 09:46:43 -0400
commit1bccb23b7bb67e029cc6b22bf7d25243ef71c43c (patch)
tree9fe0b6397d88359177131348a077aad28836285d /drivers
parent951db64186b2b9ad2e3ee30e2093deea005bdd8a (diff)
parentd52a03b130565e6b01dcbe656ebeb611d5ee1aa1 (diff)
downloadu-boot-1bccb23b7bb67e029cc6b22bf7d25243ef71c43c.zip
u-boot-1bccb23b7bb67e029cc6b22bf7d25243ef71c43c.tar.gz
u-boot-1bccb23b7bb67e029cc6b22bf7d25243ef71c43c.tar.bz2
Merge tag 'u-boot-imx-20200511' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
i.MX for 2020.07 ---------------- - i.MX NAND and nandbxb for i.MX8M - imx8MM : new beacon devkit - imx8MQ : new pico-imx8MQ - imx8QXP : extend to enable M4, fixes - add thermal support - caches in SPL (missing board) - Fixes Travis: https://travis-ci.org/github/sbabic/u-boot-imx/builds/685391011
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpu/cpu-uclass.c34
-rw-r--r--drivers/cpu/cpu_sandbox.c9
-rw-r--r--drivers/cpu/imx8_cpu.c85
-rw-r--r--drivers/dma/Kconfig2
-rw-r--r--drivers/dma/apbh_dma.c13
-rw-r--r--drivers/i2c/mxc_i2c.c17
-rw-r--r--drivers/misc/imx8/fuse.c5
-rw-r--r--drivers/misc/imx8/scu.c2
-rw-r--r--drivers/mtd/nand/raw/Kconfig6
-rw-r--r--drivers/mtd/nand/raw/mxs_nand.c330
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_dt.c91
-rw-r--r--drivers/mtd/nand/raw/mxs_nand_spl.c41
-rw-r--r--drivers/net/Kconfig6
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/dwc_eth_qos.c290
-rw-r--r--drivers/net/eth-phy-uclass.c122
-rw-r--r--drivers/net/fec_mxc.c30
-rw-r--r--drivers/net/fec_mxc.h2
-rw-r--r--drivers/net/phy/realtek.c11
-rw-r--r--drivers/pinctrl/nxp/pinctrl-scu.c9
-rw-r--r--drivers/power/domain/imx8-power-domain-legacy.c81
-rw-r--r--drivers/thermal/Kconfig9
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/imx_scu_thermal.c8
-rw-r--r--drivers/thermal/imx_tmu.c467
-rw-r--r--drivers/usb/gadget/Kconfig4
-rw-r--r--drivers/usb/gadget/f_sdp.c4
-rw-r--r--drivers/usb/host/ehci-mx6.c16
28 files changed, 1541 insertions, 155 deletions
diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c
index 457f77b..8352e2e 100644
--- a/drivers/cpu/cpu-uclass.c
+++ b/drivers/cpu/cpu-uclass.c
@@ -10,6 +10,7 @@
#include <errno.h>
#include <dm/lists.h>
#include <dm/root.h>
+#include <linux/err.h>
int cpu_probe_all(void)
{
@@ -34,6 +35,39 @@ int cpu_probe_all(void)
return 0;
}
+int cpu_is_current(struct udevice *cpu)
+{
+ struct cpu_ops *ops = cpu_get_ops(cpu);
+
+ if (ops->is_current) {
+ if (ops->is_current(cpu))
+ return 1;
+ }
+
+ return -ENOSYS;
+}
+
+struct udevice *cpu_get_current_dev(void)
+{
+ struct udevice *cpu;
+ int ret;
+
+ uclass_foreach_dev_probe(UCLASS_CPU, cpu) {
+ if (cpu_is_current(cpu) > 0)
+ return cpu;
+ }
+
+ /* If can't find current cpu device, use the first dev instead */
+ ret = uclass_first_device_err(UCLASS_CPU, &cpu);
+ if (ret) {
+ debug("%s: Could not get CPU device (err = %d)\n",
+ __func__, ret);
+ return NULL;
+ }
+
+ return cpu;
+}
+
int cpu_get_desc(struct udevice *dev, char *buf, int size)
{
struct cpu_ops *ops = cpu_get_ops(dev);
diff --git a/drivers/cpu/cpu_sandbox.c b/drivers/cpu/cpu_sandbox.c
index 05b384f..30a12e5 100644
--- a/drivers/cpu/cpu_sandbox.c
+++ b/drivers/cpu/cpu_sandbox.c
@@ -36,11 +36,20 @@ int cpu_sandbox_get_vendor(struct udevice *dev, char *buf, int size)
return 0;
}
+int cpu_sandbox_is_current(struct udevice *dev)
+{
+ if (!strcmp(dev->name, "cpu-test1"))
+ return 1;
+
+ return 0;
+}
+
static const struct cpu_ops cpu_sandbox_ops = {
.get_desc = cpu_sandbox_get_desc,
.get_info = cpu_sandbox_get_info,
.get_count = cpu_sandbox_get_count,
.get_vendor = cpu_sandbox_get_vendor,
+ .is_current = cpu_sandbox_is_current,
};
int cpu_sandbox_probe(struct udevice *dev)
diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c
index 9565368..95c14c9 100644
--- a/drivers/cpu/imx8_cpu.c
+++ b/drivers/cpu/imx8_cpu.c
@@ -20,6 +20,7 @@ struct cpu_imx_platdata {
const char *type;
u32 cpurev;
u32 freq_mhz;
+ u32 mpidr;
};
const char *get_imx8_type(u32 imxtype)
@@ -42,31 +43,35 @@ const char *get_imx8_rev(u32 rev)
return "A";
case CHIP_REV_B:
return "B";
+ case CHIP_REV_C:
+ return "C";
default:
return "?";
}
}
-const char *get_core_name(void)
+const char *get_core_name(struct udevice *dev)
{
- if (is_cortex_a35())
+ if (!device_is_compatible(dev, "arm,cortex-a35"))
return "A35";
- else if (is_cortex_a53())
+ else if (!device_is_compatible(dev, "arm,cortex-a53"))
return "A53";
- else if (is_cortex_a72())
+ else if (!device_is_compatible(dev, "arm,cortex-a72"))
return "A72";
else
return "?";
}
#if IS_ENABLED(CONFIG_IMX_SCU_THERMAL)
-static int cpu_imx_get_temp(void)
+static int cpu_imx_get_temp(struct cpu_imx_platdata *plat)
{
struct udevice *thermal_dev;
int cpu_tmp, ret;
- ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal0",
- &thermal_dev);
+ if (!strcmp(plat->name, "A72"))
+ ret = uclass_get_device(UCLASS_THERMAL, 1, &thermal_dev);
+ else
+ ret = uclass_get_device(UCLASS_THERMAL, 0, &thermal_dev);
if (!ret) {
ret = thermal_get_temp(thermal_dev, &cpu_tmp);
@@ -79,7 +84,7 @@ static int cpu_imx_get_temp(void)
return cpu_tmp;
}
#else
-static int cpu_imx_get_temp(void)
+static int cpu_imx_get_temp(struct cpu_imx_platdata *plat)
{
return 0;
}
@@ -88,7 +93,7 @@ static int cpu_imx_get_temp(void)
int cpu_imx_get_desc(struct udevice *dev, char *buf, int size)
{
struct cpu_imx_platdata *plat = dev_get_platdata(dev);
- int ret;
+ int ret, temp;
if (size < 100)
return -ENOSPC;
@@ -97,9 +102,13 @@ int cpu_imx_get_desc(struct udevice *dev, char *buf, int size)
plat->type, plat->rev, plat->name, plat->freq_mhz);
if (IS_ENABLED(CONFIG_IMX_SCU_THERMAL)) {
+ temp = cpu_imx_get_temp(plat);
buf = buf + ret;
size = size - ret;
- ret = snprintf(buf, size, " at %dC", cpu_imx_get_temp());
+ if (temp != 0xdeadbeef)
+ ret = snprintf(buf, size, " at %dC", temp);
+ else
+ ret = snprintf(buf, size, " - invalid sensor data");
}
snprintf(buf + ret, size - ret, "\n");
@@ -118,7 +127,24 @@ static int cpu_imx_get_info(struct udevice *dev, struct cpu_info *info)
static int cpu_imx_get_count(struct udevice *dev)
{
- return 4;
+ ofnode node;
+ int num = 0;
+
+ ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
+ const char *device_type;
+
+ if (!ofnode_is_available(node))
+ continue;
+
+ device_type = ofnode_read_string(node, "device_type");
+ if (!device_type)
+ continue;
+
+ if (!strcmp(device_type, "cpu"))
+ num++;
+ }
+
+ return num;
}
static int cpu_imx_get_vendor(struct udevice *dev, char *buf, int size)
@@ -127,25 +153,44 @@ static int cpu_imx_get_vendor(struct udevice *dev, char *buf, int size)
return 0;
}
+static int cpu_imx_is_current(struct udevice *dev)
+{
+ struct cpu_imx_platdata *plat = dev_get_platdata(dev);
+
+ if (plat->mpidr == (read_mpidr() & 0xffff))
+ return 1;
+
+ return 0;
+}
+
static const struct cpu_ops cpu_imx8_ops = {
.get_desc = cpu_imx_get_desc,
.get_info = cpu_imx_get_info,
.get_count = cpu_imx_get_count,
.get_vendor = cpu_imx_get_vendor,
+ .is_current = cpu_imx_is_current,
};
static const struct udevice_id cpu_imx8_ids[] = {
{ .compatible = "arm,cortex-a35" },
{ .compatible = "arm,cortex-a53" },
+ { .compatible = "arm,cortex-a72" },
{ }
};
-static ulong imx8_get_cpu_rate(void)
+static ulong imx8_get_cpu_rate(struct udevice *dev)
{
ulong rate;
- int ret;
- int type = is_cortex_a35() ? SC_R_A35 : is_cortex_a53() ?
- SC_R_A53 : SC_R_A72;
+ int ret, type;
+
+ if (!device_is_compatible(dev, "arm,cortex-a35"))
+ type = SC_R_A35;
+ else if (!device_is_compatible(dev, "arm,cortex-a53"))
+ type = SC_R_A53;
+ else if (!device_is_compatible(dev, "arm,cortex-a72"))
+ type = SC_R_A72;
+ else
+ return 0;
ret = sc_pm_get_clock_rate(-1, type, SC_PM_CLK_CPU,
(sc_pm_clock_rate_t *)&rate);
@@ -164,10 +209,16 @@ static int imx8_cpu_probe(struct udevice *dev)
cpurev = get_cpu_rev();
plat->cpurev = cpurev;
- plat->name = get_core_name();
+ plat->name = get_core_name(dev);
plat->rev = get_imx8_rev(cpurev & 0xFFF);
plat->type = get_imx8_type((cpurev & 0xFF000) >> 12);
- plat->freq_mhz = imx8_get_cpu_rate() / 1000000;
+ plat->freq_mhz = imx8_get_cpu_rate(dev) / 1000000;
+ plat->mpidr = dev_read_addr(dev);
+ if (plat->mpidr == FDT_ADDR_T_NONE) {
+ printf("%s: Failed to get CPU reg property\n", __func__);
+ return -EINVAL;
+ }
+
return 0;
}
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 4f37ba7..1993c1d 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -44,7 +44,7 @@ config TI_EDMA3
config APBH_DMA
bool "Support APBH DMA"
- depends on MX23 || MX28 || MX6 || MX7
+ depends on MX23 || MX28 || MX6 || MX7 || IMX8 || IMX8M
help
Enable APBH DMA driver.
diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c
index 1513312..69eb040 100644
--- a/drivers/dma/apbh_dma.c
+++ b/drivers/dma/apbh_dma.c
@@ -7,6 +7,8 @@
*
* Based on code from LTIB:
* Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2017 NXP
+ *
*/
#include <cpu_func.h>
@@ -88,7 +90,7 @@ void mxs_dma_flush_desc(struct mxs_dma_desc *desc)
uint32_t addr;
uint32_t size;
- addr = (uint32_t)desc;
+ addr = (uintptr_t)desc;
size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
flush_dcache_range(addr, addr + size);
@@ -215,16 +217,17 @@ static int mxs_dma_reset(int channel)
#if defined(CONFIG_MX23)
uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_ctrl0_set);
uint32_t offset = APBH_CTRL0_RESET_CHANNEL_OFFSET;
-#elif (defined(CONFIG_MX28) || defined(CONFIG_MX6) || defined(CONFIG_MX7))
- uint32_t setreg = (uint32_t)(&apbh_regs->hw_apbh_channel_ctrl_set);
- uint32_t offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET;
+#elif defined(CONFIG_MX28) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \
+ defined(CONFIG_IMX8) || defined(CONFIG_IMX8M)
+ u32 setreg = (uintptr_t)(&apbh_regs->hw_apbh_channel_ctrl_set);
+ u32 offset = APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET;
#endif
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
- writel(1 << (channel + offset), setreg);
+ writel(1 << (channel + offset), (uintptr_t)setreg);
return 0;
}
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c
index a03c465..4d6e0e3 100644
--- a/drivers/i2c/mxc_i2c.c
+++ b/drivers/i2c/mxc_i2c.c
@@ -19,6 +19,7 @@
#include <dm/device_compat.h>
#include <linux/errno.h>
#include <asm/mach-imx/mxc_i2c.h>
+#include <asm/mach-imx/sys_proto.h>
#include <asm/io.h>
#include <i2c.h>
#include <watchdog.h>
@@ -747,6 +748,14 @@ void bus_i2c_init(int index, int speed, int unused,
return;
}
+ if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+ if (i2c_fused((ulong)mxc_i2c_buses[index].base)) {
+ printf("SoC fuse indicates I2C@0x%lx is unavailable.\n",
+ (ulong)mxc_i2c_buses[index].base);
+ return;
+ }
+ }
+
/*
* Warning: Be careful to allow the assignment to a static
* variable here. This function could be called while U-Boot is
@@ -892,6 +901,14 @@ static int mxc_i2c_probe(struct udevice *bus)
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
+ if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+ if (i2c_fused((ulong)addr)) {
+ printf("SoC fuse indicates I2C@0x%lx is unavailable.\n",
+ (ulong)addr);
+ return -ENODEV;
+ }
+ }
+
i2c_bus->base = addr;
i2c_bus->index = bus->seq;
i2c_bus->bus = bus;
diff --git a/drivers/misc/imx8/fuse.c b/drivers/misc/imx8/fuse.c
index 1309215..4d7f2f5 100644
--- a/drivers/misc/imx8/fuse.c
+++ b/drivers/misc/imx8/fuse.c
@@ -15,8 +15,13 @@ DECLARE_GLOBAL_DATA_PTR;
#define FSL_ECC_WORD_START_1 0x10
#define FSL_ECC_WORD_END_1 0x10F
+#ifdef CONFIG_IMX8QM
+#define FSL_ECC_WORD_START_2 0x1A0
+#define FSL_ECC_WORD_END_2 0x1FF
+#elif defined(CONFIG_IMX8QXP)
#define FSL_ECC_WORD_START_2 0x220
#define FSL_ECC_WORD_END_2 0x31F
+#endif
#define FSL_QXP_FUSE_GAP_START 0x110
#define FSL_QXP_FUSE_GAP_END 0x21F
diff --git a/drivers/misc/imx8/scu.c b/drivers/misc/imx8/scu.c
index a7654a7..6916b75 100644
--- a/drivers/misc/imx8/scu.c
+++ b/drivers/misc/imx8/scu.c
@@ -74,7 +74,7 @@ static int mu_hal_receivemsg(struct mu_type *base, u32 reg_index, u32 *msg)
assert(reg_index < MU_TR_COUNT);
/* Wait RX register to be full. */
- ret = readl_poll_timeout(&base->sr, val, val & mask, 10000);
+ ret = readl_poll_timeout(&base->sr, val, val & mask, 1000000);
if (ret < 0) {
printf("%s timeout\n", __func__);
return -ETIMEDOUT;
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 23201ca..c4d9d31 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -259,12 +259,12 @@ config NAND_MXC
config NAND_MXS
bool "MXS NAND support"
- depends on MX23 || MX28 || MX6 || MX7
+ depends on MX23 || MX28 || MX6 || MX7 || IMX8 || IMX8M
select SYS_NAND_SELF_INIT
imply CMD_NAND
select APBH_DMA
- select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7
- select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7
+ select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 || ARCH_IMX8 || ARCH_IMX8M
+ select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 || ARCH_IMX8 || ARCH_IMX8M
help
This enables NAND driver for the NAND flash controller on the
MXS processors.
diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c
index fe8097c..e3516cd 100644
--- a/drivers/mtd/nand/raw/mxs_nand.c
+++ b/drivers/mtd/nand/raw/mxs_nand.c
@@ -10,6 +10,7 @@
*
* Copyright (C) 2010 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ * Copyright 2017-2019 NXP
*/
#include <common.h>
@@ -30,7 +31,8 @@
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
-#if (defined(CONFIG_MX6) || defined(CONFIG_MX7))
+#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_IMX8) || \
+ defined(CONFIG_IMX8M)
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2
#else
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0
@@ -54,21 +56,21 @@ struct nand_ecclayout fake_ecc_layout;
#if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF)
static void mxs_nand_flush_data_buf(struct mxs_nand_info *info)
{
- uint32_t addr = (uint32_t)info->data_buf;
+ uint32_t addr = (uintptr_t)info->data_buf;
flush_dcache_range(addr, addr + info->data_buf_size);
}
static void mxs_nand_inval_data_buf(struct mxs_nand_info *info)
{
- uint32_t addr = (uint32_t)info->data_buf;
+ uint32_t addr = (uintptr_t)info->data_buf;
invalidate_dcache_range(addr, addr + info->data_buf_size);
}
static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info)
{
- uint32_t addr = (uint32_t)info->cmd_buf;
+ uint32_t addr = (uintptr_t)info->cmd_buf;
flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE);
}
@@ -112,53 +114,32 @@ static uint32_t mxs_nand_aux_status_offset(void)
return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3;
}
-static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo,
- uint32_t page_data_size)
+static inline bool mxs_nand_bbm_in_data_chunk(struct bch_geometry *geo, struct mtd_info *mtd,
+ unsigned int *chunk_num)
{
- 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;
+ unsigned int i, j;
- chunk_total_size_in_bits =
- chunk_data_size_in_bits + chunk_ecc_size_in_bits;
-
- /* Compute the bit offset of the block mark within the physical page. */
- block_mark_bit_offset = page_data_size * 8;
-
- /* Subtract the metadata bits. */
- block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8;
-
- /*
- * Compute the chunk number (starting at zero) in which the block mark
- * appears.
- */
- block_mark_chunk_number =
- block_mark_bit_offset / chunk_total_size_in_bits;
-
- /*
- * Compute the bit offset of the block mark within its chunk, and
- * validate it.
- */
- block_mark_chunk_bit_offset = block_mark_bit_offset -
- (block_mark_chunk_number * chunk_total_size_in_bits);
+ if (geo->ecc_chunk0_size != geo->ecc_chunkn_size) {
+ dev_err(this->dev, "The size of chunk0 must equal to chunkn\n");
+ return false;
+ }
- if (block_mark_chunk_bit_offset > chunk_data_size_in_bits)
- return -EINVAL;
+ i = (mtd->writesize * 8 - MXS_NAND_METADATA_SIZE * 8) /
+ (geo->gf_len * geo->ecc_strength +
+ geo->ecc_chunkn_size * 8);
- /*
- * Now that we know the chunk number in which the block mark appears,
- * we can subtract all the ECC bits that appear before it.
- */
- block_mark_bit_offset -=
- block_mark_chunk_number * chunk_ecc_size_in_bits;
+ j = (mtd->writesize * 8 - MXS_NAND_METADATA_SIZE * 8) -
+ (geo->gf_len * geo->ecc_strength +
+ geo->ecc_chunkn_size * 8) * i;
- geo->block_mark_byte_offset = block_mark_bit_offset >> 3;
- geo->block_mark_bit_offset = block_mark_bit_offset & 0x7;
+ if (j < geo->ecc_chunkn_size * 8) {
+ *chunk_num = i + 1;
+ dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n",
+ geo->ecc_strength, *chunk_num);
+ return true;
+ }
- return 0;
+ return false;
}
static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo,
@@ -168,6 +149,7 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo,
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ unsigned int block_mark_bit_offset;
switch (ecc_step) {
case SZ_512:
@@ -180,45 +162,51 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo,
return -EINVAL;
}
- geo->ecc_chunk_size = ecc_step;
+ geo->ecc_chunk0_size = ecc_step;
+ geo->ecc_chunkn_size = ecc_step;
geo->ecc_strength = round_up(ecc_strength, 2);
/* Keep the C >= O */
- if (geo->ecc_chunk_size < mtd->oobsize)
+ if (geo->ecc_chunkn_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;
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size;
+
+ /* For bit swap. */
+ block_mark_bit_offset = mtd->writesize * 8 -
+ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
+ + MXS_NAND_METADATA_SIZE * 8);
+
+ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
return 0;
}
-static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo,
+static inline int mxs_nand_legacy_calc_ecc_layout(struct bch_geometry *geo,
struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ unsigned int block_mark_bit_offset;
/* The default for the length of Galois Field. */
geo->gf_len = 13;
/* The default for chunk size. */
- geo->ecc_chunk_size = 512;
+ geo->ecc_chunk0_size = 512;
+ geo->ecc_chunkn_size = 512;
- if (geo->ecc_chunk_size < mtd->oobsize) {
+ if (geo->ecc_chunkn_size < mtd->oobsize) {
geo->gf_len = 14;
- geo->ecc_chunk_size *= 2;
+ geo->ecc_chunk0_size *= 2;
+ geo->ecc_chunkn_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;
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size;
/*
* Determine the ECC layout with the formula:
@@ -234,6 +222,84 @@ static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo,
geo->ecc_strength = min(round_down(geo->ecc_strength, 2),
nand_info->max_ecc_strength_supported);
+ block_mark_bit_offset = mtd->writesize * 8 -
+ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
+ + MXS_NAND_METADATA_SIZE * 8);
+
+ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
+
+ return 0;
+}
+
+static inline int mxs_nand_calc_ecc_for_large_oob(struct bch_geometry *geo,
+ struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(chip);
+ unsigned int block_mark_bit_offset;
+ unsigned int max_ecc;
+ unsigned int bbm_chunk;
+ unsigned int i;
+
+ /* sanity check for the minimum ecc nand required */
+ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+ return -EINVAL;
+ geo->ecc_strength = chip->ecc_strength_ds;
+
+ /* calculate the maximum ecc platform can support*/
+ geo->gf_len = 14;
+ geo->ecc_chunk0_size = 1024;
+ geo->ecc_chunkn_size = 1024;
+ geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size;
+ max_ecc = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8)
+ / (geo->gf_len * geo->ecc_chunk_count);
+ max_ecc = min(round_down(max_ecc, 2),
+ nand_info->max_ecc_strength_supported);
+
+
+ /* search a supported ecc strength that makes bbm */
+ /* located in data chunk */
+ geo->ecc_strength = chip->ecc_strength_ds;
+ while (!(geo->ecc_strength > max_ecc)) {
+ if (mxs_nand_bbm_in_data_chunk(geo, mtd, &bbm_chunk))
+ break;
+ geo->ecc_strength += 2;
+ }
+
+ /* if none of them works, keep using the minimum ecc */
+ /* nand required but changing ecc page layout */
+ if (geo->ecc_strength > max_ecc) {
+ geo->ecc_strength = chip->ecc_strength_ds;
+ /* add extra ecc for meta data */
+ geo->ecc_chunk0_size = 0;
+ geo->ecc_chunk_count = (mtd->writesize / geo->ecc_chunkn_size) + 1;
+ geo->ecc_for_meta = 1;
+ /* check if oob can afford this extra ecc chunk */
+ if (mtd->oobsize * 8 < MXS_NAND_METADATA_SIZE * 8 +
+ geo->gf_len * geo->ecc_strength
+ * geo->ecc_chunk_count) {
+ printf("unsupported NAND chip with new layout\n");
+ return -EINVAL;
+ }
+
+ /* calculate in which chunk bbm located */
+ bbm_chunk = (mtd->writesize * 8 - MXS_NAND_METADATA_SIZE * 8 -
+ geo->gf_len * geo->ecc_strength) /
+ (geo->gf_len * geo->ecc_strength +
+ geo->ecc_chunkn_size * 8) + 1;
+ }
+
+ /* calculate the number of ecc chunk behind the bbm */
+ i = (mtd->writesize / geo->ecc_chunkn_size) - bbm_chunk + 1;
+
+ block_mark_bit_offset = mtd->writesize * 8 -
+ (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i)
+ + MXS_NAND_METADATA_SIZE * 8);
+
+ geo->block_mark_byte_offset = block_mark_bit_offset / 8;
+ geo->block_mark_bit_offset = block_mark_bit_offset % 8;
+
return 0;
}
@@ -548,6 +614,45 @@ static uint8_t mxs_nand_read_byte(struct mtd_info *mtd)
return buf;
}
+static bool mxs_nand_erased_page(struct mtd_info *mtd, struct nand_chip *nand,
+ u8 *buf, int chunk, int page)
+{
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+ struct bch_geometry *geo = &nand_info->bch_geometry;
+ unsigned int flip_bits = 0, flip_bits_noecc = 0;
+ unsigned int threshold;
+ unsigned int base = geo->ecc_chunkn_size * chunk;
+ u32 *dma_buf = (u32 *)buf;
+ int i;
+
+ threshold = geo->gf_len / 2;
+ if (threshold > geo->ecc_strength)
+ threshold = geo->ecc_strength;
+
+ for (i = 0; i < geo->ecc_chunkn_size; i++) {
+ flip_bits += hweight8(~buf[base + i]);
+ if (flip_bits > threshold)
+ return false;
+ }
+
+ nand->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ nand->read_buf(mtd, buf, mtd->writesize);
+
+ for (i = 0; i < mtd->writesize / 4; i++) {
+ flip_bits_noecc += hweight32(~dma_buf[i]);
+ if (flip_bits_noecc > threshold)
+ return false;
+ }
+
+ mtd->ecc_stats.corrected += flip_bits;
+
+ memset(buf, 0xff, mtd->writesize);
+
+ printf("The page(%d) is an erased page(%d,%d,%d,%d).\n", page, chunk, threshold, flip_bits, flip_bits_noecc);
+
+ return true;
+}
+
/*
* Read a page from NAND.
*/
@@ -557,11 +662,13 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
{
struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
struct bch_geometry *geo = &nand_info->bch_geometry;
+ struct mxs_bch_regs *bch_regs = nand_info->bch_regs;
struct mxs_dma_desc *d;
uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
uint32_t corrected = 0, failed = 0;
uint8_t *status;
int i, ret;
+ int flag = 0;
/* Compile the DMA descriptor - wait for ready. */
d = mxs_nand_get_dma_desc(nand_info);
@@ -603,6 +710,12 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+ if (nand_info->en_randomizer) {
+ d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+ GPMI_ECCCTRL_RANDOMIZER_TYPE2;
+ d->cmd.pio_words[3] |= (page % 256) << 16;
+ }
+
mxs_dma_desc_append(channel, d);
/* Compile the DMA descriptor - disable the BCH block. */
@@ -651,6 +764,8 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
goto rtn;
}
+ mxs_nand_return_dma_descs(nand_info);
+
/* Invalidate caches */
mxs_nand_inval_data_buf(nand_info);
@@ -663,10 +778,19 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
if (status[i] == 0x00)
continue;
- if (status[i] == 0xff)
+ if (status[i] == 0xff) {
+ if (!nand_info->en_randomizer &&
+ (is_mx6dqp() || is_mx7() || is_mx6ul() ||
+ is_imx8() || is_imx8m()))
+ if (readl(&bch_regs->hw_bch_debug1))
+ flag = 1;
continue;
+ }
if (status[i] == 0xfe) {
+ if (mxs_nand_erased_page(mtd, nand,
+ nand_info->data_buf, i, page))
+ break;
failed++;
continue;
}
@@ -693,6 +817,8 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
memcpy(buf, nand_info->data_buf, mtd->writesize);
+ if (flag)
+ memset(buf, 0xff, mtd->writesize);
rtn:
mxs_nand_return_dma_descs(nand_info);
@@ -741,7 +867,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
- if (is_mx7() && nand_info->en_randomizer) {
+ if (nand_info->en_randomizer) {
d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
GPMI_ECCCTRL_RANDOMIZER_TYPE2;
/*
@@ -751,7 +877,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
* The value is between 0-255. For additional details
* check 9.6.6.4 of i.MX7D Applications Processor reference
*/
- d->cmd.pio_words[3] |= (page % 255) << 16;
+ d->cmd.pio_words[3] |= (page % 256) << 16;
}
mxs_dma_desc_append(channel, d);
@@ -983,18 +1109,23 @@ static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo)
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 (chip->ecc_strength_ds > nand_info->max_ecc_strength_supported) {
+ printf("unsupported NAND chip, minimum ecc required %d\n"
+ , chip->ecc_strength_ds);
+ return -EINVAL;
+ }
- 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;
+ if ((!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0) &&
+ mtd->oobsize < 1024) || nand_info->legacy_bch_geometry) {
+ dev_warn(this->dev, "use legacy bch geometry\n");
+ return mxs_nand_legacy_calc_ecc_layout(geo, mtd);
+ }
+
+ if (mtd->oobsize > 1024 || chip->ecc_step_ds < mtd->oobsize)
+ return mxs_nand_calc_ecc_for_large_oob(geo, mtd);
- return mxs_nand_calc_ecc_layout_by_info(geo, mtd,
+ return mxs_nand_calc_ecc_layout_by_info(geo, mtd,
chip->ecc_strength_ds, chip->ecc_step_ds);
- }
return 0;
}
@@ -1025,8 +1156,6 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
if (ret)
return ret;
- mxs_nand_calc_mark_offset(geo, mtd->writesize);
-
/* Configure BCH and set NFC geometry */
mxs_reset_block(&bch_regs->hw_bch_ctrl_reg);
@@ -1034,7 +1163,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET;
- tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
+ tmp |= geo->ecc_chunk0_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);
@@ -1043,12 +1172,18 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd)
tmp = (mtd->writesize + mtd->oobsize)
<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET;
- tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT;
+ tmp |= geo->ecc_chunkn_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);
nand_info->bch_flash0layout1 = tmp;
+ /* Set erase threshold to ecc strength for mx6ul, mx6qp and mx7 */
+ if (is_mx6dqp() || is_mx7() ||
+ is_mx6ul() || is_imx8() || is_imx8m())
+ writel(BCH_MODE_ERASE_THRESHOLD(geo->ecc_strength),
+ &bch_regs->hw_bch_mode);
+
/* Set *all* chip selects to use layout 0 */
writel(0, &bch_regs->hw_bch_layoutselect);
@@ -1184,7 +1319,7 @@ int mxs_nand_init_spl(struct nand_chip *nand)
nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE;
nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
- if (is_mx6sx() || is_mx7())
+ if (is_mx6sx() || is_mx7() || is_imx8() || is_imx8m())
nand_info->max_ecc_strength_supported = 62;
else
nand_info->max_ecc_strength_supported = 40;
@@ -1268,7 +1403,7 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info)
nand->ecc.layout = &fake_ecc_layout;
nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size;
+ nand->ecc.size = nand_info->bch_geometry.ecc_chunkn_size;
nand->ecc.strength = nand_info->bch_geometry.ecc_strength;
/* second phase scan */
@@ -1347,12 +1482,14 @@ void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l)
BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET);
l->eccn = (tmp & BCH_FLASHLAYOUT1_ECCN_MASK) >>
BCH_FLASHLAYOUT1_ECCN_OFFSET;
+ l->gf_len = (tmp & BCH_FLASHLAYOUT1_GF13_0_GF14_1_MASK) >>
+ BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
}
/*
* Set BCH to specific layout used by ROM bootloader to read FCB.
*/
-void mxs_nand_mode_fcb(struct mtd_info *mtd)
+void mxs_nand_mode_fcb_62bit(struct mtd_info *mtd)
{
u32 tmp;
struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
@@ -1386,6 +1523,43 @@ void mxs_nand_mode_fcb(struct mtd_info *mtd)
}
/*
+ * Set BCH to specific layout used by ROM bootloader to read FCB.
+ */
+void mxs_nand_mode_fcb_40bit(struct mtd_info *mtd)
+{
+ u32 tmp;
+ struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mxs_nand_info *nand_info = nand_get_controller_data(nand);
+
+ /* no randomizer in this setting*/
+ nand_info->en_randomizer = 0;
+
+ mtd->writesize = 1024;
+ mtd->oobsize = 1576 - 1024;
+
+ /* 8 ecc_chunks_*/
+ tmp = 7 << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
+ /* 32 bytes for metadata */
+ tmp |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
+ /* using ECC40 level to be performed */
+ tmp |= 0x14 << BCH_FLASHLAYOUT0_ECC0_OFFSET;
+ /* 0x20 * 4 bytes of the data0 block */
+ tmp |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET;
+ tmp |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
+ writel(tmp, &bch_regs->hw_bch_flash0layout0);
+
+ /* 1024 for data + 552 for OOB */
+ tmp = 1576 << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
+ /* using ECC40 level to be performed */
+ tmp |= 0x14 << BCH_FLASHLAYOUT1_ECCN_OFFSET;
+ /* 0x20 * 4 bytes of the data0 block */
+ tmp |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET;
+ tmp |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
+ writel(tmp, &bch_regs->hw_bch_flash0layout1);
+}
+
+/*
* Restore BCH to normal settings.
*/
void mxs_nand_mode_normal(struct mtd_info *mtd)
diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c
index 8ad7d61..43dbe9e 100644
--- a/drivers/mtd/nand/raw/mxs_nand_dt.c
+++ b/drivers/mtd/nand/raw/mxs_nand_dt.c
@@ -2,6 +2,8 @@
* NXP GPMI NAND flash driver (DT initialization)
*
* Copyright (C) 2018 Toradex
+ * Copyright 2019 NXP
+ *
* Authors:
* Stefan Agner <stefan.agner@toradex.com>
*
@@ -14,6 +16,7 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/printk.h>
+#include <clk.h>
#include <mxs_nand.h>
@@ -25,19 +28,39 @@ static const struct mxs_nand_dt_data mxs_nand_imx6q_data = {
.max_ecc_strength_supported = 40,
};
+static const struct mxs_nand_dt_data mxs_nand_imx6sx_data = {
+ .max_ecc_strength_supported = 62,
+};
+
static const struct mxs_nand_dt_data mxs_nand_imx7d_data = {
.max_ecc_strength_supported = 62,
};
+static const struct mxs_nand_dt_data mxs_nand_imx8qxp_data = {
+ .max_ecc_strength_supported = 62,
+};
+
static const struct udevice_id mxs_nand_dt_ids[] = {
{
.compatible = "fsl,imx6q-gpmi-nand",
.data = (unsigned long)&mxs_nand_imx6q_data,
},
{
+ .compatible = "fsl,imx6qp-gpmi-nand",
+ .data = (unsigned long)&mxs_nand_imx6q_data,
+ },
+ {
+ .compatible = "fsl,imx6sx-gpmi-nand",
+ .data = (unsigned long)&mxs_nand_imx6sx_data,
+ },
+ {
.compatible = "fsl,imx7d-gpmi-nand",
.data = (unsigned long)&mxs_nand_imx7d_data,
},
+ {
+ .compatible = "fsl,imx8qxp-gpmi-nand",
+ .data = (unsigned long)&mxs_nand_imx8qxp_data,
+ },
{ /* sentinel */ }
};
@@ -69,6 +92,74 @@ static int mxs_nand_dt_probe(struct udevice *dev)
info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc");
+ info->legacy_bch_geometry = dev_read_bool(dev, "fsl,legacy-bch-geometry");
+
+ if (IS_ENABLED(CONFIG_CLK) && IS_ENABLED(CONFIG_IMX8)) {
+ /* Assigned clock already set clock */
+ struct clk gpmi_clk;
+
+ ret = clk_get_by_name(dev, "gpmi_io", &gpmi_clk);
+ if (ret < 0) {
+ debug("Can't get gpmi io clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(&gpmi_clk);
+ if (ret < 0) {
+ debug("Can't enable gpmi io clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "gpmi_apb", &gpmi_clk);
+ if (ret < 0) {
+ debug("Can't get gpmi_apb clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(&gpmi_clk);
+ if (ret < 0) {
+ debug("Can't enable gpmi_apb clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "gpmi_bch", &gpmi_clk);
+ if (ret < 0) {
+ debug("Can't get gpmi_bch clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(&gpmi_clk);
+ if (ret < 0) {
+ debug("Can't enable gpmi_bch clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "gpmi_apb_bch", &gpmi_clk);
+ if (ret < 0) {
+ debug("Can't get gpmi_apb_bch clk: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(&gpmi_clk);
+ if (ret < 0) {
+ debug("Can't enable gpmi_apb_bch clk: %d\n", ret);
+ return ret;
+ }
+
+ /* this clock is used for apbh_dma, since the apbh dma does not support DM,
+ * we optionally enable it here
+ */
+ ret = clk_get_by_name(dev, "gpmi_apbh_dma", &gpmi_clk);
+ if (ret < 0) {
+ debug("Can't get gpmi_apbh_dma clk: %d\n", ret);
+ } else {
+ ret = clk_enable(&gpmi_clk);
+ if (ret < 0) {
+ debug("Can't enable gpmi_apbh_dma clk: %d\n", ret);
+ }
+ }
+ }
+
return mxs_nand_init_ctrl(info);
}
diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c
index a653dfa..ae50cc3 100644
--- a/drivers/mtd/nand/raw/mxs_nand_spl.c
+++ b/drivers/mtd/nand/raw/mxs_nand_spl.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014 Gateworks Corporation
+ * Copyright 2019 NXP
* Author: Tim Harvey <tharvey@gateworks.com>
*/
#include <common.h>
@@ -38,6 +39,12 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command,
if (command == NAND_CMD_READ0) {
chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE);
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0);
+ } else if (command == NAND_CMD_RNDOUT) {
+ /* No ready / busy check necessary */
+ chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+ NAND_NCE | NAND_CLE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE);
}
/* wait for nand ready */
@@ -212,23 +219,39 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf)
unsigned int page;
unsigned int nand_page_per_block;
unsigned int sz = 0;
+ u8 *page_buf = NULL;
+ u32 page_off;
chip = mtd_to_nand(mtd);
if (!chip->numchips)
return -ENODEV;
+
+ page_buf = malloc(mtd->writesize);
+ if (!page_buf)
+ return -ENOMEM;
+
page = offs >> chip->page_shift;
+ page_off = offs & (mtd->writesize - 1);
nand_page_per_block = mtd->erasesize / mtd->writesize;
- debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page);
+ debug("%s offset:0x%08x len:%d page:%x\n", __func__, offs, size, page);
- size = roundup(size, mtd->writesize);
- while (sz < size) {
- if (mxs_read_page_ecc(mtd, buf, page) < 0)
+ while (size) {
+ if (mxs_read_page_ecc(mtd, page_buf, page) < 0)
return -1;
- sz += mtd->writesize;
+
+ if (size > (mtd->writesize - page_off))
+ sz = (mtd->writesize - page_off);
+ else
+ sz = size;
+
+ memcpy(buf, page_buf + page_off, sz);
+
offs += mtd->writesize;
page++;
- buf += mtd->writesize;
+ buf += (mtd->writesize - page_off);
+ page_off = 0;
+ size -= sz;
/*
* Check if we have crossed a block boundary, and if so
@@ -242,12 +265,16 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf)
while (is_badblock(mtd, offs, 1)) {
page = page + nand_page_per_block;
/* Check i we've reached the end of flash. */
- if (page >= mtd->size >> chip->page_shift)
+ if (page >= mtd->size >> chip->page_shift) {
+ free(page_buf);
return -ENOMEM;
+ }
}
}
}
+ free(page_buf);
+
return 0;
}
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index a2587a2..38f2bd6 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -57,6 +57,12 @@ config MDIO_MUX_SANDBOX
This driver is used for testing in test/dm/mdio.c
+config DM_ETH_PHY
+ bool "Enable Driver Model for Ethernet Generic PHY drivers"
+ depends on DM
+ help
+ Enable driver model for Ethernet Generic PHY .
+
menuconfig NETDEVICES
bool "Network device support"
depends on NET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 6d9b877..383ed1c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o
obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
obj-$(CONFIG_DNET) += dnet.o
+obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o
obj-$(CONFIG_E1000) += e1000.o
obj-$(CONFIG_E1000_SPI) += e1000_spi.o
obj-$(CONFIG_EEPRO100) += eepro100.o
diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 60dfd17..f67c5f4 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -41,6 +41,11 @@
#include <wait_bit.h>
#include <asm/gpio.h>
#include <asm/io.h>
+#include <eth_phy.h>
+#ifdef CONFIG_ARCH_IMX8M
+#include <asm/arch/clock.h>
+#include <asm/mach-imx/sys_proto.h>
+#endif
/* Core registers */
@@ -80,6 +85,7 @@ struct eqos_mac_regs {
#define EQOS_MAC_CONFIGURATION_PS BIT(15)
#define EQOS_MAC_CONFIGURATION_FES BIT(14)
#define EQOS_MAC_CONFIGURATION_DM BIT(13)
+#define EQOS_MAC_CONFIGURATION_LM BIT(12)
#define EQOS_MAC_CONFIGURATION_TE BIT(1)
#define EQOS_MAC_CONFIGURATION_RE BIT(0)
@@ -101,11 +107,19 @@ struct eqos_mac_regs {
#define EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT 0
#define EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK 0xff
+#define EQOS_MAC_HW_FEATURE0_MMCSEL_SHIFT 8
+#define EQOS_MAC_HW_FEATURE0_HDSEL_SHIFT 2
+#define EQOS_MAC_HW_FEATURE0_GMIISEL_SHIFT 1
+#define EQOS_MAC_HW_FEATURE0_MIISEL_SHIFT 0
+
#define EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT 6
#define EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_MASK 0x1f
#define EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT 0
#define EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK 0x1f
+#define EQOS_MAC_HW_FEATURE3_ASP_SHIFT 28
+#define EQOS_MAC_HW_FEATURE3_ASP_MASK 0x3
+
#define EQOS_MAC_MDIO_ADDRESS_PA_SHIFT 21
#define EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT 16
#define EQOS_MAC_MDIO_ADDRESS_CR_SHIFT 8
@@ -153,6 +167,8 @@ struct eqos_mtl_regs {
#define EQOS_MTL_RXQ0_OPERATION_MODE_RFA_MASK 0x3f
#define EQOS_MTL_RXQ0_OPERATION_MODE_EHFC BIT(7)
#define EQOS_MTL_RXQ0_OPERATION_MODE_RSF BIT(5)
+#define EQOS_MTL_RXQ0_OPERATION_MODE_FEP BIT(4)
+#define EQOS_MTL_RXQ0_OPERATION_MODE_FUP BIT(3)
#define EQOS_MTL_RXQ0_DEBUG_PRXQ_SHIFT 16
#define EQOS_MTL_RXQ0_DEBUG_PRXQ_MASK 0x7fff
@@ -367,7 +383,7 @@ static void eqos_inval_desc_tegra186(void *desc)
#endif
}
-static void eqos_inval_desc_stm32(void *desc)
+static void eqos_inval_desc_generic(void *desc)
{
#ifndef CONFIG_SYS_NONCACHED_MEMORY
unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
@@ -385,7 +401,7 @@ static void eqos_flush_desc_tegra186(void *desc)
#endif
}
-static void eqos_flush_desc_stm32(void *desc)
+static void eqos_flush_desc_generic(void *desc)
{
#ifndef CONFIG_SYS_NONCACHED_MEMORY
unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
@@ -404,7 +420,7 @@ static void eqos_inval_buffer_tegra186(void *buf, size_t size)
invalidate_dcache_range(start, end);
}
-static void eqos_inval_buffer_stm32(void *buf, size_t size)
+static void eqos_inval_buffer_generic(void *buf, size_t size)
{
unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
unsigned long end = roundup((unsigned long)buf + size,
@@ -418,7 +434,7 @@ static void eqos_flush_buffer_tegra186(void *buf, size_t size)
flush_cache((unsigned long)buf, size);
}
-static void eqos_flush_buffer_stm32(void *buf, size_t size)
+static void eqos_flush_buffer_generic(void *buf, size_t size)
{
unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
unsigned long end = roundup((unsigned long)buf + size,
@@ -521,6 +537,7 @@ static int eqos_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad,
static int eqos_start_clks_tegra186(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
int ret;
@@ -561,10 +578,12 @@ static int eqos_start_clks_tegra186(struct udevice *dev)
pr_err("clk_enable(clk_tx) failed: %d", ret);
goto err_disable_clk_ptp_ref;
}
+#endif
debug("%s: OK\n", __func__);
return 0;
+#ifdef CONFIG_CLK
err_disable_clk_ptp_ref:
clk_disable(&eqos->clk_ptp_ref);
err_disable_clk_rx:
@@ -576,10 +595,12 @@ err_disable_clk_slave_bus:
err:
debug("%s: FAILED: %d\n", __func__, ret);
return ret;
+#endif
}
static int eqos_start_clks_stm32(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
int ret;
@@ -610,10 +631,12 @@ static int eqos_start_clks_stm32(struct udevice *dev)
goto err_disable_clk_tx;
}
}
+#endif
debug("%s: OK\n", __func__);
return 0;
+#ifdef CONFIG_CLK
err_disable_clk_tx:
clk_disable(&eqos->clk_tx);
err_disable_clk_rx:
@@ -623,10 +646,17 @@ err_disable_clk_master_bus:
err:
debug("%s: FAILED: %d\n", __func__, ret);
return ret;
+#endif
+}
+
+static int eqos_start_clks_imx(struct udevice *dev)
+{
+ return 0;
}
static void eqos_stop_clks_tegra186(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
debug("%s(dev=%p):\n", __func__, dev);
@@ -636,12 +666,14 @@ static void eqos_stop_clks_tegra186(struct udevice *dev)
clk_disable(&eqos->clk_rx);
clk_disable(&eqos->clk_master_bus);
clk_disable(&eqos->clk_slave_bus);
+#endif
debug("%s: OK\n", __func__);
}
static void eqos_stop_clks_stm32(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
debug("%s(dev=%p):\n", __func__, dev);
@@ -651,10 +683,16 @@ static void eqos_stop_clks_stm32(struct udevice *dev)
clk_disable(&eqos->clk_master_bus);
if (clk_valid(&eqos->clk_ck))
clk_disable(&eqos->clk_ck);
+#endif
debug("%s: OK\n", __func__);
}
+static void eqos_stop_clks_imx(struct udevice *dev)
+{
+ /* empty */
+}
+
static int eqos_start_resets_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -722,6 +760,11 @@ static int eqos_start_resets_stm32(struct udevice *dev)
return 0;
}
+static int eqos_start_resets_imx(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_stop_resets_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -749,6 +792,11 @@ static int eqos_stop_resets_stm32(struct udevice *dev)
return 0;
}
+static int eqos_stop_resets_imx(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_calibrate_pads_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -803,16 +851,38 @@ static int eqos_disable_calibration_tegra186(struct udevice *dev)
static ulong eqos_get_tick_clk_rate_tegra186(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
return clk_get_rate(&eqos->clk_slave_bus);
+#else
+ return 0;
+#endif
}
static ulong eqos_get_tick_clk_rate_stm32(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
return clk_get_rate(&eqos->clk_master_bus);
+#else
+ return 0;
+#endif
+}
+
+__weak u32 imx_get_eqos_csr_clk(void)
+{
+ return 100 * 1000000;
+}
+__weak int imx_eqos_txclk_set_rate(unsigned long rate)
+{
+ return 0;
+}
+
+static ulong eqos_get_tick_clk_rate_imx(struct udevice *dev)
+{
+ return imx_get_eqos_csr_clk();
}
static int eqos_calibrate_pads_stm32(struct udevice *dev)
@@ -820,11 +890,21 @@ static int eqos_calibrate_pads_stm32(struct udevice *dev)
return 0;
}
+static int eqos_calibrate_pads_imx(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_disable_calibration_stm32(struct udevice *dev)
{
return 0;
}
+static int eqos_disable_calibration_imx(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_set_full_duplex(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -889,6 +969,7 @@ static int eqos_set_mii_speed_10(struct udevice *dev)
static int eqos_set_tx_clk_speed_tegra186(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
ulong rate;
int ret;
@@ -915,6 +996,7 @@ static int eqos_set_tx_clk_speed_tegra186(struct udevice *dev)
pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret);
return ret;
}
+#endif
return 0;
}
@@ -924,6 +1006,38 @@ static int eqos_set_tx_clk_speed_stm32(struct udevice *dev)
return 0;
}
+static int eqos_set_tx_clk_speed_imx(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ ulong rate;
+ int ret;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ switch (eqos->phy->speed) {
+ case SPEED_1000:
+ rate = 125 * 1000 * 1000;
+ break;
+ case SPEED_100:
+ rate = 25 * 1000 * 1000;
+ break;
+ case SPEED_10:
+ rate = 2.5 * 1000 * 1000;
+ break;
+ default:
+ pr_err("invalid speed %d", eqos->phy->speed);
+ return -EINVAL;
+ }
+
+ ret = imx_eqos_txclk_set_rate(rate);
+ if (ret < 0) {
+ pr_err("imx (tx_clk, %lu) failed: %d", rate, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int eqos_adjust_link(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1031,6 +1145,16 @@ static int eqos_write_hwaddr(struct udevice *dev)
return 0;
}
+static int eqos_read_rom_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_platdata(dev);
+
+#ifdef CONFIG_ARCH_IMX8M
+ imx_get_mac_from_fuse(dev->req_seq, pdata->enetaddr);
+#endif
+ return !is_valid_ethaddr(pdata->enetaddr);
+}
+
static int eqos_start(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1083,7 +1207,14 @@ static int eqos_start(struct udevice *dev)
* don't need to reconnect/reconfigure again
*/
if (!eqos->phy) {
- eqos->phy = phy_connect(eqos->mii, eqos->phyaddr, dev,
+ int addr = -1;
+#ifdef CONFIG_DM_ETH_PHY
+ addr = eth_phy_get_addr(dev);
+#endif
+#ifdef DWC_NET_PHYADDR
+ addr = DWC_NET_PHYADDR;
+#endif
+ eqos->phy = phy_connect(eqos->mii, addr, dev,
eqos->config->interface(dev));
if (!eqos->phy) {
pr_err("phy_connect() failed");
@@ -1123,6 +1254,7 @@ static int eqos_start(struct udevice *dev)
}
/* Configure MTL */
+ writel(0x60, &eqos->mtl_regs->txq0_quantum_weight - 0x100);
/* Enable Store and Forward mode for TX */
/* Program Tx operating mode */
@@ -1136,7 +1268,9 @@ static int eqos_start(struct udevice *dev)
/* Enable Store and Forward mode for RX, since no jumbo frame */
setbits_le32(&eqos->mtl_regs->rxq0_operation_mode,
- EQOS_MTL_RXQ0_OPERATION_MODE_RSF);
+ EQOS_MTL_RXQ0_OPERATION_MODE_RSF |
+ EQOS_MTL_RXQ0_OPERATION_MODE_FEP |
+ EQOS_MTL_RXQ0_OPERATION_MODE_FUP);
/* Transmit/Receive queue fifo size; use all RAM for 1 queue */
val = readl(&eqos->mac_regs->hw_feature1);
@@ -1212,6 +1346,19 @@ static int eqos_start(struct udevice *dev)
eqos->config->config_mac <<
EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT);
+ clrsetbits_le32(&eqos->mac_regs->rxq_ctrl0,
+ EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK <<
+ EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT,
+ 0x2 <<
+ EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT);
+
+ /* Multicast and Broadcast Queue Enable */
+ setbits_le32(&eqos->mac_regs->unused_0a4,
+ 0x00100000);
+ /* enable promise mode */
+ setbits_le32(&eqos->mac_regs->unused_004[1],
+ 0x1);
+
/* Set TX flow control parameters */
/* Set Pause Time */
setbits_le32(&eqos->mac_regs->q0_tx_flow_ctrl,
@@ -1289,7 +1436,11 @@ static int eqos_start(struct udevice *dev)
rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf +
(i * EQOS_MAX_PACKET_SIZE));
rx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_BUF1V;
+ mb();
eqos->config->ops->eqos_flush_desc(rx_desc);
+ eqos->config->ops->eqos_inval_buffer(eqos->rx_dma_buf +
+ (i * EQOS_MAX_PACKET_SIZE),
+ EQOS_MAX_PACKET_SIZE);
}
writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress);
@@ -1303,14 +1454,12 @@ static int eqos_start(struct udevice *dev)
&eqos->dma_regs->ch0_rxdesc_ring_length);
/* Enable everything */
-
- setbits_le32(&eqos->mac_regs->configuration,
- EQOS_MAC_CONFIGURATION_TE | EQOS_MAC_CONFIGURATION_RE);
-
setbits_le32(&eqos->dma_regs->ch0_tx_control,
EQOS_DMA_CH0_TX_CONTROL_ST);
setbits_le32(&eqos->dma_regs->ch0_rx_control,
EQOS_DMA_CH0_RX_CONTROL_SR);
+ setbits_le32(&eqos->mac_regs->configuration,
+ EQOS_MAC_CONFIGURATION_TE | EQOS_MAC_CONFIGURATION_RE);
/* TX tail pointer not written until we need to TX a packet */
/*
@@ -1475,6 +1624,8 @@ static int eqos_free_pkt(struct udevice *dev, uchar *packet, int length)
return -EINVAL;
}
+ eqos->config->ops->eqos_inval_buffer(packet, length);
+
rx_desc = &(eqos->rx_descs[eqos->rx_desc_idx]);
rx_desc->des0 = 0;
@@ -1751,17 +1902,52 @@ static phy_interface_t eqos_get_interface_tegra186(struct udevice *dev)
return PHY_INTERFACE_MODE_MII;
}
+static int eqos_probe_resources_imx(struct udevice *dev)
+{
+ struct eqos_priv *eqos = dev_get_priv(dev);
+ phy_interface_t interface;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ interface = eqos->config->interface(dev);
+
+ if (interface == PHY_INTERFACE_MODE_NONE) {
+ pr_err("Invalid PHY interface\n");
+ return -EINVAL;
+ }
+
+ debug("%s: OK\n", __func__);
+ return 0;
+}
+
+static phy_interface_t eqos_get_interface_imx(struct udevice *dev)
+{
+ const char *phy_mode;
+ phy_interface_t interface = PHY_INTERFACE_MODE_NONE;
+
+ debug("%s(dev=%p):\n", __func__, dev);
+
+ phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode",
+ NULL);
+ if (phy_mode)
+ interface = phy_get_interface_by_name(phy_mode);
+
+ return interface;
+}
+
static int eqos_remove_resources_tegra186(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
debug("%s(dev=%p):\n", __func__, dev);
+#ifdef CONFIG_CLK
clk_free(&eqos->clk_tx);
clk_free(&eqos->clk_ptp_ref);
clk_free(&eqos->clk_rx);
clk_free(&eqos->clk_slave_bus);
clk_free(&eqos->clk_master_bus);
+#endif
dm_gpio_free(dev, &eqos->phy_reset_gpio);
reset_free(&eqos->reset_ctl);
@@ -1771,6 +1957,7 @@ static int eqos_remove_resources_tegra186(struct udevice *dev)
static int eqos_remove_resources_stm32(struct udevice *dev)
{
+#ifdef CONFIG_CLK
struct eqos_priv *eqos = dev_get_priv(dev);
debug("%s(dev=%p):\n", __func__, dev);
@@ -1780,6 +1967,7 @@ static int eqos_remove_resources_stm32(struct udevice *dev)
clk_free(&eqos->clk_master_bus);
if (clk_valid(&eqos->clk_ck))
clk_free(&eqos->clk_ck);
+#endif
if (dm_gpio_is_valid(&eqos->phy_reset_gpio))
dm_gpio_free(dev, &eqos->phy_reset_gpio);
@@ -1788,6 +1976,11 @@ static int eqos_remove_resources_stm32(struct udevice *dev)
return 0;
}
+static int eqos_remove_resources_imx(struct udevice *dev)
+{
+ return 0;
+}
+
static int eqos_probe(struct udevice *dev)
{
struct eqos_priv *eqos = dev_get_priv(dev);
@@ -1820,23 +2013,32 @@ static int eqos_probe(struct udevice *dev)
goto err_remove_resources_core;
}
- eqos->mii = mdio_alloc();
+#ifdef CONFIG_DM_ETH_PHY
+ eqos->mii = eth_phy_get_mdio_bus(dev);
+#endif
if (!eqos->mii) {
- pr_err("mdio_alloc() failed");
- ret = -ENOMEM;
- goto err_remove_resources_tegra;
- }
- eqos->mii->read = eqos_mdio_read;
- eqos->mii->write = eqos_mdio_write;
- eqos->mii->priv = eqos;
- strcpy(eqos->mii->name, dev->name);
+ eqos->mii = mdio_alloc();
+ if (!eqos->mii) {
+ pr_err("mdio_alloc() failed");
+ ret = -ENOMEM;
+ goto err_remove_resources_tegra;
+ }
+ eqos->mii->read = eqos_mdio_read;
+ eqos->mii->write = eqos_mdio_write;
+ eqos->mii->priv = eqos;
+ strcpy(eqos->mii->name, dev->name);
- ret = mdio_register(eqos->mii);
- if (ret < 0) {
- pr_err("mdio_register() failed: %d", ret);
- goto err_free_mdio;
+ ret = mdio_register(eqos->mii);
+ if (ret < 0) {
+ pr_err("mdio_register() failed: %d", ret);
+ goto err_free_mdio;
+ }
}
+#ifdef CONFIG_DM_ETH_PHY
+ eth_phy_set_mdio_bus(dev, eqos->mii);
+#endif
+
debug("%s: OK\n", __func__);
return 0;
@@ -1874,6 +2076,7 @@ static const struct eth_ops eqos_ops = {
.recv = eqos_recv,
.free_pkt = eqos_free_pkt,
.write_hwaddr = eqos_write_hwaddr,
+ .read_rom_hwaddr = eqos_read_rom_hwaddr,
};
static struct eqos_ops eqos_tegra186_ops = {
@@ -1904,10 +2107,10 @@ static const struct eqos_config eqos_tegra186_config = {
};
static struct eqos_ops eqos_stm32_ops = {
- .eqos_inval_desc = eqos_inval_desc_stm32,
- .eqos_flush_desc = eqos_flush_desc_stm32,
- .eqos_inval_buffer = eqos_inval_buffer_stm32,
- .eqos_flush_buffer = eqos_flush_buffer_stm32,
+ .eqos_inval_desc = eqos_inval_desc_generic,
+ .eqos_flush_desc = eqos_flush_desc_generic,
+ .eqos_inval_buffer = eqos_inval_buffer_generic,
+ .eqos_flush_buffer = eqos_flush_buffer_generic,
.eqos_probe_resources = eqos_probe_resources_stm32,
.eqos_remove_resources = eqos_remove_resources_stm32,
.eqos_stop_resets = eqos_stop_resets_stm32,
@@ -1930,6 +2133,33 @@ static const struct eqos_config eqos_stm32_config = {
.ops = &eqos_stm32_ops
};
+static struct eqos_ops eqos_imx_ops = {
+ .eqos_inval_desc = eqos_inval_desc_generic,
+ .eqos_flush_desc = eqos_flush_desc_generic,
+ .eqos_inval_buffer = eqos_inval_buffer_generic,
+ .eqos_flush_buffer = eqos_flush_buffer_generic,
+ .eqos_probe_resources = eqos_probe_resources_imx,
+ .eqos_remove_resources = eqos_remove_resources_imx,
+ .eqos_stop_resets = eqos_stop_resets_imx,
+ .eqos_start_resets = eqos_start_resets_imx,
+ .eqos_stop_clks = eqos_stop_clks_imx,
+ .eqos_start_clks = eqos_start_clks_imx,
+ .eqos_calibrate_pads = eqos_calibrate_pads_imx,
+ .eqos_disable_calibration = eqos_disable_calibration_imx,
+ .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_imx,
+ .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_imx
+};
+
+struct eqos_config eqos_imx_config = {
+ .reg_access_always_ok = false,
+ .mdio_wait = 10000,
+ .swr_wait = 50,
+ .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+ .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300,
+ .interface = eqos_get_interface_imx,
+ .ops = &eqos_imx_ops
+};
+
static const struct udevice_id eqos_ids[] = {
{
.compatible = "nvidia,tegra186-eqos",
@@ -1939,6 +2169,10 @@ static const struct udevice_id eqos_ids[] = {
.compatible = "snps,dwmac-4.20a",
.data = (ulong)&eqos_stm32_config
},
+ {
+ .compatible = "fsl,imx-eqos",
+ .data = (ulong)&eqos_imx_config
+ },
{ }
};
@@ -1946,7 +2180,7 @@ static const struct udevice_id eqos_ids[] = {
U_BOOT_DRIVER(eth_eqos) = {
.name = "eth_eqos",
.id = UCLASS_ETH,
- .of_match = eqos_ids,
+ .of_match = of_match_ptr(eqos_ids),
.probe = eqos_probe,
.remove = eqos_remove,
.ops = &eqos_ops,
diff --git a/drivers/net/eth-phy-uclass.c b/drivers/net/eth-phy-uclass.c
new file mode 100644
index 0000000..b383f45
--- /dev/null
+++ b/drivers/net/eth-phy-uclass.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <net.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/lists.h>
+
+struct eth_phy_device_priv {
+ struct mii_dev *mdio_bus;
+};
+
+int eth_phy_binds_nodes(struct udevice *eth_dev)
+{
+ ofnode mdio_node, phy_node;
+ const char *node_name;
+ int ret;
+
+ mdio_node = dev_read_subnode(eth_dev, "mdio");
+ if (!ofnode_valid(mdio_node)) {
+ debug("%s: %s mdio subnode not found!", __func__,
+ eth_dev->name);
+ return -ENXIO;
+ }
+
+ ofnode_for_each_subnode(phy_node, mdio_node) {
+ node_name = ofnode_get_name(phy_node);
+
+ debug("* Found child node: '%s'\n", node_name);
+
+ ret = device_bind_driver_to_node(eth_dev,
+ "eth_phy_generic_drv",
+ node_name, phy_node, NULL);
+ if (ret) {
+ debug(" - Eth phy binding error: %d\n", ret);
+ continue;
+ }
+
+ debug(" - bound phy device: '%s'\n", node_name);
+ }
+
+ return 0;
+}
+
+int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus)
+{
+ struct udevice *dev;
+ struct eth_phy_device_priv *uc_priv;
+
+ for (uclass_first_device(UCLASS_ETH_PHY, &dev); dev;
+ uclass_next_device(&dev)) {
+ if (dev->parent == eth_dev) {
+ uc_priv = (struct eth_phy_device_priv *)(dev->uclass_priv);
+
+ if (!uc_priv->mdio_bus)
+ uc_priv->mdio_bus = mdio_bus;
+ }
+ }
+
+ return 0;
+}
+
+struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev)
+{
+ int ret;
+ struct udevice *phy_dev;
+ struct eth_phy_device_priv *uc_priv;
+
+ /* Will probe the parent of phy device, then phy device */
+ ret = uclass_get_device_by_phandle(UCLASS_ETH_PHY, eth_dev,
+ "phy-handle", &phy_dev);
+ if (!ret) {
+ if (eth_dev != phy_dev->parent) {
+ /*
+ * phy_dev is shared and controlled by
+ * other eth controller
+ */
+ uc_priv = (struct eth_phy_device_priv *)(phy_dev->uclass_priv);
+ if (uc_priv->mdio_bus)
+ printf("Get shared mii bus on %s\n", eth_dev->name);
+ else
+ printf("Can't get shared mii bus on %s\n", eth_dev->name);
+
+ return uc_priv->mdio_bus;
+ }
+ } else {
+ printf("FEC: can't find phy-handle\n");
+ }
+
+ return NULL;
+}
+
+int eth_phy_get_addr(struct udevice *dev)
+{
+ struct ofnode_phandle_args phandle_args;
+ int reg;
+
+ if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
+ &phandle_args)) {
+ debug("Failed to find phy-handle");
+ return -ENODEV;
+ }
+
+ reg = ofnode_read_u32_default(phandle_args.node, "reg", 0);
+
+ return reg;
+}
+
+UCLASS_DRIVER(eth_phy_generic) = {
+ .id = UCLASS_ETH_PHY,
+ .name = "eth_phy_generic",
+ .per_device_auto_alloc_size = sizeof(struct eth_phy_device_priv),
+};
+
+U_BOOT_DRIVER(eth_phy_generic_drv) = {
+ .name = "eth_phy_generic_drv",
+ .id = UCLASS_ETH_PHY,
+};
diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c
index 345d37b..910c961 100644
--- a/drivers/net/fec_mxc.c
+++ b/drivers/net/fec_mxc.c
@@ -28,6 +28,7 @@
#include <asm-generic/gpio.h>
#include "fec_mxc.h"
+#include <eth_phy.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -1204,6 +1205,13 @@ int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr)
#endif
int ret;
+ if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+ if (enet_fused((ulong)addr)) {
+ printf("SoC fuse indicates Ethernet@0x%x is unavailable.\n", addr);
+ return -ENODEV;
+ }
+ }
+
#ifdef CONFIG_FEC_MXC_MDIO_BASE
/*
* The i.MX28 has two ethernet interfaces, but they are not equal.
@@ -1342,6 +1350,13 @@ static int fecmxc_probe(struct udevice *dev)
uint32_t start;
int ret;
+ if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+ if (enet_fused((ulong)priv->eth)) {
+ printf("SoC fuse indicates Ethernet@0x%lx is unavailable.\n", (ulong)priv->eth);
+ return -ENODEV;
+ }
+ }
+
if (IS_ENABLED(CONFIG_IMX8)) {
ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk);
if (ret < 0) {
@@ -1430,16 +1445,27 @@ static int fecmxc_probe(struct udevice *dev)
fec_reg_setup(priv);
priv->dev_id = dev->seq;
+
+#ifdef CONFIG_DM_ETH_PHY
+ bus = eth_phy_get_mdio_bus(dev);
+#endif
+
+ if (!bus) {
#ifdef CONFIG_FEC_MXC_MDIO_BASE
- bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq);
+ bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq);
#else
- bus = fec_get_miibus((ulong)priv->eth, dev->seq);
+ bus = fec_get_miibus((ulong)priv->eth, dev->seq);
#endif
+ }
if (!bus) {
ret = -ENOMEM;
goto err_mii;
}
+#ifdef CONFIG_DM_ETH_PHY
+ eth_phy_set_mdio_bus(dev, bus);
+#endif
+
priv->bus = bus;
priv->interface = pdata->phy_interface;
switch (priv->interface) {
diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h
index 3c8fdda..0e8f08a 100644
--- a/drivers/net/fec_mxc.h
+++ b/drivers/net/fec_mxc.h
@@ -273,8 +273,6 @@ struct fec_priv {
u32 clk_rate;
};
-void imx_get_mac_from_fuse(int dev_id, unsigned char *mac);
-
/**
* @brief Numbers of buffer descriptors for receiving
*
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index 8f1d759..8f0a897 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -56,6 +56,7 @@
#define MIIM_RTL8211F_PAGE_SELECT 0x1f
#define MIIM_RTL8211F_TX_DELAY 0x100
+#define MIIM_RTL8211F_RX_DELAY 0x8
#define MIIM_RTL8211F_LCR 0x10
static int rtl8211f_phy_extread(struct phy_device *phydev, int addr,
@@ -183,6 +184,16 @@ static int rtl8211f_config(struct phy_device *phydev)
reg &= ~MIIM_RTL8211F_TX_DELAY;
phy_write(phydev, MDIO_DEVAD_NONE, 0x11, reg);
+
+ /* enable RX-delay for rgmii-id and rgmii-rxid, otherwise disable it */
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x15);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ reg |= MIIM_RTL8211F_RX_DELAY;
+ else
+ reg &= ~MIIM_RTL8211F_RX_DELAY;
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, reg);
+
/* restore to default page 0 */
phy_write(phydev, MDIO_DEVAD_NONE,
MIIM_RTL8211F_PAGE_SELECT, 0x0);
diff --git a/drivers/pinctrl/nxp/pinctrl-scu.c b/drivers/pinctrl/nxp/pinctrl-scu.c
index aa11075..c032be7 100644
--- a/drivers/pinctrl/nxp/pinctrl-scu.c
+++ b/drivers/pinctrl/nxp/pinctrl-scu.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright 2018 NXP
+ * Copyright 2018-2019 NXP
*/
#include <common.h>
@@ -29,6 +29,11 @@ static int imx_pinconf_scu_set(struct imx_pinctrl_soc_info *info, u32 pad,
* to handle that in scfw, so config it in pad conf func
*/
+ if (!sc_rm_is_pad_owned(-1, pad)) {
+ debug("Pad[%u] is not owned by curr partition\n", pad);
+ return -EPERM;
+ }
+
val |= PADRING_IFMUX_EN_MASK;
val |= PADRING_GP_EN_MASK;
val |= (mux << PADRING_IFMUX_SHIFT) & PADRING_IFMUX_MASK;
@@ -57,7 +62,7 @@ int imx_pinctrl_scu_conf_pins(struct imx_pinctrl_soc_info *info, u32 *pin_data,
config_val = pin_data[j++];
ret = imx_pinconf_scu_set(info, pin_id, mux, config_val);
- if (ret)
+ if (ret && ret != -EPERM)
printf("Set pin %d, mux %d, val %d, error\n", pin_id,
mux, config_val);
}
diff --git a/drivers/power/domain/imx8-power-domain-legacy.c b/drivers/power/domain/imx8-power-domain-legacy.c
index 6f01a60..7ba4056 100644
--- a/drivers/power/domain/imx8-power-domain-legacy.c
+++ b/drivers/power/domain/imx8-power-domain-legacy.c
@@ -11,6 +11,7 @@
#include <asm/arch/power-domain.h>
#include <dm/device-internal.h>
#include <dm/device.h>
+#include <dm/uclass-internal.h>
#include <asm/arch/sci/sci.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -19,6 +20,68 @@ struct imx8_power_domain_priv {
bool state_on;
};
+static bool check_device_power_off(struct udevice *dev,
+ const char *permanent_on_devices[],
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (!strcmp(dev->name, permanent_on_devices[i]))
+ return false;
+ }
+
+ return true;
+}
+
+void imx8_power_off_pd_devices(const char *permanent_on_devices[], int size)
+{
+ struct udevice *dev;
+ struct power_domain pd;
+
+ for (uclass_find_first_device(UCLASS_POWER_DOMAIN, &dev); dev;
+ uclass_find_next_device(&dev)) {
+ if (!device_active(dev))
+ continue;
+ /*
+ * Power off active pd devices except the permanent
+ * power on devices
+ */
+ if (check_device_power_off(dev, permanent_on_devices, size)) {
+ pd.dev = dev;
+ power_domain_off(&pd);
+ }
+ }
+}
+
+int imx8_power_domain_lookup_name(const char *name,
+ struct power_domain *power_domain)
+{
+ struct udevice *dev;
+ struct power_domain_ops *ops;
+ int ret;
+
+ debug("%s(power_domain=%p name=%s)\n", __func__, power_domain, name);
+
+ ret = uclass_get_device_by_name(UCLASS_POWER_DOMAIN, name, &dev);
+ if (ret) {
+ printf("%s fail: %s, ret = %d\n", __func__, name, ret);
+ return ret;
+ }
+
+ ops = (struct power_domain_ops *)dev->driver->ops;
+ power_domain->dev = dev;
+ ret = ops->request(power_domain);
+ if (ret) {
+ debug("ops->request() failed: %d\n", ret);
+ return ret;
+ }
+
+ debug("%s ok: %s\n", __func__, dev->name);
+
+ return 0;
+}
+
static int imx8_power_domain_request(struct power_domain *power_domain)
{
debug("%s(power_domain=%p)\n", __func__, power_domain);
@@ -62,7 +125,10 @@ static int imx8_power_domain_on(struct power_domain *power_domain)
if (ppriv->state_on)
return 0;
- if (pdata->resource_id != SC_R_LAST) {
+ if (pdata->resource_id != SC_R_NONE) {
+ if (!sc_rm_is_resource_owned(-1, pdata->resource_id))
+ printf("%s [%d] not owned by curr partition\n", dev->name, pdata->resource_id);
+
ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id,
SC_PM_PW_MODE_ON);
if (ret) {
@@ -108,14 +174,14 @@ static int imx8_power_domain_off_node(struct power_domain *power_domain)
}
}
- if (pdata->resource_id != SC_R_LAST) {
- if (!sc_rm_is_resource_owned(-1, pdata->resource_id)) {
- printf("%s not owned by curr partition\n", dev->name);
- return 0;
- }
+ if (pdata->resource_id != SC_R_NONE) {
ret = sc_pm_set_resource_power_mode(-1, pdata->resource_id,
SC_PM_PW_MODE_OFF);
if (ret) {
+ if (!sc_rm_is_resource_owned(-1, pdata->resource_id)) {
+ printf("%s not owned by curr partition %d\n", dev->name, pdata->resource_id);
+ return 0;
+ }
printf("Error: %s Power off failed! (error = %d)\n",
dev->name, ret);
return -EIO;
@@ -171,7 +237,7 @@ static int imx8_power_domain_off_parentnodes(struct power_domain *power_domain)
}
/* power off parent */
- if (pdata->resource_id != SC_R_LAST) {
+ if (pdata->resource_id != SC_R_NONE) {
ret = sc_pm_set_resource_power_mode(-1,
pdata->resource_id,
SC_PM_PW_MODE_OFF);
@@ -313,4 +379,5 @@ U_BOOT_DRIVER(imx8_power_domain) = {
.platdata_auto_alloc_size = sizeof(struct imx8_power_domain_platdata),
.priv_auto_alloc_size = sizeof(struct imx8_power_domain_priv),
.ops = &imx8_power_domain_ops,
+ .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF,
};
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index bdf8dc6..97d4163 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -26,6 +26,15 @@ config IMX_SCU_THERMAL
boot is hold to the cool device to throttle CPUs when the passive
trip is crossed
+config IMX_TMU
+ bool "Thermal Management Unit driver for NXP i.MX8M"
+ depends on ARCH_IMX8M
+ help
+ Support for Temperature sensors on NXP i.MX8M.
+ It supports one critical trip point and one passive trip point.
+ The boot is hold to the cool device to throttle CPUs when the
+ passive trip is crossed
+
config TI_DRA7_THERMAL
bool "Temperature sensor driver for TI dra7xx SOCs"
help
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index ef2929d..15fe847 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_IMX_SCU_THERMAL) += imx_scu_thermal.o
obj-$(CONFIG_TI_DRA7_THERMAL) += ti-bandgap.o
+obj-$(CONFIG_IMX_TMU) += imx_tmu.o
diff --git a/drivers/thermal/imx_scu_thermal.c b/drivers/thermal/imx_scu_thermal.c
index 7e17377..da13121 100644
--- a/drivers/thermal/imx_scu_thermal.c
+++ b/drivers/thermal/imx_scu_thermal.c
@@ -179,12 +179,20 @@ static int imx_sc_thermal_ofdata_to_platdata(struct udevice *dev)
return 0;
}
+static const sc_rsrc_t imx8qm_sensor_rsrc[] = {
+ SC_R_A53, SC_R_A72, SC_R_GPU_0_PID0, SC_R_GPU_1_PID0,
+ SC_R_DRC_0, SC_R_DRC_1, SC_R_VPU_PID0, SC_R_PMIC_0,
+ SC_R_PMIC_1, SC_R_PMIC_2,
+};
+
static const sc_rsrc_t imx8qxp_sensor_rsrc[] = {
SC_R_SYSTEM, SC_R_DRC_0, SC_R_PMIC_0,
SC_R_PMIC_1, SC_R_PMIC_2,
};
static const struct udevice_id imx_sc_thermal_ids[] = {
+ { .compatible = "nxp,imx8qm-sc-tsens", .data =
+ (ulong)&imx8qm_sensor_rsrc, },
{ .compatible = "nxp,imx8qxp-sc-tsens", .data =
(ulong)&imx8qxp_sensor_rsrc, },
{ }
diff --git a/drivers/thermal/imx_tmu.c b/drivers/thermal/imx_tmu.c
new file mode 100644
index 0000000..4ca2208
--- /dev/null
+++ b/drivers/thermal/imx_tmu.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017~2020 NXP
+ *
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sys_proto.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+#include <errno.h>
+#include <fuse.h>
+#include <malloc.h>
+#include <thermal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define SITES_MAX 16
+#define FLAGS_VER2 0x1
+#define FLAGS_VER3 0x2
+
+#define TMR_DISABLE 0x0
+#define TMR_ME 0x80000000
+#define TMR_ALPF 0x0c000000
+#define TMTMIR_DEFAULT 0x00000002
+#define TIER_DISABLE 0x0
+
+#define TER_EN 0x80000000
+#define TER_ADC_PD 0x40000000
+#define TER_ALPF 0x3
+
+/*
+ * i.MX TMU Registers
+ */
+struct imx_tmu_site_regs {
+ u32 tritsr; /* Immediate Temperature Site Register */
+ u32 tratsr; /* Average Temperature Site Register */
+ u8 res0[0x8];
+};
+
+struct imx_tmu_regs {
+ u32 tmr; /* Mode Register */
+ u32 tsr; /* Status Register */
+ u32 tmtmir; /* Temperature measurement interval Register */
+ u8 res0[0x14];
+ u32 tier; /* Interrupt Enable Register */
+ u32 tidr; /* Interrupt Detect Register */
+ u32 tiscr; /* Interrupt Site Capture Register */
+ u32 ticscr; /* Interrupt Critical Site Capture Register */
+ u8 res1[0x10];
+ u32 tmhtcrh; /* High Temperature Capture Register */
+ u32 tmhtcrl; /* Low Temperature Capture Register */
+ u8 res2[0x8];
+ u32 tmhtitr; /* High Temperature Immediate Threshold */
+ u32 tmhtatr; /* High Temperature Average Threshold */
+ u32 tmhtactr; /* High Temperature Average Crit Threshold */
+ u8 res3[0x24];
+ u32 ttcfgr; /* Temperature Configuration Register */
+ u32 tscfgr; /* Sensor Configuration Register */
+ u8 res4[0x78];
+ struct imx_tmu_site_regs site[SITES_MAX];
+ u8 res5[0x9f8];
+ u32 ipbrr0; /* IP Block Revision Register 0 */
+ u32 ipbrr1; /* IP Block Revision Register 1 */
+ u8 res6[0x310];
+ u32 ttr0cr; /* Temperature Range 0 Control Register */
+ u32 ttr1cr; /* Temperature Range 1 Control Register */
+ u32 ttr2cr; /* Temperature Range 2 Control Register */
+ u32 ttr3cr; /* Temperature Range 3 Control Register */
+};
+
+struct imx_tmu_regs_v2 {
+ u32 ter; /* TMU enable Register */
+ u32 tsr; /* Status Register */
+ u32 tier; /* Interrupt enable register */
+ u32 tidr; /* Interrupt detect register */
+ u32 tmhtitr; /* Monitor high temperature immediate threshold register */
+ u32 tmhtatr; /* Monitor high temperature average threshold register */
+ u32 tmhtactr; /* TMU monitor high temperature average critical threshold register */
+ u32 tscr; /* Sensor value capture register */
+ u32 tritsr; /* Report immediate temperature site register 0 */
+ u32 tratsr; /* Report average temperature site register 0 */
+ u32 tasr; /* Amplifier setting register */
+ u32 ttmc; /* Test MUX control */
+ u32 tcaliv;
+};
+
+struct imx_tmu_regs_v3 {
+ u32 ter; /* TMU enable Register */
+ u32 tps; /* Status Register */
+ u32 tier; /* Interrupt enable register */
+ u32 tidr; /* Interrupt detect register */
+ u32 tmhtitr; /* Monitor high temperature immediate threshold register */
+ u32 tmhtatr; /* Monitor high temperature average threshold register */
+ u32 tmhtactr; /* TMU monitor high temperature average critical threshold register */
+ u32 tscr; /* Sensor value capture register */
+ u32 tritsr; /* Report immediate temperature site register 0 */
+ u32 tratsr; /* Report average temperature site register 0 */
+ u32 tasr; /* Amplifier setting register */
+ u32 ttmc; /* Test MUX control */
+ u32 tcaliv0;
+ u32 tcaliv1;
+ u32 tcaliv_m40;
+ u32 trim;
+};
+
+union tmu_regs {
+ struct imx_tmu_regs regs_v1;
+ struct imx_tmu_regs_v2 regs_v2;
+ struct imx_tmu_regs_v3 regs_v3;
+};
+
+struct imx_tmu_plat {
+ int critical;
+ int alert;
+ int polling_delay;
+ int id;
+ bool zone_node;
+ union tmu_regs *regs;
+};
+
+static int read_temperature(struct udevice *dev, int *temp)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ ulong drv_data = dev_get_driver_data(dev);
+ u32 val;
+ u32 retry = 10;
+ u32 valid = 0;
+
+ do {
+ mdelay(100);
+ retry--;
+
+ if (drv_data & FLAGS_VER3) {
+ val = readl(&pdata->regs->regs_v3.tritsr);
+ valid = val & (1 << (30 + pdata->id));
+ } else if (drv_data & FLAGS_VER2) {
+ val = readl(&pdata->regs->regs_v2.tritsr);
+ /*
+ * Check if TEMP is in valid range, the V bit in TRITSR
+ * only reflects the RAW uncalibrated data
+ */
+ valid = ((val & 0xff) < 10 || (val & 0xff) > 125) ? 0 : 1;
+ } else {
+ val = readl(&pdata->regs->regs_v1.site[pdata->id].tritsr);
+ valid = val & 0x80000000;
+ }
+ } while (!valid && retry > 0);
+
+ if (retry > 0) {
+ if (drv_data & FLAGS_VER3) {
+ val = (val >> (pdata->id * 16)) & 0xff;
+ if (val & 0x80) /* Negative */
+ val = (~(val & 0x7f) + 1);
+
+ *temp = val;
+ if (*temp < -40 || *temp > 125) /* Check the range */
+ return -EINVAL;
+
+ *temp *= 1000;
+ } else {
+ *temp = (val & 0xff) * 1000;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int imx_tmu_get_temp(struct udevice *dev, int *temp)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ int cpu_tmp = 0;
+ int ret;
+
+ ret = read_temperature(dev, &cpu_tmp);
+ if (ret)
+ return ret;
+
+ while (cpu_tmp >= pdata->alert) {
+ printf("CPU Temperature (%dC) has beyond alert (%dC), close to critical (%dC)", cpu_tmp, pdata->alert, pdata->critical);
+ puts(" waiting...\n");
+ mdelay(pdata->polling_delay);
+ ret = read_temperature(dev, &cpu_tmp);
+ if (ret)
+ return ret;
+ }
+
+ *temp = cpu_tmp / 1000;
+
+ return 0;
+}
+
+static const struct dm_thermal_ops imx_tmu_ops = {
+ .get_temp = imx_tmu_get_temp,
+};
+
+static int imx_tmu_calibration(struct udevice *dev)
+{
+ int i, val, len, ret;
+ u32 range[4];
+ const fdt32_t *calibration;
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ ulong drv_data = dev_get_driver_data(dev);
+
+ debug("%s\n", __func__);
+
+ if (drv_data & (FLAGS_VER2 | FLAGS_VER3))
+ return 0;
+
+ ret = dev_read_u32_array(dev, "fsl,tmu-range", range, 4);
+ if (ret) {
+ printf("TMU: missing calibration range, ret = %d.\n", ret);
+ return ret;
+ }
+
+ /* Init temperature range registers */
+ writel(range[0], &pdata->regs->regs_v1.ttr0cr);
+ writel(range[1], &pdata->regs->regs_v1.ttr1cr);
+ writel(range[2], &pdata->regs->regs_v1.ttr2cr);
+ writel(range[3], &pdata->regs->regs_v1.ttr3cr);
+
+ calibration = dev_read_prop(dev, "fsl,tmu-calibration", &len);
+ if (!calibration || len % 8) {
+ printf("TMU: invalid calibration data.\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < len; i += 8, calibration += 2) {
+ val = fdt32_to_cpu(*calibration);
+ writel(val, &pdata->regs->regs_v1.ttcfgr);
+ val = fdt32_to_cpu(*(calibration + 1));
+ writel(val, &pdata->regs->regs_v1.tscfgr);
+ }
+
+ return 0;
+}
+
+void __weak imx_tmu_arch_init(void *reg_base)
+{
+}
+
+static void imx_tmu_init(struct udevice *dev)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ ulong drv_data = dev_get_driver_data(dev);
+
+ debug("%s\n", __func__);
+
+ if (drv_data & FLAGS_VER3) {
+ /* Disable monitoring */
+ writel(0x0, &pdata->regs->regs_v3.ter);
+
+ /* Disable interrupt, using polling instead */
+ writel(0x0, &pdata->regs->regs_v3.tier);
+
+ } else if (drv_data & FLAGS_VER2) {
+ /* Disable monitoring */
+ writel(0x0, &pdata->regs->regs_v2.ter);
+
+ /* Disable interrupt, using polling instead */
+ writel(0x0, &pdata->regs->regs_v2.tier);
+ } else {
+ /* Disable monitoring */
+ writel(TMR_DISABLE, &pdata->regs->regs_v1.tmr);
+
+ /* Disable interrupt, using polling instead */
+ writel(TIER_DISABLE, &pdata->regs->regs_v1.tier);
+
+ /* Set update_interval */
+ writel(TMTMIR_DEFAULT, &pdata->regs->regs_v1.tmtmir);
+ }
+
+ imx_tmu_arch_init((void *)pdata->regs);
+}
+
+static int imx_tmu_enable_msite(struct udevice *dev)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ ulong drv_data = dev_get_driver_data(dev);
+ u32 reg;
+
+ debug("%s\n", __func__);
+
+ if (!pdata->regs)
+ return -EIO;
+
+ if (drv_data & FLAGS_VER3) {
+ reg = readl(&pdata->regs->regs_v3.ter);
+ reg &= ~TER_EN;
+ writel(reg, &pdata->regs->regs_v3.ter);
+
+ writel(pdata->id << 30, &pdata->regs->regs_v3.tps);
+
+ reg &= ~TER_ALPF;
+ reg |= 0x1;
+ reg &= ~TER_ADC_PD;
+ writel(reg, &pdata->regs->regs_v3.ter);
+
+ /* Enable monitor */
+ reg |= TER_EN;
+ writel(reg, &pdata->regs->regs_v3.ter);
+ } else if (drv_data & FLAGS_VER2) {
+ reg = readl(&pdata->regs->regs_v2.ter);
+ reg &= ~TER_EN;
+ writel(reg, &pdata->regs->regs_v2.ter);
+
+ reg &= ~TER_ALPF;
+ reg |= 0x1;
+ writel(reg, &pdata->regs->regs_v2.ter);
+
+ /* Enable monitor */
+ reg |= TER_EN;
+ writel(reg, &pdata->regs->regs_v2.ter);
+ } else {
+ /* Clear the ME before setting MSITE and ALPF*/
+ reg = readl(&pdata->regs->regs_v1.tmr);
+ reg &= ~TMR_ME;
+ writel(reg, &pdata->regs->regs_v1.tmr);
+
+ reg |= 1 << (15 - pdata->id);
+ reg |= TMR_ALPF;
+ writel(reg, &pdata->regs->regs_v1.tmr);
+
+ /* Enable ME */
+ reg |= TMR_ME;
+ writel(reg, &pdata->regs->regs_v1.tmr);
+ }
+
+ return 0;
+}
+
+static int imx_tmu_bind(struct udevice *dev)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ int ret;
+ ofnode node, offset;
+ const char *name;
+ const void *prop;
+
+ debug("%s dev name %s\n", __func__, dev->name);
+
+ prop = dev_read_prop(dev, "compatible", NULL);
+ if (!prop)
+ return 0;
+
+ pdata->zone_node = 1;
+
+ node = ofnode_path("/thermal-zones");
+ ofnode_for_each_subnode(offset, node) {
+ /* Bind the subnode to this driver */
+ name = ofnode_get_name(offset);
+
+ ret = device_bind_with_driver_data(dev, dev->driver, name,
+ dev->driver_data, offset,
+ NULL);
+ if (ret)
+ printf("Error binding driver '%s': %d\n",
+ dev->driver->name, ret);
+ }
+
+ return 0;
+}
+
+static int imx_tmu_parse_fdt(struct udevice *dev)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev), *p_parent_data;
+ struct ofnode_phandle_args args;
+ ofnode trips_np;
+ int ret;
+
+ debug("%s dev name %s\n", __func__, dev->name);
+
+ if (pdata->zone_node) {
+ pdata->regs = (union tmu_regs *)dev_read_addr_ptr(dev);
+
+ if (!pdata->regs)
+ return -EINVAL;
+ return 0;
+ }
+
+ p_parent_data = dev_get_platdata(dev->parent);
+ if (p_parent_data->zone_node)
+ pdata->regs = p_parent_data->regs;
+
+ ret = dev_read_phandle_with_args(dev, "thermal-sensors",
+ "#thermal-sensor-cells",
+ 0, 0, &args);
+ if (ret)
+ return ret;
+
+ if (!ofnode_equal(args.node, dev_ofnode(dev->parent)))
+ return -EFAULT;
+
+ if (args.args_count >= 1)
+ pdata->id = args.args[0];
+ else
+ pdata->id = 0;
+
+ debug("args.args_count %d, id %d\n", args.args_count, pdata->id);
+
+ pdata->polling_delay = dev_read_u32_default(dev, "polling-delay", 1000);
+
+ trips_np = ofnode_path("/thermal-zones/cpu-thermal/trips");
+ ofnode_for_each_subnode(trips_np, trips_np) {
+ const char *type;
+
+ type = ofnode_get_property(trips_np, "type", NULL);
+ if (!type)
+ continue;
+ if (!strcmp(type, "critical"))
+ pdata->critical = ofnode_read_u32_default(trips_np, "temperature", 85);
+ else if (strcmp(type, "passive") == 0)
+ pdata->alert = ofnode_read_u32_default(trips_np, "temperature", 80);
+ else
+ continue;
+ }
+
+ debug("id %d polling_delay %d, critical %d, alert %d\n",
+ pdata->id, pdata->polling_delay, pdata->critical, pdata->alert);
+
+ return 0;
+}
+
+static int imx_tmu_probe(struct udevice *dev)
+{
+ struct imx_tmu_plat *pdata = dev_get_platdata(dev);
+ int ret;
+
+ ret = imx_tmu_parse_fdt(dev);
+ if (ret) {
+ printf("Error in parsing TMU FDT %d\n", ret);
+ return ret;
+ }
+
+ if (pdata->zone_node) {
+ imx_tmu_init(dev);
+ imx_tmu_calibration(dev);
+ } else {
+ imx_tmu_enable_msite(dev);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id imx_tmu_ids[] = {
+ { .compatible = "fsl,imx8mq-tmu", },
+ { .compatible = "fsl,imx8mm-tmu", .data = FLAGS_VER2, },
+ { .compatible = "fsl,imx8mp-tmu", .data = FLAGS_VER3, },
+ { }
+};
+
+U_BOOT_DRIVER(imx_tmu) = {
+ .name = "imx_tmu",
+ .id = UCLASS_THERMAL,
+ .ops = &imx_tmu_ops,
+ .of_match = imx_tmu_ids,
+ .bind = imx_tmu_bind,
+ .probe = imx_tmu_probe,
+ .platdata_auto_alloc_size = sizeof(struct imx_tmu_plat),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 58ca82d..46aa3fe 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -122,6 +122,10 @@ config USB_GADGET_VBUS_DRAW
This value will be used except for system-specific gadget
drivers that have more specific information.
+config SDP_LOADADDR
+ hex "Default load address at SDP_WRITE and SDP_JUMP"
+ default 0
+
# Selected by UDC drivers that support high-speed operation.
config USB_GADGET_DUALSPEED
bool
diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c
index 50836db..1732a3a 100644
--- a/drivers/usb/gadget/f_sdp.c
+++ b/drivers/usb/gadget/f_sdp.c
@@ -276,7 +276,7 @@ static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req)
sdp->error_status = SDP_WRITE_FILE_COMPLETE;
sdp->state = SDP_STATE_RX_FILE_DATA;
- sdp->dnl_address = be32_to_cpu(cmd->addr);
+ sdp->dnl_address = cmd->addr ? be32_to_cpu(cmd->addr) : CONFIG_SDP_LOADADDR;
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->dnl_bytes = sdp->dnl_bytes_remaining;
sdp->next_state = SDP_STATE_IDLE;
@@ -304,7 +304,7 @@ static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req)
sdp->always_send_status = false;
sdp->error_status = 0;
- sdp->jmp_address = be32_to_cpu(cmd->addr);
+ sdp->jmp_address = cmd->addr ? be32_to_cpu(cmd->addr) : CONFIG_SDP_LOADADDR;
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_JUMP;
break;
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index f2ceb51..04eb3c0 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -380,6 +380,14 @@ int ehci_hcd_init(int index, enum usb_init_type init,
if (index > 3)
return -EINVAL;
+ if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+ if (usb_fused((ulong)ehci)) {
+ printf("SoC fuse indicates USB@0x%lx is unavailable.\n",
+ (ulong)ehci);
+ return -ENODEV;
+ }
+ }
+
ret = ehci_mx6_common_init(ehci, index);
if (ret)
return ret;
@@ -577,6 +585,14 @@ static int ehci_usb_probe(struct udevice *dev)
struct ehci_hcor *hcor;
int ret;
+ if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+ if (usb_fused((ulong)ehci)) {
+ printf("SoC fuse indicates USB@0x%lx is unavailable.\n",
+ (ulong)ehci);
+ return -ENODEV;
+ }
+ }
+
priv->ehci = ehci;
priv->portnr = dev->seq;
priv->init_type = type;