mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 00:11:51 +01:00 
			
		
		
		
	The i.MXRT11 series has different offsets for IOCR_MUX, it also can address 64MiB of SDRAM so add a macro for that. Signed-off-by: Jesse Taube <Mr.Bossman075@gmail.com>
		
			
				
	
	
		
			455 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			455 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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 <dm/device_compat.h>
 | |
| #include <init.h>
 | |
| #include <log.h>
 | |
| #include <ram.h>
 | |
| #include <asm/io.h>
 | |
| #include <linux/bitops.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/err.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];
 | |
| };
 | |
| 
 | |
| #if !defined(TARGET_IMXRT1170_EVK)
 | |
| #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
 | |
| #else
 | |
| #define SEMC_IOCR_MUX_A8_SHIFT		0
 | |
| #define SEMC_IOCR_MUX_CSX0_SHIFT	4
 | |
| #define SEMC_IOCR_MUX_CSX1_SHIFT	8
 | |
| #define SEMC_IOCR_MUX_CSX2_SHIFT	12
 | |
| #define SEMC_IOCR_MUX_CSX3_SHIFT	16
 | |
| #define SEMC_IOCR_MUX_RDY_SHIFT		20
 | |
| #endif
 | |
| 
 | |
| 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_plat(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_of_to_plat(struct udevice *dev)
 | |
| {
 | |
| 	struct imxrt_sdram_params *params = dev_get_plat(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_plat(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,
 | |
| 	.of_to_plat = imxrt_semc_of_to_plat,
 | |
| 	.probe = imxrt_semc_probe,
 | |
| 	.plat_auto	= sizeof(struct imxrt_sdram_params),
 | |
| };
 |