Add SDRAM driver for i.MXRT SoCs.

Signed-off-by: Giulio Benetti <giulio.bene...@benettiengineering.com>
---
 drivers/ram/Kconfig                      |   8 +
 drivers/ram/Makefile                     |   2 +
 drivers/ram/imxrt_sdram.c                | 439 +++++++++++++++++++++++
 include/dt-bindings/memory/imxrt-sdram.h | 100 ++++++
 4 files changed, 549 insertions(+)
 create mode 100644 drivers/ram/imxrt_sdram.c
 create mode 100644 include/dt-bindings/memory/imxrt-sdram.h

diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index b454ceb599..56fea7c94c 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -65,5 +65,13 @@ config K3_J721E_DDRSS
          Enabling this config adds support for the DDR memory controller
          on J721E family of SoCs.
 
+config IMXRT_SDRAM
+       bool "Enable i.MXRT SDRAM support"
+       depends on RAM
+       help
+         i.MXRT family devices support smart external memory controller(SEMC)
+         to support external memories like sdram, psram & nand.
+         This driver is for the sdram memory interface with the SEMC.
+
 source "drivers/ram/rockchip/Kconfig"
 source "drivers/ram/stm32mp1/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index 4b77969b39..5c897410c6 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -15,3 +15,5 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
 obj-$(CONFIG_K3_AM654_DDRSS) += k3-am654-ddrss.o
 obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
 obj-$(CONFIG_K3_J721E_DDRSS) += k3-j721e/
+
+obj-$(CONFIG_IMXRT_SDRAM) += imxrt_sdram.o
diff --git a/drivers/ram/imxrt_sdram.c b/drivers/ram/imxrt_sdram.c
new file mode 100644
index 0000000000..af7400be82
--- /dev/null
+++ b/drivers/ram/imxrt_sdram.c
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019
+ * Author(s): Giulio Benetti <giulio.bene...@benettiengineering.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <ram.h>
+#include <asm/io.h>
+
+/* SDRAM Command Code */
+#define SD_CC_ARD              0x0     /* Master Bus (AXI) command - Read */
+#define SD_CC_AWR              0x1     /* Master Bus (AXI) command - Write */
+#define SD_CC_IRD              0x8     /* IP command - Read */
+#define SD_CC_IWR              0x9     /* IP command - Write */
+#define SD_CC_IMS              0xA     /* IP command - Set Mode Register */
+#define SD_CC_IACT             0xB     /* IP command - ACTIVE */
+#define SD_CC_IAF              0xC     /* IP command - Auto Refresh */
+#define SD_CC_ISF              0xD     /* IP Command - Self Refresh */
+#define SD_CC_IPRE             0xE     /* IP command - Precharge */
+#define SD_CC_IPREA            0xF     /* IP command - Precharge ALL */
+
+#define SEMC_MCR_MDIS          BIT(1)
+#define SEMC_MCR_DQSMD         BIT(2)
+
+#define SEMC_INTR_IPCMDERR     BIT(1)
+#define SEMC_INTR_IPCMDDONE    BIT(0)
+
+#define SEMC_IPCMD_KEY         0xA55A0000
+
+struct imxrt_semc_regs {
+       /* 0x0 */
+       u32 mcr;
+       u32 iocr;
+       u32 bmcr0;
+       u32 bmcr1;
+       u32 br[9];
+
+       /* 0x34 */
+       u32 res1;
+       u32 inten;
+       u32 intr;
+       /* 0x40 */
+       u32 sdramcr0;
+       u32 sdramcr1;
+       u32 sdramcr2;
+       u32 sdramcr3;
+       /* 0x50 */
+       u32 nandcr0;
+       u32 nandcr1;
+       u32 nandcr2;
+       u32 nandcr3;
+       /* 0x60 */
+       u32 norcr0;
+       u32 norcr1;
+       u32 norcr2;
+       u32 norcr3;
+       /* 0x70 */
+       u32 sramcr0;
+       u32 sramcr1;
+       u32 sramcr2;
+       u32 sramcr3;
+       /* 0x80 */
+       u32 dbicr0;
+       u32 dbicr1;
+       u32 res2[2];
+       /* 0x90 */
+       u32 ipcr0;
+       u32 ipcr1;
+       u32 ipcr2;
+       u32 ipcmd;
+       /* 0xA0 */
+       u32 iptxdat;
+       u32 res3[3];
+       /* 0xB0 */
+       u32 iprxdat;
+       u32 res4[3];
+       /* 0xC0 */
+       u32 sts[16];
+};
+
+#define SEMC_IOCR_MUX_A8_SHIFT         0
+#define SEMC_IOCR_MUX_CSX0_SHIFT       3
+#define SEMC_IOCR_MUX_CSX1_SHIFT       6
+#define SEMC_IOCR_MUX_CSX2_SHIFT       9
+#define SEMC_IOCR_MUX_CSX3_SHIFT       12
+#define SEMC_IOCR_MUX_RDY_SHIFT                15
+
+struct imxrt_sdram_mux {
+       u8 a8;
+       u8 csx0;
+       u8 csx1;
+       u8 csx2;
+       u8 csx3;
+       u8 rdy;
+};
+
+#define SEMC_SDRAMCR0_PS_SHIFT         0
+#define SEMC_SDRAMCR0_BL_SHIFT         4
+#define SEMC_SDRAMCR0_COL_SHIFT                8
+#define SEMC_SDRAMCR0_CL_SHIFT         10
+
+struct imxrt_sdram_control {
+       u8 memory_width;
+       u8 burst_len;
+       u8 no_columns;
+       u8 cas_latency;
+};
+
+#define SEMC_SDRAMCR1_PRE2ACT_SHIFT    0
+#define SEMC_SDRAMCR1_ACT2RW_SHIFT     4
+#define SEMC_SDRAMCR1_RFRC_SHIFT       8
+#define SEMC_SDRAMCR1_WRC_SHIFT                13
+#define SEMC_SDRAMCR1_CKEOFF_SHIFT     16
+#define SEMC_SDRAMCR1_ACT2PRE_SHIFT    20
+
+#define SEMC_SDRAMCR2_SRRC_SHIFT       0
+#define SEMC_SDRAMCR2_REF2REF_SHIFT    8
+#define SEMC_SDRAMCR2_ACT2ACT_SHIFT    16
+#define SEMC_SDRAMCR2_ITO_SHIFT                24
+
+#define SEMC_SDRAMCR3_REN              BIT(0)
+#define SEMC_SDRAMCR3_REBL_SHIFT       1
+#define SEMC_SDRAMCR3_PRESCALE_SHIFT   8
+#define SEMC_SDRAMCR3_RT_SHIFT         16
+#define SEMC_SDRAMCR3_UT_SHIFT         24
+
+struct imxrt_sdram_timing {
+       u8 pre2act;
+       u8 act2rw;
+       u8 rfrc;
+       u8 wrc;
+       u8 ckeoff;
+       u8 act2pre;
+
+       u8 srrc;
+       u8 ref2ref;
+       u8 act2act;
+       u8 ito;
+
+       u8 rebl;
+       u8 prescale;
+       u8 rt;
+       u8 ut;
+};
+
+enum imxrt_semc_bank {
+       SDRAM_BANK1,
+       SDRAM_BANK2,
+       SDRAM_BANK3,
+       SDRAM_BANK4,
+       MAX_SDRAM_BANK,
+};
+
+#define SEMC_BR_VLD_MASK               1
+#define SEMC_BR_MS_SHIFT               1
+
+struct bank_params {
+       enum imxrt_semc_bank target_bank;
+       u32 base_address;
+       u32 memory_size;
+};
+
+struct imxrt_sdram_params {
+       struct imxrt_semc_regs *base;
+
+       struct imxrt_sdram_mux *sdram_mux;
+       struct imxrt_sdram_control *sdram_control;
+       struct imxrt_sdram_timing *sdram_timing;
+
+       struct bank_params bank_params[MAX_SDRAM_BANK];
+       u8 no_sdram_banks;
+};
+
+static int imxrt_sdram_wait_ipcmd_done(struct imxrt_semc_regs *regs)
+{
+       do {
+               readl(&regs->intr);
+
+               if (regs->intr & SEMC_INTR_IPCMDDONE)
+                       return 0;
+               if (regs->intr & SEMC_INTR_IPCMDERR)
+                       return -EIO;
+
+               mdelay(50);
+       } while (1);
+}
+
+static int imxrt_sdram_ipcmd(struct imxrt_semc_regs *regs, u32 mem_addr,
+                            u32 ipcmd, u32 wd, u32 *rd)
+{
+       int ret;
+
+       if (ipcmd == SD_CC_IWR || ipcmd == SD_CC_IMS)
+               writel(wd, &regs->iptxdat);
+
+       /* set slave address for every command as specified on RM */
+       writel(mem_addr, &regs->ipcr0);
+
+       /* execute command */
+       writel(SEMC_IPCMD_KEY | ipcmd, &regs->ipcmd);
+
+       ret = imxrt_sdram_wait_ipcmd_done(regs);
+       if (ret < 0)
+               return ret;
+
+       if (ipcmd == SD_CC_IRD) {
+               if (!rd)
+                       return -EINVAL;
+
+               *rd = readl(&regs->iprxdat);
+       }
+
+       return 0;
+}
+
+int imxrt_sdram_init(struct udevice *dev)
+{
+       struct imxrt_sdram_params *params = dev_get_platdata(dev);
+       struct imxrt_sdram_mux *mux = params->sdram_mux;
+       struct imxrt_sdram_control *ctrl = params->sdram_control;
+       struct imxrt_sdram_timing *time = params->sdram_timing;
+       struct imxrt_semc_regs *regs = params->base;
+       struct bank_params *bank_params;
+       u32 rd;
+       int i;
+
+       /* enable the SEMC controller */
+       clrbits_le32(&regs->mcr, SEMC_MCR_MDIS);
+       /* set DQS mode from DQS pad */
+       setbits_le32(&regs->mcr, SEMC_MCR_DQSMD);
+
+       for (i = 0, bank_params = params->bank_params;
+               i < params->no_sdram_banks; bank_params++,
+               i++)
+               writel((bank_params->base_address & 0xfffff000)
+                      | bank_params->memory_size << SEMC_BR_MS_SHIFT
+                      | SEMC_BR_VLD_MASK,
+                      &regs->br[bank_params->target_bank]);
+
+       writel(mux->a8 << SEMC_IOCR_MUX_A8_SHIFT
+               | mux->csx0 << SEMC_IOCR_MUX_CSX0_SHIFT
+               | mux->csx1 << SEMC_IOCR_MUX_CSX1_SHIFT
+               | mux->csx2 << SEMC_IOCR_MUX_CSX2_SHIFT
+               | mux->csx3 << SEMC_IOCR_MUX_CSX3_SHIFT
+               | mux->rdy << SEMC_IOCR_MUX_RDY_SHIFT,
+               &regs->iocr);
+
+       writel(ctrl->memory_width << SEMC_SDRAMCR0_PS_SHIFT
+               | ctrl->burst_len << SEMC_SDRAMCR0_BL_SHIFT
+               | ctrl->no_columns << SEMC_SDRAMCR0_COL_SHIFT
+               | ctrl->cas_latency << SEMC_SDRAMCR0_CL_SHIFT,
+               &regs->sdramcr0);
+
+       writel(time->pre2act << SEMC_SDRAMCR1_PRE2ACT_SHIFT
+               | time->act2rw << SEMC_SDRAMCR1_ACT2RW_SHIFT
+               | time->rfrc << SEMC_SDRAMCR1_RFRC_SHIFT
+               | time->wrc << SEMC_SDRAMCR1_WRC_SHIFT
+               | time->ckeoff << SEMC_SDRAMCR1_CKEOFF_SHIFT
+               | time->act2pre << SEMC_SDRAMCR1_ACT2PRE_SHIFT,
+               &regs->sdramcr1);
+
+       writel(time->srrc << SEMC_SDRAMCR2_SRRC_SHIFT
+               | time->ref2ref << SEMC_SDRAMCR2_REF2REF_SHIFT
+               | time->act2act << SEMC_SDRAMCR2_ACT2ACT_SHIFT
+               | time->ito << SEMC_SDRAMCR2_ITO_SHIFT,
+               &regs->sdramcr2);
+
+       writel(time->rebl << SEMC_SDRAMCR3_REBL_SHIFT
+               | time->prescale << SEMC_SDRAMCR3_PRESCALE_SHIFT
+               | time->rt << SEMC_SDRAMCR3_RT_SHIFT
+               | time->ut << SEMC_SDRAMCR3_UT_SHIFT
+               | SEMC_SDRAMCR3_REN,
+               &regs->sdramcr3);
+
+       writel(2, &regs->ipcr1);
+
+       for (i = 0, bank_params = params->bank_params;
+               i < params->no_sdram_banks; bank_params++,
+               i++) {
+               mdelay(250);
+               imxrt_sdram_ipcmd(regs, bank_params->base_address, SD_CC_IPREA,
+                                 0, &rd);
+               imxrt_sdram_ipcmd(regs, bank_params->base_address, SD_CC_IAF,
+                                 0, &rd);
+               imxrt_sdram_ipcmd(regs, bank_params->base_address, SD_CC_IAF,
+                                 0, &rd);
+               imxrt_sdram_ipcmd(regs, bank_params->base_address, SD_CC_IMS,
+                                 ctrl->burst_len | (ctrl->cas_latency << 4),
+                                 &rd);
+               mdelay(250);
+       }
+
+       return 0;
+}
+
+static int imxrt_semc_ofdata_to_platdata(struct udevice *dev)
+{
+       struct imxrt_sdram_params *params = dev_get_platdata(dev);
+       ofnode bank_node;
+       u8 bank = 0;
+
+       params->sdram_mux =
+               (struct imxrt_sdram_mux *)
+                dev_read_u8_array_ptr(dev,
+                                      "fsl,sdram-mux",
+                                      sizeof(struct imxrt_sdram_mux));
+       if (!params->sdram_mux) {
+               pr_err("fsl,sdram-mux not found");
+               return -EINVAL;
+       }
+
+       params->sdram_control =
+               (struct imxrt_sdram_control *)
+                dev_read_u8_array_ptr(dev,
+                                      "fsl,sdram-control",
+                                      sizeof(struct imxrt_sdram_control));
+       if (!params->sdram_control) {
+               pr_err("fsl,sdram-control not found");
+               return -EINVAL;
+       }
+
+       params->sdram_timing =
+               (struct imxrt_sdram_timing *)
+                dev_read_u8_array_ptr(dev,
+                                      "fsl,sdram-timing",
+                                      sizeof(struct imxrt_sdram_timing));
+       if (!params->sdram_timing) {
+               pr_err("fsl,sdram-timing not found");
+               return -EINVAL;
+       }
+
+       dev_for_each_subnode(bank_node, dev) {
+               struct bank_params *bank_params;
+               char *bank_name;
+               int ret;
+
+               /* extract the bank index from DT */
+               bank_name = (char *)ofnode_get_name(bank_node);
+               strsep(&bank_name, "@");
+               if (!bank_name) {
+                       pr_err("missing sdram bank index");
+                       return -EINVAL;
+               }
+
+               bank_params = &params->bank_params[bank];
+               strict_strtoul(bank_name, 10,
+                              (unsigned long *)&bank_params->target_bank);
+               if (bank_params->target_bank >= MAX_SDRAM_BANK) {
+                       pr_err("Found bank %d , but only bank 0,1,2,3 are 
supported",
+                              bank_params->target_bank);
+                       return -EINVAL;
+               }
+
+               ret = ofnode_read_u32(bank_node,
+                                     "fsl,memory-size",
+                                     &bank_params->memory_size);
+               if (ret < 0) {
+                       pr_err("fsl,memory-size not found");
+                       return -EINVAL;
+               }
+
+               ret = ofnode_read_u32(bank_node,
+                                     "fsl,base-address",
+                                     &bank_params->base_address);
+               if (ret < 0) {
+                       pr_err("fsl,base-address not found");
+                       return -EINVAL;
+               }
+
+               debug("Found bank %s %u\n", bank_name,
+                     bank_params->target_bank);
+               bank++;
+       }
+
+       params->no_sdram_banks = bank;
+       debug("%s, no of banks = %d\n", __func__, params->no_sdram_banks);
+
+       return 0;
+}
+
+static int imxrt_semc_probe(struct udevice *dev)
+{
+       struct imxrt_sdram_params *params = dev_get_platdata(dev);
+       int ret;
+       fdt_addr_t addr;
+
+       addr = dev_read_addr(dev);
+       if (addr == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       params->base = (struct imxrt_semc_regs *)addr;
+
+#ifdef CONFIG_CLK
+       struct clk clk;
+
+       ret = clk_get_by_index(dev, 0, &clk);
+       if (ret < 0)
+               return ret;
+
+       ret = clk_enable(&clk);
+
+       if (ret) {
+               dev_err(dev, "failed to enable clock\n");
+               return ret;
+       }
+#endif
+       ret = imxrt_sdram_init(dev);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int imxrt_semc_get_info(struct udevice *dev, struct ram_info *info)
+{
+       return 0;
+}
+
+static struct ram_ops imxrt_semc_ops = {
+       .get_info = imxrt_semc_get_info,
+};
+
+static const struct udevice_id imxrt_semc_ids[] = {
+       { .compatible = "fsl,imxrt-semc", .data = 0 },
+       { }
+};
+
+U_BOOT_DRIVER(imxrt_semc) = {
+       .name = "imxrt_semc",
+       .id = UCLASS_RAM,
+       .of_match = imxrt_semc_ids,
+       .ops = &imxrt_semc_ops,
+       .ofdata_to_platdata = imxrt_semc_ofdata_to_platdata,
+       .probe = imxrt_semc_probe,
+       .platdata_auto_alloc_size = sizeof(struct imxrt_sdram_params),
+};
diff --git a/include/dt-bindings/memory/imxrt-sdram.h 
b/include/dt-bindings/memory/imxrt-sdram.h
new file mode 100644
index 0000000000..acb35bce27
--- /dev/null
+++ b/include/dt-bindings/memory/imxrt-sdram.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2019
+ * Author(s): Giulio Benetti <giulio.bene...@benettiengineering.com>
+ */
+
+#ifndef DT_BINDINGS_IMXRT_SDRAM_H
+#define DT_BINDINGS_IMXRT_SDRAM_H
+
+#define MEM_SIZE_4K            0x00
+#define MEM_SIZE_8K            0x01
+#define MEM_SIZE_16K           0x02
+#define MEM_SIZE_32K           0x03
+#define MEM_SIZE_64K           0x04
+#define MEM_SIZE_128K          0x05
+#define MEM_SIZE_256K          0x06
+#define MEM_SIZE_512K          0x07
+#define MEM_SIZE_1M            0x08
+#define MEM_SIZE_2M            0x09
+#define MEM_SIZE_4M            0x0A
+#define MEM_SIZE_8M            0x0B
+#define MEM_SIZE_16M           0x0C
+#define MEM_SIZE_32M           0x0D
+#define MEM_SIZE_64M           0x0E
+#define MEM_SIZE_128M          0x0F
+#define MEM_SIZE_256M          0x10
+#define MEM_SIZE_512M          0x11
+#define MEM_SIZE_1G            0x12
+#define MEM_SIZE_2G            0x13
+#define MEM_SIZE_4G            0x14
+
+#define MUX_A8_SDRAM_A8                0x0
+#define MUX_A8_NAND_CE         0x1
+#define MUX_A8_NOR_CE          0x2
+#define MUX_A8_PSRAM_CE                0x3
+#define MUX_A8_DBI_CSX         0x4
+
+#define MUX_CSX0_NOR_PSRAM_A24 0x0
+#define MUX_CSX0_SDRAM_CS1     0x1
+#define MUX_CSX0_SDRAM_CS2     0x2
+#define MUX_CSX0_SDRAM_CS3     0x3
+#define MUX_CSX0_NAND_CE       0x4
+#define MUX_CSX0_NOR_CE                0x5
+#define MUX_CSX0_PSRAM_CE      0x6
+#define MUX_CSX0_DBI_CSX       0x7
+
+#define MUX_CSX1_NOR_PSRAM_A25 0x0
+#define MUX_CSX1_SDRAM_CS1     0x1
+#define MUX_CSX1_SDRAM_CS2     0x2
+#define MUX_CSX1_SDRAM_CS3     0x3
+#define MUX_CSX1_NAND_CE       0x4
+#define MUX_CSX1_NOR_CE                0x5
+#define MUX_CSX1_PSRAM_CE      0x6
+#define MUX_CSX1_DBI_CSX       0x7
+
+#define MUX_CSX2_NOR_PSRAM_A26 0x0
+#define MUX_CSX2_SDRAM_CS1     0x1
+#define MUX_CSX2_SDRAM_CS2     0x2
+#define MUX_CSX2_SDRAM_CS3     0x3
+#define MUX_CSX2_NAND_CE       0x4
+#define MUX_CSX2_NOR_CE                0x5
+#define MUX_CSX2_PSRAM_CE      0x6
+#define MUX_CSX2_DBI_CSX       0x7
+
+#define MUX_CSX3_NOR_PSRAM_A27 0x0
+#define MUX_CSX3_SDRAM_CS1     0x1
+#define MUX_CSX3_SDRAM_CS2     0x2
+#define MUX_CSX3_SDRAM_CS3     0x3
+#define MUX_CSX3_NAND_CE       0x4
+#define MUX_CSX3_NOR_CE                0x5
+#define MUX_CSX3_PSRAM_CE      0x6
+#define MUX_CSX3_DBI_CSX       0x7
+
+#define MUX_RDY_NAND_RDY_WAIT  0x0
+#define MUX_RDY_SDRAM_CS1      0x1
+#define MUX_RDY_SDRAM_CS2      0x2
+#define MUX_RDY_SDRAM_CS3      0x3
+#define MUX_RDY_NOR_CE         0x4
+#define MUX_RDY_PSRAM_CE       0x5
+#define MUX_RDY_DBI_CSX                0x6
+#define MUX_RDY_NOR_PSRAM_A27  0x7
+
+#define MEM_WIDTH_8BITS                0x0
+#define MEM_WIDTH_16BITS       0x1
+
+#define BL_1                   0x0
+#define BL_2                   0x1
+#define BL_4                   0x2
+#define BL_8                   0x3
+
+#define COL_12BITS             0x0
+#define COL_11BITS             0x1
+#define COL_10BITS             0x2
+#define COL_9BITS              0x3
+
+#define CL_1                   0x0
+#define CL_2                   0x2
+#define CL_3                   0x3
+
+#endif
-- 
2.20.1

Reply via email to