Andy, The functionality of the new driver should be equivalent for the non-TPL case. All the code from the original driver is still present in the new driver for non-TPL (see rk3368_dmc_probe).
Seeing that -19 is -ENODEV, I suspect that something went wrong in the DM binding/probing: did you set 'status = “okay”’ for the DMC? Regards, Phil. > On 02 Aug 2017, at 12:06, Andy Yan <andy....@rock-chips.com> wrote: > > Hi Philipp: > > > On 2017年07月29日 03:22, Philipp Tomsich wrote: >> This adds a DRAM controller driver for the RK3368 and places it in >> drivers/ram/rockchip (where the other DM-enabled DRAM controller >> drivers for rockchip devices should also be moved eventually). >> >> At this stage, only the following feature-set is supported: >> - DDR3 >> - 32-bit configuration (i.e. fully populated) >> - dual-rank (i.e. no auto-detection of ranks) >> - DDR3-1600K speed-bin >> >> This driver expects to run from a TPL stage that will later return to >> the RK3368 BROM. It communicates with later stages through the >> os_reg2 in the pmugrf (i.e. using the same mechanism as Rockchip's DDR >> init code). >> >> Unlike other DMC drivers for RK32xx and RK33xx parts, the required >> timings are calculated within the driver based on a target frequency >> and a DDR3 speed-bin (only the DDR3-1600K speed-bin is support at this >> time). >> >> The RK3368 also has the DDRC0_CON0 (DDR ch. 0, control-register 0) >> register for controlling the operation of its (single-channel) DRAM >> controller in the GRF block. This provides for selecting DDR3, mobile >> DDR modes, and control low-power operation. >> As part of this change, DDRC0_CON0 is also added to the GRF structure >> definition (at offset 0x600). >> >> Signed-off-by: Philipp Tomsich <philipp.toms...@theobroma-systems.com> >> >> Reviewed-by: Simon Glass <s...@chromium.org> >> --- >> >> Changes in v3: >> - correctly states the location of the driver in the commit message >> >> Changes in v2: None >> >> arch/arm/include/asm/arch-rockchip/ddr_rk3368.h | 187 ++++ >> arch/arm/include/asm/arch-rockchip/grf_rk3368.h | 3 + >> arch/arm/mach-rockchip/rk3368/Makefile | 1 - >> arch/arm/mach-rockchip/rk3368/sdram_rk3368.c | 60 -- >> .../clock/rockchip,rk3368-dmc.txt | 67 ++ >> drivers/ram/Makefile | 2 + >> drivers/ram/rockchip/Makefile | 7 + >> drivers/ram/rockchip/dmc-rk3368.c | 990 >> +++++++++++++++++++++ >> include/dt-bindings/memory/rk3368-dmc.h | 30 + >> 9 files changed, 1286 insertions(+), 61 deletions(-) >> create mode 100644 arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >> delete mode 100644 arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >> create mode 100644 doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >> create mode 100644 drivers/ram/rockchip/Makefile >> create mode 100644 drivers/ram/rockchip/dmc-rk3368.c >> create mode 100644 include/dt-bindings/memory/rk3368-dmc.h >> >> diff --git a/arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >> b/arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >> new file mode 100644 >> index 0000000..4e2b233 >> --- /dev/null >> +++ b/arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >> @@ -0,0 +1,187 @@ >> +/* >> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH >> + * >> + * SPDX-License-Identifier: GPL-2.0 >> + */ >> + >> +#ifndef __ASM_ARCH_DDR_RK3368_H__ >> +#define __ASM_ARCH_DDR_RK3368_H__ >> + >> +/* >> + * The RK3368 DDR PCTL differs from the incarnation in the RK3288 only >> + * in a few details. Most notably, it has an additional field to track >> + * tREFI in controller cycles (i.e. trefi_mem_ddr3). >> + */ >> +struct rk3368_ddr_pctl { >> + u32 scfg; >> + u32 sctl; >> + u32 stat; >> + u32 intrstat; >> + u32 reserved0[12]; >> + u32 mcmd; >> + u32 powctl; >> + u32 powstat; >> + u32 cmdtstat; >> + u32 cmdtstaten; >> + u32 reserved1[3]; >> + u32 mrrcfg0; >> + u32 mrrstat0; >> + u32 mrrstat1; >> + u32 reserved2[4]; >> + u32 mcfg1; >> + u32 mcfg; >> + u32 ppcfg; >> + u32 mstat; >> + u32 lpddr2zqcfg; >> + u32 reserved3; >> + u32 dtupdes; >> + u32 dtuna; >> + u32 dtune; >> + u32 dtuprd0; >> + u32 dtuprd1; >> + u32 dtuprd2; >> + u32 dtuprd3; >> + u32 dtuawdt; >> + u32 reserved4[3]; >> + u32 togcnt1u; >> + u32 tinit; >> + u32 trsth; >> + u32 togcnt100n; >> + u32 trefi; >> + u32 tmrd; >> + u32 trfc; >> + u32 trp; >> + u32 trtw; >> + u32 tal; >> + u32 tcl; >> + u32 tcwl; >> + u32 tras; >> + u32 trc; >> + u32 trcd; >> + u32 trrd; >> + u32 trtp; >> + u32 twr; >> + u32 twtr; >> + u32 texsr; >> + u32 txp; >> + u32 txpdll; >> + u32 tzqcs; >> + u32 tzqcsi; >> + u32 tdqs; >> + u32 tcksre; >> + u32 tcksrx; >> + u32 tcke; >> + u32 tmod; >> + u32 trstl; >> + u32 tzqcl; >> + u32 tmrr; >> + u32 tckesr; >> + u32 t >> >> Hi Philipp: >> >> >> On 2017年07月29日 03:22, Philipp Tomsich wrote: >>> This adds a DRAM controller driver for the RK3368 and places it in >>> drivers/ram/rockchip (where the other DM-enabled DRAM controller >>> drivers for rockchip devices should also be moved eventually). >>> >>> At this stage, only the following feature-set is supported: >>> - DDR3 >>> - 32-bit configuration (i.e. fully populated) >>> - dual-rank (i.e. no auto-detection of ranks) >>> - DDR3-1600K speed-bin >>> >>> This driver expects to run from a TPL stage that will later return to >>> the RK3368 BROM. It communicates with later stages through the >>> os_reg2 in the pmugrf (i.e. using the same mechanism as Rockchip's DDR >>> init code). >>> >>> Unlike other DMC drivers for RK32xx and RK33xx parts, the required >>> timings are calculated within the driver based on a target frequency >>> and a DDR3 speed-bin (only the DDR3-1600K speed-bin is support at this >>> time). >>> >>> The RK3368 also has the DDRC0_CON0 (DDR ch. 0, control-register 0) >>> register for controlling the operation of its (single-channel) DRAM >>> controller in the GRF block. This provides for selecting DDR3, mobile >>> DDR modes, and control low-power operation. >>> As part of this change, DDRC0_CON0 is also added to the GRF structure >>> definition (at offset 0x600). >>> >>> Signed-off-by: Philipp Tomsich <philipp.toms...@theobroma-systems.com> >>> >>> Reviewed-by: Simon Glass <s...@chromium.org> >>> --- >>> >>> Changes in v3: >>> - correctly states the location of the driver in the commit message >>> >>> Changes in v2: None >>> >>> arch/arm/include/asm/arch-rockchip/ddr_rk3368.h | 187 ++++ >>> arch/arm/include/asm/arch-rockchip/grf_rk3368.h | 3 + >>> arch/arm/mach-rockchip/rk3368/Makefile | 1 - >>> arch/arm/mach-rockchip/rk3368/sdram_rk3368.c | 60 -- >>> .../clock/rockchip,rk3368-dmc.txt | 67 ++ >>> drivers/ram/Makefile | 2 + >>> drivers/ram/rockchip/Makefile | 7 + >>> drivers/ram/rockchip/dmc-rk3368.c | 990 >>> +++++++++++++++++++++ >>> include/dt-bindings/memory/rk3368-dmc.h | 30 + >>> 9 files changed, 1286 insertions(+), 61 deletions(-) >>> create mode 100644 arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >>> delete mode 100644 arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >>> create mode 100644 doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >>> create mode 100644 drivers/ram/rockchip/Makefile >>> create mode 100644 drivers/ram/rockchip/dmc-rk3368.c >>> create mode 100644 include/dt-bindings/memory/rk3368-dmc.h >>> >>> diff --git a/arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >>> b/arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >>> new file mode 100644 >>> index 0000000..4e2b233 >>> --- /dev/null >>> +++ b/arch/arm/include/asm/arch-rockchip/ddr_rk3368.h >>> @@ -0,0 +1,187 @@ >>> +/* >>> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH >>> + * >>> + * SPDX-License-Identifier: GPL-2.0 >>> + */ >>> + >>> +#ifndef __ASM_ARCH_DDR_RK3368_H__ >>> +#define __ASM_ARCH_DDR_RK3368_H__ >>> + >>> +/* >>> + * The RK3368 DDR PCTL differs from the incarnation in the RK3288 only >>> + * in a few details. Most notably, it has an additional field to track >>> + * tREFI in controller cycles (i.e. trefi_mem_ddr3). >>> + */ >>> +struct rk3368_ddr_pctl { >>> + u32 scfg; >>> + u32 sctl; >>> + u32 stat; >>> + u32 intrstat; >>> + u32 reserved0[12]; >>> + u32 mcmd; >>> + u32 powctl; >>> + u32 powstat; >>> + u32 cmdtstat; >>> + u32 cmdtstaten; >>> + u32 reserved1[3]; >>> + u32 mrrcfg0; >>> + u32 mrrstat0; >>> + u32 mrrstat1; >>> + u32 reserved2[4]; >>> + u32 mcfg1; >>> + u32 mcfg; >>> + u32 ppcfg; >>> + u32 mstat; >>> + u32 lpddr2zqcfg; >>> + u32 reserved3; >>> + u32 dtupdes; >>> + u32 dtuna; >>> + u32 dtune; >>> + u32 dtuprd0; >>> + u32 dtuprd1; >>> + u32 dtuprd2; >>> + u32 dtuprd3; >>> + u32 dtuawdt; >>> + u32 reserved4[3]; >>> + u32 togcnt1u; >>> + u32 tinit; >>> + u32 trsth; >>> + u32 togcnt100n; >>> + u32 trefi; >>> + u32 tmrd; >>> + u32 trfc; >>> + u32 trp; >>> + u32 trtw; >>> + u32 tal; >>> + u32 tcl; >>> + u32 tcwl; >>> + u32 tras; >>> + u32 trc; >>> + u32 trcd; >>> + u32 trrd; >>> + u32 trtp; >>> + u32 twr; >>> + u32 twtr; >>> + u32 texsr; >>> + u32 txp; >>> + u32 txpdll; >>> + u32 tzqcs; >>> + u32 tzqcsi; >>> + u32 tdqs; >>> + u32 tcksre; >>> + u32 tcksrx; >>> + u32 tcke; >>> + u32 tmod; >>> + u32 trstl; >>> + u32 tzqcl; >>> + u32 tmrr; >>> + u32 tckesr; >>> + u32 tdpd; >>> + u32 trefi_mem_ddr3; >>> + u32 reserved5[45]; >>> + u32 dtuwactl; >>> + u32 dturactl; >>> + u32 dtucfg; >>> + u32 dtuectl; >>> + u32 dtuwd0; >>> + u32 dtuwd1; >>> + u32 dtuwd2; >>> + u32 dtuwd3; >>> + u32 dtuwdm; >>> + u32 dturd0; >>> + u32 dturd1; >>> + u32 dturd2; >>> + u32 dturd3; >>> + u32 dtulfsrwd; >>> + u32 dtulfsrrd; >>> + u32 dtueaf; >>> + u32 dfitctrldelay; >>> + u32 dfiodtcfg; >>> + u32 dfiodtcfg1; >>> + u32 dfiodtrankmap; >>> + u32 dfitphywrdata; >>> + u32 dfitphywrlat; >>> + u32 reserved7[2]; >>> + u32 dfitrddataen; >>> + u32 dfitphyrdlat; >>> + u32 reserved8[2]; >>> + u32 dfitphyupdtype0; >>> + u32 dfitphyupdtype1; >>> + u32 dfitphyupdtype2; >>> + u32 dfitphyupdtype3; >>> + u32 dfitctrlupdmin; >>> + u32 dfitctrlupdmax; >>> + u32 dfitctrlupddly; >>> + u32 reserved9; >>> + u32 dfiupdcfg; >>> + u32 dfitrefmski; >>> + u32 dfitctrlupdi; >>> + u32 reserved10[4]; >>> + u32 dfitrcfg0; >>> + u32 dfitrstat0; >>> + u32 dfitrwrlvlen; >>> + u32 dfitrrdlvlen; >>> + u32 dfitrrdlvlgateen; >>> + u32 dfiststat0; >>> + u32 dfistcfg0; >>> + u32 dfistcfg1; >>> + u32 reserved11; >>> + u32 dfitdramclken; >>> + u32 dfitdramclkdis; >>> + u32 dfistcfg2; >>> + u32 dfistparclr; >>> + u32 dfistparlog; >>> + u32 reserved12[3]; >>> + u32 dfilpcfg0; >>> + u32 reserved13[3]; >>> + u32 dfitrwrlvlresp0; >>> + u32 dfitrwrlvlresp1; >>> + u32 dfitrwrlvlresp2; >>> + u32 dfitrrdlvlresp0; >>> + u32 dfitrrdlvlresp1; >>> + u32 dfitrrdlvlresp2; >>> + u32 dfitrwrlvldelay0; >>> + u32 dfitrwrlvldelay1; >>> + u32 dfitrwrlvldelay2; >>> + u32 dfitrrdlvldelay0; >>> + u32 dfitrrdlvldelay1; >>> + u32 dfitrrdlvldelay2; >>> + u32 dfitrrdlvlgatedelay0; >>> + u32 dfitrrdlvlgatedelay1; >>> + u32 dfitrrdlvlgatedelay2; >>> + u32 dfitrcmd; >>> + u32 reserved14[46]; >>> + u32 ipvr; >>> + u32 iptr; >>> +}; >>> +check_member(rk3368_ddr_pctl, iptr, 0x03fc); >>> + >>> +struct rk3368_ddrphy { >>> + u32 reg[0x100]; >>> +}; >>> +check_member(rk3368_ddrphy, reg[0xff], 0x03fc); >>> + >>> +struct rk3368_msch { >>> + u32 coreid; >>> + u32 revisionid; >>> + u32 ddrconf; >>> + u32 ddrtiming; >>> + u32 ddrmode; >>> + u32 readlatency; >>> + u32 reserved1[8]; >>> + u32 activate; >>> + u32 devtodev; >>> +}; >>> +check_member(rk3368_msch, devtodev, 0x003c); >>> + >>> +/* GRF_SOC_CON0 */ >>> +enum { >>> + NOC_RSP_ERR_STALL = BIT(9), >>> + MOBILE_DDR_SEL = BIT(4), >>> + DDR0_16BIT_EN = BIT(3), >>> + MSCH0_MAINDDR3_DDR3 = BIT(2), >>> + MSCH0_MAINPARTIALPOP = BIT(1), >>> + UPCTL_C_ACTIVE = BIT(0), >>> +}; >>> + >>> +#endif >>> diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >>> b/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >>> index 1f84ff9..6b6651a 100644 >>> --- a/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >>> +++ b/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >>> @@ -76,8 +76,11 @@ struct rk3368_grf { >>> u32 soc_con15; >>> u32 soc_con16; >>> u32 soc_con17; >>> + u32 reserved5[0x6e]; >>> + u32 ddrc0_con0; >>> }; >>> check_member(rk3368_grf, soc_con17, 0x444); >>> +check_member(rk3368_grf, ddrc0_con0, 0x600); >>> struct rk3368_pmu_grf { >>> u32 gpio0a_iomux; >>> diff --git a/arch/arm/mach-rockchip/rk3368/Makefile >>> b/arch/arm/mach-rockchip/rk3368/Makefile >>> index 0390716..46798c2 100644 >>> --- a/arch/arm/mach-rockchip/rk3368/Makefile >>> +++ b/arch/arm/mach-rockchip/rk3368/Makefile >>> @@ -5,5 +5,4 @@ >>> # >>> obj-y += clk_rk3368.o >>> obj-y += rk3368.o >>> -obj-y += sdram_rk3368.o >>> obj-y += syscon_rk3368.o >> >> the current PX5 EVB and Sheep board(they don't use spl now) depend on >> the sdram_rk3368 driver to get the sdram capacity, and so is the geekbox. >> They will be broken when this driver removed. >> >> We got the boot failed log on PX5 like bellow: >> >>> diff --git a/arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >>> b/arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >>> deleted file mode 100644 >>> index d0d0900..0000000 >>> --- a/arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >>> +++ /dev/null >>> @@ -1,60 +0,0 @@ >>> -/* >>> - * (C) Copyright 2016 Rockchip Electronics Co., Ltd. >>> - * >>> - * SPDX-License-Identifier: GPL-2.0 >>> - */ >>> - >>> -#include <common.h> >>> -#include <dm.h> >>> -#include <ram.h> >>> -#include <syscon.h> >>> -#include <asm/arch/clock.h> >>> -#include <asm/arch/grf_rk3368.h> >>> -#include <asm/arch/sdram_common.h> >>> - >>> -DECLARE_GLOBAL_DATA_PTR; >>> -struct dram_info { >>> - struct ram_info info; >>> - struct rk3368_pmu_grf *pmugrf; >>> -}; >>> - >>> -static int rk3368_dmc_probe(struct udevice *dev) >>> -{ >>> - struct dram_info *priv = dev_get_priv(dev); >>> - >>> - priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); >>> - debug("%s: grf=%p\n", __func__, priv->pmugrf); >>> - priv->info.base = CONFIG_SYS_SDRAM_BASE; >>> - priv->info.size = rockchip_sdram_size( >>> - (phys_addr_t)&priv->pmugrf->os_reg[2]); >>> - >>> - return 0; >>> -} >>> - >>> -static int rk3368_dmc_get_info(struct udevice *dev, struct ram_info *info) >>> -{ >>> - struct dram_info *priv = dev_get_priv(dev); >>> - >>> - *info = priv->info; >>> - >>> - return 0; >>> -} >>> - >>> -static struct ram_ops rk3368_dmc_ops = { >>> - .get_info = rk3368_dmc_get_info, >>> -}; >>> - >>> - >>> -static const struct udevice_id rk3368_dmc_ids[] = { >>> - { .compatible = "rockchip,rk3368-dmc" }, >>> - { } >>> -}; >>> - >>> -U_BOOT_DRIVER(dmc_rk3368) = { >>> - .name = "rockchip_rk3368_dmc", >>> - .id = UCLASS_RAM, >>> - .of_match = rk3368_dmc_ids, >>> - .ops = &rk3368_dmc_ops, >>> - .probe = rk3368_dmc_probe, >>> - .priv_auto_alloc_size = sizeof(struct dram_info), >>> -}; >>> diff --git a/doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >>> b/doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >>> new file mode 100644 >>> index 0000000..8e7357d >>> --- /dev/null >>> +++ b/doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >>> @@ -0,0 +1,67 @@ >>> +RK3368 dynamic memory controller driver >>> +======================================= >>> + >>> +The RK3368 DMC (dynamic memory controller) driver supports >>> setup/initialisation >>> +during TPL using configuration data from the DTS (i.e. OF_PLATDATA), based >>> on >>> +the following key configuration data: >>> + (a) a target-frequency (i.e. operating point) for the memory operation >>> + (b) a speed-bin (as defined in JESD-79) for the DDR3 used in hardware >>> + (c) a memory-schedule (i.e. mapping from physical addresses to the >>> address >>> + pins of the memory bus) >>> + >>> +Required properties >>> +------------------- >>> + >>> +- compatible: "rockchip,rk3368-dmc" >>> +- reg >>> + protocol controller (PCTL) address and PHY controller (DDRPHY) address >>> +- rockchip,ddr-speed-bin >>> + the DDR3 device's speed-bin (as specified according to JESD-79) >>> + DDR3_800D (5-5-5) >>> + DDR3_800E (6-6-6) >>> + DDR3_1066E (6-6-6) >>> + DDR3_1066F (7-7-7) >>> + DDR3_1066G (8-8-8) >>> + DDR3_1333F (7-7-7) >>> + DDR3_1333G (8-8-8) >>> + DDR3_1333H (9-9-9) >>> + DDR3_1333J (10-10-10) >>> + DDR3_1600G (8-8-8) >>> + DDR3_1600H (9-9-9) >>> + DDR3_1600J (10-10-10) >>> + DDR3_1600K (11-11-11) >>> + DDR3_1866J (10-10-10) >>> + DDR3_1866K (11-11-11) >>> + DDR3_1866L (12-12-12) >>> + DDR3_1866M (13-13-13) >>> + DDR3_2133K (11-11-11) >>> + DDR3_2133L (12-12-12) >>> + DDR3_2133M (13-13-13) >>> + DDR3_2133N (14-14-14) >>> +- rockchip,ddr-frequency: >>> + target DDR clock frequency in Hz (not all frequencies may be supported, >>> + as there's some cooperation from the clock-driver required) >>> +- rockchip,memory-schedule: >>> + controls the decoding of physical addresses to DRAM addressing (i.e. >>> how >>> + the physical address maps onto the address pins/chip-select of the >>> device) >>> + DMC_MSCH_CBDR: column -> bank -> device -> row >>> + DMC_MSCH_CBRD: column -> band -> row -> device >>> + DMC_MSCH_CRBD: column -> row -> band -> device >>> + >>> +Example (for DDR3-1600K and 800MHz) >>> +----------------------------------- >>> + >>> + #include <dt-bindings/memory/rk3368-dmc.h> >>> + >>> + dmc: dmc@ff610000 { >>> + u-boot,dm-pre-reloc; >>> + compatible = "rockchip,rk3368-dmc"; >>> + reg = <0 0xff610000 0 0x400 >>> + 0 0xff620000 0 0x400>; >>> + }; >>> + >>> + &dmc { >>> + rockchip,ddr-speed-bin = <DDR3_1600K>; >>> + rockchip,ddr-frequency = <800000000>; >>> + rockchip,memory-schedule = <DMC_MSCH_CBRD>; >>> + }; >>> diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile >>> index c409c48..51ae6be 100644 >>> --- a/drivers/ram/Makefile >>> +++ b/drivers/ram/Makefile >>> @@ -8,3 +8,5 @@ obj-$(CONFIG_RAM) += ram-uclass.o >>> obj-$(CONFIG_SANDBOX) += sandbox_ram.o >>> obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o >>> obj-$(CONFIG_ARCH_BMIPS) += bmips_ram.o >>> + >>> +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ >>> diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile >>> new file mode 100644 >>> index 0000000..b09d03c >>> --- /dev/null >>> +++ b/drivers/ram/rockchip/Makefile >>> @@ -0,0 +1,7 @@ >>> +# >>> +# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH >>> +# >>> +# SPDX-License-Identifier: GPL-2.0+ >>> +# >>> + >>> +obj-$(CONFIG_ROCKCHIP_RK3368) = dmc-rk3368.o >>> diff --git a/drivers/ram/rockchip/dmc-rk3368.c >>> b/drivers/ram/rockchip/dmc-rk3368.c >>> new file mode 100644 >>> index 0000000..fea96a5 >>> --- /dev/null >>> +++ b/drivers/ram/rockchip/dmc-rk3368.c >>> @@ -0,0 +1,990 @@ >>> +/* >>> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH >>> + * >>> + * SPDX-License-Identifier: GPL-2.0 >>> + */ >>> + >>> +#include <common.h> >>> +#include <clk.h> >>> +#include <dm.h> >>> +#include <dt-bindings/memory/rk3368-dmc.h> >>> +#include <dt-structs.h> >>> +#include <ram.h> >>> +#include <regmap.h> >>> +#include <syscon.h> >>> +#include <asm/io.h> >>> +#include <asm/arch/clock.h> >>> +#include <asm/arch/cru_rk3368.h> >>> +#include <asm/arch/grf_rk3368.h> >>> +#include <asm/arch/ddr_rk3368.h> >>> +#include <asm/arch/sdram.h> >>> +#include <asm/arch/sdram_common.h> >>> + >>> +DECLARE_GLOBAL_DATA_PTR; >>> + >>> +struct dram_info { >>> + struct ram_info info; >>> + struct clk ddr_clk; >>> + struct rk3368_cru *cru; >>> + struct rk3368_grf *grf; >>> + struct rk3368_ddr_pctl *pctl; >>> + struct rk3368_ddrphy *phy; >>> + struct rk3368_pmu_grf *pmugrf; >>> + struct rk3368_msch *msch; >>> +}; >>> + >>> +struct rk3368_sdram_params { >>> +#if CONFIG_IS_ENABLED(OF_PLATDATA) >>> + struct dtd_rockchip_rk3368_dmc of_plat; >>> +#endif >>> + struct rk3288_sdram_pctl_timing pctl_timing; >>> + u32 trefi_mem_ddr3; >>> + struct rk3288_sdram_channel chan; >>> + struct regmap *map; >>> + u32 ddr_freq; >>> + u32 memory_schedule; >>> + u32 ddr_speed_bin; >>> + u32 tfaw_mult; >>> +}; >>> + >>> +/* PTCL bits */ >>> +enum { >>> + /* PCTL_DFISTCFG0 */ >>> + DFI_INIT_START = BIT(0), >>> + DFI_DATA_BYTE_DISABLE_EN = BIT(2), >>> + >>> + /* PCTL_DFISTCFG1 */ >>> + DFI_DRAM_CLK_SR_EN = BIT(0), >>> + DFI_DRAM_CLK_DPD_EN = BIT(1), >>> + ODT_LEN_BL8_W_SHIFT = 16, >>> + >>> + /* PCTL_DFISTCFG2 */ >>> + DFI_PARITY_INTR_EN = BIT(0), >>> + DFI_PARITY_EN = BIT(1), >>> + >>> + /* PCTL_DFILPCFG0 */ >>> + TLP_RESP_TIME_SHIFT = 16, >>> + LP_SR_EN = BIT(8), >>> + LP_PD_EN = BIT(0), >>> + >>> + /* PCTL_DFIODTCFG */ >>> + RANK0_ODT_WRITE_SEL = BIT(3), >>> + RANK1_ODT_WRITE_SEL = BIT(11), >>> + >>> + /* PCTL_SCFG */ >>> + HW_LOW_POWER_EN = BIT(0), >>> + >>> + /* PCTL_MCMD */ >>> + START_CMD = BIT(31), >>> + MCMD_RANK0 = BIT(20), >>> + MCMD_RANK1 = BIT(21), >>> + DESELECT_CMD = 0, >>> + PREA_CMD, >>> + REF_CMD, >>> + MRS_CMD, >>> + ZQCS_CMD, >>> + ZQCL_CMD, >>> + RSTL_CMD, >>> + MRR_CMD = 8, >>> + DPDE_CMD, >>> + >>> + /* PCTL_POWCTL */ >>> + POWER_UP_START = BIT(0), >>> + >>> + /* PCTL_POWSTAT */ >>> + POWER_UP_DONE = BIT(0), >>> + >>> + /* PCTL_SCTL */ >>> + INIT_STATE = 0, >>> + CFG_STATE, >>> + GO_STATE, >>> + SLEEP_STATE, >>> + WAKEUP_STATE, >>> + >>> + /* PCTL_STAT */ >>> + LP_TRIG_SHIFT = 4, >>> + LP_TRIG_MASK = 7, >>> + PCTL_STAT_MSK = 7, >>> + INIT_MEM = 0, >>> + CONFIG, >>> + CONFIG_REQ, >>> + ACCESS, >>> + ACCESS_REQ, >>> + LOW_POWER, >>> + LOW_POWER_ENTRY_REQ, >>> + LOW_POWER_EXIT_REQ, >>> + >>> + /* PCTL_MCFG */ >>> + DDR2_DDR3_BL_8 = BIT(0), >>> + DDR3_EN = BIT(5), >>> + TFAW_TRRD_MULT4 = (0 << 18), >>> + TFAW_TRRD_MULT5 = (1 << 18), >>> + TFAW_TRRD_MULT6 = (2 << 18), >>> +}; >>> + >>> +#define DDR3_MR0_WR(n) \ >>> + ((n <= 8) ? ((n - 4) << 9) : (((n >> 1) & 0x7) << 9)) >>> +#define DDR3_MR0_CL(n) \ >>> + ((((n - 4) & 0x7) << 4) | (((n - 4) & 0x8) >> 2)) >>> +#define DDR3_MR0_BL8 \ >>> + (0 << 0) >>> +#define DDR3_MR0_DLL_RESET \ >>> + (1 << 8) >>> +#define DDR3_MR1_RTT120OHM \ >>> + ((0 << 9) | (1 << 6) | (0 << 2)) >>> +#define DDR3_MR2_TWL(n) \ >>> + (((n - 5) & 0x7) << 3) >>> + >>> + >>> +#ifdef CONFIG_TPL_BUILD >>> + >>> +static void ddr_set_noc_spr_err_stall(struct rk3368_grf *grf, bool enable) >>> +{ >>> + if (enable) >>> + rk_setreg(&grf->ddrc0_con0, NOC_RSP_ERR_STALL); >>> + else >>> + rk_clrreg(&grf->ddrc0_con0, NOC_RSP_ERR_STALL); >>> +} >>> + >>> +static void ddr_set_ddr3_mode(struct rk3368_grf *grf, bool ddr3_mode) >>> +{ >>> + if (ddr3_mode) >>> + rk_setreg(&grf->ddrc0_con0, MSCH0_MAINDDR3_DDR3); >>> + else >>> + rk_clrreg(&grf->ddrc0_con0, MSCH0_MAINDDR3_DDR3); >>> +} >>> + >>> +static void ddrphy_config(struct rk3368_ddrphy *phy, >>> + u32 tcl, u32 tal, u32 tcwl) >>> +{ >>> + int i; >>> + >>> + /* Set to DDR3 mode */ >>> + clrsetbits_le32(&phy->reg[1], 0x3, 0x0); >>> + >>> + /* DDRPHY_REGB: CL, AL */ >>> + clrsetbits_le32(&phy->reg[0xb], 0xff, tcl << 4 | tal); >>> + /* DDRPHY_REGC: CWL */ >>> + clrsetbits_le32(&phy->reg[0xc], 0x0f, tcwl); >>> + >>> + /* Update drive-strength */ >>> + writel(0xcc, &phy->reg[0x11]); >>> + writel(0xaa, &phy->reg[0x16]); >>> + /* >>> + * Update NRCOMP/PRCOMP for all 4 channels (for details of all >>> + * affected registers refer to the documentation of DDRPHY_REG20 >>> + * and DDRPHY_REG21 in the RK3368 TRM. >>> + */ >>> + for (i = 0; i < 4; ++i) { >>> + writel(0xcc, &phy->reg[0x20 + i * 0x10]); >>> + writel(0x44, &phy->reg[0x21 + i * 0x10]); >>> + } >>> + >>> + /* Enable write-leveling calibration bypass */ >>> + setbits_le32(&phy->reg[2], BIT(3)); >>> +} >>> + >>> +static void copy_to_reg(u32 *dest, const u32 *src, u32 n) >>> +{ >>> + int i; >>> + >>> + for (i = 0; i < n / sizeof(u32); i++) >>> + writel(*src++, dest++); >>> +} >>> + >>> +static void send_command(struct rk3368_ddr_pctl *pctl, u32 rank, u32 cmd) >>> +{ >>> + u32 mcmd = START_CMD | cmd | rank; >>> + >>> + debug("%s: writing %x to MCMD\n", __func__, mcmd); >>> + writel(mcmd, &pctl->mcmd); >>> + while (readl(&pctl->mcmd) & START_CMD) >>> + /* spin */; >>> +} >>> + >>> +static void send_mrs(struct rk3368_ddr_pctl *pctl, >>> + u32 rank, u32 mr_num, u32 mr_data) >>> +{ >>> + u32 mcmd = START_CMD | MRS_CMD | rank | (mr_num << 17) | (mr_data << 4); >>> + >>> + debug("%s: writing %x to MCMD\n", __func__, mcmd); >>> + writel(mcmd, &pctl->mcmd); >>> + while (readl(&pctl->mcmd) & START_CMD) >>> + /* spin */; >>> +} >>> + >>> +static int memory_init(struct rk3368_ddr_pctl *pctl, >>> + struct rk3368_sdram_params *params) >>> +{ >>> + u32 mr[4]; >>> + const ulong timeout_ms = 500; >>> + ulong tmp; >>> + >>> + /* >>> + * Power up DRAM by DDR_PCTL_POWCTL[0] register of PCTL and >>> + * wait power up DRAM finish with DDR_PCTL_POWSTAT[0] register >>> + * of PCTL. >>> + */ >>> + writel(POWER_UP_START, &pctl->powctl); >>> + >>> + tmp = get_timer(0); >>> + do { >>> + if (get_timer(tmp) > timeout_ms) { >>> + error("%s: POWER_UP_START did not complete in %ld ms\n", >>> + __func__, timeout_ms); >>> + return -ETIME; >>> + } >>> + } while (!(readl(&pctl->powstat) & POWER_UP_DONE)); >>> + >>> + /* Configure MR0 through MR3 */ >>> + mr[0] = DDR3_MR0_WR(params->pctl_timing.twr) | >>> + DDR3_MR0_CL(params->pctl_timing.tcl) | >>> + DDR3_MR0_DLL_RESET; >>> + mr[1] = DDR3_MR1_RTT120OHM; >>> + mr[2] = DDR3_MR2_TWL(params->pctl_timing.tcwl); >>> + mr[3] = 0; >>> + >>> + /* >>> + * Also see RK3368 Technical Reference Manual: >>> + * "16.6.2 Initialization (DDR3 Initialization Sequence)" >>> + */ >>> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, DESELECT_CMD); >>> + udelay(1); >>> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, PREA_CMD); >>> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 2, mr[2]); >>> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 3, mr[3]); >>> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 1, mr[1]); >>> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 0, mr[0]); >>> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, ZQCL_CMD); >>> + >>> + return 0; >>> +} >>> + >>> +static void move_to_config_state(struct rk3368_ddr_pctl *pctl) >>> +{ >>> + /* >>> + * Also see RK3368 Technical Reference Manual: >>> + * "16.6.1 State transition of PCTL (Moving to Config State)" >>> + */ >>> + u32 state = readl(&pctl->stat) & PCTL_STAT_MSK; >>> + >>> + switch (state) { >>> + case LOW_POWER: >>> + writel(WAKEUP_STATE, &pctl->sctl); >>> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS) >>> + /* spin */; >>> + >>> + /* fall-through */ >>> + case ACCESS: >>> + case INIT_MEM: >>> + writel(CFG_STATE, &pctl->sctl); >>> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG) >>> + /* spin */; >>> + break; >>> + >>> + case CONFIG: >>> + return; >>> + >>> + default: >>> + break; >>> + } >>> +} >>> + >>> +static void move_to_access_state(struct rk3368_ddr_pctl *pctl) >>> +{ >>> + /* >>> + * Also see RK3368 Technical Reference Manual: >>> + * "16.6.1 State transition of PCTL (Moving to Access State)" >>> + */ >>> + u32 state = readl(&pctl->stat) & PCTL_STAT_MSK; >>> + >>> + switch (state) { >>> + case LOW_POWER: >>> + if (((readl(&pctl->stat) >> LP_TRIG_SHIFT) & >>> + LP_TRIG_MASK) == 1) >>> + return; >>> + >>> + writel(WAKEUP_STATE, &pctl->sctl); >>> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS) >>> + /* spin */; >>> + >>> + /* fall-through */ >>> + case INIT_MEM: >>> + writel(CFG_STATE, &pctl->sctl); >>> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG) >>> + /* spin */; >>> + >>> + /* fall-through */ >>> + case CONFIG: >>> + writel(GO_STATE, &pctl->sctl); >>> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) == CONFIG) >>> + /* spin */; >>> + break; >>> + >>> + case ACCESS: >>> + return; >>> + >>> + default: >>> + break; >>> + } >>> +} >>> + >>> +static void ddrctl_reset(struct rk3368_cru *cru) >>> +{ >>> + const u32 ctl_reset = BIT(3) | BIT(2); >>> + const u32 phy_reset = BIT(1) | BIT(0); >>> + >>> + rk_setreg(&cru->softrst_con[10], ctl_reset | phy_reset); >>> + udelay(1); >>> + rk_clrreg(&cru->softrst_con[10], ctl_reset | phy_reset); >>> +} >>> + >>> +static void ddrphy_reset(struct rk3368_ddrphy *ddrphy) >>> +{ >>> + clrbits_le32(&ddrphy->reg[0], BIT(3) | BIT(2)); >>> + udelay(1); >>> + setbits_le32(&ddrphy->reg[0], BIT(3) | BIT(2)); >>> +} >>> + >>> +static void ddrphy_config_delays(struct rk3368_ddrphy *ddrphy, u32 freq) >>> +{ >>> + u32 dqs_dll_delay; >>> + >>> + setbits_le32(&ddrphy->reg[0x13], BIT(4)); >>> + clrbits_le32(&ddrphy->reg[0x14], BIT(3)); >>> + >>> + setbits_le32(&ddrphy->reg[0x26], BIT(4)); >>> + clrbits_le32(&ddrphy->reg[0x27], BIT(3)); >>> + >>> + setbits_le32(&ddrphy->reg[0x36], BIT(4)); >>> + clrbits_le32(&ddrphy->reg[0x37], BIT(3)); >>> + >>> + setbits_le32(&ddrphy->reg[0x46], BIT(4)); >>> + clrbits_le32(&ddrphy->reg[0x47], BIT(3)); >>> + >>> + setbits_le32(&ddrphy->reg[0x56], BIT(4)); >>> + clrbits_le32(&ddrphy->reg[0x57], BIT(3)); >>> + >>> + if (freq <= 400000000) >>> + setbits_le32(&ddrphy->reg[0xa4], 0x1f); >>> + else >>> + clrbits_le32(&ddrphy->reg[0xa4], 0x1f); >>> + >>> + if (freq < 681000000) >>> + dqs_dll_delay = 3; /* 67.5 degree delay */ >>> + else >>> + dqs_dll_delay = 2; /* 45 degree delay */ >>> + >>> + writel(dqs_dll_delay, &ddrphy->reg[0x28]); >>> + writel(dqs_dll_delay, &ddrphy->reg[0x38]); >>> + writel(dqs_dll_delay, &ddrphy->reg[0x48]); >>> + writel(dqs_dll_delay, &ddrphy->reg[0x58]); >>> +} >>> + >>> +static int dfi_cfg(struct rk3368_ddr_pctl *pctl) >>> +{ >>> + const ulong timeout_ms = 200; >>> + ulong tmp; >>> + >>> + writel(DFI_DATA_BYTE_DISABLE_EN, &pctl->dfistcfg0); >>> + >>> + writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN, >>> + &pctl->dfistcfg1); >>> + writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &pctl->dfistcfg2); >>> + writel(7 << TLP_RESP_TIME_SHIFT | LP_SR_EN | LP_PD_EN, >>> + &pctl->dfilpcfg0); >>> + >>> + writel(1, &pctl->dfitphyupdtype0); >>> + >>> + writel(0x1f, &pctl->dfitphyrdlat); >>> + writel(0, &pctl->dfitphywrdata); >>> + writel(0, &pctl->dfiupdcfg); /* phyupd and ctrlupd disabled */ >>> + >>> + setbits_le32(&pctl->dfistcfg0, DFI_INIT_START); >>> + >>> + tmp = get_timer(0); >>> + do { >>> + if (get_timer(tmp) > timeout_ms) { >>> + error("%s: DFI init did not complete within %ld ms\n", >>> + __func__, timeout_ms); >>> + return -ETIME; >>> + } >>> + } while ((readl(&pctl->dfiststat0) & 1) == 0); >>> + >>> + return 0; >>> +} >>> + >>> +static inline u32 ps_to_tCK(const u32 ps, const ulong freq) >>> +{ >>> + const ulong MHz = 1000000; >>> + return DIV_ROUND_UP(ps * freq, 1000000 * MHz); >>> +} >>> + >>> +static inline u32 ns_to_tCK(const u32 ns, const ulong freq) >>> +{ >>> + return ps_to_tCK(ns * 1000, freq); >>> +} >>> + >>> +static inline u32 tCK_to_ps(const ulong tCK, const ulong freq) >>> +{ >>> + const ulong MHz = 1000000; >>> + return DIV_ROUND_UP(tCK * 1000000 * MHz, freq); >>> +} >>> + >>> +static int pctl_calc_timings(struct rk3368_sdram_params *params, >>> + ulong freq) >>> +{ >>> + struct rk3288_sdram_pctl_timing *pctl_timing = ¶ms->pctl_timing; >>> + const ulong MHz = 1000000; >>> + u32 tccd; >>> + u32 tfaw_as_ps; >>> + >>> + if (params->ddr_speed_bin != DDR3_1600K) { >>> + error("%s: unimplemented DDR3 speed bin %d\n", >>> + __func__, params->ddr_speed_bin); >>> + return -1; >>> + } >>> + >>> + /* PCTL is clocked at 1/2 the DRAM clock; err on the side of caution */ >>> + pctl_timing->togcnt1u = DIV_ROUND_UP(freq, 2 * MHz); >>> + pctl_timing->togcnt100n = DIV_ROUND_UP(freq / 10, 2 * MHz); >>> + >>> + pctl_timing->tinit = 200; /* 200 usec */ >>> + pctl_timing->trsth = 500; /* 500 usec */ >>> + pctl_timing->trefi = 78; /* 7.8usec = 78 * 100ns */ >>> + params->trefi_mem_ddr3 = ns_to_tCK(pctl_timing->trefi * 100, freq); >>> + >>> + if (freq <= (400 * MHz)) { >>> + pctl_timing->tcl = 6; >>> + pctl_timing->tcwl = 10; >>> + } else if (freq <= (533 * MHz)) { >>> + pctl_timing->tcl = 8; >>> + pctl_timing->tcwl = 6; >>> + } else if (freq <= (666 * MHz)) { >>> + pctl_timing->tcl = 10; >>> + pctl_timing->tcwl = 7; >>> + } else { >>> + pctl_timing->tcl = 11; >>> + pctl_timing->tcwl = 8; >>> + } >>> + >>> + pctl_timing->tmrd = 4; /* 4 tCK (all speed bins) */ >>> + pctl_timing->trfc = ns_to_tCK(350, freq); /* tRFC: 350 (max) @ 8GBit */ >>> + pctl_timing->trp = max(4u, ps_to_tCK(13750, freq)); >>> + /* >>> + * JESD-79: >>> + * READ to WRITE Command Delay = RL + tCCD / 2 + 2tCK - WL >>> + */ >>> + tccd = 4; >>> + pctl_timing->trtw = pctl_timing->tcl + tccd/2 + 2 - pctl_timing->tcwl; >>> + pctl_timing->tal = 0; >>> + pctl_timing->tras = ps_to_tCK(35000, freq); >>> + pctl_timing->trc = ps_to_tCK(48750, freq); >>> + pctl_timing->trcd = ps_to_tCK(13750, freq); >>> + pctl_timing->trrd = max(4u, ps_to_tCK(7500, freq)); >>> + pctl_timing->trtp = max(4u, ps_to_tCK(7500, freq)); >>> + pctl_timing->twr = ps_to_tCK(15000, freq); >>> + /* The DDR3 mode-register does only support even values for tWR > 8. */ >>> + if (pctl_timing->twr > 8) >>> + pctl_timing->twr = (pctl_timing->twr + 1) & ~1; >>> + pctl_timing->twtr = max(4u, ps_to_tCK(7500, freq)); >>> + pctl_timing->texsr = 512; /* tEXSR(max) is tDLLLK */ >>> + pctl_timing->txp = max(3u, ps_to_tCK(6000, freq)); >>> + pctl_timing->txpdll = max(10u, ps_to_tCK(24000, freq)); >>> + pctl_timing->tzqcs = max(64u, ps_to_tCK(80000, freq)); >>> + pctl_timing->tzqcsi = 10000; /* as used by Rockchip */ >>> + pctl_timing->tdqs = 1; /* fixed for DDR3 */ >>> + pctl_timing->tcksre = max(5u, ps_to_tCK(10000, freq)); >>> + pctl_timing->tcksrx = max(5u, ps_to_tCK(10000, freq)); >>> + pctl_timing->tcke = max(3u, ps_to_tCK(5000, freq)); >>> + pctl_timing->tmod = max(12u, ps_to_tCK(15000, freq)); >>> + pctl_timing->trstl = ns_to_tCK(100, freq); >>> + pctl_timing->tzqcl = max(256u, ps_to_tCK(320000, freq)); /* tZQoper */ >>> + pctl_timing->tmrr = 0; >>> + pctl_timing->tckesr = pctl_timing->tcke + 1; /* JESD-79: tCKE + 1tCK */ >>> + pctl_timing->tdpd = 0; /* RK3368 TRM: "allowed values for DDR3: 0" */ >>> + >>> + >>> + /* >>> + * The controller can represent tFAW as 4x, 5x or 6x tRRD only. >>> + * We want to use the smallest multiplier that satisfies the tFAW >>> + * requirements of the given speed-bin. If necessary, we stretch out >>> + * tRRD to allow us to operate on a 6x multiplier for tFAW. >>> + */ >>> + tfaw_as_ps = 40000; /* 40ns: tFAW for DDR3-1600K, 2KB page-size */ >>> + if (tCK_to_ps(pctl_timing->trrd * 6, freq) < tfaw_as_ps) { >>> + /* If tFAW is > 6 x tRRD, we need to stretch tRRD */ >>> + pctl_timing->trrd = ps_to_tCK(DIV_ROUND_UP(40000, 6), freq); >>> + params->tfaw_mult = TFAW_TRRD_MULT6; >>> + } else if (tCK_to_ps(pctl_timing->trrd * 5, freq) < tfaw_as_ps) { >>> + params->tfaw_mult = TFAW_TRRD_MULT6; >>> + } else if (tCK_to_ps(pctl_timing->trrd * 4, freq) < tfaw_as_ps) { >>> + params->tfaw_mult = TFAW_TRRD_MULT5; >>> + } else { >>> + params->tfaw_mult = TFAW_TRRD_MULT4; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static void pctl_cfg(struct rk3368_ddr_pctl *pctl, >>> + struct rk3368_sdram_params *params, >>> + struct rk3368_grf *grf) >>> +{ >>> + /* Configure PCTL timing registers */ >>> + params->pctl_timing.trefi |= BIT(31); /* see PCTL_TREFI */ >>> + copy_to_reg(&pctl->togcnt1u, ¶ms->pctl_timing.togcnt1u, >>> + sizeof(params->pctl_timing)); >>> + writel(params->trefi_mem_ddr3, &pctl->trefi_mem_ddr3); >>> + >>> + /* Set up ODT write selector and ODT write length */ >>> + writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL), &pctl->dfiodtcfg); >>> + writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1); >>> + >>> + /* Set up the CL/CWL-dependent timings of DFI */ >>> + writel((params->pctl_timing.tcl - 1) / 2 - 1, &pctl->dfitrddataen); >>> + writel((params->pctl_timing.tcwl - 1) / 2 - 1, &pctl->dfitphywrlat); >>> + >>> + /* DDR3 */ >>> + writel(params->tfaw_mult | DDR3_EN | DDR2_DDR3_BL_8, &pctl->mcfg); >>> + writel(0x001c0004, &grf->ddrc0_con0); >>> + >>> + setbits_le32(&pctl->scfg, HW_LOW_POWER_EN); >>> +} >>> + >>> +static int ddrphy_data_training(struct rk3368_ddr_pctl *pctl, >>> + struct rk3368_ddrphy *ddrphy) >>> +{ >>> + const u32 trefi = readl(&pctl->trefi); >>> + const ulong timeout_ms = 500; >>> + ulong tmp; >>> + >>> + /* disable auto-refresh */ >>> + writel(0 | BIT(31), &pctl->trefi); >>> + >>> + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x20); >>> + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x21); >>> + >>> + tmp = get_timer(0); >>> + do { >>> + if (get_timer(tmp) > timeout_ms) { >>> + error("%s: did not complete within %ld ms\n", >>> + __func__, timeout_ms); >>> + return -ETIME; >>> + } >>> + } while ((readl(&ddrphy->reg[0xff]) & 0xf) != 0xf); >>> + >>> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, PREA_CMD); >>> + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x20); >>> + /* resume auto-refresh */ >>> + writel(trefi | BIT(31), &pctl->trefi); >>> + >>> + return 0; >>> +} >>> + >>> +static int sdram_col_row_detect(struct udevice *dev) >>> +{ >>> + struct dram_info *priv = dev_get_priv(dev); >>> + struct rk3368_sdram_params *params = dev_get_platdata(dev); >>> + struct rk3368_ddr_pctl *pctl = priv->pctl; >>> + struct rk3368_msch *msch = priv->msch; >>> + const u32 test_pattern = 0x5aa5f00f; >>> + int row, col; >>> + uintptr_t addr; >>> + >>> + move_to_config_state(pctl); >>> + writel(6, &msch->ddrconf); >>> + move_to_access_state(pctl); >>> + >>> + /* Detect col */ >>> + for (col = 11; col >= 9; col--) { >>> + writel(0, CONFIG_SYS_SDRAM_BASE); >>> + addr = CONFIG_SYS_SDRAM_BASE + >>> + (1 << (col + params->chan.bw - 1)); >>> + writel(test_pattern, addr); >>> + if ((readl(addr) == test_pattern) && >>> + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) >>> + break; >>> + } >>> + >>> + if (col == 8) { >>> + error("%s: col detect error\n", __func__); >>> + return -EINVAL; >>> + } >>> + >>> + move_to_config_state(pctl); >>> + writel(15, &msch->ddrconf); >>> + move_to_access_state(pctl); >>> + >>> + /* Detect row*/ >>> + for (row = 16; row >= 12; row--) { >>> + writel(0, CONFIG_SYS_SDRAM_BASE); >>> + addr = CONFIG_SYS_SDRAM_BASE + (1 << (row + 15 - 1)); >>> + writel(test_pattern, addr); >>> + if ((readl(addr) == test_pattern) && >>> + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) >>> + break; >>> + } >>> + >>> + if (row == 11) { >>> + error("%s: row detect error\n", __func__); >>> + return -EINVAL; >>> + } >>> + >>> + /* Record results */ >>> + debug("%s: col %d, row %d\n", __func__, col, row); >>> + params->chan.col = col; >>> + params->chan.cs0_row = row; >>> + params->chan.cs1_row = row; >>> + params->chan.row_3_4 = 0; >>> + >>> + return 0; >>> +} >>> + >>> +static int msch_niu_config(struct rk3368_msch *msch, >>> + struct rk3368_sdram_params *params) >>> +{ >>> + int i; >>> + const u8 cols = params->chan.col - ((params->chan.bw == 2) ? 0 : 1); >>> + const u8 rows = params->chan.cs0_row; >>> + >>> + /* >>> + * The DDR address-translation table always assumes a 32bit >>> + * bus and the comparison below takes care of adjusting for >>> + * a 16bit bus (i.e. one column-address is consumed). >>> + */ >>> + const struct { >>> + u8 rows; >>> + u8 columns; >>> + u8 type; >>> + } ddrconf_table[] = { >>> + /* >>> + * C-B-R-D patterns are first. For these we require an >>> + * exact match for the columns and rows (as there's >>> + * one entry per possible configuration). >>> + */ >>> + [0] = { .rows = 13, .columns = 10, .type = DMC_MSCH_CBRD }, >>> + [1] = { .rows = 14, .columns = 10, .type = DMC_MSCH_CBRD }, >>> + [2] = { .rows = 15, .columns = 10, .type = DMC_MSCH_CBRD }, >>> + [3] = { .rows = 16, .columns = 10, .type = DMC_MSCH_CBRD }, >>> + [4] = { .rows = 14, .columns = 11, .type = DMC_MSCH_CBRD }, >>> + [5] = { .rows = 15, .columns = 11, .type = DMC_MSCH_CBRD }, >>> + [6] = { .rows = 16, .columns = 11, .type = DMC_MSCH_CBRD }, >>> + [7] = { .rows = 13, .columns = 9, .type = DMC_MSCH_CBRD }, >>> + [8] = { .rows = 14, .columns = 9, .type = DMC_MSCH_CBRD }, >>> + [9] = { .rows = 15, .columns = 9, .type = DMC_MSCH_CBRD }, >>> + [10] = { .rows = 16, .columns = 9, .type = DMC_MSCH_CBRD }, >>> + /* >>> + * 11 through 13 are C-R-B-D patterns. These are >>> + * matched for an exact number of columns and to >>> + * ensure that the hardware uses at least as many rows >>> + * as the pattern requires (i.e. we make sure that >>> + * there's no gaps up until we hit the device/chip-select; >>> + * however, these patterns can accept up to 16 rows, >>> + * as the row-address continues right after the CS >>> + * switching) >>> + */ >>> + [11] = { .rows = 15, .columns = 10, .type = DMC_MSCH_CRBD }, >>> + [12] = { .rows = 14, .columns = 11, .type = DMC_MSCH_CRBD }, >>> + [13] = { .rows = 13, .columns = 10, .type = DMC_MSCH_CRBD }, >>> + /* >>> + * 14 and 15 are catch-all variants using a C-B-D-R >>> + * scheme (i.e. alternating the chip-select every time >>> + * C-B overflows) and stuffing the remaining C-bits >>> + * into the top. Matching needs to make sure that the >>> + * number of columns is either an exact match (i.e. we >>> + * can use less the the maximum number of rows) -or- >>> + * that the columns exceed what is given in this table >>> + * and the rows are an exact match (in which case the >>> + * remaining C-bits will be stuffed onto the top after >>> + * the device/chip-select switches). >>> + */ >>> + [14] = { .rows = 16, .columns = 10, .type = DMC_MSCH_CBDR }, >>> + [15] = { .rows = 16, .columns = 9, .type = DMC_MSCH_CBDR }, >>> + }; >>> + >>> + /* >>> + * For C-B-R-D, we need an exact match (i.e. both for the number of >>> + * columns and rows), while for C-B-D-R, only the the number of >>> + * columns needs to match. >>> + */ >>> + for (i = 0; i < ARRAY_SIZE(ddrconf_table); i++) { >>> + bool match = false; >>> + >>> + /* If this entry if for a different matcher, then skip it */ >>> + if (ddrconf_table[i].type != params->memory_schedule) >>> + continue; >>> + >>> + /* >>> + * Match according to the rules (exact/inexact/at-least) >>> + * documented in the ddrconf_table above. >>> + */ >>> + switch (params->memory_schedule) { >>> + case DMC_MSCH_CBRD: >>> + match = (ddrconf_table[i].columns == cols) && >>> + (ddrconf_table[i].rows == rows); >>> + break; >>> + >>> + case DMC_MSCH_CRBD: >>> + match = (ddrconf_table[i].columns == cols) && >>> + (ddrconf_table[i].rows <= rows); >>> + break; >>> + >>> + case DMC_MSCH_CBDR: >>> + match = (ddrconf_table[i].columns == cols) || >>> + ((ddrconf_table[i].columns <= cols) && >>> + (ddrconf_table[i].rows == rows)); >>> + break; >>> + >>> + default: >>> + break; >>> + } >>> + >>> + if (match) { >>> + debug("%s: setting ddrconf 0x%x\n", __func__, i); >>> + writel(i, &msch->ddrconf); >>> + return 0; >>> + } >>> + } >>> + >>> + error("%s: ddrconf (NIU config) not found\n", __func__); >>> + return -EINVAL; >>> +} >>> + >>> +static void dram_all_config(struct udevice *dev) >>> +{ >>> + struct dram_info *priv = dev_get_priv(dev); >>> + struct rk3368_pmu_grf *pmugrf = priv->pmugrf; >>> + struct rk3368_sdram_params *params = dev_get_platdata(dev); >>> + const struct rk3288_sdram_channel *info = ¶ms->chan; >>> + u32 sys_reg = 0; >>> + const int chan = 0; >>> + >>> + sys_reg |= DDR3 << SYS_REG_DDRTYPE_SHIFT; >>> + sys_reg |= 0 << SYS_REG_NUM_CH_SHIFT; >>> + >>> + sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(chan); >>> + sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(chan); >>> + sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(chan); >>> + sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(chan); >>> + sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(chan); >>> + sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(chan); >>> + sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(chan); >>> + sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(chan); >>> + sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(chan); >>> + >>> + writel(sys_reg, &pmugrf->os_reg[2]); >>> +} >>> + >>> +static int setup_sdram(struct udevice *dev) >>> +{ >>> + struct dram_info *priv = dev_get_priv(dev); >>> + struct rk3368_sdram_params *params = dev_get_platdata(dev); >>> + >>> + struct rk3368_ddr_pctl *pctl = priv->pctl; >>> + struct rk3368_ddrphy *ddrphy = priv->phy; >>> + struct rk3368_cru *cru = priv->cru; >>> + struct rk3368_grf *grf = priv->grf; >>> + struct rk3368_msch *msch = priv->msch; >>> + >>> + int ret; >>> + >>> + /* The input clock (i.e. DPLL) needs to be 2x the DRAM frequency */ >>> + ret = clk_set_rate(&priv->ddr_clk, 2 * params->ddr_freq); >>> + if (ret < 0) { >>> + debug("%s: could not set DDR clock: %d\n", __func__, ret); >>> + return ret; >>> + } >>> + >>> + /* Update the read-latency for the RK3368 */ >>> + writel(0x32, &msch->readlatency); >>> + >>> + /* Initialise the DDR PCTL and DDR PHY */ >>> + ddrctl_reset(cru); >>> + ddrphy_reset(ddrphy); >>> + ddrphy_config_delays(ddrphy, params->ddr_freq); >>> + dfi_cfg(pctl); >>> + /* Configure relative system information of grf_ddrc0_con0 register */ >>> + ddr_set_ddr3_mode(grf, true); >>> + ddr_set_noc_spr_err_stall(grf, true); >>> + /* Calculate timings */ >>> + pctl_calc_timings(params, params->ddr_freq); >>> + /* Initialise the device timings in protocol controller */ >>> + pctl_cfg(pctl, params, grf); >>> + /* Configure AL, CL ... information of PHY registers */ >>> + ddrphy_config(ddrphy, >>> + params->pctl_timing.tcl, >>> + params->pctl_timing.tal, >>> + params->pctl_timing.tcwl); >>> + >>> + /* Initialize DRAM and configure with mode-register values */ >>> + ret = memory_init(pctl, params); >>> + if (ret) >>> + goto error; >>> + >>> + move_to_config_state(pctl); >>> + /* Perform data-training */ >>> + ddrphy_data_training(pctl, ddrphy); >>> + move_to_access_state(pctl); >>> + >>> + /* TODO(prt): could detect rank in training... */ >>> + params->chan.rank = 2; >>> + /* TODO(prt): bus width is not auto-detected (yet)... */ >>> + params->chan.bw = 2; /* 32bit wide bus */ >>> + params->chan.dbw = params->chan.dbw; /* 32bit wide bus */ >>> + >>> + /* DDR3 is always 8 bank */ >>> + params->chan.bk = 3; >>> + /* Detect col and row number */ >>> + ret = sdram_col_row_detect(dev); >>> + if (ret) >>> + goto error; >>> + >>> + /* Configure NIU DDR configuration */ >>> + ret = msch_niu_config(msch, params); >>> + if (ret) >>> + goto error; >>> + >>> + /* set up OS_REG to communicate w/ next stage and OS */ >>> + dram_all_config(dev); >>> + >>> + return 0; >>> + >>> +error: >>> + printf("DRAM init failed!\n"); >>> + hang(); >>> +} >>> +#endif >>> + >>> +static int rk3368_dmc_ofdata_to_platdata(struct udevice *dev) >>> +{ >>> + int ret = 0; >>> + >>> +#if !CONFIG_IS_ENABLED(OF_PLATDATA) >>> + struct rk3368_sdram_params *plat = dev_get_platdata(dev); >>> + >>> + ret = regmap_init_mem(dev, &plat->map); >>> + if (ret) >>> + return ret; >>> +#endif >>> + >>> + return ret; >>> +} >>> + >>> +#if CONFIG_IS_ENABLED(OF_PLATDATA) >>> +static int conv_of_platdata(struct udevice *dev) >>> +{ >>> + struct rk3368_sdram_params *plat = dev_get_platdata(dev); >>> + struct dtd_rockchip_rk3368_dmc *of_plat = &plat->of_plat; >>> + int ret; >>> + >>> + plat->ddr_freq = of_plat->rockchip_ddr_frequency; >>> + plat->ddr_speed_bin = of_plat->rockchip_ddr_speed_bin; >>> + plat->memory_schedule = of_plat->rockchip_memory_schedule; >>> + >>> + ret = regmap_init_mem_platdata(dev, of_plat->reg, >>> + ARRAY_SIZE(of_plat->reg) / 2, >>> + &plat->map); >>> + if (ret) >>> + return ret; >>> + >>> + return 0; >>> +} >>> +#endif >>> + >>> +static int rk3368_dmc_probe(struct udevice *dev) >>> +{ >>> +#ifdef CONFIG_TPL_BUILD >>> + struct rk3368_sdram_params *plat = dev_get_platdata(dev); >>> + struct rk3368_ddr_pctl *pctl; >>> + struct rk3368_ddrphy *ddrphy; >>> + struct rk3368_cru *cru; >>> + struct rk3368_grf *grf; >>> + struct rk3368_msch *msch; >>> + int ret; >>> + struct udevice *dev_clk; >>> +#endif >>> + struct dram_info *priv = dev_get_priv(dev); >>> + >>> +#if CONFIG_IS_ENABLED(OF_PLATDATA) >>> + ret = conv_of_platdata(dev); >>> + if (ret) >>> + return ret; >>> +#endif >>> + >>> + priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); >>> + debug("%s: pmugrf=%p\n", __func__, priv->pmugrf); >>> + >>> +#ifdef CONFIG_TPL_BUILD >>> + pctl = regmap_get_range(plat->map, 0); >>> + ddrphy = regmap_get_range(plat->map, 1); >>> + msch = syscon_get_first_range(ROCKCHIP_SYSCON_MSCH); >>> + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); >>> + >>> + priv->pctl = pctl; >>> + priv->phy = ddrphy; >>> + priv->msch = msch; >>> + priv->grf = grf; >>> + >>> + ret = rockchip_get_clk(&dev_clk); >>> + if (ret) >>> + return ret; >>> + priv->ddr_clk.id = CLK_DDR; >>> + ret = clk_request(dev_clk, &priv->ddr_clk); >>> + if (ret) >>> + return ret; >>> + >>> + cru = rockchip_get_cru(); >>> + priv->cru = cru; >>> + if (IS_ERR(priv->cru)) >>> + return PTR_ERR(priv->cru); >>> + >>> + ret = setup_sdram(dev); >>> + if (ret) >>> + return ret; >>> +#endif >>> + >>> + priv->info.base = 0; >>> + priv->info.size = >>> + rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[2]); >>> + >>> + /* >>> + * we use the 0x00000000~0xfdffffff space since 0xff000000~0xffffffff >>> + * is SoC register space (i.e. reserved), and 0xfe000000~0xfeffffff is >>> + * inaccessible for some IP controller. >>> + */ >>> + priv->info.size = min(priv->info.size, (size_t)0xfe000000); >>> + >>> + return 0; >>> +} >>> + >>> +static int rk3368_dmc_get_info(struct udevice *dev, struct ram_info *info) >>> +{ >>> + struct dram_info *priv = dev_get_priv(dev); >>> + >>> + *info = priv->info; >>> + return 0; >>> +} >>> + >>> +static struct ram_ops rk3368_dmc_ops = { >>> + .get_info = rk3368_dmc_get_info, >>> +}; >>> + >>> + >>> +static const struct udevice_id rk3368_dmc_ids[] = { >>> + { .compatible = "rockchip,rk3368-dmc" }, >>> + { } >>> +}; >>> + >>> +U_BOOT_DRIVER(dmc_rk3368) = { >>> + .name = "rockchip_rk3368_dmc", >>> + .id = UCLASS_RAM, >>> + .of_match = rk3368_dmc_ids, >>> + .ops = &rk3368_dmc_ops, >>> + .probe = rk3368_dmc_probe, >>> + .priv_auto_alloc_size = sizeof(struct dram_info), >>> + .ofdata_to_platdata = rk3368_dmc_ofdata_to_platdata, >>> + .probe = rk3368_dmc_probe, >>> + .priv_auto_alloc_size = sizeof(struct dram_info), >>> + .platdata_auto_alloc_size = sizeof(struct rk3368_sdram_params), >>> +}; >>> diff --git a/include/dt-bindings/memory/rk3368-dmc.h >>> b/include/dt-bindings/memory/rk3368-dmc.h >>> new file mode 100644 >>> index 0000000..b06ffde >>> --- /dev/null >>> +++ b/include/dt-bindings/memory/rk3368-dmc.h >>> @@ -0,0 +1,30 @@ >>> +#ifndef DT_BINDINGS_RK3368_DMC_H >>> +#define DT_BINDINGS_RK3368_DMC_H >>> + >>> +#define DMC_MSCH_CBDR 0x0 >>> +#define DMC_MSCH_CBRD 0x1 >>> +#define DMC_MSCH_CRBD 0x2 >>> + >>> +#define DDR3_800D 0 >>> +#define DDR3_800E 1 >>> +#define DDR3_1066E 2 >>> +#define DDR3_1066F 3 >>> +#define DDR3_1066G 4 >>> +#define DDR3_1333F 5 >>> +#define DDR3_1333G 6 >>> +#define DDR3_1333H 7 >>> +#define DDR3_1333J 8 >>> +#define DDR3_1600G 9 >>> +#define DDR3_1600H 10 >>> +#define DDR3_1600J 11 >>> +#define DDR3_1600K 12 >>> +#define DDR3_1866J 13 >>> +#define DDR3_1866K 14 >>> +#define DDR3_1866L 15 >>> +#define DDR3_1866M 16 >>> +#define DDR3_2133K 17 >>> +#define DDR3_2133L 18 >>> +#define DDR3_2133M 19 >>> +#define DDR3_2133N 20 >>> + >>> +#endif >> >> dpd; >> + u32 trefi_mem_ddr3; >> + u32 reserved5[45]; >> + u32 dtuwactl; >> + u32 dturactl; >> + u32 dtucfg; >> + u32 dtuectl; >> + u32 dtuwd0; >> + u32 dtuwd1; >> + u32 dtuwd2; >> + u32 dtuwd3; >> + u32 dtuwdm; >> + u32 dturd0; >> + u32 dturd1; >> + u32 dturd2; >> + u32 dturd3; >> + u32 dtulfsrwd; >> + u32 dtulfsrrd; >> + u32 dtueaf; >> + u32 dfitctrldelay; >> + u32 dfiodtcfg; >> + u32 dfiodtcfg1; >> + u32 dfiodtrankmap; >> + u32 dfitphywrdata; >> + u32 dfitphywrlat; >> + u32 reserved7[2]; >> + u32 dfitrddataen; >> + u32 dfitphyrdlat; >> + u32 reserved8[2]; >> + u32 dfitphyupdtype0; >> + u32 dfitphyupdtype1; >> + u32 dfitphyupdtype2; >> + u32 dfitphyupdtype3; >> + u32 dfitctrlupdmin; >> + u32 dfitctrlupdmax; >> + u32 dfitctrlupddly; >> + u32 reserved9; >> + u32 dfiupdcfg; >> + u32 dfitrefmski; >> + u32 dfitctrlupdi; >> + u32 reserved10[4]; >> + u32 dfitrcfg0; >> + u32 dfitrstat0; >> + u32 dfitrwrlvlen; >> + u32 dfitrrdlvlen; >> + u32 dfitrrdlvlgateen; >> + u32 dfiststat0; >> + u32 dfistcfg0; >> + u32 dfistcfg1; >> + u32 reserved11; >> + u32 dfitdramclken; >> + u32 dfitdramclkdis; >> + u32 dfistcfg2; >> + u32 dfistparclr; >> + u32 dfistparlog; >> + u32 reserved12[3]; >> + u32 dfilpcfg0; >> + u32 reserved13[3]; >> + u32 dfitrwrlvlresp0; >> + u32 dfitrwrlvlresp1; >> + u32 dfitrwrlvlresp2; >> + u32 dfitrrdlvlresp0; >> + u32 dfitrrdlvlresp1; >> + u32 dfitrrdlvlresp2; >> + u32 dfitrwrlvldelay0; >> + u32 dfitrwrlvldelay1; >> + u32 dfitrwrlvldelay2; >> + u32 dfitrrdlvldelay0; >> + u32 dfitrrdlvldelay1; >> + u32 dfitrrdlvldelay2; >> + u32 dfitrrdlvlgatedelay0; >> + u32 dfitrrdlvlgatedelay1; >> + u32 dfitrrdlvlgatedelay2; >> + u32 dfitrcmd; >> + u32 reserved14[46]; >> + u32 ipvr; >> + u32 iptr; >> +}; >> +check_member(rk3368_ddr_pctl, iptr, 0x03fc); >> + >> +struct rk3368_ddrphy { >> + u32 reg[0x100]; >> +}; >> +check_member(rk3368_ddrphy, reg[0xff], 0x03fc); >> + >> +struct rk3368_msch { >> + u32 coreid; >> + u32 revisionid; >> + u32 ddrconf; >> + u32 ddrtiming; >> + u32 ddrmode; >> + u32 readlatency; >> + u32 reserved1[8]; >> + u32 activate; >> + u32 devtodev; >> +}; >> +check_member(rk3368_msch, devtodev, 0x003c); >> + >> +/* GRF_SOC_CON0 */ >> +enum { >> + NOC_RSP_ERR_STALL = BIT(9), >> + MOBILE_DDR_SEL = BIT(4), >> + DDR0_16BIT_EN = BIT(3), >> + MSCH0_MAINDDR3_DDR3 = BIT(2), >> + MSCH0_MAINPARTIALPOP = BIT(1), >> + UPCTL_C_ACTIVE = BIT(0), >> +}; >> + >> +#endif >> diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >> b/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >> index 1f84ff9..6b6651a 100644 >> --- a/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >> +++ b/arch/arm/include/asm/arch-rockchip/grf_rk3368.h >> @@ -76,8 +76,11 @@ struct rk3368_grf { >> u32 soc_con15; >> u32 soc_con16; >> u32 soc_con17; >> + u32 reserved5[0x6e]; >> + u32 ddrc0_con0; >> }; >> check_member(rk3368_grf, soc_con17, 0x444); >> +check_member(rk3368_grf, ddrc0_con0, 0x600); >> struct rk3368_pmu_grf { >> u32 gpio0a_iomux; >> diff --git a/arch/arm/mach-rockchip/rk3368/Makefile >> b/arch/arm/mach-rockchip/rk3368/Makefile >> index 0390716..46798c2 100644 >> --- a/arch/arm/mach-rockchip/rk3368/Makefile >> +++ b/arch/arm/mach-rockchip/rk3368/Makefile >> @@ -5,5 +5,4 @@ >> # >> obj-y += clk_rk3368.o >> obj-y += rk3368.o >> -obj-y += sdram_rk3368.o >> obj-y += syscon_rk3368.o > > > The current PX5 EVB and Sheep board(they have no spl now) depend on this > sdram_rk3368.c > to get the sdram capacity, and so is the geekbox board. > These boards will be broken when this driver removed. > We got the failed log like this on PX5 EVB: > > DRAM: DRAM init failed: -19 > initcall sequence 000000002d=378208 failed at call 02023f0 (err=-19) > ### ERROR ### Please RESET the board ### >> diff --git a/arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >> b/arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >> deleted file mode 100644 >> index d0d0900..0000000 >> --- a/arch/arm/mach-rockchip/rk3368/sdram_rk3368.c >> +++ /dev/null >> @@ -1,60 +0,0 @@ >> -/* >> - * (C) Copyright 2016 Rockchip Electronics Co., Ltd. >> - * >> - * SPDX-License-Identifier: GPL-2.0 >> - */ >> - >> -#include <common.h> >> -#include <dm.h> >> -#include <ram.h> >> -#include <syscon.h> >> -#include <asm/arch/clock.h> >> -#include <asm/arch/grf_rk3368.h> >> -#include <asm/arch/sdram_common.h> >> - >> -DECLARE_GLOBAL_DATA_PTR; >> -struct dram_info { >> - struct ram_info info; >> - struct rk3368_pmu_grf *pmugrf; >> -}; >> - >> -static int rk3368_dmc_probe(struct udevice *dev) >> -{ >> - struct dram_info *priv = dev_get_priv(dev); >> - >> - priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); >> - debug("%s: grf=%p\n", __func__, priv->pmugrf); >> - priv->info.base = CONFIG_SYS_SDRAM_BASE; >> - priv->info.size = rockchip_sdram_size( >> - (phys_addr_t)&priv->pmugrf->os_reg[2]); >> - >> - return 0; >> -} >> - >> -static int rk3368_dmc_get_info(struct udevice *dev, struct ram_info *info) >> -{ >> - struct dram_info *priv = dev_get_priv(dev); >> - >> - *info = priv->info; >> - >> - return 0; >> -} >> - >> -static struct ram_ops rk3368_dmc_ops = { >> - .get_info = rk3368_dmc_get_info, >> -}; >> - >> - >> -static const struct udevice_id rk3368_dmc_ids[] = { >> - { .compatible = "rockchip,rk3368-dmc" }, >> - { } >> -}; >> - >> -U_BOOT_DRIVER(dmc_rk3368) = { >> - .name = "rockchip_rk3368_dmc", >> - .id = UCLASS_RAM, >> - .of_match = rk3368_dmc_ids, >> - .ops = &rk3368_dmc_ops, >> - .probe = rk3368_dmc_probe, >> - .priv_auto_alloc_size = sizeof(struct dram_info), >> -}; >> diff --git a/doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >> b/doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >> new file mode 100644 >> index 0000000..8e7357d >> --- /dev/null >> +++ b/doc/device-tree-bindings/clock/rockchip,rk3368-dmc.txt >> @@ -0,0 +1,67 @@ >> +RK3368 dynamic memory controller driver >> +======================================= >> + >> +The RK3368 DMC (dynamic memory controller) driver supports >> setup/initialisation >> +during TPL using configuration data from the DTS (i.e. OF_PLATDATA), based >> on >> +the following key configuration data: >> + (a) a target-frequency (i.e. operating point) for the memory operation >> + (b) a speed-bin (as defined in JESD-79) for the DDR3 used in hardware >> + (c) a memory-schedule (i.e. mapping from physical addresses to the address >> + pins of the memory bus) >> + >> +Required properties >> +------------------- >> + >> +- compatible: "rockchip,rk3368-dmc" >> +- reg >> + protocol controller (PCTL) address and PHY controller (DDRPHY) address >> +- rockchip,ddr-speed-bin >> + the DDR3 device's speed-bin (as specified according to JESD-79) >> + DDR3_800D (5-5-5) >> + DDR3_800E (6-6-6) >> + DDR3_1066E (6-6-6) >> + DDR3_1066F (7-7-7) >> + DDR3_1066G (8-8-8) >> + DDR3_1333F (7-7-7) >> + DDR3_1333G (8-8-8) >> + DDR3_1333H (9-9-9) >> + DDR3_1333J (10-10-10) >> + DDR3_1600G (8-8-8) >> + DDR3_1600H (9-9-9) >> + DDR3_1600J (10-10-10) >> + DDR3_1600K (11-11-11) >> + DDR3_1866J (10-10-10) >> + DDR3_1866K (11-11-11) >> + DDR3_1866L (12-12-12) >> + DDR3_1866M (13-13-13) >> + DDR3_2133K (11-11-11) >> + DDR3_2133L (12-12-12) >> + DDR3_2133M (13-13-13) >> + DDR3_2133N (14-14-14) >> +- rockchip,ddr-frequency: >> + target DDR clock frequency in Hz (not all frequencies may be supported, >> + as there's some cooperation from the clock-driver required) >> +- rockchip,memory-schedule: >> + controls the decoding of physical addresses to DRAM addressing (i.e. how >> + the physical address maps onto the address pins/chip-select of the >> device) >> + DMC_MSCH_CBDR: column -> bank -> device -> row >> + DMC_MSCH_CBRD: column -> band -> row -> device >> + DMC_MSCH_CRBD: column -> row -> band -> device >> + >> +Example (for DDR3-1600K and 800MHz) >> +----------------------------------- >> + >> + #include <dt-bindings/memory/rk3368-dmc.h> >> + >> + dmc: dmc@ff610000 { >> + u-boot,dm-pre-reloc; >> + compatible = "rockchip,rk3368-dmc"; >> + reg = <0 0xff610000 0 0x400 >> + 0 0xff620000 0 0x400>; >> + }; >> + >> + &dmc { >> + rockchip,ddr-speed-bin = <DDR3_1600K>; >> + rockchip,ddr-frequency = <800000000>; >> + rockchip,memory-schedule = <DMC_MSCH_CBRD>; >> + }; >> diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile >> index c409c48..51ae6be 100644 >> --- a/drivers/ram/Makefile >> +++ b/drivers/ram/Makefile >> @@ -8,3 +8,5 @@ obj-$(CONFIG_RAM) += ram-uclass.o >> obj-$(CONFIG_SANDBOX) += sandbox_ram.o >> obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o >> obj-$(CONFIG_ARCH_BMIPS) += bmips_ram.o >> + >> +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ >> diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile >> new file mode 100644 >> index 0000000..b09d03c >> --- /dev/null >> +++ b/drivers/ram/rockchip/Makefile >> @@ -0,0 +1,7 @@ >> +# >> +# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH >> +# >> +# SPDX-License-Identifier: GPL-2.0+ >> +# >> + >> +obj-$(CONFIG_ROCKCHIP_RK3368) = dmc-rk3368.o >> diff --git a/drivers/ram/rockchip/dmc-rk3368.c >> b/drivers/ram/rockchip/dmc-rk3368.c >> new file mode 100644 >> index 0000000..fea96a5 >> --- /dev/null >> +++ b/drivers/ram/rockchip/dmc-rk3368.c >> @@ -0,0 +1,990 @@ >> +/* >> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH >> + * >> + * SPDX-License-Identifier: GPL-2.0 >> + */ >> + >> +#include <common.h> >> +#include <clk.h> >> +#include <dm.h> >> +#include <dt-bindings/memory/rk3368-dmc.h> >> +#include <dt-structs.h> >> +#include <ram.h> >> +#include <regmap.h> >> +#include <syscon.h> >> +#include <asm/io.h> >> +#include <asm/arch/clock.h> >> +#include <asm/arch/cru_rk3368.h> >> +#include <asm/arch/grf_rk3368.h> >> +#include <asm/arch/ddr_rk3368.h> >> +#include <asm/arch/sdram.h> >> +#include <asm/arch/sdram_common.h> >> + >> +DECLARE_GLOBAL_DATA_PTR; >> + >> +struct dram_info { >> + struct ram_info info; >> + struct clk ddr_clk; >> + struct rk3368_cru *cru; >> + struct rk3368_grf *grf; >> + struct rk3368_ddr_pctl *pctl; >> + struct rk3368_ddrphy *phy; >> + struct rk3368_pmu_grf *pmugrf; >> + struct rk3368_msch *msch; >> +}; >> + >> +struct rk3368_sdram_params { >> +#if CONFIG_IS_ENABLED(OF_PLATDATA) >> + struct dtd_rockchip_rk3368_dmc of_plat; >> +#endif >> + struct rk3288_sdram_pctl_timing pctl_timing; >> + u32 trefi_mem_ddr3; >> + struct rk3288_sdram_channel chan; >> + struct regmap *map; >> + u32 ddr_freq; >> + u32 memory_schedule; >> + u32 ddr_speed_bin; >> + u32 tfaw_mult; >> +}; >> + >> +/* PTCL bits */ >> +enum { >> + /* PCTL_DFISTCFG0 */ >> + DFI_INIT_START = BIT(0), >> + DFI_DATA_BYTE_DISABLE_EN = BIT(2), >> + >> + /* PCTL_DFISTCFG1 */ >> + DFI_DRAM_CLK_SR_EN = BIT(0), >> + DFI_DRAM_CLK_DPD_EN = BIT(1), >> + ODT_LEN_BL8_W_SHIFT = 16, >> + >> + /* PCTL_DFISTCFG2 */ >> + DFI_PARITY_INTR_EN = BIT(0), >> + DFI_PARITY_EN = BIT(1), >> + >> + /* PCTL_DFILPCFG0 */ >> + TLP_RESP_TIME_SHIFT = 16, >> + LP_SR_EN = BIT(8), >> + LP_PD_EN = BIT(0), >> + >> + /* PCTL_DFIODTCFG */ >> + RANK0_ODT_WRITE_SEL = BIT(3), >> + RANK1_ODT_WRITE_SEL = BIT(11), >> + >> + /* PCTL_SCFG */ >> + HW_LOW_POWER_EN = BIT(0), >> + >> + /* PCTL_MCMD */ >> + START_CMD = BIT(31), >> + MCMD_RANK0 = BIT(20), >> + MCMD_RANK1 = BIT(21), >> + DESELECT_CMD = 0, >> + PREA_CMD, >> + REF_CMD, >> + MRS_CMD, >> + ZQCS_CMD, >> + ZQCL_CMD, >> + RSTL_CMD, >> + MRR_CMD = 8, >> + DPDE_CMD, >> + >> + /* PCTL_POWCTL */ >> + POWER_UP_START = BIT(0), >> + >> + /* PCTL_POWSTAT */ >> + POWER_UP_DONE = BIT(0), >> + >> + /* PCTL_SCTL */ >> + INIT_STATE = 0, >> + CFG_STATE, >> + GO_STATE, >> + SLEEP_STATE, >> + WAKEUP_STATE, >> + >> + /* PCTL_STAT */ >> + LP_TRIG_SHIFT = 4, >> + LP_TRIG_MASK = 7, >> + PCTL_STAT_MSK = 7, >> + INIT_MEM = 0, >> + CONFIG, >> + CONFIG_REQ, >> + ACCESS, >> + ACCESS_REQ, >> + LOW_POWER, >> + LOW_POWER_ENTRY_REQ, >> + LOW_POWER_EXIT_REQ, >> + >> + /* PCTL_MCFG */ >> + DDR2_DDR3_BL_8 = BIT(0), >> + DDR3_EN = BIT(5), >> + TFAW_TRRD_MULT4 = (0 << 18), >> + TFAW_TRRD_MULT5 = (1 << 18), >> + TFAW_TRRD_MULT6 = (2 << 18), >> +}; >> + >> +#define DDR3_MR0_WR(n) \ >> + ((n <= 8) ? ((n - 4) << 9) : (((n >> 1) & 0x7) << 9)) >> +#define DDR3_MR0_CL(n) \ >> + ((((n - 4) & 0x7) << 4) | (((n - 4) & 0x8) >> 2)) >> +#define DDR3_MR0_BL8 \ >> + (0 << 0) >> +#define DDR3_MR0_DLL_RESET \ >> + (1 << 8) >> +#define DDR3_MR1_RTT120OHM \ >> + ((0 << 9) | (1 << 6) | (0 << 2)) >> +#define DDR3_MR2_TWL(n) \ >> + (((n - 5) & 0x7) << 3) >> + >> + >> +#ifdef CONFIG_TPL_BUILD >> + >> +static void ddr_set_noc_spr_err_stall(struct rk3368_grf *grf, bool enable) >> +{ >> + if (enable) >> + rk_setreg(&grf->ddrc0_con0, NOC_RSP_ERR_STALL); >> + else >> + rk_clrreg(&grf->ddrc0_con0, NOC_RSP_ERR_STALL); >> +} >> + >> +static void ddr_set_ddr3_mode(struct rk3368_grf *grf, bool ddr3_mode) >> +{ >> + if (ddr3_mode) >> + rk_setreg(&grf->ddrc0_con0, MSCH0_MAINDDR3_DDR3); >> + else >> + rk_clrreg(&grf->ddrc0_con0, MSCH0_MAINDDR3_DDR3); >> +} >> + >> +static void ddrphy_config(struct rk3368_ddrphy *phy, >> + u32 tcl, u32 tal, u32 tcwl) >> +{ >> + int i; >> + >> + /* Set to DDR3 mode */ >> + clrsetbits_le32(&phy->reg[1], 0x3, 0x0); >> + >> + /* DDRPHY_REGB: CL, AL */ >> + clrsetbits_le32(&phy->reg[0xb], 0xff, tcl << 4 | tal); >> + /* DDRPHY_REGC: CWL */ >> + clrsetbits_le32(&phy->reg[0xc], 0x0f, tcwl); >> + >> + /* Update drive-strength */ >> + writel(0xcc, &phy->reg[0x11]); >> + writel(0xaa, &phy->reg[0x16]); >> + /* >> + * Update NRCOMP/PRCOMP for all 4 channels (for details of all >> + * affected registers refer to the documentation of DDRPHY_REG20 >> + * and DDRPHY_REG21 in the RK3368 TRM. >> + */ >> + for (i = 0; i < 4; ++i) { >> + writel(0xcc, &phy->reg[0x20 + i * 0x10]); >> + writel(0x44, &phy->reg[0x21 + i * 0x10]); >> + } >> + >> + /* Enable write-leveling calibration bypass */ >> + setbits_le32(&phy->reg[2], BIT(3)); >> +} >> + >> +static void copy_to_reg(u32 *dest, const u32 *src, u32 n) >> +{ >> + int i; >> + >> + for (i = 0; i < n / sizeof(u32); i++) >> + writel(*src++, dest++); >> +} >> + >> +static void send_command(struct rk3368_ddr_pctl *pctl, u32 rank, u32 cmd) >> +{ >> + u32 mcmd = START_CMD | cmd | rank; >> + >> + debug("%s: writing %x to MCMD\n", __func__, mcmd); >> + writel(mcmd, &pctl->mcmd); >> + while (readl(&pctl->mcmd) & START_CMD) >> + /* spin */; >> +} >> + >> +static void send_mrs(struct rk3368_ddr_pctl *pctl, >> + u32 rank, u32 mr_num, u32 mr_data) >> +{ >> + u32 mcmd = START_CMD | MRS_CMD | rank | (mr_num << 17) | (mr_data << 4); >> + >> + debug("%s: writing %x to MCMD\n", __func__, mcmd); >> + writel(mcmd, &pctl->mcmd); >> + while (readl(&pctl->mcmd) & START_CMD) >> + /* spin */; >> +} >> + >> +static int memory_init(struct rk3368_ddr_pctl *pctl, >> + struct rk3368_sdram_params *params) >> +{ >> + u32 mr[4]; >> + const ulong timeout_ms = 500; >> + ulong tmp; >> + >> + /* >> + * Power up DRAM by DDR_PCTL_POWCTL[0] register of PCTL and >> + * wait power up DRAM finish with DDR_PCTL_POWSTAT[0] register >> + * of PCTL. >> + */ >> + writel(POWER_UP_START, &pctl->powctl); >> + >> + tmp = get_timer(0); >> + do { >> + if (get_timer(tmp) > timeout_ms) { >> + error("%s: POWER_UP_START did not complete in %ld ms\n", >> + __func__, timeout_ms); >> + return -ETIME; >> + } >> + } while (!(readl(&pctl->powstat) & POWER_UP_DONE)); >> + >> + /* Configure MR0 through MR3 */ >> + mr[0] = DDR3_MR0_WR(params->pctl_timing.twr) | >> + DDR3_MR0_CL(params->pctl_timing.tcl) | >> + DDR3_MR0_DLL_RESET; >> + mr[1] = DDR3_MR1_RTT120OHM; >> + mr[2] = DDR3_MR2_TWL(params->pctl_timing.tcwl); >> + mr[3] = 0; >> + >> + /* >> + * Also see RK3368 Technical Reference Manual: >> + * "16.6.2 Initialization (DDR3 Initialization Sequence)" >> + */ >> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, DESELECT_CMD); >> + udelay(1); >> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, PREA_CMD); >> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 2, mr[2]); >> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 3, mr[3]); >> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 1, mr[1]); >> + send_mrs(pctl, MCMD_RANK0 | MCMD_RANK1, 0, mr[0]); >> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, ZQCL_CMD); >> + >> + return 0; >> +} >> + >> +static void move_to_config_state(struct rk3368_ddr_pctl *pctl) >> +{ >> + /* >> + * Also see RK3368 Technical Reference Manual: >> + * "16.6.1 State transition of PCTL (Moving to Config State)" >> + */ >> + u32 state = readl(&pctl->stat) & PCTL_STAT_MSK; >> + >> + switch (state) { >> + case LOW_POWER: >> + writel(WAKEUP_STATE, &pctl->sctl); >> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS) >> + /* spin */; >> + >> + /* fall-through */ >> + case ACCESS: >> + case INIT_MEM: >> + writel(CFG_STATE, &pctl->sctl); >> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG) >> + /* spin */; >> + break; >> + >> + case CONFIG: >> + return; >> + >> + default: >> + break; >> + } >> +} >> + >> +static void move_to_access_state(struct rk3368_ddr_pctl *pctl) >> +{ >> + /* >> + * Also see RK3368 Technical Reference Manual: >> + * "16.6.1 State transition of PCTL (Moving to Access State)" >> + */ >> + u32 state = readl(&pctl->stat) & PCTL_STAT_MSK; >> + >> + switch (state) { >> + case LOW_POWER: >> + if (((readl(&pctl->stat) >> LP_TRIG_SHIFT) & >> + LP_TRIG_MASK) == 1) >> + return; >> + >> + writel(WAKEUP_STATE, &pctl->sctl); >> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != ACCESS) >> + /* spin */; >> + >> + /* fall-through */ >> + case INIT_MEM: >> + writel(CFG_STATE, &pctl->sctl); >> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) != CONFIG) >> + /* spin */; >> + >> + /* fall-through */ >> + case CONFIG: >> + writel(GO_STATE, &pctl->sctl); >> + while ((readl(&pctl->stat) & PCTL_STAT_MSK) == CONFIG) >> + /* spin */; >> + break; >> + >> + case ACCESS: >> + return; >> + >> + default: >> + break; >> + } >> +} >> + >> +static void ddrctl_reset(struct rk3368_cru *cru) >> +{ >> + const u32 ctl_reset = BIT(3) | BIT(2); >> + const u32 phy_reset = BIT(1) | BIT(0); >> + >> + rk_setreg(&cru->softrst_con[10], ctl_reset | phy_reset); >> + udelay(1); >> + rk_clrreg(&cru->softrst_con[10], ctl_reset | phy_reset); >> +} >> + >> +static void ddrphy_reset(struct rk3368_ddrphy *ddrphy) >> +{ >> + clrbits_le32(&ddrphy->reg[0], BIT(3) | BIT(2)); >> + udelay(1); >> + setbits_le32(&ddrphy->reg[0], BIT(3) | BIT(2)); >> +} >> + >> +static void ddrphy_config_delays(struct rk3368_ddrphy *ddrphy, u32 freq) >> +{ >> + u32 dqs_dll_delay; >> + >> + setbits_le32(&ddrphy->reg[0x13], BIT(4)); >> + clrbits_le32(&ddrphy->reg[0x14], BIT(3)); >> + >> + setbits_le32(&ddrphy->reg[0x26], BIT(4)); >> + clrbits_le32(&ddrphy->reg[0x27], BIT(3)); >> + >> + setbits_le32(&ddrphy->reg[0x36], BIT(4)); >> + clrbits_le32(&ddrphy->reg[0x37], BIT(3)); >> + >> + setbits_le32(&ddrphy->reg[0x46], BIT(4)); >> + clrbits_le32(&ddrphy->reg[0x47], BIT(3)); >> + >> + setbits_le32(&ddrphy->reg[0x56], BIT(4)); >> + clrbits_le32(&ddrphy->reg[0x57], BIT(3)); >> + >> + if (freq <= 400000000) >> + setbits_le32(&ddrphy->reg[0xa4], 0x1f); >> + else >> + clrbits_le32(&ddrphy->reg[0xa4], 0x1f); >> + >> + if (freq < 681000000) >> + dqs_dll_delay = 3; /* 67.5 degree delay */ >> + else >> + dqs_dll_delay = 2; /* 45 degree delay */ >> + >> + writel(dqs_dll_delay, &ddrphy->reg[0x28]); >> + writel(dqs_dll_delay, &ddrphy->reg[0x38]); >> + writel(dqs_dll_delay, &ddrphy->reg[0x48]); >> + writel(dqs_dll_delay, &ddrphy->reg[0x58]); >> +} >> + >> +static int dfi_cfg(struct rk3368_ddr_pctl *pctl) >> +{ >> + const ulong timeout_ms = 200; >> + ulong tmp; >> + >> + writel(DFI_DATA_BYTE_DISABLE_EN, &pctl->dfistcfg0); >> + >> + writel(DFI_DRAM_CLK_SR_EN | DFI_DRAM_CLK_DPD_EN, >> + &pctl->dfistcfg1); >> + writel(DFI_PARITY_INTR_EN | DFI_PARITY_EN, &pctl->dfistcfg2); >> + writel(7 << TLP_RESP_TIME_SHIFT | LP_SR_EN | LP_PD_EN, >> + &pctl->dfilpcfg0); >> + >> + writel(1, &pctl->dfitphyupdtype0); >> + >> + writel(0x1f, &pctl->dfitphyrdlat); >> + writel(0, &pctl->dfitphywrdata); >> + writel(0, &pctl->dfiupdcfg); /* phyupd and ctrlupd disabled */ >> + >> + setbits_le32(&pctl->dfistcfg0, DFI_INIT_START); >> + >> + tmp = get_timer(0); >> + do { >> + if (get_timer(tmp) > timeout_ms) { >> + error("%s: DFI init did not complete within %ld ms\n", >> + __func__, timeout_ms); >> + return -ETIME; >> + } >> + } while ((readl(&pctl->dfiststat0) & 1) == 0); >> + >> + return 0; >> +} >> + >> +static inline u32 ps_to_tCK(const u32 ps, const ulong freq) >> +{ >> + const ulong MHz = 1000000; >> + return DIV_ROUND_UP(ps * freq, 1000000 * MHz); >> +} >> + >> +static inline u32 ns_to_tCK(const u32 ns, const ulong freq) >> +{ >> + return ps_to_tCK(ns * 1000, freq); >> +} >> + >> +static inline u32 tCK_to_ps(const ulong tCK, const ulong freq) >> +{ >> + const ulong MHz = 1000000; >> + return DIV_ROUND_UP(tCK * 1000000 * MHz, freq); >> +} >> + >> +static int pctl_calc_timings(struct rk3368_sdram_params *params, >> + ulong freq) >> +{ >> + struct rk3288_sdram_pctl_timing *pctl_timing = ¶ms->pctl_timing; >> + const ulong MHz = 1000000; >> + u32 tccd; >> + u32 tfaw_as_ps; >> + >> + if (params->ddr_speed_bin != DDR3_1600K) { >> + error("%s: unimplemented DDR3 speed bin %d\n", >> + __func__, params->ddr_speed_bin); >> + return -1; >> + } >> + >> + /* PCTL is clocked at 1/2 the DRAM clock; err on the side of caution */ >> + pctl_timing->togcnt1u = DIV_ROUND_UP(freq, 2 * MHz); >> + pctl_timing->togcnt100n = DIV_ROUND_UP(freq / 10, 2 * MHz); >> + >> + pctl_timing->tinit = 200; /* 200 usec */ >> + pctl_timing->trsth = 500; /* 500 usec */ >> + pctl_timing->trefi = 78; /* 7.8usec = 78 * 100ns */ >> + params->trefi_mem_ddr3 = ns_to_tCK(pctl_timing->trefi * 100, freq); >> + >> + if (freq <= (400 * MHz)) { >> + pctl_timing->tcl = 6; >> + pctl_timing->tcwl = 10; >> + } else if (freq <= (533 * MHz)) { >> + pctl_timing->tcl = 8; >> + pctl_timing->tcwl = 6; >> + } else if (freq <= (666 * MHz)) { >> + pctl_timing->tcl = 10; >> + pctl_timing->tcwl = 7; >> + } else { >> + pctl_timing->tcl = 11; >> + pctl_timing->tcwl = 8; >> + } >> + >> + pctl_timing->tmrd = 4; /* 4 tCK (all speed bins) */ >> + pctl_timing->trfc = ns_to_tCK(350, freq); /* tRFC: 350 (max) @ 8GBit */ >> + pctl_timing->trp = max(4u, ps_to_tCK(13750, freq)); >> + /* >> + * JESD-79: >> + * READ to WRITE Command Delay = RL + tCCD / 2 + 2tCK - WL >> + */ >> + tccd = 4; >> + pctl_timing->trtw = pctl_timing->tcl + tccd/2 + 2 - pctl_timing->tcwl; >> + pctl_timing->tal = 0; >> + pctl_timing->tras = ps_to_tCK(35000, freq); >> + pctl_timing->trc = ps_to_tCK(48750, freq); >> + pctl_timing->trcd = ps_to_tCK(13750, freq); >> + pctl_timing->trrd = max(4u, ps_to_tCK(7500, freq)); >> + pctl_timing->trtp = max(4u, ps_to_tCK(7500, freq)); >> + pctl_timing->twr = ps_to_tCK(15000, freq); >> + /* The DDR3 mode-register does only support even values for tWR > 8. */ >> + if (pctl_timing->twr > 8) >> + pctl_timing->twr = (pctl_timing->twr + 1) & ~1; >> + pctl_timing->twtr = max(4u, ps_to_tCK(7500, freq)); >> + pctl_timing->texsr = 512; /* tEXSR(max) is tDLLLK */ >> + pctl_timing->txp = max(3u, ps_to_tCK(6000, freq)); >> + pctl_timing->txpdll = max(10u, ps_to_tCK(24000, freq)); >> + pctl_timing->tzqcs = max(64u, ps_to_tCK(80000, freq)); >> + pctl_timing->tzqcsi = 10000; /* as used by Rockchip */ >> + pctl_timing->tdqs = 1; /* fixed for DDR3 */ >> + pctl_timing->tcksre = max(5u, ps_to_tCK(10000, freq)); >> + pctl_timing->tcksrx = max(5u, ps_to_tCK(10000, freq)); >> + pctl_timing->tcke = max(3u, ps_to_tCK(5000, freq)); >> + pctl_timing->tmod = max(12u, ps_to_tCK(15000, freq)); >> + pctl_timing->trstl = ns_to_tCK(100, freq); >> + pctl_timing->tzqcl = max(256u, ps_to_tCK(320000, freq)); /* tZQoper */ >> + pctl_timing->tmrr = 0; >> + pctl_timing->tckesr = pctl_timing->tcke + 1; /* JESD-79: tCKE + 1tCK */ >> + pctl_timing->tdpd = 0; /* RK3368 TRM: "allowed values for DDR3: 0" */ >> + >> + >> + /* >> + * The controller can represent tFAW as 4x, 5x or 6x tRRD only. >> + * We want to use the smallest multiplier that satisfies the tFAW >> + * requirements of the given speed-bin. If necessary, we stretch out >> + * tRRD to allow us to operate on a 6x multiplier for tFAW. >> + */ >> + tfaw_as_ps = 40000; /* 40ns: tFAW for DDR3-1600K, 2KB page-size */ >> + if (tCK_to_ps(pctl_timing->trrd * 6, freq) < tfaw_as_ps) { >> + /* If tFAW is > 6 x tRRD, we need to stretch tRRD */ >> + pctl_timing->trrd = ps_to_tCK(DIV_ROUND_UP(40000, 6), freq); >> + params->tfaw_mult = TFAW_TRRD_MULT6; >> + } else if (tCK_to_ps(pctl_timing->trrd * 5, freq) < tfaw_as_ps) { >> + params->tfaw_mult = TFAW_TRRD_MULT6; >> + } else if (tCK_to_ps(pctl_timing->trrd * 4, freq) < tfaw_as_ps) { >> + params->tfaw_mult = TFAW_TRRD_MULT5; >> + } else { >> + params->tfaw_mult = TFAW_TRRD_MULT4; >> + } >> + >> + return 0; >> +} >> + >> +static void pctl_cfg(struct rk3368_ddr_pctl *pctl, >> + struct rk3368_sdram_params *params, >> + struct rk3368_grf *grf) >> +{ >> + /* Configure PCTL timing registers */ >> + params->pctl_timing.trefi |= BIT(31); /* see PCTL_TREFI */ >> + copy_to_reg(&pctl->togcnt1u, ¶ms->pctl_timing.togcnt1u, >> + sizeof(params->pctl_timing)); >> + writel(params->trefi_mem_ddr3, &pctl->trefi_mem_ddr3); >> + >> + /* Set up ODT write selector and ODT write length */ >> + writel((RANK0_ODT_WRITE_SEL | RANK1_ODT_WRITE_SEL), &pctl->dfiodtcfg); >> + writel(7 << ODT_LEN_BL8_W_SHIFT, &pctl->dfiodtcfg1); >> + >> + /* Set up the CL/CWL-dependent timings of DFI */ >> + writel((params->pctl_timing.tcl - 1) / 2 - 1, &pctl->dfitrddataen); >> + writel((params->pctl_timing.tcwl - 1) / 2 - 1, &pctl->dfitphywrlat); >> + >> + /* DDR3 */ >> + writel(params->tfaw_mult | DDR3_EN | DDR2_DDR3_BL_8, &pctl->mcfg); >> + writel(0x001c0004, &grf->ddrc0_con0); >> + >> + setbits_le32(&pctl->scfg, HW_LOW_POWER_EN); >> +} >> + >> +static int ddrphy_data_training(struct rk3368_ddr_pctl *pctl, >> + struct rk3368_ddrphy *ddrphy) >> +{ >> + const u32 trefi = readl(&pctl->trefi); >> + const ulong timeout_ms = 500; >> + ulong tmp; >> + >> + /* disable auto-refresh */ >> + writel(0 | BIT(31), &pctl->trefi); >> + >> + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x20); >> + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x21); >> + >> + tmp = get_timer(0); >> + do { >> + if (get_timer(tmp) > timeout_ms) { >> + error("%s: did not complete within %ld ms\n", >> + __func__, timeout_ms); >> + return -ETIME; >> + } >> + } while ((readl(&ddrphy->reg[0xff]) & 0xf) != 0xf); >> + >> + send_command(pctl, MCMD_RANK0 | MCMD_RANK1, PREA_CMD); >> + clrsetbits_le32(&ddrphy->reg[2], 0x33, 0x20); >> + /* resume auto-refresh */ >> + writel(trefi | BIT(31), &pctl->trefi); >> + >> + return 0; >> +} >> + >> +static int sdram_col_row_detect(struct udevice *dev) >> +{ >> + struct dram_info *priv = dev_get_priv(dev); >> + struct rk3368_sdram_params *params = dev_get_platdata(dev); >> + struct rk3368_ddr_pctl *pctl = priv->pctl; >> + struct rk3368_msch *msch = priv->msch; >> + const u32 test_pattern = 0x5aa5f00f; >> + int row, col; >> + uintptr_t addr; >> + >> + move_to_config_state(pctl); >> + writel(6, &msch->ddrconf); >> + move_to_access_state(pctl); >> + >> + /* Detect col */ >> + for (col = 11; col >= 9; col--) { >> + writel(0, CONFIG_SYS_SDRAM_BASE); >> + addr = CONFIG_SYS_SDRAM_BASE + >> + (1 << (col + params->chan.bw - 1)); >> + writel(test_pattern, addr); >> + if ((readl(addr) == test_pattern) && >> + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) >> + break; >> + } >> + >> + if (col == 8) { >> + error("%s: col detect error\n", __func__); >> + return -EINVAL; >> + } >> + >> + move_to_config_state(pctl); >> + writel(15, &msch->ddrconf); >> + move_to_access_state(pctl); >> + >> + /* Detect row*/ >> + for (row = 16; row >= 12; row--) { >> + writel(0, CONFIG_SYS_SDRAM_BASE); >> + addr = CONFIG_SYS_SDRAM_BASE + (1 << (row + 15 - 1)); >> + writel(test_pattern, addr); >> + if ((readl(addr) == test_pattern) && >> + (readl(CONFIG_SYS_SDRAM_BASE) == 0)) >> + break; >> + } >> + >> + if (row == 11) { >> + error("%s: row detect error\n", __func__); >> + return -EINVAL; >> + } >> + >> + /* Record results */ >> + debug("%s: col %d, row %d\n", __func__, col, row); >> + params->chan.col = col; >> + params->chan.cs0_row = row; >> + params->chan.cs1_row = row; >> + params->chan.row_3_4 = 0; >> + >> + return 0; >> +} >> + >> +static int msch_niu_config(struct rk3368_msch *msch, >> + struct rk3368_sdram_params *params) >> +{ >> + int i; >> + const u8 cols = params->chan.col - ((params->chan.bw == 2) ? 0 : 1); >> + const u8 rows = params->chan.cs0_row; >> + >> + /* >> + * The DDR address-translation table always assumes a 32bit >> + * bus and the comparison below takes care of adjusting for >> + * a 16bit bus (i.e. one column-address is consumed). >> + */ >> + const struct { >> + u8 rows; >> + u8 columns; >> + u8 type; >> + } ddrconf_table[] = { >> + /* >> + * C-B-R-D patterns are first. For these we require an >> + * exact match for the columns and rows (as there's >> + * one entry per possible configuration). >> + */ >> + [0] = { .rows = 13, .columns = 10, .type = DMC_MSCH_CBRD }, >> + [1] = { .rows = 14, .columns = 10, .type = DMC_MSCH_CBRD }, >> + [2] = { .rows = 15, .columns = 10, .type = DMC_MSCH_CBRD }, >> + [3] = { .rows = 16, .columns = 10, .type = DMC_MSCH_CBRD }, >> + [4] = { .rows = 14, .columns = 11, .type = DMC_MSCH_CBRD }, >> + [5] = { .rows = 15, .columns = 11, .type = DMC_MSCH_CBRD }, >> + [6] = { .rows = 16, .columns = 11, .type = DMC_MSCH_CBRD }, >> + [7] = { .rows = 13, .columns = 9, .type = DMC_MSCH_CBRD }, >> + [8] = { .rows = 14, .columns = 9, .type = DMC_MSCH_CBRD }, >> + [9] = { .rows = 15, .columns = 9, .type = DMC_MSCH_CBRD }, >> + [10] = { .rows = 16, .columns = 9, .type = DMC_MSCH_CBRD }, >> + /* >> + * 11 through 13 are C-R-B-D patterns. These are >> + * matched for an exact number of columns and to >> + * ensure that the hardware uses at least as many rows >> + * as the pattern requires (i.e. we make sure that >> + * there's no gaps up until we hit the device/chip-select; >> + * however, these patterns can accept up to 16 rows, >> + * as the row-address continues right after the CS >> + * switching) >> + */ >> + [11] = { .rows = 15, .columns = 10, .type = DMC_MSCH_CRBD }, >> + [12] = { .rows = 14, .columns = 11, .type = DMC_MSCH_CRBD }, >> + [13] = { .rows = 13, .columns = 10, .type = DMC_MSCH_CRBD }, >> + /* >> + * 14 and 15 are catch-all variants using a C-B-D-R >> + * scheme (i.e. alternating the chip-select every time >> + * C-B overflows) and stuffing the remaining C-bits >> + * into the top. Matching needs to make sure that the >> + * number of columns is either an exact match (i.e. we >> + * can use less the the maximum number of rows) -or- >> + * that the columns exceed what is given in this table >> + * and the rows are an exact match (in which case the >> + * remaining C-bits will be stuffed onto the top after >> + * the device/chip-select switches). >> + */ >> + [14] = { .rows = 16, .columns = 10, .type = DMC_MSCH_CBDR }, >> + [15] = { .rows = 16, .columns = 9, .type = DMC_MSCH_CBDR }, >> + }; >> + >> + /* >> + * For C-B-R-D, we need an exact match (i.e. both for the number of >> + * columns and rows), while for C-B-D-R, only the the number of >> + * columns needs to match. >> + */ >> + for (i = 0; i < ARRAY_SIZE(ddrconf_table); i++) { >> + bool match = false; >> + >> + /* If this entry if for a different matcher, then skip it */ >> + if (ddrconf_table[i].type != params->memory_schedule) >> + continue; >> + >> + /* >> + * Match according to the rules (exact/inexact/at-least) >> + * documented in the ddrconf_table above. >> + */ >> + switch (params->memory_schedule) { >> + case DMC_MSCH_CBRD: >> + match = (ddrconf_table[i].columns == cols) && >> + (ddrconf_table[i].rows == rows); >> + break; >> + >> + case DMC_MSCH_CRBD: >> + match = (ddrconf_table[i].columns == cols) && >> + (ddrconf_table[i].rows <= rows); >> + break; >> + >> + case DMC_MSCH_CBDR: >> + match = (ddrconf_table[i].columns == cols) || >> + ((ddrconf_table[i].columns <= cols) && >> + (ddrconf_table[i].rows == rows)); >> + break; >> + >> + default: >> + break; >> + } >> + >> + if (match) { >> + debug("%s: setting ddrconf 0x%x\n", __func__, i); >> + writel(i, &msch->ddrconf); >> + return 0; >> + } >> + } >> + >> + error("%s: ddrconf (NIU config) not found\n", __func__); >> + return -EINVAL; >> +} >> + >> +static void dram_all_config(struct udevice *dev) >> +{ >> + struct dram_info *priv = dev_get_priv(dev); >> + struct rk3368_pmu_grf *pmugrf = priv->pmugrf; >> + struct rk3368_sdram_params *params = dev_get_platdata(dev); >> + const struct rk3288_sdram_channel *info = ¶ms->chan; >> + u32 sys_reg = 0; >> + const int chan = 0; >> + >> + sys_reg |= DDR3 << SYS_REG_DDRTYPE_SHIFT; >> + sys_reg |= 0 << SYS_REG_NUM_CH_SHIFT; >> + >> + sys_reg |= info->row_3_4 << SYS_REG_ROW_3_4_SHIFT(chan); >> + sys_reg |= 1 << SYS_REG_CHINFO_SHIFT(chan); >> + sys_reg |= (info->rank - 1) << SYS_REG_RANK_SHIFT(chan); >> + sys_reg |= (info->col - 9) << SYS_REG_COL_SHIFT(chan); >> + sys_reg |= info->bk == 3 ? 0 : 1 << SYS_REG_BK_SHIFT(chan); >> + sys_reg |= (info->cs0_row - 13) << SYS_REG_CS0_ROW_SHIFT(chan); >> + sys_reg |= (info->cs1_row - 13) << SYS_REG_CS1_ROW_SHIFT(chan); >> + sys_reg |= (2 >> info->bw) << SYS_REG_BW_SHIFT(chan); >> + sys_reg |= (2 >> info->dbw) << SYS_REG_DBW_SHIFT(chan); >> + >> + writel(sys_reg, &pmugrf->os_reg[2]); >> +} >> + >> +static int setup_sdram(struct udevice *dev) >> +{ >> + struct dram_info *priv = dev_get_priv(dev); >> + struct rk3368_sdram_params *params = dev_get_platdata(dev); >> + >> + struct rk3368_ddr_pctl *pctl = priv->pctl; >> + struct rk3368_ddrphy *ddrphy = priv->phy; >> + struct rk3368_cru *cru = priv->cru; >> + struct rk3368_grf *grf = priv->grf; >> + struct rk3368_msch *msch = priv->msch; >> + >> + int ret; >> + >> + /* The input clock (i.e. DPLL) needs to be 2x the DRAM frequency */ >> + ret = clk_set_rate(&priv->ddr_clk, 2 * params->ddr_freq); >> + if (ret < 0) { >> + debug("%s: could not set DDR clock: %d\n", __func__, ret); >> + return ret; >> + } >> + >> + /* Update the read-latency for the RK3368 */ >> + writel(0x32, &msch->readlatency); >> + >> + /* Initialise the DDR PCTL and DDR PHY */ >> + ddrctl_reset(cru); >> + ddrphy_reset(ddrphy); >> + ddrphy_config_delays(ddrphy, params->ddr_freq); >> + dfi_cfg(pctl); >> + /* Configure relative system information of grf_ddrc0_con0 register */ >> + ddr_set_ddr3_mode(grf, true); >> + ddr_set_noc_spr_err_stall(grf, true); >> + /* Calculate timings */ >> + pctl_calc_timings(params, params->ddr_freq); >> + /* Initialise the device timings in protocol controller */ >> + pctl_cfg(pctl, params, grf); >> + /* Configure AL, CL ... information of PHY registers */ >> + ddrphy_config(ddrphy, >> + params->pctl_timing.tcl, >> + params->pctl_timing.tal, >> + params->pctl_timing.tcwl); >> + >> + /* Initialize DRAM and configure with mode-register values */ >> + ret = memory_init(pctl, params); >> + if (ret) >> + goto error; >> + >> + move_to_config_state(pctl); >> + /* Perform data-training */ >> + ddrphy_data_training(pctl, ddrphy); >> + move_to_access_state(pctl); >> + >> + /* TODO(prt): could detect rank in training... */ >> + params->chan.rank = 2; >> + /* TODO(prt): bus width is not auto-detected (yet)... */ >> + params->chan.bw = 2; /* 32bit wide bus */ >> + params->chan.dbw = params->chan.dbw; /* 32bit wide bus */ >> + >> + /* DDR3 is always 8 bank */ >> + params->chan.bk = 3; >> + /* Detect col and row number */ >> + ret = sdram_col_row_detect(dev); >> + if (ret) >> + goto error; >> + >> + /* Configure NIU DDR configuration */ >> + ret = msch_niu_config(msch, params); >> + if (ret) >> + goto error; >> + >> + /* set up OS_REG to communicate w/ next stage and OS */ >> + dram_all_config(dev); >> + >> + return 0; >> + >> +error: >> + printf("DRAM init failed!\n"); >> + hang(); >> +} >> +#endif >> + >> +static int rk3368_dmc_ofdata_to_platdata(struct udevice *dev) >> +{ >> + int ret = 0; >> + >> +#if !CONFIG_IS_ENABLED(OF_PLATDATA) >> + struct rk3368_sdram_params *plat = dev_get_platdata(dev); >> + >> + ret = regmap_init_mem(dev, &plat->map); >> + if (ret) >> + return ret; >> +#endif >> + >> + return ret; >> +} >> + >> +#if CONFIG_IS_ENABLED(OF_PLATDATA) >> +static int conv_of_platdata(struct udevice *dev) >> +{ >> + struct rk3368_sdram_params *plat = dev_get_platdata(dev); >> + struct dtd_rockchip_rk3368_dmc *of_plat = &plat->of_plat; >> + int ret; >> + >> + plat->ddr_freq = of_plat->rockchip_ddr_frequency; >> + plat->ddr_speed_bin = of_plat->rockchip_ddr_speed_bin; >> + plat->memory_schedule = of_plat->rockchip_memory_schedule; >> + >> + ret = regmap_init_mem_platdata(dev, of_plat->reg, >> + ARRAY_SIZE(of_plat->reg) / 2, >> + &plat->map); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> +#endif >> + >> +static int rk3368_dmc_probe(struct udevice *dev) >> +{ >> +#ifdef CONFIG_TPL_BUILD >> + struct rk3368_sdram_params *plat = dev_get_platdata(dev); >> + struct rk3368_ddr_pctl *pctl; >> + struct rk3368_ddrphy *ddrphy; >> + struct rk3368_cru *cru; >> + struct rk3368_grf *grf; >> + struct rk3368_msch *msch; >> + int ret; >> + struct udevice *dev_clk; >> +#endif >> + struct dram_info *priv = dev_get_priv(dev); >> + >> +#if CONFIG_IS_ENABLED(OF_PLATDATA) >> + ret = conv_of_platdata(dev); >> + if (ret) >> + return ret; >> +#endif >> + >> + priv->pmugrf = syscon_get_first_range(ROCKCHIP_SYSCON_PMUGRF); >> + debug("%s: pmugrf=%p\n", __func__, priv->pmugrf); >> + >> +#ifdef CONFIG_TPL_BUILD >> + pctl = regmap_get_range(plat->map, 0); >> + ddrphy = regmap_get_range(plat->map, 1); >> + msch = syscon_get_first_range(ROCKCHIP_SYSCON_MSCH); >> + grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); >> + >> + priv->pctl = pctl; >> + priv->phy = ddrphy; >> + priv->msch = msch; >> + priv->grf = grf; >> + >> + ret = rockchip_get_clk(&dev_clk); >> + if (ret) >> + return ret; >> + priv->ddr_clk.id = CLK_DDR; >> + ret = clk_request(dev_clk, &priv->ddr_clk); >> + if (ret) >> + return ret; >> + >> + cru = rockchip_get_cru(); >> + priv->cru = cru; >> + if (IS_ERR(priv->cru)) >> + return PTR_ERR(priv->cru); >> + >> + ret = setup_sdram(dev); >> + if (ret) >> + return ret; >> +#endif >> + >> + priv->info.base = 0; >> + priv->info.size = >> + rockchip_sdram_size((phys_addr_t)&priv->pmugrf->os_reg[2]); >> + >> + /* >> + * we use the 0x00000000~0xfdffffff space since 0xff000000~0xffffffff >> + * is SoC register space (i.e. reserved), and 0xfe000000~0xfeffffff is >> + * inaccessible for some IP controller. >> + */ >> + priv->info.size = min(priv->info.size, (size_t)0xfe000000); >> + >> + return 0; >> +} >> + >> +static int rk3368_dmc_get_info(struct udevice *dev, struct ram_info *info) >> +{ >> + struct dram_info *priv = dev_get_priv(dev); >> + >> + *info = priv->info; >> + return 0; >> +} >> + >> +static struct ram_ops rk3368_dmc_ops = { >> + .get_info = rk3368_dmc_get_info, >> +}; >> + >> + >> +static const struct udevice_id rk3368_dmc_ids[] = { >> + { .compatible = "rockchip,rk3368-dmc" }, >> + { } >> +}; >> + >> +U_BOOT_DRIVER(dmc_rk3368) = { >> + .name = "rockchip_rk3368_dmc", >> + .id = UCLASS_RAM, >> + .of_match = rk3368_dmc_ids, >> + .ops = &rk3368_dmc_ops, >> + .probe = rk3368_dmc_probe, >> + .priv_auto_alloc_size = sizeof(struct dram_info), >> + .ofdata_to_platdata = rk3368_dmc_ofdata_to_platdata, >> + .probe = rk3368_dmc_probe, >> + .priv_auto_alloc_size = sizeof(struct dram_info), >> + .platdata_auto_alloc_size = sizeof(struct rk3368_sdram_params), >> +}; >> diff --git a/include/dt-bindings/memory/rk3368-dmc.h >> b/include/dt-bindings/memory/rk3368-dmc.h >> new file mode 100644 >> index 0000000..b06ffde >> --- /dev/null >> +++ b/include/dt-bindings/memory/rk3368-dmc.h >> @@ -0,0 +1,30 @@ >> +#ifndef DT_BINDINGS_RK3368_DMC_H >> +#define DT_BINDINGS_RK3368_DMC_H >> + >> +#define DMC_MSCH_CBDR 0x0 >> +#define DMC_MSCH_CBRD 0x1 >> +#define DMC_MSCH_CRBD 0x2 >> + >> +#define DDR3_800D 0 >> +#define DDR3_800E 1 >> +#define DDR3_1066E 2 >> +#define DDR3_1066F 3 >> +#define DDR3_1066G 4 >> +#define DDR3_1333F 5 >> +#define DDR3_1333G 6 >> +#define DDR3_1333H 7 >> +#define DDR3_1333J 8 >> +#define DDR3_1600G 9 >> +#define DDR3_1600H 10 >> +#define DDR3_1600J 11 >> +#define DDR3_1600K 12 >> +#define DDR3_1866J 13 >> +#define DDR3_1866K 14 >> +#define DDR3_1866L 15 >> +#define DDR3_1866M 16 >> +#define DDR3_2133K 17 >> +#define DDR3_2133L 18 >> +#define DDR3_2133M 19 >> +#define DDR3_2133N 20 >> + >> +#endif _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot