U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
February 2024
- 210 participants
- 489 discussions

[PATCH v2 1/1] drivers: misc: Add socfpga_dtreg driver for Intel SoCFPGA
by wan.yee.lau@intel.com 05 Feb '24
by wan.yee.lau@intel.com 05 Feb '24
05 Feb '24
From: Wan Yee Lau <wan.yee.lau(a)intel.com>
Add socfpga_dtreg driver enablement for Intel SoCFPGA.
Signed-off-by: Wan Yee Lau <wan.yee.lau(a)intel.com>
---
Changes for v2:
- Rearranged header file in socfpga_drteg.c, moved <asm/io.h> lower.
arch/arm/Kconfig | 2 +
.../misc/socfpga_dtreg.txt | 74 +++++++++++
drivers/misc/Kconfig | 7 ++
drivers/misc/Makefile | 1 +
drivers/misc/socfpga_dtreg.c | 117 ++++++++++++++++++
5 files changed, 201 insertions(+)
create mode 100644 doc/device-tree-bindings/misc/socfpga_dtreg.txt
create mode 100644 drivers/misc/socfpga_dtreg.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 531b081de9..6087345eb8 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1092,6 +1092,8 @@ config ARCH_SOCFPGA
select SPL_LIBGENERIC_SUPPORT
select SPL_OF_CONTROL
select SPL_SEPARATE_BSS if TARGET_SOCFPGA_SOC64
+ select SPL_DRIVERS_MISC if TARGET_SOCFPGA_SOC64
+ select SPL_SOCFPGA_DT_REG if TARGET_SOCFPGA_SOC64
select SPL_SERIAL
select SPL_SYSRESET
select SPL_WATCHDOG
diff --git a/doc/device-tree-bindings/misc/socfpga_dtreg.txt b/doc/device-tree-bindings/misc/socfpga_dtreg.txt
new file mode 100644
index 0000000000..6057f792b9
--- /dev/null
+++ b/doc/device-tree-bindings/misc/socfpga_dtreg.txt
@@ -0,0 +1,74 @@
+* Firewall and privilege register settings in device tree
+
+Required properties:
+--------------------
+
+- compatible: should contain "intel,socfpga-dtreg"
+- reg: Physical base address and size of block register.
+- intel,offset-settings: 32-bit offset address of block register,
+ followed by 32-bit value settings and
+ the masking bits, only masking bit
+ set to 1 allows modification.
+
+The device tree node which describes secure and privilege register access
+configuration in compile time.
+
+Most of these registers are expected to work except for the case which some
+registers configuration are required for granting access to some other
+registers, for example CCU registers have to be properly configured before
+allowing register configuration access to fpga2sdram firewall as shown in
+below example.
+
+Some registers depend on runtime data for proper configuration are expected
+to be part of driver that generating these data for example configuration for
+soc_noc_fw_ddr_mpu_inst_0_ddr_scr block register depend on DDR size parsed from
+memory device tree node.
+
+Please refer details of tested examples below for both fpga2sdram and QoS
+configuration with default reset value and the comments.
+
+Example:
+--------
+
+Configuration for multiple dtreg node support in device tree:
+
+ socfpga_dtreg0: socfpga-dtreg0 {
+ compatible = "intel,socfpga-dtreg";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ bootph-all;
+
+ coh_cpu0_bypass_OC_Firewall_main_Firewall@f7100200 {
+ reg = <0xf7100200 0x00000014>;
+ intel,offset-settings =
+ /* Disable ocram security at CCU for non secure access */
+ <0x0000004 0x8000ffff 0xe007ffff>,
+ <0x0000008 0x8000ffff 0xe007ffff>,
+ <0x000000c 0x8000ffff 0xe007ffff>,
+ <0x0000010 0x8000ffff 0xe007ffff>;
+ bootph-all;
+ };
+ };
+
+ socfpga_dtreg1: socfpga-dtreg1 {
+ compatible = "intel,socfpga-dtreg";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ bootph-all;
+
+ soc_noc_fw_mpfe_csr_inst_0_mpfe_scr@f8020000 {
+ reg = <0xf8020000 0x0000001c>;
+ intel,offset-settings =
+ /* Disable MPFE firewall for SMMU */
+ <0x00000000 0x00010101 0x00010101>,
+ /* Disable MPFE firewall for HMC adapter */
+ <0x00000004 0x00000001 0x00010101>;
+ bootph-all;
+ };
+ };
+
+To call the nodes use:
+
+ ret = uclass_get_device_by_name(UCLASS_NOP, "socfpga-dtreg0", &dev);
+ ret = uclass_get_device_by_name(UCLASS_NOP, "socfpga-dtreg1", &dev);
+
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index fccd9b89b8..c423905ba2 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -683,4 +683,11 @@ config SL28CPLD
the base driver which provides common access methods for the
sub-drivers.
+config SPL_SOCFPGA_DT_REG
+ bool "Enable register setting from device tree in SPL"
+ depends on SPL
+ help
+ Enable register setting from device tree. This also
+ provides user a clean interface and all register settings are
+ centralized in one place, device tree.
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index b67b82358a..b74c8b9f80 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -89,3 +89,4 @@ obj-$(CONFIG_K3_AVS0) += k3_avs.o
obj-$(CONFIG_ESM_K3) += k3_esm.o
obj-$(CONFIG_ESM_PMIC) += esm_pmic.o
obj-$(CONFIG_SL28CPLD) += sl28cpld.o
+obj-$(CONFIG_SPL_SOCFPGA_SEC_REG) += socfpga_dtreg.o
diff --git a/drivers/misc/socfpga_dtreg.c b/drivers/misc/socfpga_dtreg.c
new file mode 100644
index 0000000000..a8554bfd1d
--- /dev/null
+++ b/drivers/misc/socfpga_dtreg.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Intel Corporation <www.intel.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/sizes.h>
+
+#define NUMBER_OF_ELEMENTS 3
+
+static int socfpga_dtreg_probe(struct udevice *dev)
+{
+ const fdt32_t *list;
+ fdt_addr_t offset, base;
+ fdt_val_t val, read_val, mask, set_mask;
+ int size, i;
+ u32 blk_sz, reg;
+ ofnode node;
+ const char *name = NULL;
+
+ debug("%s(dev=%p)\n", __func__, dev);
+
+ if (!dev_has_ofnode(dev))
+ return 0;
+
+ dev_for_each_subnode(node, dev) {
+ name = ofnode_get_name(node);
+ if (!name)
+ return -EINVAL;
+
+ if (ofnode_read_u32_index(node, "reg", 1, &blk_sz))
+ return -EINVAL;
+
+ base = ofnode_get_addr(node);
+ if (base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ debug("%s(node_offset 0x%lx node_name %s ", __func__,
+ node.of_offset, name);
+ debug("node addr 0x%llx blk sz 0x%x)\n", base, blk_sz);
+
+ list = ofnode_read_prop(node, "intel,offset-settings", &size);
+ if (!list)
+ return -EINVAL;
+
+ debug("%s(intel,offset-settings property size=%x)\n", __func__,
+ size);
+ size /= sizeof(*list) * NUMBER_OF_ELEMENTS;
+
+ /*
+ * First element: offset
+ * Second element: val
+ * Third element: mask
+ */
+ for (i = 0; i < size; i++) {
+ offset = fdt32_to_cpu(*list++);
+ val = fdt32_to_cpu(*list++);
+
+ /* Reads the masking bit value from the list */
+ mask = fdt32_to_cpu(*list++);
+
+ /*
+ * Reads out the offsets, value and masking bits
+ * Ex: <0x00000000 0x00000230 0xffffffff>
+ */
+ debug("%s(intel,offset-settings 0x%llx : 0x%llx : 0x%llx)\n",
+ __func__, offset, val, mask);
+
+ if (blk_sz < offset + SZ_4) {
+ printf("%s: Overflow as offset 0x%llx or reg",
+ __func__, offset);
+ printf(" write is more than block size 0x%x\n",
+ blk_sz);
+ return -EINVAL;
+ }
+
+ if (mask != 0) {
+ if (mask == 0xffffffff) {
+ reg = base + offset;
+ writel(val, (uintptr_t)reg);
+ } else {
+ /* Mask the value with the masking bits */
+ set_mask = val & mask;
+
+ reg = base + offset;
+
+ /* Clears and sets specific bits in the register */
+ clrsetbits_le32((uintptr_t)reg, mask, set_mask);
+ }
+ }
+
+ read_val = readl((uintptr_t)reg);
+
+ /* Reads out the register, masked value and the read value */
+ debug("%s(reg 0x%x = wr : 0x%llx rd : 0x%llx)\n",
+ __func__, reg, set_mask, read_val);
+
+ }
+ }
+
+ return 0;
+};
+
+static const struct udevice_id socfpga_dtreg_ids[] = {
+ {.compatible = "intel,socfpga-dtreg"},
+ { }
+};
+
+U_BOOT_DRIVER(socfpga_dtreg) = {
+ .name = "socfpga-dtreg",
+ .id = UCLASS_NOP,
+ .of_match = socfpga_dtreg_ids,
+ .probe = socfpga_dtreg_probe,
+};
--
2.25.1
2
1
From: BELOUARGA Mohamed <m.belouarga(a)technologyandstrategy.com>
The actual driver does not work when there is no linked PHY. These
changes add support for fixed-link feature in the device tree.
Signed-off-by: BELOUARGA Mohamed <m.belouarga(a)technologyandstrategy.com>
---
drivers/net/macb.c | 189 ++++++++++++++++++++++++++++-----------------
1 file changed, 117 insertions(+), 72 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index bfc48dac07..53f0189153 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -128,6 +128,8 @@ struct macb_device {
unsigned long dummy_desc_dma;
const struct device *dev;
+ unsigned int duplex;
+ unsigned int speed;
unsigned short phy_addr;
struct mii_dev *bus;
#ifdef CONFIG_PHYLIB
@@ -178,6 +180,12 @@ static int gem_is_gigabit_capable(struct macb_device *macb)
return macb_is_gem(macb) && !cpu_is_sama5d2() && !cpu_is_sama5d4();
}
+/* Is the port a fixed link */
+static int macb_port_is_fixed_link(struct macb_device *macb)
+{
+ return macb->phy_addr > PHY_MAX_ADDR;
+}
+
static void macb_mdio_write(struct macb_device *macb, u8 phy_adr, u8 reg,
u16 value)
{
@@ -666,97 +674,110 @@ static int macb_phy_init(struct udevice *dev, const char *name)
int i;
arch_get_mdio_control(name);
- /* Auto-detect phy_addr */
- ret = macb_phy_find(macb, name);
- if (ret)
- return ret;
+ /* If port is not fixed -> setup PHY */
+ if (!macb_port_is_fixed_link(macb)) {
+ /* Auto-detect phy_addr */
+ ret = macb_phy_find(macb, name);
+ if (ret)
+ return ret;
- /* Check if the PHY is up to snuff... */
- phy_id = macb_mdio_read(macb, macb->phy_addr, MII_PHYSID1);
- if (phy_id == 0xffff) {
- printf("%s: No PHY present\n", name);
- return -ENODEV;
- }
+ /* Check if the PHY is up to snuff... */
+ phy_id = macb_mdio_read(macb, macb->phy_addr, MII_PHYSID1);
+ if (phy_id == 0xffff) {
+ printf("%s: No PHY present\n", name);
+ return -ENODEV;
+ }
#ifdef CONFIG_PHYLIB
- macb->phydev = phy_connect(macb->bus, macb->phy_addr, dev,
- macb->phy_interface);
- if (!macb->phydev) {
- printf("phy_connect failed\n");
- return -ENODEV;
- }
+ macb->phydev = phy_connect(macb->bus, macb->phy_addr, dev,
+ macb->phy_interface);
+ if (!macb->phydev) {
+ printf("phy_connect failed\n");
+ return -ENODEV;
+ }
- phy_config(macb->phydev);
+ phy_config(macb->phydev);
#endif
- status = macb_mdio_read(macb, macb->phy_addr, MII_BMSR);
- if (!(status & BMSR_LSTATUS)) {
- /* Try to re-negotiate if we don't have link already. */
- macb_phy_reset(macb, name);
-
- for (i = 0; i < MACB_AUTONEG_TIMEOUT / 100; i++) {
- status = macb_mdio_read(macb, macb->phy_addr, MII_BMSR);
- if (status & BMSR_LSTATUS) {
- /*
- * Delay a bit after the link is established,
- * so that the next xfer does not fail
- */
- mdelay(10);
- break;
+ status = macb_mdio_read(macb, macb->phy_addr, MII_BMSR);
+ if (!(status & BMSR_LSTATUS)) {
+ /* Try to re-negotiate if we don't have link already. */
+ macb_phy_reset(macb, name);
+
+ for (i = 0; i < MACB_AUTONEG_TIMEOUT / 100; i++) {
+ status = macb_mdio_read(macb, macb->phy_addr, MII_BMSR);
+ if (status & BMSR_LSTATUS) {
+ /*
+ * Delay a bit after the link is established,
+ * so that the next xfer does not fail
+ */
+ mdelay(10);
+ break;
+ }
+ udelay(100);
}
- udelay(100);
}
- }
- if (!(status & BMSR_LSTATUS)) {
- printf("%s: link down (status: 0x%04x)\n",
- name, status);
- return -ENETDOWN;
- }
+ if (!(status & BMSR_LSTATUS)) {
+ printf("%s: link down (status: 0x%04x)\n",
+ name, status);
+ return -ENETDOWN;
+ }
- /* First check for GMAC and that it is GiB capable */
- if (gem_is_gigabit_capable(macb)) {
- lpa = macb_mdio_read(macb, macb->phy_addr, MII_STAT1000);
+ /* First check for GMAC and that it is GiB capable */
+ if (gem_is_gigabit_capable(macb)) {
+ lpa = macb_mdio_read(macb, macb->phy_addr, MII_STAT1000);
- if (lpa & (LPA_1000FULL | LPA_1000HALF | LPA_1000XFULL |
- LPA_1000XHALF)) {
- duplex = ((lpa & (LPA_1000FULL | LPA_1000XFULL)) ?
- 1 : 0);
+ if (lpa & (LPA_1000FULL | LPA_1000HALF | LPA_1000XFULL |
+ LPA_1000XHALF)) {
+ duplex = ((lpa & (LPA_1000FULL | LPA_1000XFULL)) ?
+ 1 : 0);
- printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n",
- name,
- duplex ? "full" : "half",
- lpa);
+ printf("%s: link up, 1000Mbps %s-duplex (lpa: 0x%04x)\n",
+ name,
+ duplex ? "full" : "half",
+ lpa);
- ncfgr = macb_readl(macb, NCFGR);
- ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
- ncfgr |= GEM_BIT(GBE);
+ ncfgr = macb_readl(macb, NCFGR);
+ ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD));
+ ncfgr |= GEM_BIT(GBE);
- if (duplex)
- ncfgr |= MACB_BIT(FD);
+ if (duplex)
+ ncfgr |= MACB_BIT(FD);
- macb_writel(macb, NCFGR, ncfgr);
+ macb_writel(macb, NCFGR, ncfgr);
- ret = macb_linkspd_cb(dev, _1000BASET);
- if (ret)
- return ret;
+ ret = macb_linkspd_cb(dev, _1000BASET);
+ if (ret)
+ return ret;
- return 0;
+ return 0;
+ }
}
- }
- /* fall back for EMAC checking */
- adv = macb_mdio_read(macb, macb->phy_addr, MII_ADVERTISE);
- lpa = macb_mdio_read(macb, macb->phy_addr, MII_LPA);
- media = mii_nway_result(lpa & adv);
- speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
- ? 1 : 0);
- duplex = (media & ADVERTISE_FULL) ? 1 : 0;
- printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
- name,
- speed ? "100" : "10",
- duplex ? "full" : "half",
- lpa);
+ /* fall back for EMAC checking */
+ adv = macb_mdio_read(macb, macb->phy_addr, MII_ADVERTISE);
+ lpa = macb_mdio_read(macb, macb->phy_addr, MII_LPA);
+ media = mii_nway_result(lpa & adv);
+ speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
+ ? 1 : 0);
+ duplex = (media & ADVERTISE_FULL) ? 1 : 0;
+ printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
+ name,
+ speed ? "100" : "10",
+ duplex ? "full" : "half",
+ lpa);
+ }
+ else {
+ /* if macb port is a fixed link */
+ /* TODO : manage gigabit capable processors */
+ speed = macb->speed;
+ duplex = macb->duplex;
+ printf("%s: link up, %sMbps %s-duplex\n",
+ name,
+ speed ? "100" : "10",
+ duplex ? "full" : "half");
+ }
ncfgr = macb_readl(macb, NCFGR);
ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | GEM_BIT(GBE));
@@ -1276,6 +1297,30 @@ int __weak macb_late_eth_of_to_plat(struct udevice *dev)
static int macb_eth_of_to_plat(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_plat(dev);
+ struct macb_device *macb = dev_get_priv(dev);
+ void *blob = (void *)gd->fdt_blob;
+ int node = dev_of_offset(dev);
+ int fl_node, speed_fdt;
+
+ /* fetch 'fixed-link' property */
+ fl_node = fdt_subnode_offset(blob, node, "fixed-link");
+ if (fl_node != -FDT_ERR_NOTFOUND) {
+ /* set phy_addr to invalid value for fixed link */
+ macb->phy_addr = PHY_MAX_ADDR + 1;
+ macb->duplex = fdtdec_get_bool(blob, fl_node, "full-duplex");
+ speed_fdt = fdtdec_get_int(blob, fl_node, "speed", 0);
+ if (speed_fdt == 100) {
+ macb->speed = 1;
+ }
+ else if (speed_fdt == 10) {
+ macb->speed = 0;
+ }
+ else {
+ printf("%s: The given speed %d of ethernet in the DT is not supported\n",
+ __func__, speed_fdt);
+ return -EINVAL;
+ }
+ }
pdata->iobase = (uintptr_t)dev_remap_addr(dev);
if (!pdata->iobase)
--
2.25.1
2
1
Writing to eMMC using HS200 mode work more reliably then other modes on
RK356x boards.
Add device tree props and enable Kconfig options for eMMC HS200 mode on
the generic RK3566/RK3568 board. Also enable the pinctrl driver in SPL
and add missing rk3568-generic.dtb to Makefile.
Signed-off-by: Jonas Karlman <jonas(a)kwiboo.se>
---
This patch depends on the following series:
- rockchip: rk35xx: Sync device tree with linux v6.8-rc1 [1]
- rockchip: rk35xx: Fix writing to eMMC [2]
[1] https://patchwork.ozlabs.org/cover/1891669/
[2] https://patchwork.ozlabs.org/cover/1891692/
---
arch/arm/dts/Makefile | 1 +
arch/arm/dts/rk3568-generic.dts | 12 +++++++++++-
configs/generic-rk3568_defconfig | 5 ++++-
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 50f35e3db3f0..0fcae77cefe3 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -181,6 +181,7 @@ dtb-$(CONFIG_ROCKCHIP_RK3568) += \
rk3566-soquartz-model-a.dtb \
rk3568-bpi-r2-pro.dtb \
rk3568-evb.dtb \
+ rk3568-generic.dtb \
rk3568-lubancat-2.dtb \
rk3568-nanopi-r5c.dtb \
rk3568-nanopi-r5s.dtb \
diff --git a/arch/arm/dts/rk3568-generic.dts b/arch/arm/dts/rk3568-generic.dts
index 1006ea55bb98..88eb1bfd2aab 100644
--- a/arch/arm/dts/rk3568-generic.dts
+++ b/arch/arm/dts/rk3568-generic.dts
@@ -10,7 +10,12 @@
model = "Generic RK3566/RK3568";
compatible = "rockchip,rk3568";
- chosen: chosen {
+ aliases {
+ mmc0 = &sdhci;
+ mmc1 = &sdmmc;
+ };
+
+ chosen {
stdout-path = "serial2:1500000n8";
};
};
@@ -18,6 +23,9 @@
&sdhci {
bus-width = <8>;
cap-mmc-highspeed;
+ mmc-hs200-1_8v;
+ no-sd;
+ no-sdio;
non-removable;
pinctrl-names = "default";
pinctrl-0 = <&emmc_bus8 &emmc_clk &emmc_cmd>;
@@ -28,6 +36,8 @@
bus-width = <4>;
cap-sd-highspeed;
disable-wp;
+ no-mmc;
+ no-sdio;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd>;
status = "okay";
diff --git a/configs/generic-rk3568_defconfig b/configs/generic-rk3568_defconfig
index 8f0a9c8c449f..18a62b0033a0 100644
--- a/configs/generic-rk3568_defconfig
+++ b/configs/generic-rk3568_defconfig
@@ -42,7 +42,7 @@ CONFIG_CMD_MMC=y
# CONFIG_SPL_DOS_PARTITION is not set
CONFIG_SPL_OF_CONTROL=y
CONFIG_OF_LIVE=y
-CONFIG_OF_SPL_REMOVE_PROPS="pinctrl-0 pinctrl-names clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
+CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
CONFIG_SPL_DM_SEQ_ALIAS=y
CONFIG_SPL_REGMAP=y
CONFIG_SPL_SYSCON=y
@@ -51,11 +51,14 @@ CONFIG_ROCKCHIP_GPIO=y
CONFIG_MISC=y
# CONFIG_ROCKCHIP_IODOMAIN is not set
CONFIG_SUPPORT_EMMC_RPMB=y
+CONFIG_MMC_HS200_SUPPORT=y
+CONFIG_SPL_MMC_HS200_SUPPORT=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_ROCKCHIP=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_SDMA=y
CONFIG_MMC_SDHCI_ROCKCHIP=y
+CONFIG_SPL_PINCTRL=y
CONFIG_SPL_RAM=y
CONFIG_BAUDRATE=1500000
CONFIG_DEBUG_UART_SHIFT=2
--
2.43.0
2
1
From: Andy Yan <andy.yan(a)rock-chips.com>
Enable USB releated config to support
boot from usb.
Signed-off-by: Andy Yan <andy.yan(a)rock-chips.com>
---
configs/evb-rk3588_defconfig | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/configs/evb-rk3588_defconfig b/configs/evb-rk3588_defconfig
index 0b7b4f2f627..8a6aa91cb29 100644
--- a/configs/evb-rk3588_defconfig
+++ b/configs/evb-rk3588_defconfig
@@ -40,6 +40,7 @@ CONFIG_SPL_ATF=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_GPT=y
CONFIG_CMD_MMC=y
+CONFIG_CMD_USB=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_REGULATOR=y
# CONFIG_SPL_DOS_PARTITION is not set
@@ -61,6 +62,9 @@ CONFIG_MMC_SDHCI_ROCKCHIP=y
CONFIG_PHY_REALTEK=y
CONFIG_DWC_ETH_QOS=y
CONFIG_DWC_ETH_QOS_ROCKCHIP=y
+CONFIG_PHY_ROCKCHIP_INNO_USB2=y
+CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
+CONFIG_PHY_ROCKCHIP_USBDP=y
CONFIG_REGULATOR_PWM=y
CONFIG_PWM_ROCKCHIP=y
CONFIG_SPL_RAM=y
@@ -68,4 +72,12 @@ CONFIG_BAUDRATE=1500000
CONFIG_DEBUG_UART_SHIFT=2
CONFIG_SYS_NS16550_MEM32=y
CONFIG_SYSRESET=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_GENERIC=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_GENERIC=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_GENERIC=y
CONFIG_ERRNO_STR=y
--
2.34.1
2
1
On 05.02.2024 10:00, Michael Nazzareno Trimarchi wrote:
> Hi
>
> Il lun 5 feb 2024, 07:43 Arseniy Krasnov <avkrasnov(a)salutedevices.com> ha
> scritto:
>
>> Hi, sorry, but pls, ping :)
>>
>> On 15.01.2024 09:01, Arseniy Krasnov wrote:
>>> Hi, thanks for review! Two questions below...
>>>
>>> On 09.01.2024 11:42, Michael Nazzareno Trimarchi wrote:
>>>> Hi Arseniy
>>>>
>>>>
>>>> On Fri, Dec 15, 2023 at 1:32 PM Arseniy Krasnov
>>>> <avkrasnov(a)salutedevices.com> wrote:
>>>>>
>>>>> Basic support for Amlogic Meson NAND controller on AXG.
>>>>>
>>>>> Based on Linux version 6.7.0-rc4.
>>>>>
>>>>> Signed-off-by: Arseniy Krasnov <avkrasnov(a)salutedevices.com>
>>>>> ---
>>>>> Changelog:
>>>>> v1 -> v2:
>>>>> * Update commit message with 'Based on Linux ...'.
>>>>> * Add Linux driver author to .c file header.
>>>>> * Add comment for defines 'NFC_DEFAULT_BUS_CYCLE' and
>>>>> 'NFC_DEFAULT_BUS_TIMING'.
>>>>> * Use 'dev_read_addr_index_ptr()' instead of 'dev_read_addr()'.
>>>>>
>>>>> drivers/mtd/nand/raw/Kconfig | 9 +
>>>>> drivers/mtd/nand/raw/Makefile | 1 +
>>>>> drivers/mtd/nand/raw/meson_nand.c | 1241 +++++++++++++++++++++++++++++
>>>>> 3 files changed, 1251 insertions(+)
>>>>> create mode 100644 drivers/mtd/nand/raw/meson_nand.c
>>>>>
>>>>> diff --git a/drivers/mtd/nand/raw/Kconfig
>> b/drivers/mtd/nand/raw/Kconfig
>>>>> index d624589a89..7b7b0226ab 100644
>>>>> --- a/drivers/mtd/nand/raw/Kconfig
>>>>> +++ b/drivers/mtd/nand/raw/Kconfig
>>>>> @@ -488,6 +488,15 @@ config NAND_ARASAN
>>>>> controller. This uses the hardware ECC for read and
>>>>> write operations.
>>>>>
>>>>> +config NAND_MESON
>>>>> + bool "Meson NAND support"
>>>>> + select SYS_NAND_SELF_INIT
>>>>> + depends on DM_MTD && ARCH_MESON
>>>>> + imply CMD_NAND
>>>>> + help
>>>>> + This enables Nand driver support for Meson raw NAND flash
>>>>> + controller.
>>>>> +
>>>>> config NAND_MXC
>>>>> bool "MXC NAND support"
>>>>> depends on CPU_ARM926EJS || CPU_ARM1136 || MX5
>>>>> diff --git a/drivers/mtd/nand/raw/Makefile
>> b/drivers/mtd/nand/raw/Makefile
>>>>> index add2b4cf65..5b4efd52c9 100644
>>>>> --- a/drivers/mtd/nand/raw/Makefile
>>>>> +++ b/drivers/mtd/nand/raw/Makefile
>>>>> @@ -61,6 +61,7 @@ obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
>>>>> obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o
>>>>> obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o
>>>>> obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o
>>>>> +obj-$(CONFIG_NAND_MESON) += meson_nand.o
>>>>> obj-$(CONFIG_NAND_MXC) += mxc_nand.o
>>>>> obj-$(CONFIG_NAND_MXS) += mxs_nand.o
>>>>> obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o
>>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c
>> b/drivers/mtd/nand/raw/meson_nand.c
>>>>> new file mode 100644
>>>>> index 0000000000..a6dbe99d84
>>>>> --- /dev/null
>>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>>> @@ -0,0 +1,1241 @@
>>>>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>>>>> +/*
>>>>> + * Amlogic Meson Nand Flash Controller Driver
>>>>> + *
>>>>> + * Copyright (c) 2018 Amlogic, inc.
>>>>> + * Author: Liang Yang <liang.yang(a)amlogic.com>
>>>>> + *
>>>>> + * Copyright (c) 2023 SaluteDevices, Inc.
>>>>> + * Author: Arseniy Krasnov <avkrasnov(a)salutedevices.com>
>>>>> + */
>>>>> +
>>>>> +#include <common.h>
>>>>> +#include <nand.h>
>>>>> +#include <asm/io.h>
>>>>> +#include <dm.h>
>>>>> +#include <dm/device_compat.h>
>>>>> +#include <dm/ofnode.h>
>>>>> +#include <dm/uclass.h>
>>>>> +#include <linux/bug.h>
>>>>> +#include <linux/clk-provider.h>
>>>>> +#include <linux/delay.h>
>>>>> +#include <linux/dma-mapping.h>
>>>>> +#include <linux/iopoll.h>
>>>>> +#include <linux/mtd/mtd.h>
>>>>> +#include <linux/mtd/rawnand.h>
>>>>> +#include <linux/sizes.h>
>>>>> +
>>>>> +#define NFC_CMD_IDLE (0xc << 14)
>>>>> +#define NFC_CMD_CLE (0x5 << 14)
>>>>> +#define NFC_CMD_ALE (0x6 << 14)
>>>>> +#define NFC_CMD_DWR (0x4 << 14)
>>>>> +#define NFC_CMD_DRD (0x8 << 14)
>>>>> +#define NFC_CMD_ADL ((0 << 16) | (3 << 20))
>>>>> +#define NFC_CMD_ADH ((1 << 16) | (3 << 20))
>>>>> +#define NFC_CMD_AIL ((2 << 16) | (3 << 20))
>>>>> +#define NFC_CMD_AIH ((3 << 16) | (3 << 20))
>>>>> +#define NFC_CMD_SEED ((8 << 16) | (3 << 20))
>>>>> +#define NFC_CMD_M2N ((0 << 17) | (2 << 20))
>>>>> +#define NFC_CMD_N2M ((1 << 17) | (2 << 20))
>>>>> +#define NFC_CMD_RB BIT(20)
>>>>> +#define NFC_CMD_SCRAMBLER_ENABLE BIT(19)
>>>>> +#define NFC_CMD_SCRAMBLER_DISABLE 0
>>>>> +#define NFC_CMD_SHORTMODE_DISABLE 0
>>>>> +#define NFC_CMD_RB_INT BIT(14)
>>>>> +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) |
>> BIT(16))
>>>>> +
>>>>> +#define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
>>>>> +
>>>>> +#define NFC_REG_CMD 0x00
>>>>> +#define NFC_REG_CFG 0x04
>>>>> +#define NFC_REG_DADR 0x08
>>>>> +#define NFC_REG_IADR 0x0c
>>>>> +#define NFC_REG_BUF 0x10
>>>>> +#define NFC_REG_INFO 0x14
>>>>> +#define NFC_REG_DC 0x18
>>>>> +#define NFC_REG_ADR 0x1c
>>>>> +#define NFC_REG_DL 0x20
>>>>> +#define NFC_REG_DH 0x24
>>>>> +#define NFC_REG_CADR 0x28
>>>>> +#define NFC_REG_SADR 0x2c
>>>>> +#define NFC_REG_PINS 0x30
>>>>> +#define NFC_REG_VER 0x38
>>>>> +
>>>>> +#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)
>> \
>>>>> + (
>> \
>>>>> + (cmd_dir) |
>> \
>>>>> + ((ran) << 19) |
>> \
>>>>> + ((bch) << 14) |
>> \
>>>>> + ((short_mode) << 13) |
>> \
>>>>> + (((page_size) & 0x7f) << 6) |
>> \
>>>>> + ((pages) & 0x3f)
>> \
>>>>> + )
>>>>> +
>>>>> +#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) &
>> 0xffff))
>>>>> +#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >>
>> 16) & 0xffff))
>>>>> +#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) &
>> 0xffff))
>>>>> +#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >>
>> 16) & 0xffff))
>>>>> +
>>>>> +#define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
>>>>> +
>>>>> +#define ECC_CHECK_RETURN_FF -1
>>>>> +
>>>>> +#define NAND_CE0 (0xe << 10)
>>>>> +#define NAND_CE1 (0xd << 10)
>>>>> +
>>>>> +#define DMA_BUSY_TIMEOUT_US 1000000
>>>>> +#define CMD_DRAIN_TIMEOUT_US 1000
>>>>> +#define ECC_POLL_TIMEOUT_US 15
>>>>> +
>>>>> +#define MAX_CE_NUM 2
>>>>> +
>>>>> +/* eMMC clock register, misc control */
>>>>> +#define CLK_SELECT_NAND BIT(31)
>>>>> +#define CLK_ALWAYS_ON_NAND BIT(24)
>>>>> +#define CLK_ENABLE_VALUE 0x245
>>>>> +
>>>>> +#define DIRREAD 1
>>>>> +#define DIRWRITE 0
>>>>> +
>>>>> +#define ECC_PARITY_BCH8_512B 14
>>>>> +#define ECC_COMPLETE BIT(31)
>>>>> +#define ECC_ERR_CNT(x) (((x) >> 24) & GENMASK(5, 0))
>>>>> +#define ECC_ZERO_CNT(x) (((x) >> 16) & GENMASK(5, 0))
>>>>> +#define ECC_UNCORRECTABLE 0x3f
>>>>> +
>>>>> +#define PER_INFO_BYTE 8
>>>>> +
>>>>> +#define NFC_SEND_CMD(host, cmd) \
>>>>> + (writel((cmd), (host)->reg_base + NFC_REG_CMD))
>>>>> +
>>>>> +#define NFC_GET_CMD(host) \
>>>>> + (readl((host)->reg_base + NFC_REG_CMD))
>>>>> +
>>>>> +#define NFC_CMDFIFO_SIZE(host) ((NFC_GET_CMD((host)) >> 22) &
>> GENMASK(4, 0))
>>>>> +
>>>>> +#define NFC_CMD_MAKE_IDLE(ce, delay) ((ce) | NFC_CMD_IDLE |
>> ((delay) & 0x3ff))
>>>>> +#define NFC_CMD_MAKE_DRD(ce, size) ((ce) | NFC_CMD_DRD | (size))
>>>>> +#define NFC_CMD_MAKE_DWR(ce, data) ((ce) | NFC_CMD_DWR | ((data)
>> & 0xff))
>>>>> +#define NFC_CMD_MAKE_CLE(ce, cmd_val) ((ce) | NFC_CMD_CLE |
>> ((cmd_val) & 0xff))
>>>>> +#define NFC_CMD_MAKE_ALE(ce, addr) ((ce) | NFC_CMD_ALE | ((addr)
>> & 0xff))
>>>>> +
>>>>> +#define NAND_TWB_TIME_CYCLE 10
>>>>> +
>>>>> +#define NFC_DEV_READY_TICK_MAX 5000
>>>>> +
>>>>> +/* Both values are recommended by vendor, as the most
>>>>> + * tested with almost all SLC NAND flash. Second value
>>>>> + * could be calculated dynamically from timing parameters,
>>>>> + * but we need both values for initial start of the NAND
>>>>> + * controller (e.g. before NAND subsystem processes timings),
>>>>> + * so use hardcoded constants.
>>>>> + */
>>>>> +#define NFC_DEFAULT_BUS_CYCLE 6
>>>>> +#define NFC_DEFAULT_BUS_TIMING 7
>>>>> +
>>>>
>>>> Still missing for me how this can be compliant with EDO mode timing
>> calculation
>>>
>>> You mean to implement this like in kernel driver?
>>>
>>>
>> https://elixir.bootlin.com/linux/v6.7-rc8/source/drivers/mtd/nand/raw/meson…
>>>
>
>
>
> Yes referring to that one. Right one what is important is not to full
> compliant here but at least understand the limit of the actual
> implementation to allow to add subsequent patches. Imx and driver was
> working on first edo mode for years. You can implement it or improve your
> commit message
>
> Michael
Ok, thanks for explanation!
Thanks, Arseniy
>
>>
>>>>
>>>>> +#define NFC_SEED_OFFSET 0xc2
>>>>> +#define NFC_SEED_MASK 0x7fff
>>>>> +
>>>>> +#define DMA_ADDR_ALIGN 8
>>>>> +
>>>>> +struct meson_nfc_nand_chip {
>>>>> + struct list_head node;
>>>>> + struct nand_chip nand;
>>>>> +
>>>>> + u32 bch_mode;
>>>>> + u8 *data_buf;
>>>>> + __le64 *info_buf;
>>>>> + u32 nsels;
>>>>> + u8 sels[];
>>>>> +};
>>>>> +
>>>>> +struct meson_nfc_param {
>>>>> + u32 chip_select;
>>>>> + u32 rb_select;
>>>>> +};
>>>>> +
>>>>> +struct meson_nfc {
>>>>> + void __iomem *reg_base;
>>>>> + void __iomem *reg_clk;
>>>>> + struct list_head chips;
>>>>> + struct meson_nfc_param param;
>>>>> + struct udevice *dev;
>>>>> + dma_addr_t daddr;
>>>>> + dma_addr_t iaddr;
>>>>> + u32 data_bytes;
>>>>> + u32 info_bytes;
>>>>> + u64 assigned_cs;
>>>>> +};
>>>>> +
>>>>> +struct meson_nand_ecc {
>>>>> + u32 bch;
>>>>> + u32 strength;
>>>>> + u32 size;
>>>>> +};
>>>>> +
>>>>> +enum {
>>>>> + NFC_ECC_BCH8_512 = 1,
>>>>> + NFC_ECC_BCH8_1K,
>>>>> + NFC_ECC_BCH24_1K,
>>>>> + NFC_ECC_BCH30_1K,
>>>>> + NFC_ECC_BCH40_1K,
>>>>> + NFC_ECC_BCH50_1K,
>>>>> + NFC_ECC_BCH60_1K,
>>>>> +};
>>>>> +
>>>>> +#define MESON_ECC_DATA(b, s, sz) { .bch = (b), .strength = (s), .size
>> = (sz) }
>>>>> +
>>>>> +static struct meson_nand_ecc meson_ecc[] = {
>>>>> + MESON_ECC_DATA(NFC_ECC_BCH8_512, 8, 512),
>>>>> + MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 1024),
>>>>> +};
>>>>> +
>>>>> +static int meson_nand_calc_ecc_bytes(int step_size, int strength)
>>>>> +{
>>>>> + int ecc_bytes;
>>>>> +
>>>>> + if (step_size == 512 && strength == 8)
>>>>> + return ECC_PARITY_BCH8_512B;
>>>>> +
>>>>> + ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
>>>>> + ecc_bytes = ALIGN(ecc_bytes, 2);
>>>>> +
>>>>> + return ecc_bytes;
>>>>> +}
>>>>> +
>>>>> +static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip
>> *nand)
>>>>> +{
>>>>> + return container_of(nand, struct meson_nfc_nand_chip, nand);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_nand_select_chip(struct mtd_info *mtd, int chip)
>>>>> +{
>>>>> + struct nand_chip *nand = mtd_to_nand(mtd);
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +
>>>>> + nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 :
>> NAND_CE0;
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
>>>>> +{
>>>>> + writel(NFC_CMD_MAKE_IDLE(nfc->param.chip_select, time),
>>>>> + nfc->reg_base + NFC_REG_CMD);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_cmd_seed(const struct meson_nfc *nfc, u32 seed)
>>>>> +{
>>>>> + writel(NFC_CMD_SEED | (NFC_SEED_OFFSET + (seed &
>> NFC_SEED_MASK)),
>>>>> + nfc->reg_base + NFC_REG_CMD);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_cmd_access(struct nand_chip *nand, bool raw,
>> bool dir,
>>>>> + int scrambler)
>>>>> +{
>>>>> + struct mtd_info *mtd = nand_to_mtd(nand);
>>>>> + const struct meson_nfc *nfc =
>> nand_get_controller_data(mtd_to_nand(mtd));
>>>>> + const struct meson_nfc_nand_chip *meson_chip =
>> to_meson_nand(nand);
>>>>> + u32 bch = meson_chip->bch_mode, cmd;
>>>>> + int len = mtd->writesize, pagesize, pages;
>>>>> +
>>>>> + pagesize = nand->ecc.size;
>>>>> +
>>>>> + if (raw) {
>>>>> + len = mtd->writesize + mtd->oobsize;
>>>>> + cmd = len | scrambler | DMA_DIR(dir);
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + pages = len / nand->ecc.size;
>>>>> +
>>>>> + cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch,
>>>>> + NFC_CMD_SHORTMODE_DISABLE, pagesize, pages);
>>>>> +
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
>>>>> +{
>>>>> + /*
>>>>> + * Insert two commands to make sure all valid commands are
>> finished.
>>>>> + *
>>>>> + * The Nand flash controller is designed as two stages
>> pipleline -
>>>>> + * a) fetch and b) execute.
>>>>> + * There might be cases when the driver see command queue is
>> empty,
>>>>> + * but the Nand flash controller still has two commands
>> buffered,
>>>>> + * one is fetched into NFC request queue (ready to run), and
>> another
>>>>> + * is actively executing. So pushing 2 "IDLE" commands
>> guarantees that
>>>>> + * the pipeline is emptied.
>>>>> + */
>>>>> + meson_nfc_cmd_idle(nfc, 0);
>>>>> + meson_nfc_cmd_idle(nfc, 0);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_wait_cmd_finish(const struct meson_nfc *nfc,
>>>>> + unsigned int timeout_us)
>>>>> +{
>>>>> + u32 cmd_size = 0;
>>>>> +
>>>>> + /* wait cmd fifo is empty */
>>>>> + return readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD,
>> cmd_size,
>>>>> + !NFC_CMD_GET_SIZE(cmd_size),
>>>>> + timeout_us);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
>>>>> +{
>>>>> + meson_nfc_drain_cmd(nfc);
>>>>> +
>>>>> + return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT_US);
>>>>> +}
>>>>> +
>>>>> +static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>>>> +{
>>>>> + const struct meson_nfc_nand_chip *meson_chip =
>> to_meson_nand(nand);
>>>>> + int len;
>>>>> +
>>>>> + len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>>>>> +
>>>>> + return meson_chip->data_buf + len;
>>>>> +}
>>>>> +
>>>>> +static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>> +{
>>>>> + const struct meson_nfc_nand_chip *meson_chip =
>> to_meson_nand(nand);
>>>>> + int len, temp;
>>>>> +
>>>>> + temp = nand->ecc.size + nand->ecc.bytes;
>>>>> + len = (temp + 2) * i;
>>>>> +
>>>>> + return meson_chip->data_buf + len;
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>>>> + u8 *buf, u8 *oobbuf)
>>>>> +{
>>>>> + u8 *dsrc, *osrc;
>>>>> + int i, oob_len;
>>>>> +
>>>>> + oob_len = nand->ecc.bytes + 2;
>>>>> + for (i = 0; i < nand->ecc.steps; i++) {
>>>>> + if (buf) {
>>>>> + dsrc = meson_nfc_data_ptr(nand, i);
>>>>> + memcpy(buf, dsrc, nand->ecc.size);
>>>>> + buf += nand->ecc.size;
>>>>> + }
>>>>> +
>>>>> + if (oobbuf) {
>>>>> + osrc = meson_nfc_oob_ptr(nand, i);
>>>>> + memcpy(oobbuf, osrc, oob_len);
>>>>> + oobbuf += oob_len;
>>>>> + }
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>> + const u8 *buf, u8 *oobbuf)
>>>>> +{
>>>>> + int i, oob_len;
>>>>> +
>>>>> + oob_len = nand->ecc.bytes + 2;
>>>>> + for (i = 0; i < nand->ecc.steps; i++) {
>>>>> + u8 *osrc;
>>>>> +
>>>>> + if (buf) {
>>>>> + u8 *dsrc;
>>>>> +
>>>>> + dsrc = meson_nfc_data_ptr(nand, i);
>>>>> + memcpy(dsrc, buf, nand->ecc.size);
>>>>> + buf += nand->ecc.size;
>>>>> + }
>>>>> +
>>>>> + osrc = meson_nfc_oob_ptr(nand, i);
>>>>> + memcpy(osrc, oobbuf, oob_len);
>>>>> + oobbuf += oob_len;
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_set_user_byte(struct nand_chip *nand, const u8
>> *oob_buf)
>>>>> +{
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + int i, count;
>>>>> +
>>>>> + for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 +
>> nand->ecc.bytes)) {
>>>>> + __le64 *info = &meson_chip->info_buf[i];
>>>>> +
>>>>> + *info |= oob_buf[count];
>>>>> + *info |= oob_buf[count + 1] << 8;
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_get_user_byte(struct nand_chip *nand, u8
>> *oob_buf)
>>>>> +{
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + int i, count;
>>>>> +
>>>>> + for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 +
>> nand->ecc.bytes)) {
>>>>> + const __le64 *info = &meson_chip->info_buf[i];
>>>>> +
>>>>> + oob_buf[count] = *info;
>>>>> + oob_buf[count + 1] = *info >> 8;
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_ecc_correct(struct nand_chip *nand, u32
>> *bitflips,
>>>>> + u64 *correct_bitmap)
>>>>> +{
>>>>> + struct mtd_info *mtd = nand_to_mtd(nand);
>>>>> + int ret = 0, i;
>>>>> +
>>>>> + for (i = 0; i < nand->ecc.steps; i++) {
>>>>> + struct meson_nfc_nand_chip *meson_chip =
>> to_meson_nand(nand);
>>>>> + const __le64 *info = &meson_chip->info_buf[i];
>>>>> +
>>>>> + if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
>>>>> + mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
>>>>> + *bitflips = max_t(u32, *bitflips,
>> ECC_ERR_CNT(*info));
>>>>> + *correct_bitmap |= BIT_ULL(i);
>>>>> + continue;
>>>>> + }
>>>>> +
>>>>> + if ((nand->options & NAND_NEED_SCRAMBLING) &&
>>>>> + ECC_ZERO_CNT(*info) < nand->ecc.strength) {
>>>>> + mtd->ecc_stats.corrected +=
>> ECC_ZERO_CNT(*info);
>>>>> + *bitflips = max_t(u32, *bitflips,
>>>>> + ECC_ZERO_CNT(*info));
>>>>> + ret = ECC_CHECK_RETURN_FF;
>>>>> + } else {
>>>>> + ret = -EBADMSG;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void
>> *databuf,
>>>>> + int datalen, void *infobuf, int
>> infolen,
>>>>> + enum dma_data_direction dir)
>>>>> +{
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> + int ret;
>>>>> + u32 cmd;
>>>>> +
>>>>> + nfc->daddr = dma_map_single(databuf, datalen,
>> DMA_BIDIRECTIONAL);
>>>>> + ret = dma_mapping_error(nfc->dev, nfc->daddr);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + if (infobuf) {
>>>>> + nfc->iaddr = dma_map_single(infobuf, infolen,
>>>>> + DMA_BIDIRECTIONAL);
>>>>> + ret = dma_mapping_error(nfc->dev, nfc->iaddr);
>>>>> + if (ret) {
>>>>> + dma_unmap_single(nfc->daddr, datalen, dir);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + nfc->info_bytes = infolen;
>>>>> + cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
>>>>> + int datalen, int infolen,
>>>>> + enum dma_data_direction dir)
>>>>> +{
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +
>>>>> + dma_unmap_single(nfc->daddr, datalen, dir);
>>>>> +
>>>>> + if (infolen) {
>>>>> + dma_unmap_single(nfc->iaddr, infolen, dir);
>>>>> + nfc->info_bytes = 0;
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int
>> size)
>>>>> +{
>>>>> + struct nand_chip *nand = mtd_to_nand(mtd);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + u8 *dma_buf;
>>>>> + int ret;
>>>>> + u32 cmd;
>>>>> +
>>>>> + if ((uintptr_t)buf % DMA_ADDR_ALIGN) {
>>>>> + unsigned long tmp_addr;
>>>>> +
>>>>> + dma_buf = dma_alloc_coherent(size, &tmp_addr);
>>>>> + if (!dma_buf)
>>>>> + return;
>>>>> + } else {
>>>>> + dma_buf = buf;
>>>>> + }
>>>>> +
>>>>> + ret = meson_nfc_dma_buffer_setup(nand, dma_buf, size,
>> meson_chip->info_buf,
>>>>> + PER_INFO_BYTE,
>> DMA_FROM_DEVICE);
>>>>> + if (ret) {
>>>>> + pr_err("Failed to setup DMA buffer %p/%p\n", dma_buf,
>>>>> + meson_chip->info_buf);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + cmd = NFC_CMD_N2M | size;
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + meson_nfc_drain_cmd(nfc);
>>>>> + meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US);
>>>>> + meson_nfc_dma_buffer_release(nand, size, PER_INFO_BYTE,
>> DMA_FROM_DEVICE);
>>>>> +
>>>>> + if (buf != dma_buf) {
>>>>> + memcpy(buf, dma_buf, size);
>>>>> + dma_free_coherent(dma_buf);
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_write_buf(struct mtd_info *mtd, const u8 *buf,
>> int size)
>>>>> +{
>>>>> + struct nand_chip *nand = mtd_to_nand(mtd);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> + u8 *dma_buf;
>>>>> + int ret;
>>>>> + u32 cmd;
>>>>> +
>>>>> + if ((uintptr_t)buf % DMA_ADDR_ALIGN) {
>>>>> + unsigned long tmp_addr;
>>>>> +
>>>>> + dma_buf = dma_alloc_coherent(size, &tmp_addr);
>>>>> + if (!dma_buf)
>>>>> + return;
>>>>> +
>>>>> + memcpy(dma_buf, buf, size);
>>>>> + } else {
>>>>> + dma_buf = (u8 *)buf;
>>>>> + }
>>>>> +
>>>>> + ret = meson_nfc_dma_buffer_setup(nand, (void *)dma_buf, size,
>> NULL,
>>>>> + 0, DMA_TO_DEVICE);
>>>>> + if (ret) {
>>>>> + pr_err("Failed to setup DMA buffer %p\n", dma_buf);
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + cmd = NFC_CMD_M2N | size;
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + meson_nfc_drain_cmd(nfc);
>>>>> + meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US);
>>>>> + meson_nfc_dma_buffer_release(nand, size, 0, DMA_TO_DEVICE);
>>>>> +
>>>>> + if (buf != dma_buf)
>>>>> + dma_free_coherent(dma_buf);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>>> + int page, bool raw)
>>>>> +{
>>>>> + const struct mtd_info *mtd = nand_to_mtd(nand);
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> + int data_len, info_len;
>>>>> + int ret;
>>>>> + u32 cmd;
>>>>> +
>>>>> + data_len = mtd->writesize + mtd->oobsize;
>>>>> + info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>> +
>>>>> + ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>>>>> + data_len,
>> meson_chip->info_buf,
>>>>> + info_len, DMA_TO_DEVICE);
>>>>> + if (ret) {
>>>>> + pr_err("Failed to setup DMA buffer %p/%p\n",
>>>>> + meson_chip->data_buf, meson_chip->info_buf);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + if (nand->options & NAND_NEED_SCRAMBLING) {
>>>>> + meson_nfc_cmd_seed(nfc, page);
>>>>> + meson_nfc_cmd_access(nand, raw, DIRWRITE,
>>>>> + NFC_CMD_SCRAMBLER_ENABLE);
>>>>> + } else {
>>>>> + meson_nfc_cmd_access(nand, raw, DIRWRITE,
>>>>> + NFC_CMD_SCRAMBLER_DISABLE);
>>>>> + }
>>>>> +
>>>>> + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + meson_nfc_dma_buffer_release(nand, data_len, info_len,
>> DMA_TO_DEVICE);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_write_page_raw(struct mtd_info *mtd, struct
>> nand_chip *chip,
>>>>> + const u8 *buf, int oob_required,
>> int page)
>>>>> +{
>>>>> + meson_nfc_set_data_oob(chip, buf, oob_required ? chip->oob_poi
>> : NULL);
>>>>> +
>>>>> + return meson_nfc_write_page_sub(chip, page, true);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct
>> nand_chip *chip,
>>>>> + const u8 *buf, int oob_required,
>> int page)
>>>>> +{
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip);
>>>>> +
>>>>> + if (buf)
>>>>> + memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>>>> +
>>>>> + memset(meson_chip->info_buf, 0, chip->ecc.steps *
>> PER_INFO_BYTE);
>>>>> +
>>>>> + if (oob_required)
>>>>> + meson_nfc_set_user_byte(chip, chip->oob_poi);
>>>>> +
>>>>> + return meson_nfc_write_page_sub(chip, page, false);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>>> + struct nand_chip *nand,
>> bool raw)
>>>>> +{
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + __le64 *info;
>>>>> + u32 neccpages;
>>>>> + int ret;
>>>>> +
>>>>> + neccpages = raw ? 1 : nand->ecc.steps;
>>>>> + info = &meson_chip->info_buf[neccpages - 1];
>>>>> + do {
>>>>> + udelay(ECC_POLL_TIMEOUT_US);
>>>>> + /* info is updated by nfc dma engine*/
>>>>> + rmb();
>>>>> + invalidate_dcache_range(nfc->iaddr, nfc->iaddr +
>> nfc->info_bytes);
>>>>> + ret = *info & ECC_COMPLETE;
>>>>> + } while (!ret);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_read_page_sub(struct nand_chip *nand,
>>>>> + int page, bool raw)
>>>>> +{
>>>>> + struct mtd_info *mtd = nand_to_mtd(nand);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + u32 data_len, info_len;
>>>>> + int ret;
>>>>> +
>>>>> + data_len = mtd->writesize + mtd->oobsize;
>>>>> + info_len = nand->ecc.steps * PER_INFO_BYTE;
>>>>> +
>>>>> + ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
>> data_len,
>>>>> + meson_chip->info_buf,
>> info_len,
>>>>> + DMA_FROM_DEVICE);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + meson_nfc_cmd_access(nand, raw, DIRREAD, 0);
>>>>> +
>>>>> + meson_nfc_wait_dma_finish(nfc);
>>>>> + meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
>>>>> +
>>>>> + meson_nfc_dma_buffer_release(nand, data_len, info_len,
>>>>> + DMA_FROM_DEVICE);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_read_page_raw(struct mtd_info *mtd, struct
>> nand_chip *chip,
>>>>> + u8 *buf, int oob_required, int page)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = meson_nfc_read_page_sub(chip, page, true);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + meson_nfc_get_data_oob(chip, buf, oob_required ? chip->oob_poi
>> : NULL);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_read_page_hwecc(struct mtd_info *mtd, struct
>> nand_chip *chip,
>>>>> + u8 *buf, int oob_required, int
>> page)
>>>>> +{
>>>>> + const struct meson_nfc_nand_chip *meson_chip =
>> to_meson_nand(chip);
>>>>> + u64 correct_bitmap = 0;
>>>>> + u32 bitflips = 0;
>>>>> + int ret;
>>>>> +
>>>>> + ret = meson_nfc_read_page_sub(chip, page, false);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + if (oob_required)
>>>>> + meson_nfc_get_user_byte(chip, chip->oob_poi);
>>>>> +
>>>>> + ret = meson_nfc_ecc_correct(chip, &bitflips, &correct_bitmap);
>>>>> +
>>>>> + if (ret == ECC_CHECK_RETURN_FF) {
>>>>> + if (buf)
>>>>> + memset(buf, 0xff, mtd->writesize);
>>>>> +
>>>>> + if (oob_required)
>>>>> + memset(chip->oob_poi, 0xff, mtd->oobsize);
>>>>> + } else if (ret < 0) {
>>>>> + struct nand_ecc_ctrl *ecc;
>>>>> + int i;
>>>>> +
>>>>> + if ((chip->options & NAND_NEED_SCRAMBLING) || !buf) {
>>>>> + mtd->ecc_stats.failed++;
>>>>> + return bitflips;
>>>>> + }
>>>>> +
>>>>> + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
>>>>> +
>>>>> + ret = meson_nfc_read_page_raw(mtd, chip, buf, 1, page);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ecc = &chip->ecc;
>>>>> +
>>>>> + for (i = 0; i < chip->ecc.steps ; i++) {
>>>>> + u8 *data = buf + i * ecc->size;
>>>>> + u8 *oob = chip->oob_poi + i * (ecc->bytes + 2);
>>>>> +
>>>>> + if (correct_bitmap & BIT_ULL(i))
>>>>> + continue;
>>>>> +
>>>>> + ret = nand_check_erased_ecc_chunk(data,
>> ecc->size,
>>>>> + oob,
>> ecc->bytes + 2,
>>>>> + NULL, 0,
>>>>> +
>> ecc->strength);
>>>>> + if (ret < 0) {
>>>>> + mtd->ecc_stats.failed++;
>>>>> + } else {
>>>>> + mtd->ecc_stats.corrected += ret;
>>>>> + bitflips = max_t(u32, bitflips, ret);
>>>>> + }
>>>>> + }
>>>>> + } else if (buf && buf != meson_chip->data_buf) {
>>>>> + memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>>>> + }
>>>>> +
>>>>> + return bitflips;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_read_oob_raw(struct mtd_info *mtd, struct
>> nand_chip *chip,
>>>>> + int page)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = nand_read_page_op(chip, page, 0, NULL, 0);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return meson_nfc_read_page_raw(mtd, chip, NULL, 1, page);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_read_oob(struct mtd_info *mtd, struct nand_chip
>> *chip,
>>>>> + int page)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = nand_read_page_op(chip, page, 0, NULL, 0);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return meson_nfc_read_page_hwecc(mtd, chip, NULL, 1, page);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_write_oob_raw(struct mtd_info *mtd, struct
>> nand_chip *chip,
>>>>> + int page)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = meson_nfc_write_page_raw(mtd, chip, NULL, 1, page);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return nand_prog_page_end_op(chip);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_write_oob(struct mtd_info *mtd, struct nand_chip
>> *chip,
>>>>> + int page)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = meson_nfc_write_page_hwecc(mtd, chip, NULL, 1, page);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return nand_prog_page_end_op(chip);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_nand_cmd_function(struct mtd_info *mtd,
>> unsigned int command,
>>>>> + int column, int page_addr)
>>>>> +{
>>>>> + struct nand_chip *chip = mtd_to_nand(mtd);
>>>>> +
>>>>> + chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE |
>> NAND_CTRL_CHANGE);
>>>>> +
>>>>> + if (column != -1 || page_addr != -1) {
>>>>> + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
>>>>> +
>>>>> + /* Serially input address */
>>>>> + if (column != -1) {
>>>>> + /* Adjust columns for 16 bit buswidth */
>>>>> + if (chip->options & NAND_BUSWIDTH_16 &&
>>>>> + !nand_opcode_8bits(command))
>>>>> + column >>= 1;
>>>>> +
>>>>> + chip->cmd_ctrl(mtd, column, ctrl);
>>>>> + ctrl &= ~NAND_CTRL_CHANGE;
>>>>> + /* Only output a single addr cycle for 8bits
>>>>> + * opcodes.
>>>>> + */
>>>>> + if (!nand_opcode_8bits(command))
>>>>> + chip->cmd_ctrl(mtd, column >> 8, ctrl);
>>>>> + }
>>>>> +
>>>>> + if (page_addr != -1) {
>>>>> + chip->cmd_ctrl(mtd, page_addr, ctrl);
>>>>> + chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE |
>>>>> + NAND_ALE);
>>>>> + /* One more address cycle for devices > 128MiB
>> */
>>>>> + if (chip->chipsize > SZ_128M)
>>>>> + chip->cmd_ctrl(mtd, page_addr >> 16,
>>>>> + NAND_NCE | NAND_ALE);
>>>>> + }
>>>>> +
>>>>> + switch (command) {
>>>>> + case NAND_CMD_READ0:
>>>>> + chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
>>>>> + NAND_NCE | NAND_CLE |
>> NAND_CTRL_CHANGE);
>>>>> + fallthrough;
>>>>> + case NAND_CMD_PARAM:
>>>>> + nand_wait_ready(mtd);
>>>>> + nand_exit_status_op(chip);
>>>>> + }
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd,
>> unsigned int ctrl)
>>>>> +{
>>>>> + struct nand_chip *nand = mtd_to_nand(mtd);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +
>>>>> + if (cmd == NAND_CMD_NONE)
>>>>> + return;
>>>>> +
>>>>> + if (ctrl & NAND_CLE)
>>>>> + cmd = NFC_CMD_MAKE_CLE(nfc->param.chip_select, cmd);
>>>>> + else
>>>>> + cmd = NFC_CMD_MAKE_ALE(nfc->param.chip_select, cmd);
>>>>> +
>>>>> + writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_wait_cmd_fifo(struct meson_nfc *nfc)
>>>>> +{
>>>>> + while ((NFC_GET_CMD(nfc) >> 22) & GENMASK(4, 0))
>>>>> + ;
>>>>> +}
>>>>> +
>>>>> +static u8 meson_nfc_nand_read_byte(struct mtd_info *mtd)
>>>>> +{
>>>>> + struct nand_chip *nand = mtd_to_nand(mtd);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +
>>>>> + writel(NFC_CMD_MAKE_DRD(nfc->param.chip_select, 0),
>> nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
>>>>> + meson_nfc_cmd_idle(nfc, 0);
>>>>> + meson_nfc_cmd_idle(nfc, 0);
>>>>> +
>>>>> + meson_nfc_wait_cmd_fifo(nfc);
>>>>> +
>>>>> + return readl(nfc->reg_base + NFC_REG_BUF);
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_nand_write_byte(struct mtd_info *mtd, u8 val)
>>>>> +{
>>>>> + struct nand_chip *nand = mtd_to_nand(mtd);
>>>>> + struct meson_nfc *nfc = nand_get_controller_data(nand);
>>>>> +
>>>>> + meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
>>>>> +
>>>>> + writel(NFC_CMD_MAKE_DWR(nfc->param.chip_select, val),
>> nfc->reg_base + NFC_REG_CMD);
>>>>> +
>>>>> + meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
>>>>> + meson_nfc_cmd_idle(nfc, 0);
>>>>> + meson_nfc_cmd_idle(nfc, 0);
>>>>> +
>>>>> + meson_nfc_wait_cmd_fifo(nfc);
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_dev_ready(struct mtd_info *mtd)
>>>>> +{
>>>>> + struct nand_chip *chip = mtd_to_nand(mtd);
>>>>> + unsigned int time_out_cnt = 0;
>>>>> +
>>>>> + chip->select_chip(mtd, 0);
>>>>> +
>>>>> + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
>>>>> +
>>>>> + do {
>>>>> + int status;
>>>>> +
>>>>> + status = (int)chip->read_byte(mtd);
>>>>> + if (status & NAND_STATUS_READY)
>>>>> + break;
>>>>> + } while (time_out_cnt++ < NFC_DEV_READY_TICK_MAX);
>>>>> +
>>>>> + return time_out_cnt != NFC_DEV_READY_TICK_MAX;
>>>>> +}
>>>>> +
>>>>> +static int meson_chip_buffer_init(struct nand_chip *nand)
>>>>> +{
>>>>> + const struct mtd_info *mtd = nand_to_mtd(nand);
>>>>> + struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>> + u32 page_bytes, info_bytes, nsectors;
>>>>> + unsigned long tmp_addr;
>>>>> +
>>>>> + nsectors = mtd->writesize / nand->ecc.size;
>>>>> +
>>>>> + page_bytes = mtd->writesize + mtd->oobsize;
>>>>> + info_bytes = nsectors * PER_INFO_BYTE;
>>>>> +
>>>>> + meson_chip->data_buf = dma_alloc_coherent(page_bytes,
>> &tmp_addr);
>>>>> + if (!meson_chip->data_buf)
>>>>> + return -ENOMEM;
>>>>> +
>>>>> + meson_chip->info_buf = dma_alloc_coherent(info_bytes,
>> &tmp_addr);
>>>>> + if (!meson_chip->info_buf) {
>>>>> + dma_free_coherent(meson_chip->data_buf);
>>>>> + return -ENOMEM;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static const int axg_stepinfo_strengths[] = { 8 };
>>>>> +static const struct nand_ecc_step_info axg_stepinfo_1024 = {
>>>>> + .stepsize = 1024,
>>>>> + .strengths = axg_stepinfo_strengths,
>>>>> + .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
>>>>> +};
>>>>> +
>>>>> +static const struct nand_ecc_step_info axg_stepinfo_512 = {
>>>>> + .stepsize = 512,
>>>>> + .strengths = axg_stepinfo_strengths,
>>>>> + .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
>>>>> +};
>>>>> +
>>>>> +static const struct nand_ecc_step_info axg_stepinfo[] = {
>> axg_stepinfo_1024, axg_stepinfo_512 };
>>>>> +
>>>>> +static const struct nand_ecc_caps meson_axg_ecc_caps = {
>>>>> + .stepinfos = axg_stepinfo,
>>>>> + .nstepinfos = ARRAY_SIZE(axg_stepinfo),
>>>>> + .calc_ecc_bytes = meson_nand_calc_ecc_bytes,
>>>>> +};
>>>>> +
>>>>> +/*
>>>>> + * OOB layout:
>>>>> + *
>>>>> + * For ECC with 512 bytes step size:
>>>>> + * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB
>>>>> + * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC
>>>>> + * 0x20:
>>>>> + * 0x30:
>>>>> + *
>>>>> + * For ECC with 1024 bytes step size:
>>>>> + * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB
>>>>> + * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC
>>>>> + * 0x20: AA AA DD DD DD DD DD DD DD DD DD DD DD DD DD DD
>>>>> + * 0x30: AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE
>>>>> + *
>>>>> + * AA - user bytes.
>>>>> + * BB, CC, DD, EE - ECC code bytes for each step.
>>>>> + */
>>>>> +static struct nand_ecclayout nand_oob;
>>>>> +
>>>>> +static void meson_nfc_init_nand_oob(struct nand_chip *nand)
>>>>> +{
>>>>> + int section_size = 2 + nand->ecc.bytes;
>>>>> + int i;
>>>>> + int k;
>>>>> +
>>>>> + nand_oob.eccbytes = nand->ecc.steps * nand->ecc.bytes;
>>>>> + k = 0;
>>>>> +
>>>>> + for (i = 0; i < nand->ecc.steps; i++) {
>>>>> + int j;
>>>>> +
>>>>> + for (j = 0; j < nand->ecc.bytes; j++)
>>>>> + nand_oob.eccpos[k++] = (i * section_size) + 2
>> + j;
>>>>> +
>>>>> + nand_oob.oobfree[i].offset = (i * section_size);
>>>>> + nand_oob.oobfree[i].length = 2;
>>>>> + }
>>>>> +
>>>>> + nand_oob.oobavail = 2 * nand->ecc.steps;
>>>>> + nand->ecc.layout = &nand_oob;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_init_ecc(struct nand_chip *nand, ofnode node)
>>>>> +{
>>>>> + const struct mtd_info *mtd = nand_to_mtd(nand);
>>>>> + int ret;
>>>>> + int i;
>>>>> +
>>>>> + ret = nand_check_ecc_caps(nand, &meson_axg_ecc_caps,
>> mtd->oobsize - 2);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) {
>>>>> + if (meson_ecc[i].strength == nand->ecc.strength &&
>>>>> + meson_ecc[i].size == nand->ecc.size) {
>>>>> + struct meson_nfc_nand_chip *meson_chip =
>> to_meson_nand(nand);
>>>>> +
>>>>> + nand->ecc.steps = mtd->writesize /
>> nand->ecc.size;
>>>>> + meson_chip->bch_mode = meson_ecc[i].bch;
>>>>> +
>>>>> + meson_nfc_init_nand_oob(nand);
>>>>> +
>>>>> + return 0;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + return -EINVAL;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_nand_chip_init(struct udevice *dev, struct
>> meson_nfc *nfc,
>>>>> + ofnode node)
>>>>> +{
>>>>> + struct meson_nfc_nand_chip *meson_chip;
>>>>> + struct nand_chip *nand;
>>>>> + struct mtd_info *mtd;
>>>>> + u32 cs[MAX_CE_NUM];
>>>>> + u32 nsels;
>>>>> + int ret;
>>>>> + int i;
>>>>> +
>>>>> + if (!ofnode_get_property(node, "reg", &nsels)) {
>>>>> + dev_err(dev, "\"reg\" property is not found\n");
>>>>> + return -ENODEV;
>>>>> + }
>>>>> +
>>>>> + nsels /= sizeof(u32);
>>>>> + if (nsels >= MAX_CE_NUM) {
>>>>> + dev_err(dev, "invalid size of CS array, max is %d\n",
>>>>> + MAX_CE_NUM);
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + ret = ofnode_read_u32_array(node, "reg", cs, nsels);
>>>>> + if (ret < 0) {
>>>>> + dev_err(dev, "failed to read \"reg\" property\n");
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + for (i = 0; i < nsels; i++) {
>>>>> + if (test_and_set_bit(cs[i], &nfc->assigned_cs)) {
>>>>> + dev_err(dev, "CS %d already assigned\n",
>> cs[i]);
>>>>> + return -EINVAL;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + meson_chip = malloc(sizeof(*meson_chip) + nsels *
>> sizeof(meson_chip->sels[0]));
>>>>> + if (!meson_chip) {
>>>>> + dev_err(dev, "failed to allocate memory for chip\n");
>>>>> + return -ENOMEM;
>>>>> + }
>>>>> +
>>>>> + meson_chip->nsels = nsels;
>>>>> + nand = &meson_chip->nand;
>>>>> +
>>>>> + nand->flash_node = node;
>>>>> + nand_set_controller_data(nand, nfc);
>>>>> + /* Set the driver entry points for MTD */
>>>>> + nand->cmdfunc = meson_nfc_nand_cmd_function;
>>>>> + nand->cmd_ctrl = meson_nfc_cmd_ctrl;
>>>>> + nand->select_chip = meson_nfc_nand_select_chip;
>>>>> + nand->read_byte = meson_nfc_nand_read_byte;
>>>>> + nand->write_byte = meson_nfc_nand_write_byte;
>>>>> + nand->dev_ready = meson_nfc_dev_ready;
>>>>> +
>>>>> + /* Buffer read/write routines */
>>>>> + nand->read_buf = meson_nfc_read_buf;
>>>>> + nand->write_buf = meson_nfc_write_buf;
>>>>> + nand->options |= NAND_NO_SUBPAGE_WRITE;
>>>>> +
>>>>> + nand->ecc.mode = NAND_ECC_HW;
>>>>> + nand->ecc.hwctl = NULL;
>>>>> + nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>>>> + nand->ecc.write_page = meson_nfc_write_page_hwecc;
>>>>> + nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>>>> + nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>>>> +
>>>>> + nand->ecc.read_oob = meson_nfc_read_oob;
>>>>> + nand->ecc.write_oob = meson_nfc_write_oob;
>>>>> + nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>>>>> + nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>>>>> +
>>>>> + nand->ecc.algo = NAND_ECC_BCH;
>>>>> +
>>>>> + mtd = nand_to_mtd(nand);
>>>>> +
>>>>> + ret = nand_scan_ident(mtd, 1, NULL);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "'nand_scan_ident()' failed: %d\n", ret);
>>>>> + goto err_chip_free;
>>>>> + }
>>>>> +
>>>>> + ret = meson_nfc_init_ecc(nand, node);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "failed to init ECC settings: %d\n", ret);
>>>>> + goto err_chip_free;
>>>>> + }
>>>>> +
>>>>> + ret = meson_chip_buffer_init(nand);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "failed to init DMA buffers: %d\n", ret);
>>>>> + goto err_chip_free;
>>>>> + }
>>>>> +
>>>>> + /* 'nand_scan_tail()' needs ECC parameters to be already
>>>>> + * set and correct.
>>>>> + */
>>>>
>>>> Can you split in
>>>> /*
>>>> *
>>>> ?
>>>
>>> You mean to add this comment as a single line?
>>>
>>> Thanks, Arseniy
>>>
>>>>
>>>>> + ret = nand_scan_tail(mtd);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "'nand_scan_tail()' failed: %d\n", ret);
>>>>> + goto err_chip_buf_free;
>>>>> + }
>>>>> +
>>>>> + ret = nand_register(0, mtd);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "'nand_register()' failed: %d\n", ret);
>>>>> + goto err_chip_buf_free;
>>>>> + }
>>>>> +
>>>>> + list_add_tail(&meson_chip->node, &nfc->chips);
>>>>> +
>>>>> + return 0;
>>>>> +
>>>>> +err_chip_buf_free:
>>>>> + dma_free_coherent(meson_chip->info_buf);
>>>>> + dma_free_coherent(meson_chip->data_buf);
>>>>> +
>>>>> +err_chip_free:
>>>>> + free(meson_chip);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static int meson_nfc_nand_chips_init(struct udevice *dev,
>>>>> + struct meson_nfc *nfc)
>>>>> +{
>>>>> + ofnode parent = dev_ofnode(dev);
>>>>> + ofnode node;
>>>>> +
>>>>> + ofnode_for_each_subnode(node, parent) {
>>>>> + int ret = meson_nfc_nand_chip_init(dev, nfc, node);
>>>>> +
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static void meson_nfc_clk_init(struct meson_nfc *nfc)
>>>>> +{
>>>>> + u32 bus_cycle = NFC_DEFAULT_BUS_CYCLE;
>>>>> + u32 bus_timing = NFC_DEFAULT_BUS_TIMING;
>>>>> + u32 bus_cfg_val;
>>>>> +
>>>>> + writel(CLK_ALWAYS_ON_NAND | CLK_SELECT_NAND |
>> CLK_ENABLE_VALUE, nfc->reg_clk);
>>>>> + writel(0, nfc->reg_base + NFC_REG_CFG);
>>>>> +
>>>>> + bus_cfg_val = (((bus_cycle - 1) & 31) | ((bus_timing & 31) <<
>> 5));
>>>>> + writel(bus_cfg_val, nfc->reg_base + NFC_REG_CFG);
>>>>> + writel(BIT(31), nfc->reg_base + NFC_REG_CMD);
>>>>> +}
>>>>> +
>>>>> +static int meson_probe(struct udevice *dev)
>>>>> +{
>>>>> + struct meson_nfc *nfc = dev_get_priv(dev);
>>>>> + void *addr;
>>>>> + int ret;
>>>>> +
>>>>> + addr = dev_read_addr_ptr(dev);
>>>>> + if (!addr) {
>>>>> + dev_err(dev, "base register address not found\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + nfc->reg_base = addr;
>>>>> +
>>>>> + addr = dev_read_addr_index_ptr(dev, 1);
>>>>> + if (!addr) {
>>>>> + dev_err(dev, "clk register address not found\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + nfc->reg_clk = addr;
>>>>> + nfc->dev = dev;
>>>>> +
>>>>> + meson_nfc_clk_init(nfc);
>>>>> +
>>>>> + ret = meson_nfc_nand_chips_init(dev, nfc);
>>>>> + if (ret) {
>>>>> + dev_err(nfc->dev, "failed to init chips\n");
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct udevice_id meson_nand_dt_ids[] = {
>>>>> + {.compatible = "amlogic,meson-axg-nfc",},
>>>>> + { /* sentinel */ }
>>>>> +};
>>>>> +
>>>>> +U_BOOT_DRIVER(meson_nand) = {
>>>>> + .name = "meson_nand",
>>>>> + .id = UCLASS_MTD,
>>>>> + .of_match = meson_nand_dt_ids,
>>>>> + .probe = meson_probe,
>>>>> + .priv_auto = sizeof(struct meson_nfc),
>>>>> +};
>>>>> +
>>>>> +void board_nand_init(void)
>>>>> +{
>>>>> + struct udevice *dev;
>>>>> + int ret;
>>>>> +
>>>>> + ret = uclass_get_device_by_driver(UCLASS_MTD,
>>>>> + DM_DRIVER_GET(meson_nand),
>> &dev);
>>>>> +
>>>>> + if (ret && ret != -ENODEV)
>>>>> + pr_err("Failed to initialize: %d\n", ret);
>>>>> +}
>>>>> --
>>>>> 2.35.0
>>>>>
>>>>
>>>>
>>>> --
>>>> Michael Nazzareno Trimarchi
>>>> Co-Founder & Chief Executive Officer
>>>> M. +39 347 913 2170
>>>> michael(a)amarulasolutions.com
>>>> __________________________________
>>>>
>>>> Amarula Solutions BV
>>>> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
>>>> T. +31 (0)85 111 9172
>>>> info(a)amarulasolutions.com
>>>> www.amarulasolutions.com
>>
>
1
0
Basic support for Amlogic Meson NAND controller on AXG.
Based on Linux version 6.7.0-rc4.
Signed-off-by: Arseniy Krasnov <avkrasnov(a)salutedevices.com>
---
Changelog:
v1 -> v2:
* Update commit message with 'Based on Linux ...'.
* Add Linux driver author to .c file header.
* Add comment for defines 'NFC_DEFAULT_BUS_CYCLE' and
'NFC_DEFAULT_BUS_TIMING'.
* Use 'dev_read_addr_index_ptr()' instead of 'dev_read_addr()'.
drivers/mtd/nand/raw/Kconfig | 9 +
drivers/mtd/nand/raw/Makefile | 1 +
drivers/mtd/nand/raw/meson_nand.c | 1241 +++++++++++++++++++++++++++++
3 files changed, 1251 insertions(+)
create mode 100644 drivers/mtd/nand/raw/meson_nand.c
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index d624589a89..7b7b0226ab 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -488,6 +488,15 @@ config NAND_ARASAN
controller. This uses the hardware ECC for read and
write operations.
+config NAND_MESON
+ bool "Meson NAND support"
+ select SYS_NAND_SELF_INIT
+ depends on DM_MTD && ARCH_MESON
+ imply CMD_NAND
+ help
+ This enables Nand driver support for Meson raw NAND flash
+ controller.
+
config NAND_MXC
bool "MXC NAND support"
depends on CPU_ARM926EJS || CPU_ARM1136 || MX5
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index add2b4cf65..5b4efd52c9 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o
obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o
obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o
obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o
+obj-$(CONFIG_NAND_MESON) += meson_nand.o
obj-$(CONFIG_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_NAND_MXS) += mxs_nand.o
obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o
diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
new file mode 100644
index 0000000000..a6dbe99d84
--- /dev/null
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -0,0 +1,1241 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Amlogic Meson Nand Flash Controller Driver
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Liang Yang <liang.yang(a)amlogic.com>
+ *
+ * Copyright (c) 2023 SaluteDevices, Inc.
+ * Author: Arseniy Krasnov <avkrasnov(a)salutedevices.com>
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/ofnode.h>
+#include <dm/uclass.h>
+#include <linux/bug.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/sizes.h>
+
+#define NFC_CMD_IDLE (0xc << 14)
+#define NFC_CMD_CLE (0x5 << 14)
+#define NFC_CMD_ALE (0x6 << 14)
+#define NFC_CMD_DWR (0x4 << 14)
+#define NFC_CMD_DRD (0x8 << 14)
+#define NFC_CMD_ADL ((0 << 16) | (3 << 20))
+#define NFC_CMD_ADH ((1 << 16) | (3 << 20))
+#define NFC_CMD_AIL ((2 << 16) | (3 << 20))
+#define NFC_CMD_AIH ((3 << 16) | (3 << 20))
+#define NFC_CMD_SEED ((8 << 16) | (3 << 20))
+#define NFC_CMD_M2N ((0 << 17) | (2 << 20))
+#define NFC_CMD_N2M ((1 << 17) | (2 << 20))
+#define NFC_CMD_RB BIT(20)
+#define NFC_CMD_SCRAMBLER_ENABLE BIT(19)
+#define NFC_CMD_SCRAMBLER_DISABLE 0
+#define NFC_CMD_SHORTMODE_DISABLE 0
+#define NFC_CMD_RB_INT BIT(14)
+#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16))
+
+#define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0))
+
+#define NFC_REG_CMD 0x00
+#define NFC_REG_CFG 0x04
+#define NFC_REG_DADR 0x08
+#define NFC_REG_IADR 0x0c
+#define NFC_REG_BUF 0x10
+#define NFC_REG_INFO 0x14
+#define NFC_REG_DC 0x18
+#define NFC_REG_ADR 0x1c
+#define NFC_REG_DL 0x20
+#define NFC_REG_DH 0x24
+#define NFC_REG_CADR 0x28
+#define NFC_REG_SADR 0x2c
+#define NFC_REG_PINS 0x30
+#define NFC_REG_VER 0x38
+
+#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \
+ ( \
+ (cmd_dir) | \
+ ((ran) << 19) | \
+ ((bch) << 14) | \
+ ((short_mode) << 13) | \
+ (((page_size) & 0x7f) << 6) | \
+ ((pages) & 0x3f) \
+ )
+
+#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff))
+#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff))
+#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff))
+#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff))
+
+#define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
+
+#define ECC_CHECK_RETURN_FF -1
+
+#define NAND_CE0 (0xe << 10)
+#define NAND_CE1 (0xd << 10)
+
+#define DMA_BUSY_TIMEOUT_US 1000000
+#define CMD_DRAIN_TIMEOUT_US 1000
+#define ECC_POLL_TIMEOUT_US 15
+
+#define MAX_CE_NUM 2
+
+/* eMMC clock register, misc control */
+#define CLK_SELECT_NAND BIT(31)
+#define CLK_ALWAYS_ON_NAND BIT(24)
+#define CLK_ENABLE_VALUE 0x245
+
+#define DIRREAD 1
+#define DIRWRITE 0
+
+#define ECC_PARITY_BCH8_512B 14
+#define ECC_COMPLETE BIT(31)
+#define ECC_ERR_CNT(x) (((x) >> 24) & GENMASK(5, 0))
+#define ECC_ZERO_CNT(x) (((x) >> 16) & GENMASK(5, 0))
+#define ECC_UNCORRECTABLE 0x3f
+
+#define PER_INFO_BYTE 8
+
+#define NFC_SEND_CMD(host, cmd) \
+ (writel((cmd), (host)->reg_base + NFC_REG_CMD))
+
+#define NFC_GET_CMD(host) \
+ (readl((host)->reg_base + NFC_REG_CMD))
+
+#define NFC_CMDFIFO_SIZE(host) ((NFC_GET_CMD((host)) >> 22) & GENMASK(4, 0))
+
+#define NFC_CMD_MAKE_IDLE(ce, delay) ((ce) | NFC_CMD_IDLE | ((delay) & 0x3ff))
+#define NFC_CMD_MAKE_DRD(ce, size) ((ce) | NFC_CMD_DRD | (size))
+#define NFC_CMD_MAKE_DWR(ce, data) ((ce) | NFC_CMD_DWR | ((data) & 0xff))
+#define NFC_CMD_MAKE_CLE(ce, cmd_val) ((ce) | NFC_CMD_CLE | ((cmd_val) & 0xff))
+#define NFC_CMD_MAKE_ALE(ce, addr) ((ce) | NFC_CMD_ALE | ((addr) & 0xff))
+
+#define NAND_TWB_TIME_CYCLE 10
+
+#define NFC_DEV_READY_TICK_MAX 5000
+
+/* Both values are recommended by vendor, as the most
+ * tested with almost all SLC NAND flash. Second value
+ * could be calculated dynamically from timing parameters,
+ * but we need both values for initial start of the NAND
+ * controller (e.g. before NAND subsystem processes timings),
+ * so use hardcoded constants.
+ */
+#define NFC_DEFAULT_BUS_CYCLE 6
+#define NFC_DEFAULT_BUS_TIMING 7
+
+#define NFC_SEED_OFFSET 0xc2
+#define NFC_SEED_MASK 0x7fff
+
+#define DMA_ADDR_ALIGN 8
+
+struct meson_nfc_nand_chip {
+ struct list_head node;
+ struct nand_chip nand;
+
+ u32 bch_mode;
+ u8 *data_buf;
+ __le64 *info_buf;
+ u32 nsels;
+ u8 sels[];
+};
+
+struct meson_nfc_param {
+ u32 chip_select;
+ u32 rb_select;
+};
+
+struct meson_nfc {
+ void __iomem *reg_base;
+ void __iomem *reg_clk;
+ struct list_head chips;
+ struct meson_nfc_param param;
+ struct udevice *dev;
+ dma_addr_t daddr;
+ dma_addr_t iaddr;
+ u32 data_bytes;
+ u32 info_bytes;
+ u64 assigned_cs;
+};
+
+struct meson_nand_ecc {
+ u32 bch;
+ u32 strength;
+ u32 size;
+};
+
+enum {
+ NFC_ECC_BCH8_512 = 1,
+ NFC_ECC_BCH8_1K,
+ NFC_ECC_BCH24_1K,
+ NFC_ECC_BCH30_1K,
+ NFC_ECC_BCH40_1K,
+ NFC_ECC_BCH50_1K,
+ NFC_ECC_BCH60_1K,
+};
+
+#define MESON_ECC_DATA(b, s, sz) { .bch = (b), .strength = (s), .size = (sz) }
+
+static struct meson_nand_ecc meson_ecc[] = {
+ MESON_ECC_DATA(NFC_ECC_BCH8_512, 8, 512),
+ MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 1024),
+};
+
+static int meson_nand_calc_ecc_bytes(int step_size, int strength)
+{
+ int ecc_bytes;
+
+ if (step_size == 512 && strength == 8)
+ return ECC_PARITY_BCH8_512B;
+
+ ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
+ ecc_bytes = ALIGN(ecc_bytes, 2);
+
+ return ecc_bytes;
+}
+
+static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
+{
+ return container_of(nand, struct meson_nfc_nand_chip, nand);
+}
+
+static void meson_nfc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+ nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0;
+}
+
+static void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
+{
+ writel(NFC_CMD_MAKE_IDLE(nfc->param.chip_select, time),
+ nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_seed(const struct meson_nfc *nfc, u32 seed)
+{
+ writel(NFC_CMD_SEED | (NFC_SEED_OFFSET + (seed & NFC_SEED_MASK)),
+ nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_access(struct nand_chip *nand, bool raw, bool dir,
+ int scrambler)
+{
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ const struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+ const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ u32 bch = meson_chip->bch_mode, cmd;
+ int len = mtd->writesize, pagesize, pages;
+
+ pagesize = nand->ecc.size;
+
+ if (raw) {
+ len = mtd->writesize + mtd->oobsize;
+ cmd = len | scrambler | DMA_DIR(dir);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ return;
+ }
+
+ pages = len / nand->ecc.size;
+
+ cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch,
+ NFC_CMD_SHORTMODE_DISABLE, pagesize, pages);
+
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_drain_cmd(struct meson_nfc *nfc)
+{
+ /*
+ * Insert two commands to make sure all valid commands are finished.
+ *
+ * The Nand flash controller is designed as two stages pipleline -
+ * a) fetch and b) execute.
+ * There might be cases when the driver see command queue is empty,
+ * but the Nand flash controller still has two commands buffered,
+ * one is fetched into NFC request queue (ready to run), and another
+ * is actively executing. So pushing 2 "IDLE" commands guarantees that
+ * the pipeline is emptied.
+ */
+ meson_nfc_cmd_idle(nfc, 0);
+ meson_nfc_cmd_idle(nfc, 0);
+}
+
+static int meson_nfc_wait_cmd_finish(const struct meson_nfc *nfc,
+ unsigned int timeout_us)
+{
+ u32 cmd_size = 0;
+
+ /* wait cmd fifo is empty */
+ return readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
+ !NFC_CMD_GET_SIZE(cmd_size),
+ timeout_us);
+}
+
+static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
+{
+ meson_nfc_drain_cmd(nfc);
+
+ return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT_US);
+}
+
+static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
+{
+ const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ int len;
+
+ len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
+
+ return meson_chip->data_buf + len;
+}
+
+static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
+{
+ const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ int len, temp;
+
+ temp = nand->ecc.size + nand->ecc.bytes;
+ len = (temp + 2) * i;
+
+ return meson_chip->data_buf + len;
+}
+
+static void meson_nfc_get_data_oob(struct nand_chip *nand,
+ u8 *buf, u8 *oobbuf)
+{
+ u8 *dsrc, *osrc;
+ int i, oob_len;
+
+ oob_len = nand->ecc.bytes + 2;
+ for (i = 0; i < nand->ecc.steps; i++) {
+ if (buf) {
+ dsrc = meson_nfc_data_ptr(nand, i);
+ memcpy(buf, dsrc, nand->ecc.size);
+ buf += nand->ecc.size;
+ }
+
+ if (oobbuf) {
+ osrc = meson_nfc_oob_ptr(nand, i);
+ memcpy(oobbuf, osrc, oob_len);
+ oobbuf += oob_len;
+ }
+ }
+}
+
+static void meson_nfc_set_data_oob(struct nand_chip *nand,
+ const u8 *buf, u8 *oobbuf)
+{
+ int i, oob_len;
+
+ oob_len = nand->ecc.bytes + 2;
+ for (i = 0; i < nand->ecc.steps; i++) {
+ u8 *osrc;
+
+ if (buf) {
+ u8 *dsrc;
+
+ dsrc = meson_nfc_data_ptr(nand, i);
+ memcpy(dsrc, buf, nand->ecc.size);
+ buf += nand->ecc.size;
+ }
+
+ osrc = meson_nfc_oob_ptr(nand, i);
+ memcpy(osrc, oobbuf, oob_len);
+ oobbuf += oob_len;
+ }
+}
+
+static void meson_nfc_set_user_byte(struct nand_chip *nand, const u8 *oob_buf)
+{
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ int i, count;
+
+ for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) {
+ __le64 *info = &meson_chip->info_buf[i];
+
+ *info |= oob_buf[count];
+ *info |= oob_buf[count + 1] << 8;
+ }
+}
+
+static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
+{
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ int i, count;
+
+ for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) {
+ const __le64 *info = &meson_chip->info_buf[i];
+
+ oob_buf[count] = *info;
+ oob_buf[count + 1] = *info >> 8;
+ }
+}
+
+static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
+ u64 *correct_bitmap)
+{
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ int ret = 0, i;
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ const __le64 *info = &meson_chip->info_buf[i];
+
+ if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
+ mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
+ *bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info));
+ *correct_bitmap |= BIT_ULL(i);
+ continue;
+ }
+
+ if ((nand->options & NAND_NEED_SCRAMBLING) &&
+ ECC_ZERO_CNT(*info) < nand->ecc.strength) {
+ mtd->ecc_stats.corrected += ECC_ZERO_CNT(*info);
+ *bitflips = max_t(u32, *bitflips,
+ ECC_ZERO_CNT(*info));
+ ret = ECC_CHECK_RETURN_FF;
+ } else {
+ ret = -EBADMSG;
+ }
+ }
+
+ return ret;
+}
+
+static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf,
+ int datalen, void *infobuf, int infolen,
+ enum dma_data_direction dir)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ int ret;
+ u32 cmd;
+
+ nfc->daddr = dma_map_single(databuf, datalen, DMA_BIDIRECTIONAL);
+ ret = dma_mapping_error(nfc->dev, nfc->daddr);
+ if (ret)
+ return ret;
+
+ cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ if (infobuf) {
+ nfc->iaddr = dma_map_single(infobuf, infolen,
+ DMA_BIDIRECTIONAL);
+ ret = dma_mapping_error(nfc->dev, nfc->iaddr);
+ if (ret) {
+ dma_unmap_single(nfc->daddr, datalen, dir);
+ return ret;
+ }
+
+ nfc->info_bytes = infolen;
+ cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ }
+
+ return 0;
+}
+
+static void meson_nfc_dma_buffer_release(struct nand_chip *nand,
+ int datalen, int infolen,
+ enum dma_data_direction dir)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+ dma_unmap_single(nfc->daddr, datalen, dir);
+
+ if (infolen) {
+ dma_unmap_single(nfc->iaddr, infolen, dir);
+ nfc->info_bytes = 0;
+ }
+}
+
+static void meson_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int size)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ u8 *dma_buf;
+ int ret;
+ u32 cmd;
+
+ if ((uintptr_t)buf % DMA_ADDR_ALIGN) {
+ unsigned long tmp_addr;
+
+ dma_buf = dma_alloc_coherent(size, &tmp_addr);
+ if (!dma_buf)
+ return;
+ } else {
+ dma_buf = buf;
+ }
+
+ ret = meson_nfc_dma_buffer_setup(nand, dma_buf, size, meson_chip->info_buf,
+ PER_INFO_BYTE, DMA_FROM_DEVICE);
+ if (ret) {
+ pr_err("Failed to setup DMA buffer %p/%p\n", dma_buf,
+ meson_chip->info_buf);
+ return;
+ }
+
+ cmd = NFC_CMD_N2M | size;
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_drain_cmd(nfc);
+ meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US);
+ meson_nfc_dma_buffer_release(nand, size, PER_INFO_BYTE, DMA_FROM_DEVICE);
+
+ if (buf != dma_buf) {
+ memcpy(buf, dma_buf, size);
+ dma_free_coherent(dma_buf);
+ }
+}
+
+static void meson_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int size)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ u8 *dma_buf;
+ int ret;
+ u32 cmd;
+
+ if ((uintptr_t)buf % DMA_ADDR_ALIGN) {
+ unsigned long tmp_addr;
+
+ dma_buf = dma_alloc_coherent(size, &tmp_addr);
+ if (!dma_buf)
+ return;
+
+ memcpy(dma_buf, buf, size);
+ } else {
+ dma_buf = (u8 *)buf;
+ }
+
+ ret = meson_nfc_dma_buffer_setup(nand, (void *)dma_buf, size, NULL,
+ 0, DMA_TO_DEVICE);
+ if (ret) {
+ pr_err("Failed to setup DMA buffer %p\n", dma_buf);
+ return;
+ }
+
+ cmd = NFC_CMD_M2N | size;
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_drain_cmd(nfc);
+ meson_nfc_wait_cmd_finish(nfc, CMD_DRAIN_TIMEOUT_US);
+ meson_nfc_dma_buffer_release(nand, size, 0, DMA_TO_DEVICE);
+
+ if (buf != dma_buf)
+ dma_free_coherent(dma_buf);
+}
+
+static int meson_nfc_write_page_sub(struct nand_chip *nand,
+ int page, bool raw)
+{
+ const struct mtd_info *mtd = nand_to_mtd(nand);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ int data_len, info_len;
+ int ret;
+ u32 cmd;
+
+ data_len = mtd->writesize + mtd->oobsize;
+ info_len = nand->ecc.steps * PER_INFO_BYTE;
+
+ ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
+ data_len, meson_chip->info_buf,
+ info_len, DMA_TO_DEVICE);
+ if (ret) {
+ pr_err("Failed to setup DMA buffer %p/%p\n",
+ meson_chip->data_buf, meson_chip->info_buf);
+ return ret;
+ }
+
+ if (nand->options & NAND_NEED_SCRAMBLING) {
+ meson_nfc_cmd_seed(nfc, page);
+ meson_nfc_cmd_access(nand, raw, DIRWRITE,
+ NFC_CMD_SCRAMBLER_ENABLE);
+ } else {
+ meson_nfc_cmd_access(nand, raw, DIRWRITE,
+ NFC_CMD_SCRAMBLER_DISABLE);
+ }
+
+ cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
+
+ return 0;
+}
+
+static int meson_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int oob_required, int page)
+{
+ meson_nfc_set_data_oob(chip, buf, oob_required ? chip->oob_poi : NULL);
+
+ return meson_nfc_write_page_sub(chip, page, true);
+}
+
+static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ const u8 *buf, int oob_required, int page)
+{
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip);
+
+ if (buf)
+ memcpy(meson_chip->data_buf, buf, mtd->writesize);
+
+ memset(meson_chip->info_buf, 0, chip->ecc.steps * PER_INFO_BYTE);
+
+ if (oob_required)
+ meson_nfc_set_user_byte(chip, chip->oob_poi);
+
+ return meson_nfc_write_page_sub(chip, page, false);
+}
+
+static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
+ struct nand_chip *nand, bool raw)
+{
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ __le64 *info;
+ u32 neccpages;
+ int ret;
+
+ neccpages = raw ? 1 : nand->ecc.steps;
+ info = &meson_chip->info_buf[neccpages - 1];
+ do {
+ udelay(ECC_POLL_TIMEOUT_US);
+ /* info is updated by nfc dma engine*/
+ rmb();
+ invalidate_dcache_range(nfc->iaddr, nfc->iaddr + nfc->info_bytes);
+ ret = *info & ECC_COMPLETE;
+ } while (!ret);
+}
+
+static int meson_nfc_read_page_sub(struct nand_chip *nand,
+ int page, bool raw)
+{
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ u32 data_len, info_len;
+ int ret;
+
+ data_len = mtd->writesize + mtd->oobsize;
+ info_len = nand->ecc.steps * PER_INFO_BYTE;
+
+ ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, data_len,
+ meson_chip->info_buf, info_len,
+ DMA_FROM_DEVICE);
+ if (ret)
+ return ret;
+
+ meson_nfc_cmd_access(nand, raw, DIRREAD, 0);
+
+ meson_nfc_wait_dma_finish(nfc);
+ meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
+
+ meson_nfc_dma_buffer_release(nand, data_len, info_len,
+ DMA_FROM_DEVICE);
+
+ return 0;
+}
+
+static int meson_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ int ret;
+
+ ret = meson_nfc_read_page_sub(chip, page, true);
+ if (ret)
+ return ret;
+
+ meson_nfc_get_data_oob(chip, buf, oob_required ? chip->oob_poi : NULL);
+
+ return 0;
+}
+
+static int meson_nfc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ u8 *buf, int oob_required, int page)
+{
+ const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(chip);
+ u64 correct_bitmap = 0;
+ u32 bitflips = 0;
+ int ret;
+
+ ret = meson_nfc_read_page_sub(chip, page, false);
+ if (ret)
+ return ret;
+
+ if (oob_required)
+ meson_nfc_get_user_byte(chip, chip->oob_poi);
+
+ ret = meson_nfc_ecc_correct(chip, &bitflips, &correct_bitmap);
+
+ if (ret == ECC_CHECK_RETURN_FF) {
+ if (buf)
+ memset(buf, 0xff, mtd->writesize);
+
+ if (oob_required)
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ } else if (ret < 0) {
+ struct nand_ecc_ctrl *ecc;
+ int i;
+
+ if ((chip->options & NAND_NEED_SCRAMBLING) || !buf) {
+ mtd->ecc_stats.failed++;
+ return bitflips;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ ret = meson_nfc_read_page_raw(mtd, chip, buf, 1, page);
+ if (ret)
+ return ret;
+
+ ecc = &chip->ecc;
+
+ for (i = 0; i < chip->ecc.steps ; i++) {
+ u8 *data = buf + i * ecc->size;
+ u8 *oob = chip->oob_poi + i * (ecc->bytes + 2);
+
+ if (correct_bitmap & BIT_ULL(i))
+ continue;
+
+ ret = nand_check_erased_ecc_chunk(data, ecc->size,
+ oob, ecc->bytes + 2,
+ NULL, 0,
+ ecc->strength);
+ if (ret < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += ret;
+ bitflips = max_t(u32, bitflips, ret);
+ }
+ }
+ } else if (buf && buf != meson_chip->data_buf) {
+ memcpy(buf, meson_chip->data_buf, mtd->writesize);
+ }
+
+ return bitflips;
+}
+
+static int meson_nfc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int ret;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ return meson_nfc_read_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static int meson_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int ret;
+
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ return meson_nfc_read_page_hwecc(mtd, chip, NULL, 1, page);
+}
+
+static int meson_nfc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = meson_nfc_write_page_raw(mtd, chip, NULL, 1, page);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int meson_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int ret;
+
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = meson_nfc_write_page_hwecc(mtd, chip, NULL, 1, page);
+ if (ret)
+ return ret;
+
+ return nand_prog_page_end_op(chip);
+}
+
+static void meson_nfc_nand_cmd_function(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+
+ chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+ if (column != -1 || page_addr != -1) {
+ int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (chip->options & NAND_BUSWIDTH_16 &&
+ !nand_opcode_8bits(command))
+ column >>= 1;
+
+ chip->cmd_ctrl(mtd, column, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ /* Only output a single addr cycle for 8bits
+ * opcodes.
+ */
+ if (!nand_opcode_8bits(command))
+ chip->cmd_ctrl(mtd, column >> 8, ctrl);
+ }
+
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, ctrl);
+ chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE |
+ NAND_ALE);
+ /* One more address cycle for devices > 128MiB */
+ if (chip->chipsize > SZ_128M)
+ chip->cmd_ctrl(mtd, page_addr >> 16,
+ NAND_NCE | NAND_ALE);
+ }
+
+ switch (command) {
+ case NAND_CMD_READ0:
+ chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ fallthrough;
+ case NAND_CMD_PARAM:
+ nand_wait_ready(mtd);
+ nand_exit_status_op(chip);
+ }
+ }
+}
+
+static void meson_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ cmd = NFC_CMD_MAKE_CLE(nfc->param.chip_select, cmd);
+ else
+ cmd = NFC_CMD_MAKE_ALE(nfc->param.chip_select, cmd);
+
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_wait_cmd_fifo(struct meson_nfc *nfc)
+{
+ while ((NFC_GET_CMD(nfc) >> 22) & GENMASK(4, 0))
+ ;
+}
+
+static u8 meson_nfc_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+ writel(NFC_CMD_MAKE_DRD(nfc->param.chip_select, 0), nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+ meson_nfc_cmd_idle(nfc, 0);
+ meson_nfc_cmd_idle(nfc, 0);
+
+ meson_nfc_wait_cmd_fifo(nfc);
+
+ return readl(nfc->reg_base + NFC_REG_BUF);
+}
+
+static void meson_nfc_nand_write_byte(struct mtd_info *mtd, u8 val)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+
+ writel(NFC_CMD_MAKE_DWR(nfc->param.chip_select, val), nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+ meson_nfc_cmd_idle(nfc, 0);
+ meson_nfc_cmd_idle(nfc, 0);
+
+ meson_nfc_wait_cmd_fifo(nfc);
+}
+
+static int meson_nfc_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ unsigned int time_out_cnt = 0;
+
+ chip->select_chip(mtd, 0);
+
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+
+ do {
+ int status;
+
+ status = (int)chip->read_byte(mtd);
+ if (status & NAND_STATUS_READY)
+ break;
+ } while (time_out_cnt++ < NFC_DEV_READY_TICK_MAX);
+
+ return time_out_cnt != NFC_DEV_READY_TICK_MAX;
+}
+
+static int meson_chip_buffer_init(struct nand_chip *nand)
+{
+ const struct mtd_info *mtd = nand_to_mtd(nand);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ u32 page_bytes, info_bytes, nsectors;
+ unsigned long tmp_addr;
+
+ nsectors = mtd->writesize / nand->ecc.size;
+
+ page_bytes = mtd->writesize + mtd->oobsize;
+ info_bytes = nsectors * PER_INFO_BYTE;
+
+ meson_chip->data_buf = dma_alloc_coherent(page_bytes, &tmp_addr);
+ if (!meson_chip->data_buf)
+ return -ENOMEM;
+
+ meson_chip->info_buf = dma_alloc_coherent(info_bytes, &tmp_addr);
+ if (!meson_chip->info_buf) {
+ dma_free_coherent(meson_chip->data_buf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static const int axg_stepinfo_strengths[] = { 8 };
+static const struct nand_ecc_step_info axg_stepinfo_1024 = {
+ .stepsize = 1024,
+ .strengths = axg_stepinfo_strengths,
+ .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
+};
+
+static const struct nand_ecc_step_info axg_stepinfo_512 = {
+ .stepsize = 512,
+ .strengths = axg_stepinfo_strengths,
+ .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
+};
+
+static const struct nand_ecc_step_info axg_stepinfo[] = { axg_stepinfo_1024, axg_stepinfo_512 };
+
+static const struct nand_ecc_caps meson_axg_ecc_caps = {
+ .stepinfos = axg_stepinfo,
+ .nstepinfos = ARRAY_SIZE(axg_stepinfo),
+ .calc_ecc_bytes = meson_nand_calc_ecc_bytes,
+};
+
+/*
+ * OOB layout:
+ *
+ * For ECC with 512 bytes step size:
+ * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB
+ * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC
+ * 0x20:
+ * 0x30:
+ *
+ * For ECC with 1024 bytes step size:
+ * 0x00: AA AA BB BB BB BB BB BB BB BB BB BB BB BB BB BB
+ * 0x10: AA AA CC CC CC CC CC CC CC CC CC CC CC CC CC CC
+ * 0x20: AA AA DD DD DD DD DD DD DD DD DD DD DD DD DD DD
+ * 0x30: AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE
+ *
+ * AA - user bytes.
+ * BB, CC, DD, EE - ECC code bytes for each step.
+ */
+static struct nand_ecclayout nand_oob;
+
+static void meson_nfc_init_nand_oob(struct nand_chip *nand)
+{
+ int section_size = 2 + nand->ecc.bytes;
+ int i;
+ int k;
+
+ nand_oob.eccbytes = nand->ecc.steps * nand->ecc.bytes;
+ k = 0;
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ int j;
+
+ for (j = 0; j < nand->ecc.bytes; j++)
+ nand_oob.eccpos[k++] = (i * section_size) + 2 + j;
+
+ nand_oob.oobfree[i].offset = (i * section_size);
+ nand_oob.oobfree[i].length = 2;
+ }
+
+ nand_oob.oobavail = 2 * nand->ecc.steps;
+ nand->ecc.layout = &nand_oob;
+}
+
+static int meson_nfc_init_ecc(struct nand_chip *nand, ofnode node)
+{
+ const struct mtd_info *mtd = nand_to_mtd(nand);
+ int ret;
+ int i;
+
+ ret = nand_check_ecc_caps(nand, &meson_axg_ecc_caps, mtd->oobsize - 2);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) {
+ if (meson_ecc[i].strength == nand->ecc.strength &&
+ meson_ecc[i].size == nand->ecc.size) {
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+
+ nand->ecc.steps = mtd->writesize / nand->ecc.size;
+ meson_chip->bch_mode = meson_ecc[i].bch;
+
+ meson_nfc_init_nand_oob(nand);
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int meson_nfc_nand_chip_init(struct udevice *dev, struct meson_nfc *nfc,
+ ofnode node)
+{
+ struct meson_nfc_nand_chip *meson_chip;
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ u32 cs[MAX_CE_NUM];
+ u32 nsels;
+ int ret;
+ int i;
+
+ if (!ofnode_get_property(node, "reg", &nsels)) {
+ dev_err(dev, "\"reg\" property is not found\n");
+ return -ENODEV;
+ }
+
+ nsels /= sizeof(u32);
+ if (nsels >= MAX_CE_NUM) {
+ dev_err(dev, "invalid size of CS array, max is %d\n",
+ MAX_CE_NUM);
+ return -EINVAL;
+ }
+
+ ret = ofnode_read_u32_array(node, "reg", cs, nsels);
+ if (ret < 0) {
+ dev_err(dev, "failed to read \"reg\" property\n");
+ return ret;
+ }
+
+ for (i = 0; i < nsels; i++) {
+ if (test_and_set_bit(cs[i], &nfc->assigned_cs)) {
+ dev_err(dev, "CS %d already assigned\n", cs[i]);
+ return -EINVAL;
+ }
+ }
+
+ meson_chip = malloc(sizeof(*meson_chip) + nsels * sizeof(meson_chip->sels[0]));
+ if (!meson_chip) {
+ dev_err(dev, "failed to allocate memory for chip\n");
+ return -ENOMEM;
+ }
+
+ meson_chip->nsels = nsels;
+ nand = &meson_chip->nand;
+
+ nand->flash_node = node;
+ nand_set_controller_data(nand, nfc);
+ /* Set the driver entry points for MTD */
+ nand->cmdfunc = meson_nfc_nand_cmd_function;
+ nand->cmd_ctrl = meson_nfc_cmd_ctrl;
+ nand->select_chip = meson_nfc_nand_select_chip;
+ nand->read_byte = meson_nfc_nand_read_byte;
+ nand->write_byte = meson_nfc_nand_write_byte;
+ nand->dev_ready = meson_nfc_dev_ready;
+
+ /* Buffer read/write routines */
+ nand->read_buf = meson_nfc_read_buf;
+ nand->write_buf = meson_nfc_write_buf;
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.hwctl = NULL;
+ nand->ecc.read_page = meson_nfc_read_page_hwecc;
+ nand->ecc.write_page = meson_nfc_write_page_hwecc;
+ nand->ecc.read_page_raw = meson_nfc_read_page_raw;
+ nand->ecc.write_page_raw = meson_nfc_write_page_raw;
+
+ nand->ecc.read_oob = meson_nfc_read_oob;
+ nand->ecc.write_oob = meson_nfc_write_oob;
+ nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
+ nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
+
+ nand->ecc.algo = NAND_ECC_BCH;
+
+ mtd = nand_to_mtd(nand);
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret) {
+ dev_err(dev, "'nand_scan_ident()' failed: %d\n", ret);
+ goto err_chip_free;
+ }
+
+ ret = meson_nfc_init_ecc(nand, node);
+ if (ret) {
+ dev_err(dev, "failed to init ECC settings: %d\n", ret);
+ goto err_chip_free;
+ }
+
+ ret = meson_chip_buffer_init(nand);
+ if (ret) {
+ dev_err(dev, "failed to init DMA buffers: %d\n", ret);
+ goto err_chip_free;
+ }
+
+ /* 'nand_scan_tail()' needs ECC parameters to be already
+ * set and correct.
+ */
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ dev_err(dev, "'nand_scan_tail()' failed: %d\n", ret);
+ goto err_chip_buf_free;
+ }
+
+ ret = nand_register(0, mtd);
+ if (ret) {
+ dev_err(dev, "'nand_register()' failed: %d\n", ret);
+ goto err_chip_buf_free;
+ }
+
+ list_add_tail(&meson_chip->node, &nfc->chips);
+
+ return 0;
+
+err_chip_buf_free:
+ dma_free_coherent(meson_chip->info_buf);
+ dma_free_coherent(meson_chip->data_buf);
+
+err_chip_free:
+ free(meson_chip);
+
+ return ret;
+}
+
+static int meson_nfc_nand_chips_init(struct udevice *dev,
+ struct meson_nfc *nfc)
+{
+ ofnode parent = dev_ofnode(dev);
+ ofnode node;
+
+ ofnode_for_each_subnode(node, parent) {
+ int ret = meson_nfc_nand_chip_init(dev, nfc, node);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void meson_nfc_clk_init(struct meson_nfc *nfc)
+{
+ u32 bus_cycle = NFC_DEFAULT_BUS_CYCLE;
+ u32 bus_timing = NFC_DEFAULT_BUS_TIMING;
+ u32 bus_cfg_val;
+
+ writel(CLK_ALWAYS_ON_NAND | CLK_SELECT_NAND | CLK_ENABLE_VALUE, nfc->reg_clk);
+ writel(0, nfc->reg_base + NFC_REG_CFG);
+
+ bus_cfg_val = (((bus_cycle - 1) & 31) | ((bus_timing & 31) << 5));
+ writel(bus_cfg_val, nfc->reg_base + NFC_REG_CFG);
+ writel(BIT(31), nfc->reg_base + NFC_REG_CMD);
+}
+
+static int meson_probe(struct udevice *dev)
+{
+ struct meson_nfc *nfc = dev_get_priv(dev);
+ void *addr;
+ int ret;
+
+ addr = dev_read_addr_ptr(dev);
+ if (!addr) {
+ dev_err(dev, "base register address not found\n");
+ return -EINVAL;
+ }
+
+ nfc->reg_base = addr;
+
+ addr = dev_read_addr_index_ptr(dev, 1);
+ if (!addr) {
+ dev_err(dev, "clk register address not found\n");
+ return -EINVAL;
+ }
+
+ nfc->reg_clk = addr;
+ nfc->dev = dev;
+
+ meson_nfc_clk_init(nfc);
+
+ ret = meson_nfc_nand_chips_init(dev, nfc);
+ if (ret) {
+ dev_err(nfc->dev, "failed to init chips\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id meson_nand_dt_ids[] = {
+ {.compatible = "amlogic,meson-axg-nfc",},
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(meson_nand) = {
+ .name = "meson_nand",
+ .id = UCLASS_MTD,
+ .of_match = meson_nand_dt_ids,
+ .probe = meson_probe,
+ .priv_auto = sizeof(struct meson_nfc),
+};
+
+void board_nand_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_MTD,
+ DM_DRIVER_GET(meson_nand), &dev);
+
+ if (ret && ret != -ENODEV)
+ pr_err("Failed to initialize: %d\n", ret);
+}
--
2.35.0
2
6
Upgrade the used QEMU to v8.2.1. This avoids patching the source.
Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt(a)canonical.com>
---
tools/docker/Dockerfile | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile
index 6122776bc6..7df68d9c9c 100644
--- a/tools/docker/Dockerfile
+++ b/tools/docker/Dockerfile
@@ -178,12 +178,10 @@ RUN git clone git://git.savannah.gnu.org/grub.git /tmp/grub && \
RUN git clone https://gitlab.com/qemu-project/qemu.git /tmp/qemu && \
cd /tmp/qemu && \
- git checkout v8.2.0 && \
+ git checkout v8.2.1 && \
# config user.name and user.email to make 'git am' happy
git config user.name u-boot && \
git config user.email u-boot(a)denx.de && \
- git format-patch 0c7ffc977195~..0c7ffc977195 && \
- git am 0001-hw-net-cadence_gem-Fix-MDIO_OP_xxx-values.patch && \
./configure --prefix=/opt/qemu --target-list="aarch64-softmmu,arm-softmmu,i386-softmmu,m68k-softmmu,mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu,ppc-softmmu,riscv32-softmmu,riscv64-softmmu,sh4-softmmu,x86_64-softmmu,xtensa-softmmu" && \
make -j$(nproc) all install && \
rm -rf /tmp/qemu
--
2.43.0
2
1
On 1/28/24 18:38, Bhumkar, Tejas Arvind wrote:
> [AMD Official Use Only - General]
>
> Hi Heinrich,
>
>> -----Original Message-----
>> From: Heinrich Schuchardt <xypron.glpk(a)gmx.de>
>> Sent: Sunday, January 28, 2024 2:51 PM
>> To: Bhumkar, Tejas Arvind <tejas.arvind.bhumkar(a)amd.com>
>> Cc: u-boot(a)lists.denx.de; trini(a)konsulko.com; sjg(a)chromium.org; Simek, Michal
>> <michal.simek(a)amd.com>; Abbarapu, Venkatesh
>> <venkatesh.abbarapu(a)amd.com>; git(a)xilinx.com; Ilias Apalodimas
>> <ilias.apalodimas(a)linaro.org>
>> Subject: Re: [PATCH] efi_loader : Suppress error print message
>>
>> Caution: This message originated from an External Source. Use proper caution
>> when opening attachments, clicking links, or responding.
>>
>>
>> On 1/28/24 06:20, Bhumkar, Tejas Arvind wrote:
>>> [AMD Official Use Only - General]
>>>
>>> Hi Heinrich,
>>>
>>>> -----Original Message-----
>>>> From: Heinrich Schuchardt <xypron.glpk(a)gmx.de>
>>>> Sent: Wednesday, January 24, 2024 2:09 AM
>>>> To: Bhumkar, Tejas Arvind <tejas.arvind.bhumkar(a)amd.com>
>>>> Cc: u-boot(a)lists.denx.de; trini(a)konsulko.com; sjg(a)chromium.org;
>>>> Simek, Michal <michal.simek(a)amd.com>; Abbarapu, Venkatesh
>>>> <venkatesh.abbarapu(a)amd.com>; git(a)xilinx.com; Ilias Apalodimas
>>>> <ilias.apalodimas(a)linaro.org>
>>>> Subject: Re: [PATCH] efi_loader : Suppress error print message
>>>>
>>>> Caution: This message originated from an External Source. Use proper
>>>> caution when opening attachments, clicking links, or responding.
>>>>
>>>>
>>>> On 1/23/24 19:53, Bhumkar, Tejas Arvind wrote:
>>>>> [AMD Official Use Only - General]
>>>>>
>>>>> Hi Ilias & Heinrich,
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Heinrich Schuchardt <xypron.glpk(a)gmx.de>
>>>>>> Sent: Tuesday, January 23, 2024 3:30 PM
>>>>>> To: Ilias Apalodimas <ilias.apalodimas(a)linaro.org>; Bhumkar, Tejas
>>>>>> Arvind <tejas.arvind.bhumkar(a)amd.com>
>>>>>> Cc: u-boot(a)lists.denx.de; xypron.glpk(a)gmx.de; trini(a)konsulko.com;
>>>>>> sjg(a)chromium.org; Simek, Michal <michal.simek(a)amd.com>; Abbarapu,
>>>>>> Venkatesh <venkatesh.abbarapu(a)amd.com>; git(a)xilinx.com
>>>>>> Subject: Re: [PATCH] efi_loader : Suppress error print message
>>>>>>
>>>>>> Caution: This message originated from an External Source. Use
>>>>>> proper caution when opening attachments, clicking links, or responding.
>>>>>>
>>>>>>
>>>>>> On 1/23/24 09:33, Ilias Apalodimas wrote:
>>>>>>> Hi Tejas,
>>>>>>>
>>>>>>> On Mon, 22 Jan 2024 at 21:12, Tejas Bhumkar
>>>>>>> <tejas.arvind.bhumkar(a)amd.com> wrote:
>>>>>>>>
>>>>>>>> Currently, on certain Xilinx platforms, an issue has been
>>>>>>>> identified, manifesting as follows:
>>>>>>
>>>>>> Hello Tejas,
>>>>>>
>>>>>> thank you for bringing forward this issue.
>>>>>>
>>>>>> Which defconfig are you relating to?
>>>>>
>>>>> [Tejas]:
>>>>> Checking with xilinx_zynqmp_virt_defconfig
>>>>>
>>>>>>
>>>>>>>>
>>>>>>>> Starting kernel ...
>>>>>>>>
>>>>>>>> efi_free_pool: illegal free 0x0000000077830040
>>>>>>>> efi_free_pool: illegal free 0x000000007782d040
>>>>>>>> efi_free_pool: illegal free 0x000000007782c040
>>>>>>>>
>>>>>>>> The issue arises when the ramdisk image is relocated, placing it
>>>>>>>> within the previously allocated EFI memory region( as EFI is
>>>>>>>> established quite early in U-Boot).
>>>>>>
>>>>>> Which version of U-Boot are you on? Commit 06d514d77c37 ("lmb:
>>>>>> consider EFI memory map") was meant to avoid such a situation.
>>>>>
>>>>> [Tejas] :
>>>>> I'm verifying against the latest changes in the master branch. The
>>>>> introduction of commit 06d514d77c37 ("lmb: consider EFI memory map")
>>>>> has resolved the occurrence of the "efi_free_pool: illegal free"
>>>>> error. but, it leads to a new error, as detailed in the following
>>>>> patch:
>>>>> https://lore.kernel.org/all/20230212150706.2967007-1-sjoerd@collabora.
>>>>> com/
>>>>
>>>> Could you, please, try to reproduce your issues with origin/master as
>>>> of today and provide the full boot log. Please, also provide the
>>>> output of the bdinfo and the printenv command as well as the sizes of the
>> kernel and the RAM disk.
>>>
>>> [Tejas]: I've provided two logs: one obtained from the latest u-boot
>> origin/master, resulting in the "ERROR: reserving fdt memory region failed" error,
>> and the other from reverting commit 06d514d77c37 ("lmb: consider EFI memory
>> map"), which leads to the "efi_free_pool: illegal free" error.
>>>
>>> Thank You,
>>> Tejas.
>>
>> Where can I find these logs?
> [Tejas ] : I've sent the attachment in a previous email. I'm not sure why you didn't receive it.
> I've attached it again now. Please confirm if you still haven't received it.
>
> Thank You,
> Tejas.
>>
If I understand your FDT_memory_region_failed.log correctly:
Except for some added debug messages this log matches booting with an
unmodified upstream U-Boot as of Jan 22nd.
Your memory is split into two regions:
0x0 - 0x7ffffff and
0x800000000 - 0x87ffffff.
Your device-tree has a reserved region 0x7ff00000-0x7fffffff.
U-Boot relocates itself into the lower memory block due to current design.
efi_lmb_reserve() marks memory up to 0x7fffffff and
0x800000000-0x87fffffff as reserved.
boot_fdt_reserve_region() writes an error when it tries to reserve the
overlapping region 0x7ff00000-0x7fffffff because LMB recognizes this
region is already reserved.
This should not interfere with booting as boot_fdt_reserve_region() does
not have a return value but is not very clean and should be fixed.
Best regards
Heinrich
>>>>
>>>>>
>>>>>>
>>>>>>>
>>>>>>> I don't mind suppressing the print for some time, but out of
>>>>>>> curiosity, how is the ramdisk relocated? LMB should be aware of
>>>>>>> the EFI regions by then, so I assume the relocation code doesn't
>>>>>>> check those?
>>>>>
>>>>>
>>>>> [Tejas] : I observe that the relocation of the RAM disk is taking
>>>>> place in the line
>>>> below.
>>>>> https://github.com/u-boot/u-boot/blob/master/lib/lmb.c#L480-L491
>>>>> Yes, the relocated code specifically examines the LMB region and
>>>>> does not
>>>> consider the EFI region.
>>>>>
>>>>>>
>>>>>> The indicated situation is serious. If the EFI sub-system is using
>>>>>> the same memory area as the ramdisk, this may lead to corruption of
>>>>>> the ramdisk. Suppressing the messages is by no means adequate to
>>>>>> solve the
>>>> problem.
>>>>>
>>>>> [Tejas] :
>>>>> The challenge arises when relocating the ramdisk image, inserting it
>>>>> into the previously assigned EFI memory region, established early in
>>>>> U-Boot. Consequently, when attempting to release memory in the EFI
>>>>> region
>>>> during the handover process to the kernel in the efi_free_pool
>>>> function, we first verify if the memory was allocated by efi_allocate_pool().
>>>>> The issue originates from a checksum mismatch because, during the
>>>>> ramdisk
>>>> relocation, we overwrite memory allocated by EFI.
>>>>> This leads to the appearance of the error message: efi_free_pool:
>>>>> illegal free
>>>> 0x0000000077830040.
>>>>> Crucially, there is no corruption of the ramdisk seen since we do
>>>>> not actually
>>>> releasing memory due to the checksum mismatch.
>>>>> In our testing, this issue was observed only when the ramdisk size
>>>>> exceeded
>>>> approximately 24 MB.
>>>>>
>>>>>>
>>>>>> Best regards
>>>>>>
>>>>>> Heinrich
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Thanks
>>>>>>> /Ilias
>>>>>>>
>>>>>>>> Consequently, when
>>>>>>>> attempting to release memory in the EFI memory region during the
>>>>>>>> handover process to the kernel,we encounter memory violations.
>>>>>>>>
>>>>>>>> Highlighting that EFI remains active primarily during the booting
>>>>>>>> of an EFI application, and the lmb persists while configuring
>>>>>>>> images for the boot process. Since we aren't utilizing the EFI
>>>>>>>> memory region during the boot process, there is no adverse impact
>>>>>>>> even in the event of a violation.
>>>>>>>>
>>>>>>>> Currently, there is an ongoing discussion regarding the handling
>>>>>>>> strategies of three memory allocators: malloc, lmb, and EFI. This
>>>>>>>> discussion is documented in the email chain titled "Proposal:
>>>>>>>> U-Boot memory management."
>>>>>>>>
>>>>>>>> Therefore, it is advisable to suppress the print message during
>>>>>>>> the boot process for now.
>>>>>>>>
>>>>>>>> Signed-off-by: Tejas Bhumkar <tejas.arvind.bhumkar(a)amd.com>
>>>>>>>> ---
>>>>>>>> lib/efi_loader/efi_memory.c | 2 +-
>>>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>>>>
>>>>>>>> diff --git a/lib/efi_loader/efi_memory.c
>>>>>>>> b/lib/efi_loader/efi_memory.c index edfad2d95a..821fe7616e 100644
>>>>>>>> --- a/lib/efi_loader/efi_memory.c
>>>>>>>> +++ b/lib/efi_loader/efi_memory.c
>>>>>>>> @@ -713,7 +713,7 @@ efi_status_t efi_free_pool(void *buffer)
>>>>>>>> /* Check that this memory was allocated by efi_allocate_pool() */
>>>>>>>> if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
>>>>>>>> alloc->checksum != checksum(alloc)) {
>>>>>>>> - printf("%s: illegal free 0x%p\n", __func__, buffer);
>>>>>>>> + debug("%s: illegal free 0x%p\n", __func__,
>>>>>>>> + buffer);
>>>>>>>> return EFI_INVALID_PARAMETER;
>>>>>>>> }
>>>>>>>> /* Avoid double free */
>>>>>>>> --
>>>>>>>> 2.27.0
>>>>>>>>
>>>>>
>>>
>
2
1

I'm trying to create a new nv_uboot-snow-usb.kpart file changing some parameters inside the file snow.h file, but the modifications I made aren't detected.
by Mario Marietto 04 Feb '24
by Mario Marietto 04 Feb '24
04 Feb '24
Hello.
My ARM Chromebook SNOW model has the mmc slot broken,but I've realized
that I can boot Linux using the USB port ! To enable the booting of Linux
via USB
I have modified this file :
https://github.com/virtualopensystems/u-boot/blob/eecfeb578e296ef3b739ac918…
in this way :
/*
* Copyright (C) 2013 - Virtual Open Systems
* Author: Nikolay Nikolaev <n.nikolaev(a)virtualopensystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __configs_snow_h__
#define __configs_snow_h__
#include <configs/smdk5250.h>
#undef CONFIG_DEFAULT_DEVICE_TREE
#define CONFIG_DEFAULT_DEVICE_TREE exynos5250-snow
/* Generally verified boot needs more heap space */
#undef CONFIG_SYS_MALLOC_LEN
#define CONFIG_SYS_MALLOC_LEN (32 << 20)
#define CONFIG_INITRD_ADDRESS 0x44000000
#include <configs/chromeos.h>
/* Disable ChromeOS specifics */
#undef CONFIG_CHROMEOS
/* Force reading the environment */
#undef CONFIG_OF_LOAD_ENVIRONMENT
#define CONFIG_CHROMEOS_USB
/* Support vboot flag reading from GPIO hardwrae */
#define CONFIG_CHROMEOS_GPIO_FLAG
/* Support vboot flag reading from EC */
#define CONFIG_CHROMEOS_MKBP_FLAG
/* Use the default arch_phys_memset implementation */
#define CONFIG_PHYSMEM
/* Adjust the display resolution. */
#undef MAIN_VRESOL_VAL
#undef MAIN_HRESOL_VAL
#define MAIN_VRESOL_VAL 0x300
#define MAIN_HRESOL_VAL 0x556
#undef LCD_XRES
#undef LCD_YRES
#define LCD_XRES 1366
#define LCD_YRES 768
#define CONFIG_SYS_WHITE_ON_BLACK
/*
* Extra bootargs used for direct booting, but not for vboot.
* - console of the board
* - debug and earlyprintk: easier to debug; they could be removed later
*/
#define CONFIG_DIRECT_BOOTARGS \
"console=tty1 debug clk_ignore_unused"
/* Standard input, output and error device of U-Boot console. */
#define CONFIG_STD_DEVICES_SETTINGS EXYNOS_DEVICE_SETTINGS
#define CONFIG_CHROMEOS_SD_TO_SPI \
"sd_to_spi=echo Flashing U-Boot from SD card to SPI flash; " \
"if mmc dev 1 && " \
"mmc rescan && " \
"mmc read 40008000 1 1000 && " \
"sf probe 1:0 && " \
"sf update 40008000 0 80000; then " \
"echo Flash completed; else " \
"echo Flash failed; " \
"fi\0"
/* Replace default CONFIG_EXTRA_ENV_SETTINGS */
#ifdef CONFIG_EXTRA_ENV_SETTINGS
#undef CONFIG_EXTRA_ENV_SETTINGS
#endif
#define CONFIG_EXTRA_ENV_SETTINGS \
EXYNOS_DEVICE_SETTINGS \
"dtaddr=0x43000000\0"\
"initrdaddr=0x44000000\0"\
"boot_noinitrd=usb start ; usb dev 0 ; ext2load usb 0:2 ${loadaddr}
uImage ; ext2load usb 0:2 ${dtaddr} exynos5250-snow.dtb ; bootm
${loadaddr} - ${dtaddr}\0"\
"boot_initrd=usb start ; usb dev 0 ; ext2load usb 0:2 ${loadaddr}
uImage ; ext2load usb 0:2 ${initrdaddr} initrd ; ext2load usb 0:2
${dtaddr} exynos5250-snow.dtb ; bootm ${loadaddr} ${initrdaddr}
${dtaddr}\0"\
"bootdelay=10\0"
#ifdef CONFIG_BOOTARGS
#undef CONFIG_BOOTARGS
#endif
#define CONFIG_BOOTARGS \
"console=tty1 root=/dev/da0p4 rw rootwait clk_ignore_unused --no-log"
/* Replace default CONFIG_BOOTCOMMAND */
#ifdef CONFIG_BOOTCOMMAND
#undef CONFIG_BOOTCOMMAND
#endif
#define CONFIG_BOOTCOMMAND \
"run boot_noinitrd"
/* Enable splash screens */
#define CONFIG_CROS_SPLASH
/* Enable simple framebuffer */
#define CONFIG_SIMPLEFB
/* Enable writing on a FAT formatted filesystem */
#define CONFIG_FAT_WRITE
/* Enable virt mode commands */
#define CONFIG_ARMV7_VIRT
/* Disable d-cache */
#define CONFIG_SYS_DCACHE_OFF
#endif /* __configs_snow_h__ */
I have modified mmc with usb 0 and I've changed the timeout from
"bootdelay=3\0"
to "bootdelay=10\0" and I ran the script that located here :
https://github.com/virtualopensystems/u-boot/blob/eecfeb578e296ef3b739ac918…
that creates a new : nv_uboot-snow-usb.kpart file,that I install on the sd
card where
I have installed Linux,using the following commands :
# dd if=/dev/zero of=/dev/sdi1
dd: writing to '/dev/sdi1': No space left on device
32769+0 records in
32768+0 records out
16777216 bytes (17 MB, 16 MiB) copied, 2.07775 s, 8.1 MB/s
# sudo dd if=nv_uboot-snow-usb.kpart of=/dev/sdi1
At this point I attach the usb to sd card converter on the Chromebook and I
try to
boot Linux,but to my surprise I see that the bootloader is still set to 3
seconds.
It means that for some reason,the new nv_uboot-snow-usb.kpart is not
detected,
because when I have recompiled it,I have chosen 10 seconds of timeout for
u-boot.
I really don't have any idea of the reasons why the new settings aren't
applied.
--
Mario.
1
0
Dear Tom,
The following changes since commit 050a9b981d6a835133521b599be3ae189ce70f41:
Merge tag 'u-boot-amlogic-fixes-20240201' of
https://source.denx.de/u-boot/custodians/u-boot-amlogic (2024-02-01
09:59:53 -0500)
are available in the Git repository at:
https://source.denx.de/u-boot/custodians/u-boot-efi.git
tags/smbios-2024-04-rc2
for you to fetch changes up to 406c410ef747d66e16f2f5494cbf88ba1307224f:
smbios: correctly name Structure Table Maximum Size field (2024-02-02
19:57:45 +0100)
Gitlab CI showed no issues:
https://source.denx.de/u-boot/custodians/u-boot-efi/-/pipelines/19538
----------------------------------------------------------------
Pull request smbios-2024-04-rc2
* In smbios command
- write 'Not Specified' for missing strings
- show correct table size for SMBIOS2.1 entry point
- adjust formatting of handle numbers
- add missing colon after UUID
* In generated SMBIOS table
- avoid introducing 'Unknown' string for missing properties
- provide RISC-V vendor ID in the type 4 structure
- provide the correct chassis handle in structure type 2
* Rename Structure Table Maximum Size field in SMBIOS 3 entry point
----------------------------------------------------------------
Heinrich Schuchardt (11):
lib: smbios_entr() use logical or for booleans
smbios: get_str_from_dt() - add sysinfo_id description
cmd: smbios: always use '0x%04x' for printing handles
cmd: smbios: add missing colon after UUID
cmd: smbios: replace missing string by 'Not Specified'
smbios: if a string value is unknown, use string number 0
smbios: provide type 4 RISC-V SMBIOS Processor ID
smbios: correctly fill chassis handle
cmd: smbios: show correct table size for SMBIOS2.1 entry point
smbios: do not determine maximum structure size
smbios: correctly name Structure Table Maximum Size field
Matthias Brugger (1):
smbios: Fix table when no string is present
cmd/smbios.c | 20 ++++++++++--------
drivers/cpu/riscv_cpu.c | 12 +++++++++++
drivers/misc/qfw_smbios.c | 2 +-
include/smbios.h | 2 +-
lib/efi_loader/efi_tcg2.c | 4 ++--
lib/efi_loader/smbiosdump.c | 6 +++---
lib/smbios-parser.c | 4 ++--
lib/smbios.c | 49
+++++++++++++++++++++++++--------------------
8 files changed, 60 insertions(+), 39 deletions(-)
2
1