diff options
author | Giulio Benetti <giulio.benetti@benettiengineering.com> | 2020-01-10 17:51:44 +0300 |
---|---|---|
committer | Stefano Babic <sbabic@denx.de> | 2020-01-15 00:54:00 +0300 |
commit | cd647fc4fb8e6018089d0e0a3dfa71bd6beffde4 (patch) | |
tree | 48a497aa0d5f4198b81d2028b2f6b37569b68309 /drivers | |
parent | c32449a161cb2280553bddc8346817c043e99f4f (diff) | |
download | u-boot-cd647fc4fb8e6018089d0e0a3dfa71bd6beffde4.tar.xz |
ram: add SDRAM driver for i.MXRT SoCs
Add SDRAM driver for i.MXRT SoCs.
Signed-off-by: Giulio Benetti <giulio.benetti@benettiengineering.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ram/Kconfig | 8 | ||||
-rw-r--r-- | drivers/ram/Makefile | 2 | ||||
-rw-r--r-- | drivers/ram/imxrt_sdram.c | 439 |
3 files changed, 449 insertions, 0 deletions
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.benetti@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(®s->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, ®s->iptxdat); + + /* set slave address for every command as specified on RM */ + writel(mem_addr, ®s->ipcr0); + + /* execute command */ + writel(SEMC_IPCMD_KEY | ipcmd, ®s->ipcmd); + + ret = imxrt_sdram_wait_ipcmd_done(regs); + if (ret < 0) + return ret; + + if (ipcmd == SD_CC_IRD) { + if (!rd) + return -EINVAL; + + *rd = readl(®s->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(®s->mcr, SEMC_MCR_MDIS); + /* set DQS mode from DQS pad */ + setbits_le32(®s->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, + ®s->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, + ®s->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, + ®s->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, + ®s->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, + ®s->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, + ®s->sdramcr3); + + writel(2, ®s->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 = ¶ms->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), +}; |