@@ -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"
@@ -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/
new file mode 100644
@@ -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),
+};
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 <benoit.monin@bootlin.com> --- drivers/ram/Kconfig | 7 + drivers/ram/Makefile | 2 + drivers/ram/mt2824-ddr.c | 706 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 715 insertions(+)