Add a DDR controller driver for the Metanoia MT2824 SoC, supporting
initialization and training of the DDR subsystem in SPL, and implementing
the driver-model for the RAM uclass.

The driver relies on a firmware to configure the DDR controller, and
expects the firmware to be found via a device tree reg entry named "fw".

The driver expects the DDR firmware to be packaged inside a FIT image,
as the firmware node of the default configuration, and it supports signed
FIT image.

Signed-off-by: Benoît Monin <[email protected]>
---
 drivers/ram/Kconfig      |   7 +
 drivers/ram/Makefile     |   2 +
 drivers/ram/mt2824-ddr.c | 706 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 715 insertions(+)

diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index acd6d9cfd6a..feb1754e40e 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -139,6 +139,13 @@ config K3_MULTI_DDR
          responsible for interleaving between the controllers.
        default y if SOC_K3_J721S2 || SOC_K3_J784S4
 
+config SPL_METANOIA_MT2824_DDR
+       bool "Metanoia MT2824 DDR driver in SPL"
+       depends on SPL_RAM
+       help
+          Enable support for the internal DDR Memory Controller
+          of the Metanoia MT2824 SoC in the SPL.
+
 source "drivers/ram/aspeed/Kconfig"
 source "drivers/ram/cadence/Kconfig"
 source "drivers/ram/octeon/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index 82afd5fcbcc..6e1b86127ce 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -19,6 +19,8 @@ obj-$(CONFIG_K3_DDRSS) += k3-ddrss/
 
 obj-$(CONFIG_IMXRT_SDRAM) += imxrt_sdram.o
 
+obj-$(CONFIG_SPL_METANOIA_MT2824_DDR) += mt2824-ddr.o
+
 obj-$(CONFIG_RAM_SIFIVE) += sifive/
 ifdef CONFIG_XPL_BUILD
 obj-$(CONFIG_SPL_STARFIVE_DDR) += starfive/
diff --git a/drivers/ram/mt2824-ddr.c b/drivers/ram/mt2824-ddr.c
new file mode 100644
index 00000000000..c01c30089ca
--- /dev/null
+++ b/drivers/ram/mt2824-ddr.c
@@ -0,0 +1,706 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <image.h>
+#include <ram.h>
+#include <reset.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <u-boot/crc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* controller registers */
+#define CTRL_STAT                              0x00000004
+#define CTRL_PWRCTL                            0x00000030
+#define CTRL_RFSHCTL3                          0x00000060
+#define CTRL_INIT7                             0x000000ec
+#define CTRL_DFITMG0                           0x00000190
+#define CTRL_DFITMG1                           0x00000194
+#define CTRL_DFIMISC                           0x000001b0
+#define CTRL_DFISTAT                           0x000001bc
+#define CTRL_SWCTL                             0x00000320
+#define CTRL_SWSTAT                            0x00000324
+
+#define CTRL_STAT_MODE                         GENMASK(2, 0)
+#define CTRL_STAT_NORMAL                       1
+
+#define CTRL_PWRCTL_SELFREF                    BIT(5)
+#define CTRL_PWRCTL_CLKDIS                     BIT(3)
+#define CTRL_PWRCTL_PWRDN                      BIT(1)
+
+#define CTRL_RFSHCTL3_DIS_AUTO                 BIT(0)
+
+#define CTRL_INIT7_MR22                                GENMASK(31, 16)
+#define CTRL_INIT7_MR14                                GENMASK(15, 0)
+
+#define CTRL_DFITMG0_CTRL_DELAY                        GENMASK(28, 24)
+#define CTRL_DFITMG0_RDDATA_USE_DFI_PHY_CLK    BIT(23)
+#define CTRL_DFITMG0_RDDATA_EN                 GENMASK(22, 16)
+#define CTRL_DFITMG0_WRDATA_USE_DFI_PHY_CLK    BIT(15)
+#define CTRL_DFITMG0_TPHY_WRDATA               GENMASK(13, 8)
+#define CTRL_DFITMG0_TPHY_WRLAT                        GENMASK(5, 0)
+
+#define CTRL_DFITMG1_WRDATA_DELAY              GENMASK(20, 16)
+#define CTRL_DFITMG1_CLK_DIS                   GENMASK(12, 8)
+#define CTRL_DFITMG1_CLK_EN                    GENMASK(4, 0)
+
+#define CTRL_DFIMISC_START                     BIT(5)
+#define CTRL_DFIMISC_COMPLETE                  BIT(0)
+
+#define CTRL_DFISTAT_COMPLETE                  BIT(0)
+
+#define CTRL_SWCTL_DONE                                BIT(0)
+#define CTRL_SWSTAT_DONE                       BIT(0)
+
+/* PHY registers */
+#define PHY_DQS_DELAY_GRP0                     0x000201a0
+#define PHY_RDPTR_INITVAL                      0x0004005c
+#define PHY_ICCM_BASE                          0x000a0000
+#define PHY_DCCM_BASE                          0x000a8000
+#define PHY_DCCM_V26                           0x000a804c
+#define PHY_DCCM_V27                           0x000a804e
+#define PHY_DCCM_V40                           0x000a8080
+#define PHY_PMU_MUX_SEL                                0x001a0000
+#define PHY_UCTSHADOWREGS                      0x001a0008
+#define PHY_DCTWRITEPROT                       0x001a0062
+#define PHY_UCTWRITEONLYSHADOW                 0x001a0064
+#define PHY_UCTDATWRITEONLYSHADOW              0x001a0068
+#define PHY_UC_CTRL                            0x001a0132
+
+#define PHY_DQS_DELAY_COARSE                   GENMASK(9, 6)
+#define PHY_DQS_DELAY_FINE                     GENMASK(4, 0)
+
+#define PHY_ICCM_SIZE                          0x00008000
+#define PHY_DCCM_SIZE                          0x00002000
+
+#define PHY_PMU_MUX_SEL_UC                     BIT(0)
+
+#define PHY_UC_CTRL_RESET                      BIT(3)
+#define PHY_UC_CTRL_STALL                      BIT(0)
+
+/* DDR parameters */
+#define PARAM_BL                               16
+#define PARAM_CLK_DIS                          2
+#define PARAM_CLK_EN                           2
+#define PARAM_MISC                             0
+#define PARAM_MR22                             5
+#define PARAM_RD                               0
+#define PARAM_RL                               28
+#define PARAM_TDQSS                            1
+#define PARAM_WL                               14
+#define PARAM_WR                               0
+#define PARAM_WRDATA                           2
+
+#define TRAINING_STAGES                                2
+
+#define POLL_TIMEOUT                           1000000
+
+#define MSG_TRAINING_DONE                      0x07
+#define MSG_TRAINING_FAIL                      0xff
+
+#define FW_MAGIC                               0x44445246 /* "DDRF" */
+#define FW_VERSION                             0x01000000
+
+/* Base address used in the firmware for the controller */
+#define CTRL_FW_BASE                           0x10060000
+
+enum {
+       FW_COMP_CTRL_CFG = 0,
+       FW_COMP_PHY_CFG,
+       FW_COMP_PMU_1D_IMEM,
+       FW_COMP_PMU_1D_DMEM,
+       FW_COMP_PMU_2D_IMEM,
+       FW_COMP_PMU_2D_DMEM,
+       FW_COMP_PIE_IMAGE,
+       FW_COMP_MAX
+};
+
+struct fw_comp {
+       u32 offset;
+       u32 size;
+       u32 crc32;
+       u32 reserved;
+};
+
+struct fw_header {
+       u32 magic;
+       u32 version;
+       u32 header_size;
+       u32 total_size;
+       u32 flags;
+       u32 timestamp;
+       u32 reserved1;
+       u32 reserved2;
+       struct fw_comp comps[FW_COMP_MAX];
+       u32 header_crc32;
+       u32 reserved[27]; /* Padding to 256 bytes */
+};
+
+struct fw_reg {
+       u32 addr;
+       u32 value;
+};
+
+struct mt2824_ddr_priv {
+       void __iomem *ctrl;
+       resource_size_t ctrl_size;
+       void __iomem *phy;
+       resource_size_t phy_size;
+       void *fw;
+       resource_size_t fw_size;
+       struct reset_ctl rst;
+       const void *fw_img;
+};
+
+static int get_fw_img_from_fit(const void *fit, size_t fit_size,
+                              const void **fw_img, size_t *fw_size)
+{
+       int conf_noffset;
+       const char *img_name;
+       int img_noffset;
+
+       if (fit_check_format(fit, fit_size))
+               return -EINVAL;
+
+       conf_noffset = fit_conf_get_node(fit, NULL);
+       if (conf_noffset < 0)
+               return -EINVAL;
+
+       printf("## Checking hash(es) for config %s ... ",
+              fit_get_name(fit, conf_noffset, NULL));
+       if (fit_config_verify(fit, conf_noffset))
+               return -EPERM;
+       puts("OK\n");
+
+       img_name = fdt_getprop(fit, conf_noffset, FIT_FIRMWARE_PROP, NULL);
+       if (!img_name)
+               return -EINVAL;
+
+       img_noffset = fit_image_get_node(fit, img_name);
+       if (img_noffset < 0)
+               return -EINVAL;
+
+       if (fit_image_get_emb_data(fit, img_noffset, fw_img, fw_size))
+               return -EINVAL;
+
+       printf("## Checking hash(es) for image %s ... ", img_name);
+       if (!fit_image_verify_with_data(fit, img_noffset, gd_fdt_blob(), 
*fw_img, *fw_size))
+               return -EPERM;
+       puts("OK\n");
+
+       return 0;
+}
+
+static int check_firmware(struct udevice *dev)
+{
+       struct mt2824_ddr_priv *priv = dev_get_priv(dev);
+       const struct fw_header *header;
+       u32 calculated_crc;
+       int ret = 0;
+       size_t fw_size;
+
+       ret = get_fw_img_from_fit(priv->fw, priv->fw_size, &priv->fw_img, 
&fw_size);
+       if (ret)
+               return ret;
+
+       header = priv->fw_img;
+
+       if (header->magic != FW_MAGIC) {
+               dev_err(dev, "Invalid firmware magic: 0x%08x (expected 
0x%08x)\n",
+                       header->magic, FW_MAGIC);
+               return -EINVAL;
+       }
+
+       if (header->version != FW_VERSION) {
+               dev_err(dev, "Invalid firmware version %u\n", header->version);
+               return -EINVAL;
+       }
+
+       if (header->header_size != sizeof(struct fw_header)) {
+               dev_err(dev, "Invalid firmware header size %u\n", 
header->header_size);
+               return -EINVAL;
+       }
+
+       if (header->total_size > fw_size) {
+               dev_err(dev, "firmware too big\n");
+               return -EINVAL;
+       }
+
+       calculated_crc = crc32(0, priv->fw_img, offsetof(struct fw_header, 
header_crc32));
+       if (calculated_crc != header->header_crc32) {
+               dev_err(dev, "Header CRC mismatch: calculated=0x%08x, 
expected=0x%08x\n",
+                       calculated_crc, header->header_crc32);
+               return -EINVAL;
+       }
+
+       for (int i = 0; i < FW_COMP_MAX; i++) {
+               u32 offset = header->comps[i].offset;
+               u32 size = header->comps[i].size;
+               u32 expected_crc = header->comps[i].crc32;
+
+               if (size == 0) {
+                       dev_err(dev, "Component #%d is empty\n", i);
+                       ret = -EINVAL;
+                       continue;
+               }
+
+               if ((resource_size_t)offset + size > fw_size) {
+                       dev_err(dev, "Component #%d out of range\n", i);
+                       ret = -EINVAL;
+                       continue;
+               }
+
+               calculated_crc = crc32(0, priv->fw_img + offset, size);
+               if (calculated_crc != expected_crc) {
+                       dev_err(dev, "Component #%d CRC verification failed\n", 
i);
+                       ret = -EINVAL;
+               }
+       }
+
+       return ret;
+}
+
+static int ddr_ctrl_load_config(struct mt2824_ddr_priv *priv)
+{
+       const struct fw_header *header = priv->fw_img;
+       const struct fw_reg *reg;
+       unsigned int nb_regs;
+       u32 addr;
+
+       /* address + value per register */
+       nb_regs = header->comps[FW_COMP_CTRL_CFG].size / sizeof(struct fw_reg);
+       reg = priv->fw_img + header->comps[FW_COMP_CTRL_CFG].offset;
+
+       /* Apply each register configuration */
+       for (unsigned int i = 0; i < nb_regs; i++, reg++) {
+               addr = reg->addr;
+
+               /*
+                * Some firmware version use an absolute address,
+                * convert to a relative offset
+                */
+               if (addr >= CTRL_FW_BASE)
+                       addr -= CTRL_FW_BASE;
+
+               if (addr >= priv->ctrl_size)
+                       return -EINVAL;
+
+               writel(reg->value, priv->ctrl + addr);
+       }
+
+       return 0;
+}
+
+static int ddr_ctrl_init(struct mt2824_ddr_priv *priv)
+{
+       u32 val;
+
+       setbits_le32(priv->ctrl + CTRL_RFSHCTL3, CTRL_RFSHCTL3_DIS_AUTO);
+       clrbits_le32(priv->ctrl + CTRL_PWRCTL, CTRL_PWRCTL_PWRDN | 
CTRL_PWRCTL_CLKDIS);
+       writel(0, priv->ctrl + CTRL_SWCTL);
+       clrbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_COMPLETE);
+       setbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+
+       return readl_poll_timeout(priv->ctrl + CTRL_SWSTAT, val,
+                                 val == CTRL_SWSTAT_DONE, POLL_TIMEOUT);
+}
+
+static int ddr_phy_load_comp(struct mt2824_ddr_priv *priv, unsigned int comp)
+{
+       const struct fw_header *header = priv->fw_img;
+       const struct fw_reg *reg;
+       unsigned int nb_regs;
+
+       /* address + value per register */
+       nb_regs = header->comps[comp].size / sizeof(struct fw_reg);
+       reg = priv->fw_img + header->comps[comp].offset;
+
+       /* Apply each register configuration */
+       for (unsigned int i = 0; i < nb_regs; i++, reg++) {
+               if (reg->addr >= priv->phy_size / 2)
+                       return -EINVAL;
+               writew((u16)(reg->value & 0xFFFF), priv->phy + reg->addr * 2);
+       }
+
+       return 0;
+}
+
+static int phy_load_training_data(struct mt2824_ddr_priv *priv, unsigned int 
comp,
+                                 void __iomem *base, resource_size_t size)
+{
+       const struct fw_header *header = priv->fw_img;
+       unsigned int nb_vals;
+       const u16 *val;
+
+       if (header->comps[comp].size > size)
+               return -EINVAL;
+
+       nb_vals = header->comps[comp].size / sizeof(u16);
+       val = priv->fw_img + header->comps[comp].offset;
+
+       for (unsigned int i = 0; i < nb_vals; i++) {
+               writew(*val, base + i * 2);
+               val++;
+       }
+
+       return 0;
+}
+
+static u32 get_mail(struct mt2824_ddr_priv *priv, int mode)
+{
+       u32 mail;
+       u16 val;
+
+       readw_poll_sleep_timeout(priv->phy + PHY_UCTSHADOWREGS, val,
+                                !(val & BIT(0)), mode ? 0 : 100, 0);
+
+       mail = readw(priv->phy + PHY_UCTWRITEONLYSHADOW);
+       if (mode == 1)
+               mail |= (readw(priv->phy + PHY_UCTDATWRITEONLYSHADOW) << 16);
+
+       writew(0, priv->phy + PHY_DCTWRITEPROT);
+       readw_poll_timeout(priv->phy + PHY_UCTSHADOWREGS, val, val, 0);
+       writew(1, priv->phy + PHY_DCTWRITEPROT);
+
+       return mail;
+}
+
+static void decode_streaming_message(struct mt2824_ddr_priv *priv)
+{
+       u32 msg_len = get_mail(priv, 1) & 0xffff;
+
+       for (unsigned int i = 0; i < msg_len; i++)
+               get_mail(priv, 1);
+}
+
+static void decode_major_message(struct mt2824_ddr_priv *priv, u32 mail)
+{
+       switch (mail) {
+       case MSG_TRAINING_DONE:
+               debug("DDRPHY: Training has run successfully\n");
+               break;
+       case MSG_TRAINING_FAIL:
+               debug("DDRPHY: Training has failed\n");
+               break;
+       case 0x00:
+               debug("DDRPHY: End of initialization\n");
+               break;
+       case 0x01:
+               debug("DDRPHY: End of fine write leveling\n");
+               break;
+       case 0x02:
+               debug("DDRPHY: End of read enable training\n");
+               break;
+       case 0x03:
+               debug("DDRPHY: End of read delay center optimization\n");
+               break;
+       case 0x04:
+               debug("DDRPHY: End of write delay center optimization\n");
+               break;
+       case 0x05:
+               debug("DDRPHY: End of 2D read delay/voltage center 
optimization\n");
+               break;
+       case 0x06:
+               debug("DDRPHY: End of 2D write delay/voltage center 
optimization\n");
+               break;
+       case 0x09:
+               debug("DDRPHY: End of max read latency training\n");
+               break;
+       case 0x0a:
+               debug("DDRPHY: End of read dq deskew training\n");
+               break;
+       case 0x0b:
+               debug("DDRPHY: Reserved\n");
+               break;
+       case 0x0c:
+               debug("DDRPHY: End of all DB training (MREP/DWL/MRD/MWD 
complete)\n");
+               break;
+       case 0x0d:
+               debug("DDRPHY: End of CA training\n");
+               break;
+       case 0xfd:
+               debug("DDRPHY: End of MPR read delay center optimization\n");
+               break;
+       case 0xfe:
+               debug("DDRPHY: End of Write leveling coarse delay\n");
+               break;
+       case 0x08:
+               debug("DDRPHY: Start streaming message mode\n");
+               decode_streaming_message(priv);
+               break;
+       default:
+               debug("DDRPHY: Error, unknown major_message %x\n", mail);
+       }
+}
+
+static int ddr_phy_wait_training_done(struct mt2824_ddr_priv *priv)
+{
+       u32 mail = 0;
+
+       do {
+               mail = get_mail(priv, 0);
+               decode_major_message(priv, mail);
+       } while (mail != MSG_TRAINING_DONE && mail != MSG_TRAINING_FAIL);
+
+       return (mail == MSG_TRAINING_DONE) ? 0 : -EIO;
+}
+
+static void write_mr14_to_register(struct mt2824_ddr_priv *priv)
+{
+       u16 v26 = readw(priv->phy + PHY_DCCM_V26);
+       u16 v27 = readw(priv->phy + PHY_DCCM_V27);
+       u16 v40 = readw(priv->phy + PHY_DCCM_V40);
+
+       u8 b0 = (v26 >> 8) & 0xff;
+       u8 b1 = v27 & 0xff;
+       u8 b2 = v40 & 0xff;
+       u8 b3 = (v40 >> 8) & 0xff;
+
+       u32 sum = (u32)b0 + b1 + b2 + b3;
+       u8 avg = (sum + 2) / 4;
+
+       writel(FIELD_PREP(CTRL_INIT7_MR22, PARAM_MR22) | 
FIELD_PREP(CTRL_INIT7_MR14, avg),
+              priv->ctrl + CTRL_INIT7);
+}
+
+static int ddr_phy_training(struct mt2824_ddr_priv *priv)
+{
+       struct {
+               unsigned int imem_comp;
+               unsigned int dmem_comp;
+       } fw_info[TRAINING_STAGES] = {
+               {FW_COMP_PMU_1D_IMEM, FW_COMP_PMU_1D_DMEM},
+               {FW_COMP_PMU_2D_IMEM, FW_COMP_PMU_2D_DMEM}
+       };
+       int ret;
+
+       for (unsigned int stage = 0; stage < TRAINING_STAGES; stage++) {
+               ret = phy_load_training_data(priv, fw_info[stage].imem_comp,
+                                            priv->phy + PHY_ICCM_BASE, 
PHY_ICCM_SIZE);
+               if (ret)
+                       return ret;
+
+               writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+               writew(0, priv->phy + PHY_PMU_MUX_SEL);
+
+               ret = phy_load_training_data(priv, fw_info[stage].dmem_comp,
+                                            priv->phy + PHY_DCCM_BASE, 
PHY_DCCM_SIZE);
+               if (ret)
+                       return ret;
+
+               /*
+                * MicroContMuxSel=1, written twice to match the PhyInit
+                * sequence (end of DMEM load and start of the firmware).
+                */
+               writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+               writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+
+               writew(PHY_UC_CTRL_RESET | PHY_UC_CTRL_STALL, priv->phy + 
PHY_UC_CTRL);
+               writew(PHY_UC_CTRL_STALL, priv->phy + PHY_UC_CTRL);
+               writew(0, priv->phy + PHY_UC_CTRL);
+
+               ret = ddr_phy_wait_training_done(priv);
+               if (ret)
+                       return ret;
+
+               writew(PHY_UC_CTRL_STALL, priv->phy + PHY_UC_CTRL);
+               writew(0, priv->phy + PHY_PMU_MUX_SEL);
+
+               if (stage == 1)
+                       write_mr14_to_register(priv);
+
+               writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+               writew(0, priv->phy + PHY_PMU_MUX_SEL);
+       }
+
+       return 0;
+}
+
+static int ddr_ctrl_configure(struct mt2824_ddr_priv *priv)
+{
+       u8 trddata_en = PARAM_RL - 5 + 2 * (PARAM_MISC - PARAM_RD);
+       u8 tphy_wrlat = PARAM_WL - 5 + 2 * (PARAM_MISC - PARAM_WR) + 
PARAM_TDQSS;
+       u8 twrdata_delay;
+       u8 tctrl_delay;
+       u8 ardptrinitval;
+       u16 dqs_dly;
+       u8 trained_txdqsdly;
+       u32 val;
+       int ret;
+
+       clrbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+       setbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_START);
+       setbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+
+       ret = readl_poll_timeout(priv->ctrl + CTRL_SWSTAT, val,
+                                val == CTRL_SWSTAT_DONE, POLL_TIMEOUT);
+       if (ret)
+               return ret;
+
+       ret = readl_poll_timeout(priv->ctrl + CTRL_DFISTAT, val,
+                                FIELD_GET(CTRL_DFISTAT_COMPLETE, val), 
POLL_TIMEOUT);
+       if (ret)
+               return ret;
+
+       clrbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+       clrbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_START);
+       setbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_COMPLETE);
+       clrbits_le32(priv->ctrl + CTRL_PWRCTL, CTRL_PWRCTL_SELFREF);
+
+       writew(0, priv->phy + PHY_PMU_MUX_SEL);
+
+       /* read the trained DQS delay and round it up */
+       dqs_dly = readw(priv->phy + PHY_DQS_DELAY_GRP0);
+       trained_txdqsdly = FIELD_GET(PHY_DQS_DELAY_COARSE, dqs_dly) +
+                          (FIELD_GET(PHY_DQS_DELAY_FINE, dqs_dly) +
+                           FIELD_MAX(PHY_DQS_DELAY_FINE)) /
+                          (FIELD_MAX(PHY_DQS_DELAY_FINE) + 1);
+
+       ardptrinitval = readw(priv->phy + PHY_RDPTR_INITVAL);
+       tctrl_delay = ((ardptrinitval / 2) + (PARAM_MISC * 2) * 1 + 3);
+       twrdata_delay = tctrl_delay + (6 + PARAM_BL / 2) + trained_txdqsdly;
+
+       writew(PHY_PMU_MUX_SEL_UC, priv->phy + PHY_PMU_MUX_SEL);
+
+       writel(FIELD_PREP(CTRL_DFITMG0_CTRL_DELAY, tctrl_delay) |
+              CTRL_DFITMG0_RDDATA_USE_DFI_PHY_CLK |
+              FIELD_PREP(CTRL_DFITMG0_RDDATA_EN, trddata_en) |
+              CTRL_DFITMG0_WRDATA_USE_DFI_PHY_CLK |
+              FIELD_PREP(CTRL_DFITMG0_TPHY_WRDATA, PARAM_WRDATA) |
+              FIELD_PREP(CTRL_DFITMG0_TPHY_WRLAT, tphy_wrlat),
+              priv->ctrl + CTRL_DFITMG0);
+
+       writel(FIELD_PREP(CTRL_DFITMG1_WRDATA_DELAY, twrdata_delay) |
+              FIELD_PREP(CTRL_DFITMG1_CLK_DIS, PARAM_CLK_DIS) |
+              FIELD_PREP(CTRL_DFITMG1_CLK_EN, PARAM_CLK_EN),
+              priv->ctrl + CTRL_DFITMG1);
+
+       setbits_le32(priv->ctrl + CTRL_DFIMISC, CTRL_DFIMISC_COMPLETE);
+
+       clrbits_le32(priv->ctrl + CTRL_PWRCTL, CTRL_PWRCTL_SELFREF);
+
+       setbits_le32(priv->ctrl + CTRL_SWCTL, CTRL_SWCTL_DONE);
+       ret = readl_poll_timeout(priv->ctrl + CTRL_SWSTAT, val,
+                                val == CTRL_SWSTAT_DONE, POLL_TIMEOUT);
+       if (ret)
+               return ret;
+
+       ret = readl_poll_timeout(priv->ctrl + CTRL_STAT, val,
+                                FIELD_GET(CTRL_STAT_MODE, val) == 
CTRL_STAT_NORMAL,
+                                POLL_TIMEOUT);
+       if (ret)
+               return ret;
+
+       clrbits_le32(priv->ctrl + CTRL_RFSHCTL3, CTRL_RFSHCTL3_DIS_AUTO);
+
+       return 0;
+}
+
+static int mt2824_ddr_init(struct udevice *dev)
+{
+       struct mt2824_ddr_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = check_firmware(dev);
+       if (ret)
+               return ret;
+
+       ret = reset_assert(&priv->rst);
+       if (ret)
+               return ret;
+
+       ret = ddr_ctrl_load_config(priv);
+       if (ret)
+               return ret;
+
+       ret = reset_deassert(&priv->rst);
+       if (ret)
+               return ret;
+
+       ret = ddr_ctrl_init(priv);
+       if (ret)
+               return ret;
+
+       ret = ddr_phy_load_comp(priv, FW_COMP_PHY_CFG);
+       if (ret)
+               return ret;
+
+       ret = ddr_phy_training(priv);
+       if (ret)
+               return ret;
+
+       ret = ddr_phy_load_comp(priv, FW_COMP_PIE_IMAGE);
+       if (ret)
+               return ret;
+
+       ret = ddr_ctrl_configure(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int mt2824_ddr_probe(struct udevice *dev)
+{
+       struct mt2824_ddr_priv *priv = dev_get_priv(dev);
+       struct resource res;
+       int ret;
+
+       ret = dev_read_resource_byname(dev, "ctrl", &res);
+       if (ret)
+               return ret;
+
+       priv->ctrl_size = resource_size(&res);
+       priv->ctrl = devm_ioremap(dev, res.start, priv->ctrl_size);
+
+       ret = dev_read_resource_byname(dev, "phy", &res);
+       if (ret)
+               return ret;
+
+       priv->phy_size = resource_size(&res);
+       priv->phy = devm_ioremap(dev, res.start, priv->phy_size);
+
+       ret = dev_read_resource_byname(dev, "fw", &res);
+       if (ret)
+               return ret;
+
+       priv->fw_size = resource_size(&res);
+       priv->fw = devm_ioremap(dev, res.start, priv->fw_size);
+
+       ret = reset_get_by_index(dev, 0, &priv->rst);
+       if (ret)
+               return ret;
+
+       return mt2824_ddr_init(dev);
+}
+
+static int mt2824_ddr_get_info(struct udevice *dev, struct ram_info *info)
+{
+       info->base = gd->ram_base;
+       info->size = gd->ram_size;
+
+       return 0;
+}
+
+static const struct ram_ops mt2824_ddr_ops = {
+       .get_info = mt2824_ddr_get_info,
+};
+
+static const struct udevice_id mt2824_ddr_ids[] = {
+       { .compatible = "metanoia,mt2824-ddrc" },
+       { }
+};
+
+U_BOOT_DRIVER(mt2824_ddr) = {
+       .name = "mt2824_ddr",
+       .id = UCLASS_RAM,
+       .ops = &mt2824_ddr_ops,
+       .of_match = mt2824_ddr_ids,
+       .probe = mt2824_ddr_probe,
+       .priv_auto = sizeof(struct mt2824_ddr_priv),
+};

-- 
2.55.0

Reply via email to