[U-Boot] [PATCH v2 05/13] drivers: ddr: Add DDR2 SDRAM controller driver for Microchip PIC32.

Signed-off-by: Paul Thacker paul.thacker@microchip.com Signed-off-by: Purna Chandra Mandal purna.mandal@microchip.com
---
Changes in v2: - move ddr2 initialization from board/microchip/ to drivers/ddr/microchip
arch/mips/mach-pic32/include/mach/ddr.h | 32 ++++ drivers/Makefile | 1 + drivers/ddr/microchip/Makefile | 6 + drivers/ddr/microchip/ddr2.c | 277 ++++++++++++++++++++++++++++++++ drivers/ddr/microchip/ddr2_regs.h | 151 +++++++++++++++++ drivers/ddr/microchip/ddr2_timing.h | 65 ++++++++ 6 files changed, 532 insertions(+) create mode 100644 arch/mips/mach-pic32/include/mach/ddr.h create mode 100644 drivers/ddr/microchip/Makefile create mode 100644 drivers/ddr/microchip/ddr2.c create mode 100644 drivers/ddr/microchip/ddr2_regs.h create mode 100644 drivers/ddr/microchip/ddr2_timing.h
diff --git a/arch/mips/mach-pic32/include/mach/ddr.h b/arch/mips/mach-pic32/include/mach/ddr.h new file mode 100644 index 0000000..7e0b9d5 --- /dev/null +++ b/arch/mips/mach-pic32/include/mach/ddr.h @@ -0,0 +1,32 @@ +/* + * (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef __MICROCHIP_PIC32_DDR_H +#define __MICROCHIP_PIC32_DDR_H + +/* called by initdram() function */ +void ddr2_phy_init(void); +void ddr2_ctrl_init(void); +phys_size_t ddr2_calculate_size(void); + +/* Maximum number of agents */ +#define NUM_AGENTS 5 + +/* Board can provide agent specific parameters for arbitration by + * filling struct ddr2_arbiter_params for all the agents and + * implementing board_get_ddr_arbiter_params() to return the filled + * structure. + */ +struct ddr2_arbiter_params { + u32 min_limit; /* min bursts to execute per arbitration */ + u32 req_period; /* request period threshold for accepted cmds */ + u32 min_cmd_acpt; /* min number of accepted cmds */ +}; + +struct ddr2_arbiter_params *board_get_ddr_arbiter_params(void); + +#endif /* __MICROCHIP_PIC32_DDR_H */ diff --git a/drivers/Makefile b/drivers/Makefile index c9031f2..0ab54d9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -68,4 +68,5 @@ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/
+obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/ddr/microchip/Makefile b/drivers/ddr/microchip/Makefile new file mode 100644 index 0000000..305c48b --- /dev/null +++ b/drivers/ddr/microchip/Makefile @@ -0,0 +1,6 @@ +# +# Copyright (C) 2015 Microchip Technology Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-$(CONFIG_MACH_PIC32) += ddr2.o diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c new file mode 100644 index 0000000..2ca8eaf --- /dev/null +++ b/drivers/ddr/microchip/ddr2.c @@ -0,0 +1,277 @@ +/* + * (c) 2015 Paul Thacker paul.thacker@microchip.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ +#include <asm/io.h> +#include <config.h> +#include <wait_bit.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <mach/pic32.h> +#include <mach/ddr.h> +#include "ddr2_regs.h" +#include "ddr2_timing.h" + +/* init DDR2 Phy */ +void ddr2_phy_init(void) +{ + struct ddr2_phy_regs *ddr2_phy; + u32 pad_ctl; + + ddr2_phy = (struct ddr2_phy_regs *)pic32_ioremap(PIC32_DDR2P_BASE); + + /* PHY_DLL_RECALIB */ + writel(DELAY_START_VAL(3) | DISABLE_RECALIB(0) | + RECALIB_CNT(0x10), &ddr2_phy->dll_recalib); + + /* PHY_PAD_CTRL */ + pad_ctl = ODT_SEL | ODT_EN | DRIVE_SEL(0) | + ODT_PULLDOWN(2) | ODT_PULLUP(3) | + EXTRA_OEN_CLK(0) | NOEXT_DLL | + DLR_DFT_WRCMD | HALF_RATE | + DRVSTR_PFET(0xe) | DRVSTR_NFET(0xe) | + RCVR_EN | PREAMBLE_DLY(2); + writel(pad_ctl, &ddr2_phy->pad_ctrl); + + /* SCL_CONFIG_0 */ + writel(SCL_BURST8 | SCL_DDR_CONNECTED | SCL_RCAS_LAT(RL) | + SCL_ODTCSWW, &ddr2_phy->scl_config_1); + + /* SCL_CONFIG_1 */ + writel(SCL_CSEN | SCL_WCAS_LAT(WL), &ddr2_phy->scl_config_2); + + /* SCL_LAT */ + writel(SCL_CAPCLKDLY(3) | SCL_DDRCLKDLY(4), &ddr2_phy->scl_latency); +} + +/* start phy self calibration logic */ +static int ddr2_phy_calib_start(void) +{ + struct ddr2_phy_regs *ddr2_phy; + + ddr2_phy = (struct ddr2_phy_regs *)pic32_ioremap(PIC32_DDR2P_BASE); + + /* DDR Phy SCL Start */ + writel(SCL_START | SCL_EN, &ddr2_phy->scl_start); + + /* Wait for SCL for data byte to pass */ + return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS, + true, CONFIG_SYS_HZ, false); +} + +/* DDR2 Controller initialization */ + +/* Target Agent Arbiter */ +static void ddr_set_arbiter(struct ddr2_ctrl_regs *ctrl, + struct ddr2_arbiter_params *param) +{ + int i; + + for (i = 0; i < NUM_AGENTS; i++) { + /* set min burst size */ + writel(i * MIN_LIM_WIDTH, &ctrl->tsel); + writel(param->min_limit, &ctrl->minlim); + + /* set request period (4 * req_period clocks) */ + writel(i * RQST_PERIOD_WIDTH, &ctrl->tsel); + writel(param->req_period, &ctrl->reqprd); + + /* set number of burst accepted */ + writel(i * MIN_CMDACPT_WIDTH, &ctrl->tsel); + writel(param->min_cmd_acpt, &ctrl->mincmd); + } +} + +struct ddr2_arbiter_params *__weak board_get_ddr_arbiter_params(void) +{ + /* default arbiter parameters */ + static struct ddr2_arbiter_params arb_params[] = { + { .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x04,}, + { .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,}, + { .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,}, + { .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,}, + { .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,}, + }; + + return (struct ddr2_arbiter_params *)arb_params; +} + +static void host_load_cmd(struct ddr2_ctrl_regs *ctrl, u32 offset, + u32 hostcmd2, u32 hostcmd1, u32 delay) +{ + u32 hc_delay; + + hc_delay = max_t(u32, DIV_ROUND_UP(delay, tCK), 2) - 2; + writel(hostcmd1, &ctrl->cmd10 + offset); + writel((hostcmd2 & 0x7ff) | (hc_delay << 11), &ctrl->cmd20 + offset); +} + +/* init DDR2 Controller */ +void ddr2_ctrl_init(void) +{ + u32 wr2prech, rd2prech, wr2rd, wr2rd_cs; + u32 ras2ras, ras2cas, prech2ras, temp; + struct ddr2_arbiter_params *arb_params; + struct ddr2_ctrl_regs *ctrl; + + ctrl = (struct ddr2_ctrl_regs *)pic32_ioremap(PIC32_DDR2C_BASE); + + /* PIC32 DDR2 controller always work in HALF_RATE */ + writel(HALF_RATE_MODE, &ctrl->memwidth); + + /* Set arbiter configuration per target */ + arb_params = board_get_ddr_arbiter_params(); + ddr_set_arbiter(ctrl, arb_params); + + /* Address Configuration, model {CS, ROW, BA, COL} */ + writel((ROW_ADDR_RSHIFT | (BA_RSHFT << 8) | (CS_ADDR_RSHIFT << 16) | + (COL_HI_RSHFT << 24) | (SB_PRI << 29) | + (EN_AUTO_PRECH << 30)), &ctrl->memcfg0); + + writel(ROW_ADDR_MASK, &ctrl->memcfg1); + writel(COL_HI_MASK, &ctrl->memcfg2); + writel(COL_LO_MASK, &ctrl->memcfg3); + writel(BA_MASK | (CS_ADDR_MASK << 8), &ctrl->memcfg4); + + /* Refresh Config */ + writel(REFCNT_CLK(DIV_ROUND_UP(tRFI, tCK_CTRL) - 2) | + REFDLY_CLK(DIV_ROUND_UP(tRFC_MIN, tCK_CTRL) - 2) | + MAX_PEND_REF(7), + &ctrl->refcfg); + + /* Power Config */ + writel(ECC_EN(0) | ERR_CORR_EN(0) | EN_AUTO_PWR_DN(0) | + EN_AUTO_SELF_REF(3) | PWR_DN_DLY(8) | + SELF_REF_DLY(17) | PRECH_PWR_DN_ONLY(0), + &ctrl->pwrcfg); + + /* Delay Config */ + wr2rd = max_t(u32, DIV_ROUND_UP(tWTR, tCK_CTRL), + DIV_ROUND_UP(tWTR_TCK, 2)) + WL + BL; + wr2rd_cs = max_t(u32, wr2rd - 1, 3); + wr2prech = DIV_ROUND_UP(tWR, tCK_CTRL) + (WL + BL); + rd2prech = max_t(u32, DIV_ROUND_UP(tRTP, tCK_CTRL), + DIV_ROUND_UP(tRTP_TCK, 2)) + BL - 2; + ras2ras = max_t(u32, DIV_ROUND_UP(tRRD, tCK_CTRL), + DIV_ROUND_UP(tRRD_TCK, 2)) - 1; + ras2cas = DIV_ROUND_UP(tRCD, tCK_CTRL) - 1; + prech2ras = DIV_ROUND_UP(tRP, tCK_CTRL) - 1; + + writel(((wr2rd & 0x0f) | + ((wr2rd_cs & 0x0f) << 4) | + ((BL - 1) << 8) | + (BL << 12) | + ((BL - 1) << 16) | + ((BL - 1) << 20) | + ((BL + 2) << 24) | + ((RL - WL + 3) << 28)), &ctrl->dlycfg0); + + writel(((tCKE_TCK - 1) | + (((DIV_ROUND_UP(tDLLK, 2) - 2) & 0xff) << 8) | + ((tCKE_TCK - 1) << 16) | + ((max_t(u32, tXP_TCK, tCKE_TCK) - 1) << 20) | + ((wr2prech >> 4) << 26) | + ((wr2rd >> 4) << 27) | + ((wr2rd_cs >> 4) << 28) | + (((RL + 5) >> 4) << 29) | + ((DIV_ROUND_UP(tDLLK, 2) >> 8) << 30)), &ctrl->dlycfg1); + + writel((DIV_ROUND_UP(tRP, tCK_CTRL) | + (rd2prech << 8) | + ((wr2prech & 0x0f) << 12) | + (ras2ras << 16) | + (ras2cas << 20) | + (prech2ras << 24) | + ((RL + 3) << 28)), &ctrl->dlycfg2); + + writel(((DIV_ROUND_UP(tRAS_MIN, tCK_CTRL) - 1) | + ((DIV_ROUND_UP(tRC, tCK_CTRL) - 1) << 8) | + ((DIV_ROUND_UP(tFAW, tCK_CTRL) - 1) << 16)), + &ctrl->dlycfg3); + + /* ODT Config */ + writel(0x0, &ctrl->odtcfg); + writel(BIT(16), &ctrl->odtencfg); + writel(ODTRDLY(RL - 3) | ODTWDLY(WL - 3) | ODTRLEN(2) | ODTWLEN(3), + &ctrl->odtcfg); + + /* Transfer Configuration */ + writel(NXTDATRQDLY(2) | NXDATAVDLY(4) | RDATENDLY(2) | + MAX_BURST(3) | (7 << 28) | BIG_ENDIAN(0), + &ctrl->xfercfg); + + /* DRAM Initialization */ + /* CKE high after reset and wait 400 nsec */ + host_load_cmd(ctrl, 0, 0, IDLE_NOP, 400000); + + /* issue precharge all command */ + host_load_cmd(ctrl, 4, 0x04, PRECH_ALL_CMD, tRP + tCK); + + /* initialize EMR2 */ + host_load_cmd(ctrl, 8, 0x200, LOAD_MODE_CMD, tMRD_TCK * tCK); + + /* initialize EMR3 */ + host_load_cmd(ctrl, 0xc, 0x300, LOAD_MODE_CMD, tMRD_TCK * tCK); + + /* + * RDQS disable, DQSB enable, OCD exit, 150 ohm termination, + * AL=0, DLL enable + */ + host_load_cmd(ctrl, 0x10, 0x100, + LOAD_MODE_CMD | (0x40 << 24), tMRD_TCK * tCK); + /* + * PD fast exit, WR REC = tWR in clocks -1, + * DLL reset, CAS = RL, burst = 4 + */ + temp = ((DIV_ROUND_UP(tWR, tCK) - 1) << 1) | 1; + host_load_cmd(ctrl, 0x14, temp, LOAD_MODE_CMD | (RL << 28) | (2 << 24), + tMRD_TCK * tCK); + + /* issue precharge all command */ + host_load_cmd(ctrl, 0x18, 4, PRECH_ALL_CMD, tRP + tCK); + + /* issue refresh command */ + host_load_cmd(ctrl, 0x1c, 0, REF_CMD, tRFC_MIN); + + /* issue refresh command */ + host_load_cmd(ctrl, 0x20, 0, REF_CMD, tRFC_MIN); + + /* Mode register programming as before without DLL reset */ + host_load_cmd(ctrl, 0x24, temp, LOAD_MODE_CMD | (RL << 28) | (3 << 24), + tMRD_TCK * tCK); + + /* extended mode register same as before with OCD default */ + host_load_cmd(ctrl, 0x28, 0x103, LOAD_MODE_CMD | (0xc << 24), + tMRD_TCK * tCK); + + /* extended mode register same as before with OCD exit */ + host_load_cmd(ctrl, 0x2c, 0x100, LOAD_MODE_CMD | (0x4 << 28), + 140 * tCK); + + writel(CMD_VALID | NUMHOSTCMD(11), &ctrl->cmdissue); + + /* start memory initialization */ + writel(INIT_START, &ctrl->memcon); + + /* wait for all host cmds to be transmitted */ + wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false, + CONFIG_SYS_HZ, false); + + /* inform all cmds issued, ready for normal operation */ + writel(INIT_START | INIT_DONE, &ctrl->memcon); + + /* perform phy caliberation */ + ddr2_phy_calib_start(); +} + +phys_size_t ddr2_calculate_size(void) +{ + u32 temp; + + temp = 1 << (COL_BITS + BA_BITS + ROW_BITS); + /* 16-bit data width between controller and DIMM */ + temp = temp * CS_BITS * (16 / 8); + return (phys_size_t)temp; +} diff --git a/drivers/ddr/microchip/ddr2_regs.h b/drivers/ddr/microchip/ddr2_regs.h new file mode 100644 index 0000000..3ed58de --- /dev/null +++ b/drivers/ddr/microchip/ddr2_regs.h @@ -0,0 +1,151 @@ +/* + * (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef __MICROCHIP_DDR2_REGS_H +#define __MICROCHIP_DDR2_REGS_H + +#include <config.h> +#include <linux/bitops.h> + +/* DDR2 Controller */ +struct ddr2_ctrl_regs { + u32 tsel; + u32 minlim; + u32 reqprd; + u32 mincmd; + u32 memcon; + u32 memcfg0; + u32 memcfg1; + u32 memcfg2; + u32 memcfg3; + u32 memcfg4; + u32 refcfg; + u32 pwrcfg; + u32 dlycfg0; + u32 dlycfg1; + u32 dlycfg2; + u32 dlycfg3; + u32 odtcfg; + u32 xfercfg; + u32 cmdissue; + u32 odtencfg; + u32 memwidth; + u32 unused[11]; + u32 cmd10; + u32 othercmd10[15]; + u32 cmd20; + u32 othercmd20[15]; +}; + +/* Arbiter Config */ +#define MIN_LIM_WIDTH 5 +#define RQST_PERIOD_WIDTH 8 +#define MIN_CMDACPT_WIDTH 8 + +/* Refresh Config */ +#define REFCNT_CLK(x) (x) +#define REFDLY_CLK(x) ((x) << 16) +#define MAX_PEND_REF(x) ((x) << 24) + +/* Power Config */ +#define PRECH_PWR_DN_ONLY(x) ((x) << 22) +#define SELF_REF_DLY(x) ((x) << 12) +#define PWR_DN_DLY(x) ((x) << 4) +#define EN_AUTO_SELF_REF(x) ((x) << 3) +#define EN_AUTO_PWR_DN(x) ((x) << 2) +#define ERR_CORR_EN(x) ((x) << 1) +#define ECC_EN(x) (x) + +/* Memory Width */ +#define HALF_RATE_MODE BIT(3) + +/* Delay Config */ +#define ODTWLEN(x) ((x) << 20) +#define ODTRLEN(x) ((x) << 16) +#define ODTWDLY(x) ((x) << 12) +#define ODTRDLY(x) ((x) << 8) + +/* Xfer Config */ +#define BIG_ENDIAN(x) ((x) << 31) +#define MAX_BURST(x) ((x) << 24) +#define RDATENDLY(x) ((x) << 16) +#define NXDATAVDLY(x) ((x) << 4) +#define NXTDATRQDLY(x) ((x) << 0) + +/* Host Commands */ +#define IDLE_NOP 0x00ffffff +#define PRECH_ALL_CMD 0x00fff401 +#define REF_CMD 0x00fff801 +#define LOAD_MODE_CMD 0x00fff001 +#define CKE_LOW 0x00ffeffe + +#define NUM_HOST_CMDS 12 + +/* Host CMD Issue */ +#define CMD_VALID BIT(4) +#define NUMHOSTCMD(x) (x) + +/* Memory Control */ +#define INIT_DONE BIT(1) +#define INIT_START BIT(0) + +/* Address Control */ +#define EN_AUTO_PRECH 0 +#define SB_PRI 1 + +/* DDR2 Phy Register */ +struct ddr2_phy_regs { + u32 scl_start; + u32 unused1[2]; + u32 scl_latency; + u32 unused2[2]; + u32 scl_config_1; + u32 scl_config_2; + u32 pad_ctrl; + u32 dll_recalib; +}; + +/* PHY PAD CONTROL */ +#define ODT_SEL BIT(0) +#define ODT_EN BIT(1) +#define DRIVE_SEL(x) ((x) << 2) +#define ODT_PULLDOWN(x) ((x) << 4) +#define ODT_PULLUP(x) ((x) << 6) +#define EXTRA_OEN_CLK(x) ((x) << 8) +#define NOEXT_DLL BIT(9) +#define DLR_DFT_WRCMD BIT(13) +#define HALF_RATE BIT(14) +#define DRVSTR_PFET(x) ((x) << 16) +#define DRVSTR_NFET(x) ((x) << 20) +#define RCVR_EN BIT(28) +#define PREAMBLE_DLY(x) ((x) << 29) + +/* PHY DLL RECALIBRATE */ +#define RECALIB_CNT(x) ((x) << 8) +#define DISABLE_RECALIB(x) ((x) << 26) +#define DELAY_START_VAL(x) ((x) << 28) + +/* PHY SCL CONFIG1 */ +#define SCL_BURST8 BIT(0) +#define SCL_DDR_CONNECTED BIT(1) +#define SCL_RCAS_LAT(x) ((x) << 4) +#define SCL_ODTCSWW BIT(24) + +/* PHY SCL CONFIG2 */ +#define SCL_CSEN BIT(0) +#define SCL_WCAS_LAT(x) ((x) << 8) + +/* PHY SCL Latency */ +#define SCL_CAPCLKDLY(x) ((x) << 0) +#define SCL_DDRCLKDLY(x) ((x) << 4) + +/* PHY SCL START */ +#define SCL_START BIT(28) +#define SCL_EN BIT(26) +#define SCL_LUBPASS (BIT(1) | BIT(0)) + +#endif /* __MICROCHIP_DDR2_REGS_H */ diff --git a/drivers/ddr/microchip/ddr2_timing.h b/drivers/ddr/microchip/ddr2_timing.h new file mode 100644 index 0000000..928897e --- /dev/null +++ b/drivers/ddr/microchip/ddr2_timing.h @@ -0,0 +1,65 @@ +/* + * (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef __MICROCHIP_DDR2_TIMING_H +#define __MICROCHIP_DDR2_TIMING_H + +/* MPLL freq is 400MHz */ +#define tCK 2500 /* 2500 psec */ +#define tCK_CTRL (tCK * 2) + +/* Burst length in cycles */ +#define BL 2 +/* default CAS latency for all speed grades */ +#define RL 5 +/* default write latency for all speed grades = CL-1 */ +#define WL 4 + +/* From Micron MT47H64M16HR-3 data sheet */ +#define tRFC_MIN 127500 /* psec */ +#define tWR 15000 /* psec */ +#define tRP 12500 /* psec */ +#define tRCD 12500 /* psec */ +#define tRRD 7500 /* psec */ +/* tRRD_TCK is minimum of 2 clk periods, regardless of freq */ +#define tRRD_TCK 2 +#define tWTR 7500 /* psec */ +/* tWTR_TCK is minimum of 2 clk periods, regardless of freq */ +#define tWTR_TCK 2 +#define tRTP 7500 /* psec */ +#define tRTP_TCK (BL / 2) +#define tXP_TCK 2 /* clocks */ +#define tCKE_TCK 3 /* clocks */ +#define tXSNR (tRFC_MIN + 10000) /* psec */ +#define tDLLK 200 /* clocks */ +#define tRAS_MIN 45000 /* psec */ +#define tRC 57500 /* psec */ +#define tFAW 35000 /* psec */ +#define tMRD_TCK 2 /* clocks */ +#define tRFI 7800000 /* psec */ + +/* DDR Addressing */ +#define COL_BITS 10 +#define BA_BITS 3 +#define ROW_BITS 13 +#define CS_BITS 1 + +/* DDR Addressing scheme: {CS, ROW, BA, COL} */ +#define COL_HI_RSHFT 0 +#define COL_HI_MASK 0 +#define COL_LO_MASK ((1 << COL_BITS) - 1) + +#define BA_RSHFT COL_BITS +#define BA_MASK ((1 << BA_BITS) - 1) + +#define ROW_ADDR_RSHIFT (BA_RSHFT + BA_BITS) +#define ROW_ADDR_MASK ((1 << ROW_BITS) - 1) + +#define CS_ADDR_RSHIFT 0 +#define CS_ADDR_MASK 0 + +#endif /* __MICROCHIP_DDR2_TIMING_H */

2016-01-04 15:01 GMT+01:00 Purna Chandra Mandal purna.mandal@microchip.com:
Signed-off-by: Paul Thacker paul.thacker@microchip.com Signed-off-by: Purna Chandra Mandal purna.mandal@microchip.com
Changes in v2:
- move ddr2 initialization from board/microchip/ to drivers/ddr/microchip
arch/mips/mach-pic32/include/mach/ddr.h | 32 ++++ drivers/Makefile | 1 + drivers/ddr/microchip/Makefile | 6 + drivers/ddr/microchip/ddr2.c | 277 ++++++++++++++++++++++++++++++++ drivers/ddr/microchip/ddr2_regs.h | 151 +++++++++++++++++ drivers/ddr/microchip/ddr2_timing.h | 65 ++++++++ 6 files changed, 532 insertions(+) create mode 100644 arch/mips/mach-pic32/include/mach/ddr.h create mode 100644 drivers/ddr/microchip/Makefile create mode 100644 drivers/ddr/microchip/ddr2.c create mode 100644 drivers/ddr/microchip/ddr2_regs.h create mode 100644 drivers/ddr/microchip/ddr2_timing.h
diff --git a/arch/mips/mach-pic32/include/mach/ddr.h b/arch/mips/mach-pic32/include/mach/ddr.h new file mode 100644 index 0000000..7e0b9d5 --- /dev/null +++ b/arch/mips/mach-pic32/include/mach/ddr.h @@ -0,0 +1,32 @@ +/*
- (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MICROCHIP_PIC32_DDR_H +#define __MICROCHIP_PIC32_DDR_H
+/* called by initdram() function */ +void ddr2_phy_init(void); +void ddr2_ctrl_init(void); +phys_size_t ddr2_calculate_size(void);
+/* Maximum number of agents */ +#define NUM_AGENTS 5
+/* Board can provide agent specific parameters for arbitration by
- filling struct ddr2_arbiter_params for all the agents and
- implementing board_get_ddr_arbiter_params() to return the filled
- structure.
- */
+struct ddr2_arbiter_params {
u32 min_limit; /* min bursts to execute per arbitration */
u32 req_period; /* request period threshold for accepted cmds */
u32 min_cmd_acpt; /* min number of accepted cmds */
+};
+struct ddr2_arbiter_params *board_get_ddr_arbiter_params(void);
+#endif /* __MICROCHIP_PIC32_DDR_H */ diff --git a/drivers/Makefile b/drivers/Makefile index c9031f2..0ab54d9 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -68,4 +68,5 @@ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/
+obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/ddr/microchip/Makefile b/drivers/ddr/microchip/Makefile new file mode 100644 index 0000000..305c48b --- /dev/null +++ b/drivers/ddr/microchip/Makefile @@ -0,0 +1,6 @@ +# +# Copyright (C) 2015 Microchip Technology Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-$(CONFIG_MACH_PIC32) += ddr2.o diff --git a/drivers/ddr/microchip/ddr2.c b/drivers/ddr/microchip/ddr2.c new file mode 100644 index 0000000..2ca8eaf --- /dev/null +++ b/drivers/ddr/microchip/ddr2.c @@ -0,0 +1,277 @@ +/*
- (c) 2015 Paul Thacker paul.thacker@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <asm/io.h> +#include <config.h> +#include <wait_bit.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <mach/pic32.h> +#include <mach/ddr.h> +#include "ddr2_regs.h" +#include "ddr2_timing.h"
+/* init DDR2 Phy */ +void ddr2_phy_init(void) +{
struct ddr2_phy_regs *ddr2_phy;
u32 pad_ctl;
ddr2_phy = (struct ddr2_phy_regs *)pic32_ioremap(PIC32_DDR2P_BASE);
an explicit cast from 'void *' is not needed
/* PHY_DLL_RECALIB */
writel(DELAY_START_VAL(3) | DISABLE_RECALIB(0) |
RECALIB_CNT(0x10), &ddr2_phy->dll_recalib);
/* PHY_PAD_CTRL */
pad_ctl = ODT_SEL | ODT_EN | DRIVE_SEL(0) |
ODT_PULLDOWN(2) | ODT_PULLUP(3) |
EXTRA_OEN_CLK(0) | NOEXT_DLL |
DLR_DFT_WRCMD | HALF_RATE |
DRVSTR_PFET(0xe) | DRVSTR_NFET(0xe) |
RCVR_EN | PREAMBLE_DLY(2);
writel(pad_ctl, &ddr2_phy->pad_ctrl);
/* SCL_CONFIG_0 */
writel(SCL_BURST8 | SCL_DDR_CONNECTED | SCL_RCAS_LAT(RL) |
SCL_ODTCSWW, &ddr2_phy->scl_config_1);
/* SCL_CONFIG_1 */
writel(SCL_CSEN | SCL_WCAS_LAT(WL), &ddr2_phy->scl_config_2);
/* SCL_LAT */
writel(SCL_CAPCLKDLY(3) | SCL_DDRCLKDLY(4), &ddr2_phy->scl_latency);
+}
+/* start phy self calibration logic */ +static int ddr2_phy_calib_start(void) +{
struct ddr2_phy_regs *ddr2_phy;
ddr2_phy = (struct ddr2_phy_regs *)pic32_ioremap(PIC32_DDR2P_BASE);
an explicit cast from 'void *' is not needed
/* DDR Phy SCL Start */
writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
/* Wait for SCL for data byte to pass */
return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS,
true, CONFIG_SYS_HZ, false);
+}
+/* DDR2 Controller initialization */
+/* Target Agent Arbiter */ +static void ddr_set_arbiter(struct ddr2_ctrl_regs *ctrl,
struct ddr2_arbiter_params *param)
*param should be const
+{
int i;
for (i = 0; i < NUM_AGENTS; i++) {
/* set min burst size */
writel(i * MIN_LIM_WIDTH, &ctrl->tsel);
writel(param->min_limit, &ctrl->minlim);
/* set request period (4 * req_period clocks) */
writel(i * RQST_PERIOD_WIDTH, &ctrl->tsel);
writel(param->req_period, &ctrl->reqprd);
/* set number of burst accepted */
writel(i * MIN_CMDACPT_WIDTH, &ctrl->tsel);
writel(param->min_cmd_acpt, &ctrl->mincmd);
}
+}
+struct ddr2_arbiter_params *__weak board_get_ddr_arbiter_params(void) +{
/* default arbiter parameters */
static struct ddr2_arbiter_params arb_params[] = {
{ .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x04,},
{ .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,},
{ .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,},
{ .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,},
{ .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,},
};
is that a constant table? If yes the declaration of the table and the return value of this function should be annotated with const
return (struct ddr2_arbiter_params *)arb_params;
the cast is not needed. To return the address of the first element:
return &arb_params[0];
+}
+static void host_load_cmd(struct ddr2_ctrl_regs *ctrl, u32 offset,
u32 hostcmd2, u32 hostcmd1, u32 delay)
+{
u32 hc_delay;
hc_delay = max_t(u32, DIV_ROUND_UP(delay, tCK), 2) - 2;
writel(hostcmd1, &ctrl->cmd10 + offset);
writel((hostcmd2 & 0x7ff) | (hc_delay << 11), &ctrl->cmd20 + offset);
+}
+/* init DDR2 Controller */ +void ddr2_ctrl_init(void) +{
u32 wr2prech, rd2prech, wr2rd, wr2rd_cs;
u32 ras2ras, ras2cas, prech2ras, temp;
struct ddr2_arbiter_params *arb_params;
as mentioned above, this pointer should be const
struct ddr2_ctrl_regs *ctrl;
ctrl = (struct ddr2_ctrl_regs *)pic32_ioremap(PIC32_DDR2C_BASE);
an explicit cast from 'void *' is not needed
/* PIC32 DDR2 controller always work in HALF_RATE */
writel(HALF_RATE_MODE, &ctrl->memwidth);
/* Set arbiter configuration per target */
arb_params = board_get_ddr_arbiter_params();
ddr_set_arbiter(ctrl, arb_params);
/* Address Configuration, model {CS, ROW, BA, COL} */
writel((ROW_ADDR_RSHIFT | (BA_RSHFT << 8) | (CS_ADDR_RSHIFT << 16) |
(COL_HI_RSHFT << 24) | (SB_PRI << 29) |
(EN_AUTO_PRECH << 30)), &ctrl->memcfg0);
writel(ROW_ADDR_MASK, &ctrl->memcfg1);
writel(COL_HI_MASK, &ctrl->memcfg2);
writel(COL_LO_MASK, &ctrl->memcfg3);
writel(BA_MASK | (CS_ADDR_MASK << 8), &ctrl->memcfg4);
/* Refresh Config */
writel(REFCNT_CLK(DIV_ROUND_UP(tRFI, tCK_CTRL) - 2) |
REFDLY_CLK(DIV_ROUND_UP(tRFC_MIN, tCK_CTRL) - 2) |
MAX_PEND_REF(7),
&ctrl->refcfg);
/* Power Config */
writel(ECC_EN(0) | ERR_CORR_EN(0) | EN_AUTO_PWR_DN(0) |
EN_AUTO_SELF_REF(3) | PWR_DN_DLY(8) |
SELF_REF_DLY(17) | PRECH_PWR_DN_ONLY(0),
&ctrl->pwrcfg);
/* Delay Config */
wr2rd = max_t(u32, DIV_ROUND_UP(tWTR, tCK_CTRL),
DIV_ROUND_UP(tWTR_TCK, 2)) + WL + BL;
wr2rd_cs = max_t(u32, wr2rd - 1, 3);
wr2prech = DIV_ROUND_UP(tWR, tCK_CTRL) + (WL + BL);
rd2prech = max_t(u32, DIV_ROUND_UP(tRTP, tCK_CTRL),
DIV_ROUND_UP(tRTP_TCK, 2)) + BL - 2;
ras2ras = max_t(u32, DIV_ROUND_UP(tRRD, tCK_CTRL),
DIV_ROUND_UP(tRRD_TCK, 2)) - 1;
ras2cas = DIV_ROUND_UP(tRCD, tCK_CTRL) - 1;
prech2ras = DIV_ROUND_UP(tRP, tCK_CTRL) - 1;
writel(((wr2rd & 0x0f) |
((wr2rd_cs & 0x0f) << 4) |
((BL - 1) << 8) |
(BL << 12) |
((BL - 1) << 16) |
((BL - 1) << 20) |
((BL + 2) << 24) |
((RL - WL + 3) << 28)), &ctrl->dlycfg0);
writel(((tCKE_TCK - 1) |
(((DIV_ROUND_UP(tDLLK, 2) - 2) & 0xff) << 8) |
((tCKE_TCK - 1) << 16) |
((max_t(u32, tXP_TCK, tCKE_TCK) - 1) << 20) |
((wr2prech >> 4) << 26) |
((wr2rd >> 4) << 27) |
((wr2rd_cs >> 4) << 28) |
(((RL + 5) >> 4) << 29) |
((DIV_ROUND_UP(tDLLK, 2) >> 8) << 30)), &ctrl->dlycfg1);
writel((DIV_ROUND_UP(tRP, tCK_CTRL) |
(rd2prech << 8) |
((wr2prech & 0x0f) << 12) |
(ras2ras << 16) |
(ras2cas << 20) |
(prech2ras << 24) |
((RL + 3) << 28)), &ctrl->dlycfg2);
writel(((DIV_ROUND_UP(tRAS_MIN, tCK_CTRL) - 1) |
((DIV_ROUND_UP(tRC, tCK_CTRL) - 1) << 8) |
((DIV_ROUND_UP(tFAW, tCK_CTRL) - 1) << 16)),
&ctrl->dlycfg3);
/* ODT Config */
writel(0x0, &ctrl->odtcfg);
writel(BIT(16), &ctrl->odtencfg);
writel(ODTRDLY(RL - 3) | ODTWDLY(WL - 3) | ODTRLEN(2) | ODTWLEN(3),
&ctrl->odtcfg);
/* Transfer Configuration */
writel(NXTDATRQDLY(2) | NXDATAVDLY(4) | RDATENDLY(2) |
MAX_BURST(3) | (7 << 28) | BIG_ENDIAN(0),
&ctrl->xfercfg);
/* DRAM Initialization */
/* CKE high after reset and wait 400 nsec */
host_load_cmd(ctrl, 0, 0, IDLE_NOP, 400000);
/* issue precharge all command */
host_load_cmd(ctrl, 4, 0x04, PRECH_ALL_CMD, tRP + tCK);
/* initialize EMR2 */
host_load_cmd(ctrl, 8, 0x200, LOAD_MODE_CMD, tMRD_TCK * tCK);
/* initialize EMR3 */
host_load_cmd(ctrl, 0xc, 0x300, LOAD_MODE_CMD, tMRD_TCK * tCK);
/*
* RDQS disable, DQSB enable, OCD exit, 150 ohm termination,
* AL=0, DLL enable
*/
host_load_cmd(ctrl, 0x10, 0x100,
LOAD_MODE_CMD | (0x40 << 24), tMRD_TCK * tCK);
/*
* PD fast exit, WR REC = tWR in clocks -1,
* DLL reset, CAS = RL, burst = 4
*/
temp = ((DIV_ROUND_UP(tWR, tCK) - 1) << 1) | 1;
host_load_cmd(ctrl, 0x14, temp, LOAD_MODE_CMD | (RL << 28) | (2 << 24),
tMRD_TCK * tCK);
/* issue precharge all command */
host_load_cmd(ctrl, 0x18, 4, PRECH_ALL_CMD, tRP + tCK);
/* issue refresh command */
host_load_cmd(ctrl, 0x1c, 0, REF_CMD, tRFC_MIN);
/* issue refresh command */
host_load_cmd(ctrl, 0x20, 0, REF_CMD, tRFC_MIN);
/* Mode register programming as before without DLL reset */
host_load_cmd(ctrl, 0x24, temp, LOAD_MODE_CMD | (RL << 28) | (3 << 24),
tMRD_TCK * tCK);
/* extended mode register same as before with OCD default */
host_load_cmd(ctrl, 0x28, 0x103, LOAD_MODE_CMD | (0xc << 24),
tMRD_TCK * tCK);
/* extended mode register same as before with OCD exit */
host_load_cmd(ctrl, 0x2c, 0x100, LOAD_MODE_CMD | (0x4 << 28),
140 * tCK);
writel(CMD_VALID | NUMHOSTCMD(11), &ctrl->cmdissue);
/* start memory initialization */
writel(INIT_START, &ctrl->memcon);
/* wait for all host cmds to be transmitted */
wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false,
CONFIG_SYS_HZ, false);
/* inform all cmds issued, ready for normal operation */
writel(INIT_START | INIT_DONE, &ctrl->memcon);
/* perform phy caliberation */
ddr2_phy_calib_start();
+}
+phys_size_t ddr2_calculate_size(void) +{
u32 temp;
temp = 1 << (COL_BITS + BA_BITS + ROW_BITS);
/* 16-bit data width between controller and DIMM */
temp = temp * CS_BITS * (16 / 8);
return (phys_size_t)temp;
+} diff --git a/drivers/ddr/microchip/ddr2_regs.h b/drivers/ddr/microchip/ddr2_regs.h new file mode 100644 index 0000000..3ed58de --- /dev/null +++ b/drivers/ddr/microchip/ddr2_regs.h @@ -0,0 +1,151 @@ +/*
- (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MICROCHIP_DDR2_REGS_H +#define __MICROCHIP_DDR2_REGS_H
+#include <config.h> +#include <linux/bitops.h>
+/* DDR2 Controller */ +struct ddr2_ctrl_regs {
u32 tsel;
u32 minlim;
u32 reqprd;
u32 mincmd;
u32 memcon;
u32 memcfg0;
u32 memcfg1;
u32 memcfg2;
u32 memcfg3;
u32 memcfg4;
u32 refcfg;
u32 pwrcfg;
u32 dlycfg0;
u32 dlycfg1;
u32 dlycfg2;
u32 dlycfg3;
u32 odtcfg;
u32 xfercfg;
u32 cmdissue;
u32 odtencfg;
u32 memwidth;
u32 unused[11];
u32 cmd10;
u32 othercmd10[15];
u32 cmd20;
u32 othercmd20[15];
+};
+/* Arbiter Config */ +#define MIN_LIM_WIDTH 5 +#define RQST_PERIOD_WIDTH 8 +#define MIN_CMDACPT_WIDTH 8
+/* Refresh Config */ +#define REFCNT_CLK(x) (x) +#define REFDLY_CLK(x) ((x) << 16) +#define MAX_PEND_REF(x) ((x) << 24)
+/* Power Config */ +#define PRECH_PWR_DN_ONLY(x) ((x) << 22) +#define SELF_REF_DLY(x) ((x) << 12) +#define PWR_DN_DLY(x) ((x) << 4) +#define EN_AUTO_SELF_REF(x) ((x) << 3) +#define EN_AUTO_PWR_DN(x) ((x) << 2) +#define ERR_CORR_EN(x) ((x) << 1) +#define ECC_EN(x) (x)
+/* Memory Width */ +#define HALF_RATE_MODE BIT(3)
+/* Delay Config */ +#define ODTWLEN(x) ((x) << 20) +#define ODTRLEN(x) ((x) << 16) +#define ODTWDLY(x) ((x) << 12) +#define ODTRDLY(x) ((x) << 8)
+/* Xfer Config */ +#define BIG_ENDIAN(x) ((x) << 31) +#define MAX_BURST(x) ((x) << 24) +#define RDATENDLY(x) ((x) << 16) +#define NXDATAVDLY(x) ((x) << 4) +#define NXTDATRQDLY(x) ((x) << 0)
+/* Host Commands */ +#define IDLE_NOP 0x00ffffff +#define PRECH_ALL_CMD 0x00fff401 +#define REF_CMD 0x00fff801 +#define LOAD_MODE_CMD 0x00fff001 +#define CKE_LOW 0x00ffeffe
+#define NUM_HOST_CMDS 12
+/* Host CMD Issue */ +#define CMD_VALID BIT(4) +#define NUMHOSTCMD(x) (x)
+/* Memory Control */ +#define INIT_DONE BIT(1) +#define INIT_START BIT(0)
+/* Address Control */ +#define EN_AUTO_PRECH 0 +#define SB_PRI 1
+/* DDR2 Phy Register */ +struct ddr2_phy_regs {
u32 scl_start;
u32 unused1[2];
u32 scl_latency;
u32 unused2[2];
u32 scl_config_1;
u32 scl_config_2;
u32 pad_ctrl;
u32 dll_recalib;
+};
+/* PHY PAD CONTROL */ +#define ODT_SEL BIT(0) +#define ODT_EN BIT(1) +#define DRIVE_SEL(x) ((x) << 2) +#define ODT_PULLDOWN(x) ((x) << 4) +#define ODT_PULLUP(x) ((x) << 6) +#define EXTRA_OEN_CLK(x) ((x) << 8) +#define NOEXT_DLL BIT(9) +#define DLR_DFT_WRCMD BIT(13) +#define HALF_RATE BIT(14) +#define DRVSTR_PFET(x) ((x) << 16) +#define DRVSTR_NFET(x) ((x) << 20) +#define RCVR_EN BIT(28) +#define PREAMBLE_DLY(x) ((x) << 29)
+/* PHY DLL RECALIBRATE */ +#define RECALIB_CNT(x) ((x) << 8) +#define DISABLE_RECALIB(x) ((x) << 26) +#define DELAY_START_VAL(x) ((x) << 28)
+/* PHY SCL CONFIG1 */ +#define SCL_BURST8 BIT(0) +#define SCL_DDR_CONNECTED BIT(1) +#define SCL_RCAS_LAT(x) ((x) << 4) +#define SCL_ODTCSWW BIT(24)
+/* PHY SCL CONFIG2 */ +#define SCL_CSEN BIT(0) +#define SCL_WCAS_LAT(x) ((x) << 8)
+/* PHY SCL Latency */ +#define SCL_CAPCLKDLY(x) ((x) << 0) +#define SCL_DDRCLKDLY(x) ((x) << 4)
+/* PHY SCL START */ +#define SCL_START BIT(28) +#define SCL_EN BIT(26) +#define SCL_LUBPASS (BIT(1) | BIT(0))
+#endif /* __MICROCHIP_DDR2_REGS_H */ diff --git a/drivers/ddr/microchip/ddr2_timing.h b/drivers/ddr/microchip/ddr2_timing.h new file mode 100644 index 0000000..928897e --- /dev/null +++ b/drivers/ddr/microchip/ddr2_timing.h @@ -0,0 +1,65 @@ +/*
- (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MICROCHIP_DDR2_TIMING_H +#define __MICROCHIP_DDR2_TIMING_H
+/* MPLL freq is 400MHz */ +#define tCK 2500 /* 2500 psec */ +#define tCK_CTRL (tCK * 2)
+/* Burst length in cycles */ +#define BL 2 +/* default CAS latency for all speed grades */ +#define RL 5 +/* default write latency for all speed grades = CL-1 */ +#define WL 4
+/* From Micron MT47H64M16HR-3 data sheet */ +#define tRFC_MIN 127500 /* psec */ +#define tWR 15000 /* psec */ +#define tRP 12500 /* psec */ +#define tRCD 12500 /* psec */ +#define tRRD 7500 /* psec */ +/* tRRD_TCK is minimum of 2 clk periods, regardless of freq */ +#define tRRD_TCK 2 +#define tWTR 7500 /* psec */ +/* tWTR_TCK is minimum of 2 clk periods, regardless of freq */ +#define tWTR_TCK 2 +#define tRTP 7500 /* psec */ +#define tRTP_TCK (BL / 2) +#define tXP_TCK 2 /* clocks */ +#define tCKE_TCK 3 /* clocks */ +#define tXSNR (tRFC_MIN + 10000) /* psec */ +#define tDLLK 200 /* clocks */ +#define tRAS_MIN 45000 /* psec */ +#define tRC 57500 /* psec */ +#define tFAW 35000 /* psec */ +#define tMRD_TCK 2 /* clocks */ +#define tRFI 7800000 /* psec */
please avoid CamelCase. This should be renamed to something like this:
#define T_RFC_MIN #define T_WR
+/* DDR Addressing */ +#define COL_BITS 10 +#define BA_BITS 3 +#define ROW_BITS 13 +#define CS_BITS 1
+/* DDR Addressing scheme: {CS, ROW, BA, COL} */ +#define COL_HI_RSHFT 0 +#define COL_HI_MASK 0 +#define COL_LO_MASK ((1 << COL_BITS) - 1)
+#define BA_RSHFT COL_BITS +#define BA_MASK ((1 << BA_BITS) - 1)
+#define ROW_ADDR_RSHIFT (BA_RSHFT + BA_BITS) +#define ROW_ADDR_MASK ((1 << ROW_BITS) - 1)
+#define CS_ADDR_RSHIFT 0 +#define CS_ADDR_MASK 0
+#endif /* __MICROCHIP_DDR2_TIMING_H */
1.8.3.1

On 01/06/2016 12:32 AM, Daniel Schwierzeck wrote:
[...]
+++ b/drivers/ddr/microchip/ddr2.c @@ -0,0 +1,277 @@ +/*
- (c) 2015 Paul Thacker paul.thacker@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <asm/io.h> +#include <config.h> +#include <wait_bit.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <mach/pic32.h> +#include <mach/ddr.h> +#include "ddr2_regs.h" +#include "ddr2_timing.h"
+/* init DDR2 Phy */ +void ddr2_phy_init(void) +{
struct ddr2_phy_regs *ddr2_phy;
u32 pad_ctl;
ddr2_phy = (struct ddr2_phy_regs *)pic32_ioremap(PIC32_DDR2P_BASE);
an explicit cast from 'void *' is not needed
ack. Will remove.
/* PHY_DLL_RECALIB */
writel(DELAY_START_VAL(3) | DISABLE_RECALIB(0) |
RECALIB_CNT(0x10), &ddr2_phy->dll_recalib);
/* PHY_PAD_CTRL */
pad_ctl = ODT_SEL | ODT_EN | DRIVE_SEL(0) |
ODT_PULLDOWN(2) | ODT_PULLUP(3) |
EXTRA_OEN_CLK(0) | NOEXT_DLL |
DLR_DFT_WRCMD | HALF_RATE |
DRVSTR_PFET(0xe) | DRVSTR_NFET(0xe) |
RCVR_EN | PREAMBLE_DLY(2);
writel(pad_ctl, &ddr2_phy->pad_ctrl);
/* SCL_CONFIG_0 */
writel(SCL_BURST8 | SCL_DDR_CONNECTED | SCL_RCAS_LAT(RL) |
SCL_ODTCSWW, &ddr2_phy->scl_config_1);
/* SCL_CONFIG_1 */
writel(SCL_CSEN | SCL_WCAS_LAT(WL), &ddr2_phy->scl_config_2);
/* SCL_LAT */
writel(SCL_CAPCLKDLY(3) | SCL_DDRCLKDLY(4), &ddr2_phy->scl_latency);
+}
+/* start phy self calibration logic */ +static int ddr2_phy_calib_start(void) +{
struct ddr2_phy_regs *ddr2_phy;
ddr2_phy = (struct ddr2_phy_regs *)pic32_ioremap(PIC32_DDR2P_BASE);
an explicit cast from 'void *' is not needed
ack.
/* DDR Phy SCL Start */
writel(SCL_START | SCL_EN, &ddr2_phy->scl_start);
/* Wait for SCL for data byte to pass */
return wait_for_bit(__func__, &ddr2_phy->scl_start, SCL_LUBPASS,
true, CONFIG_SYS_HZ, false);
+}
+/* DDR2 Controller initialization */
+/* Target Agent Arbiter */ +static void ddr_set_arbiter(struct ddr2_ctrl_regs *ctrl,
struct ddr2_arbiter_params *param)
*param should be const
ack.
+{
int i;
for (i = 0; i < NUM_AGENTS; i++) {
/* set min burst size */
writel(i * MIN_LIM_WIDTH, &ctrl->tsel);
writel(param->min_limit, &ctrl->minlim);
/* set request period (4 * req_period clocks) */
writel(i * RQST_PERIOD_WIDTH, &ctrl->tsel);
writel(param->req_period, &ctrl->reqprd);
/* set number of burst accepted */
writel(i * MIN_CMDACPT_WIDTH, &ctrl->tsel);
writel(param->min_cmd_acpt, &ctrl->mincmd);
}
+}
+struct ddr2_arbiter_params *__weak board_get_ddr_arbiter_params(void) +{
/* default arbiter parameters */
static struct ddr2_arbiter_params arb_params[] = {
{ .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x04,},
{ .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,},
{ .min_limit = 0x1f, .req_period = 0xff, .min_cmd_acpt = 0x10,},
{ .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,},
{ .min_limit = 0x04, .req_period = 0xff, .min_cmd_acpt = 0x04,},
};
is that a constant table? If yes the declaration of the table and the return value of this function should be annotated with const
Yes, it is constant table. will annotate accordingly.
return (struct ddr2_arbiter_params *)arb_params;
the cast is not needed. To return the address of the first element:
return &arb_params[0];
ack.
+}
+static void host_load_cmd(struct ddr2_ctrl_regs *ctrl, u32 offset,
u32 hostcmd2, u32 hostcmd1, u32 delay)
+{
u32 hc_delay;
hc_delay = max_t(u32, DIV_ROUND_UP(delay, tCK), 2) - 2;
writel(hostcmd1, &ctrl->cmd10 + offset);
writel((hostcmd2 & 0x7ff) | (hc_delay << 11), &ctrl->cmd20 + offset);
+}
+/* init DDR2 Controller */ +void ddr2_ctrl_init(void) +{
u32 wr2prech, rd2prech, wr2rd, wr2rd_cs;
u32 ras2ras, ras2cas, prech2ras, temp;
struct ddr2_arbiter_params *arb_params;
as mentioned above, this pointer should be const
ack.
struct ddr2_ctrl_regs *ctrl;
ctrl = (struct ddr2_ctrl_regs *)pic32_ioremap(PIC32_DDR2C_BASE);
an explicit cast from 'void *' is not needed
ack.
/* PIC32 DDR2 controller always work in HALF_RATE */
writel(HALF_RATE_MODE, &ctrl->memwidth);
/* Set arbiter configuration per target */
arb_params = board_get_ddr_arbiter_params();
ddr_set_arbiter(ctrl, arb_params);
/* Address Configuration, model {CS, ROW, BA, COL} */
writel((ROW_ADDR_RSHIFT | (BA_RSHFT << 8) | (CS_ADDR_RSHIFT << 16) |
(COL_HI_RSHFT << 24) | (SB_PRI << 29) |
(EN_AUTO_PRECH << 30)), &ctrl->memcfg0);
writel(ROW_ADDR_MASK, &ctrl->memcfg1);
writel(COL_HI_MASK, &ctrl->memcfg2);
writel(COL_LO_MASK, &ctrl->memcfg3);
writel(BA_MASK | (CS_ADDR_MASK << 8), &ctrl->memcfg4);
/* Refresh Config */
writel(REFCNT_CLK(DIV_ROUND_UP(tRFI, tCK_CTRL) - 2) |
REFDLY_CLK(DIV_ROUND_UP(tRFC_MIN, tCK_CTRL) - 2) |
MAX_PEND_REF(7),
&ctrl->refcfg);
/* Power Config */
writel(ECC_EN(0) | ERR_CORR_EN(0) | EN_AUTO_PWR_DN(0) |
EN_AUTO_SELF_REF(3) | PWR_DN_DLY(8) |
SELF_REF_DLY(17) | PRECH_PWR_DN_ONLY(0),
&ctrl->pwrcfg);
/* Delay Config */
wr2rd = max_t(u32, DIV_ROUND_UP(tWTR, tCK_CTRL),
DIV_ROUND_UP(tWTR_TCK, 2)) + WL + BL;
wr2rd_cs = max_t(u32, wr2rd - 1, 3);
wr2prech = DIV_ROUND_UP(tWR, tCK_CTRL) + (WL + BL);
rd2prech = max_t(u32, DIV_ROUND_UP(tRTP, tCK_CTRL),
DIV_ROUND_UP(tRTP_TCK, 2)) + BL - 2;
ras2ras = max_t(u32, DIV_ROUND_UP(tRRD, tCK_CTRL),
DIV_ROUND_UP(tRRD_TCK, 2)) - 1;
ras2cas = DIV_ROUND_UP(tRCD, tCK_CTRL) - 1;
prech2ras = DIV_ROUND_UP(tRP, tCK_CTRL) - 1;
writel(((wr2rd & 0x0f) |
((wr2rd_cs & 0x0f) << 4) |
((BL - 1) << 8) |
(BL << 12) |
((BL - 1) << 16) |
((BL - 1) << 20) |
((BL + 2) << 24) |
((RL - WL + 3) << 28)), &ctrl->dlycfg0);
writel(((tCKE_TCK - 1) |
(((DIV_ROUND_UP(tDLLK, 2) - 2) & 0xff) << 8) |
((tCKE_TCK - 1) << 16) |
((max_t(u32, tXP_TCK, tCKE_TCK) - 1) << 20) |
((wr2prech >> 4) << 26) |
((wr2rd >> 4) << 27) |
((wr2rd_cs >> 4) << 28) |
(((RL + 5) >> 4) << 29) |
((DIV_ROUND_UP(tDLLK, 2) >> 8) << 30)), &ctrl->dlycfg1);
writel((DIV_ROUND_UP(tRP, tCK_CTRL) |
(rd2prech << 8) |
((wr2prech & 0x0f) << 12) |
(ras2ras << 16) |
(ras2cas << 20) |
(prech2ras << 24) |
((RL + 3) << 28)), &ctrl->dlycfg2);
writel(((DIV_ROUND_UP(tRAS_MIN, tCK_CTRL) - 1) |
((DIV_ROUND_UP(tRC, tCK_CTRL) - 1) << 8) |
((DIV_ROUND_UP(tFAW, tCK_CTRL) - 1) << 16)),
&ctrl->dlycfg3);
/* ODT Config */
writel(0x0, &ctrl->odtcfg);
writel(BIT(16), &ctrl->odtencfg);
writel(ODTRDLY(RL - 3) | ODTWDLY(WL - 3) | ODTRLEN(2) | ODTWLEN(3),
&ctrl->odtcfg);
/* Transfer Configuration */
writel(NXTDATRQDLY(2) | NXDATAVDLY(4) | RDATENDLY(2) |
MAX_BURST(3) | (7 << 28) | BIG_ENDIAN(0),
&ctrl->xfercfg);
/* DRAM Initialization */
/* CKE high after reset and wait 400 nsec */
host_load_cmd(ctrl, 0, 0, IDLE_NOP, 400000);
/* issue precharge all command */
host_load_cmd(ctrl, 4, 0x04, PRECH_ALL_CMD, tRP + tCK);
/* initialize EMR2 */
host_load_cmd(ctrl, 8, 0x200, LOAD_MODE_CMD, tMRD_TCK * tCK);
/* initialize EMR3 */
host_load_cmd(ctrl, 0xc, 0x300, LOAD_MODE_CMD, tMRD_TCK * tCK);
/*
* RDQS disable, DQSB enable, OCD exit, 150 ohm termination,
* AL=0, DLL enable
*/
host_load_cmd(ctrl, 0x10, 0x100,
LOAD_MODE_CMD | (0x40 << 24), tMRD_TCK * tCK);
/*
* PD fast exit, WR REC = tWR in clocks -1,
* DLL reset, CAS = RL, burst = 4
*/
temp = ((DIV_ROUND_UP(tWR, tCK) - 1) << 1) | 1;
host_load_cmd(ctrl, 0x14, temp, LOAD_MODE_CMD | (RL << 28) | (2 << 24),
tMRD_TCK * tCK);
/* issue precharge all command */
host_load_cmd(ctrl, 0x18, 4, PRECH_ALL_CMD, tRP + tCK);
/* issue refresh command */
host_load_cmd(ctrl, 0x1c, 0, REF_CMD, tRFC_MIN);
/* issue refresh command */
host_load_cmd(ctrl, 0x20, 0, REF_CMD, tRFC_MIN);
/* Mode register programming as before without DLL reset */
host_load_cmd(ctrl, 0x24, temp, LOAD_MODE_CMD | (RL << 28) | (3 << 24),
tMRD_TCK * tCK);
/* extended mode register same as before with OCD default */
host_load_cmd(ctrl, 0x28, 0x103, LOAD_MODE_CMD | (0xc << 24),
tMRD_TCK * tCK);
/* extended mode register same as before with OCD exit */
host_load_cmd(ctrl, 0x2c, 0x100, LOAD_MODE_CMD | (0x4 << 28),
140 * tCK);
writel(CMD_VALID | NUMHOSTCMD(11), &ctrl->cmdissue);
/* start memory initialization */
writel(INIT_START, &ctrl->memcon);
/* wait for all host cmds to be transmitted */
wait_for_bit(__func__, &ctrl->cmdissue, CMD_VALID, false,
CONFIG_SYS_HZ, false);
/* inform all cmds issued, ready for normal operation */
writel(INIT_START | INIT_DONE, &ctrl->memcon);
/* perform phy caliberation */
ddr2_phy_calib_start();
+}
+phys_size_t ddr2_calculate_size(void) +{
u32 temp;
temp = 1 << (COL_BITS + BA_BITS + ROW_BITS);
/* 16-bit data width between controller and DIMM */
temp = temp * CS_BITS * (16 / 8);
return (phys_size_t)temp;
+} diff --git a/drivers/ddr/microchip/ddr2_regs.h b/drivers/ddr/microchip/ddr2_regs.h new file mode 100644 index 0000000..3ed58de --- /dev/null +++ b/drivers/ddr/microchip/ddr2_regs.h @@ -0,0 +1,151 @@ +/*
- (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MICROCHIP_DDR2_REGS_H +#define __MICROCHIP_DDR2_REGS_H
+#include <config.h> +#include <linux/bitops.h>
+/* DDR2 Controller */ +struct ddr2_ctrl_regs {
u32 tsel;
u32 minlim;
u32 reqprd;
u32 mincmd;
u32 memcon;
u32 memcfg0;
u32 memcfg1;
u32 memcfg2;
u32 memcfg3;
u32 memcfg4;
u32 refcfg;
u32 pwrcfg;
u32 dlycfg0;
u32 dlycfg1;
u32 dlycfg2;
u32 dlycfg3;
u32 odtcfg;
u32 xfercfg;
u32 cmdissue;
u32 odtencfg;
u32 memwidth;
u32 unused[11];
u32 cmd10;
u32 othercmd10[15];
u32 cmd20;
u32 othercmd20[15];
+};
+/* Arbiter Config */ +#define MIN_LIM_WIDTH 5 +#define RQST_PERIOD_WIDTH 8 +#define MIN_CMDACPT_WIDTH 8
+/* Refresh Config */ +#define REFCNT_CLK(x) (x) +#define REFDLY_CLK(x) ((x) << 16) +#define MAX_PEND_REF(x) ((x) << 24)
+/* Power Config */ +#define PRECH_PWR_DN_ONLY(x) ((x) << 22) +#define SELF_REF_DLY(x) ((x) << 12) +#define PWR_DN_DLY(x) ((x) << 4) +#define EN_AUTO_SELF_REF(x) ((x) << 3) +#define EN_AUTO_PWR_DN(x) ((x) << 2) +#define ERR_CORR_EN(x) ((x) << 1) +#define ECC_EN(x) (x)
+/* Memory Width */ +#define HALF_RATE_MODE BIT(3)
+/* Delay Config */ +#define ODTWLEN(x) ((x) << 20) +#define ODTRLEN(x) ((x) << 16) +#define ODTWDLY(x) ((x) << 12) +#define ODTRDLY(x) ((x) << 8)
+/* Xfer Config */ +#define BIG_ENDIAN(x) ((x) << 31) +#define MAX_BURST(x) ((x) << 24) +#define RDATENDLY(x) ((x) << 16) +#define NXDATAVDLY(x) ((x) << 4) +#define NXTDATRQDLY(x) ((x) << 0)
+/* Host Commands */ +#define IDLE_NOP 0x00ffffff +#define PRECH_ALL_CMD 0x00fff401 +#define REF_CMD 0x00fff801 +#define LOAD_MODE_CMD 0x00fff001 +#define CKE_LOW 0x00ffeffe
+#define NUM_HOST_CMDS 12
+/* Host CMD Issue */ +#define CMD_VALID BIT(4) +#define NUMHOSTCMD(x) (x)
+/* Memory Control */ +#define INIT_DONE BIT(1) +#define INIT_START BIT(0)
+/* Address Control */ +#define EN_AUTO_PRECH 0 +#define SB_PRI 1
+/* DDR2 Phy Register */ +struct ddr2_phy_regs {
u32 scl_start;
u32 unused1[2];
u32 scl_latency;
u32 unused2[2];
u32 scl_config_1;
u32 scl_config_2;
u32 pad_ctrl;
u32 dll_recalib;
+};
+/* PHY PAD CONTROL */ +#define ODT_SEL BIT(0) +#define ODT_EN BIT(1) +#define DRIVE_SEL(x) ((x) << 2) +#define ODT_PULLDOWN(x) ((x) << 4) +#define ODT_PULLUP(x) ((x) << 6) +#define EXTRA_OEN_CLK(x) ((x) << 8) +#define NOEXT_DLL BIT(9) +#define DLR_DFT_WRCMD BIT(13) +#define HALF_RATE BIT(14) +#define DRVSTR_PFET(x) ((x) << 16) +#define DRVSTR_NFET(x) ((x) << 20) +#define RCVR_EN BIT(28) +#define PREAMBLE_DLY(x) ((x) << 29)
+/* PHY DLL RECALIBRATE */ +#define RECALIB_CNT(x) ((x) << 8) +#define DISABLE_RECALIB(x) ((x) << 26) +#define DELAY_START_VAL(x) ((x) << 28)
+/* PHY SCL CONFIG1 */ +#define SCL_BURST8 BIT(0) +#define SCL_DDR_CONNECTED BIT(1) +#define SCL_RCAS_LAT(x) ((x) << 4) +#define SCL_ODTCSWW BIT(24)
+/* PHY SCL CONFIG2 */ +#define SCL_CSEN BIT(0) +#define SCL_WCAS_LAT(x) ((x) << 8)
+/* PHY SCL Latency */ +#define SCL_CAPCLKDLY(x) ((x) << 0) +#define SCL_DDRCLKDLY(x) ((x) << 4)
+/* PHY SCL START */ +#define SCL_START BIT(28) +#define SCL_EN BIT(26) +#define SCL_LUBPASS (BIT(1) | BIT(0))
+#endif /* __MICROCHIP_DDR2_REGS_H */ diff --git a/drivers/ddr/microchip/ddr2_timing.h b/drivers/ddr/microchip/ddr2_timing.h new file mode 100644 index 0000000..928897e --- /dev/null +++ b/drivers/ddr/microchip/ddr2_timing.h @@ -0,0 +1,65 @@ +/*
- (c) 2015 Purna Chandra Mandal purna.mandal@microchip.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __MICROCHIP_DDR2_TIMING_H +#define __MICROCHIP_DDR2_TIMING_H
+/* MPLL freq is 400MHz */ +#define tCK 2500 /* 2500 psec */ +#define tCK_CTRL (tCK * 2)
+/* Burst length in cycles */ +#define BL 2 +/* default CAS latency for all speed grades */ +#define RL 5 +/* default write latency for all speed grades = CL-1 */ +#define WL 4
+/* From Micron MT47H64M16HR-3 data sheet */ +#define tRFC_MIN 127500 /* psec */ +#define tWR 15000 /* psec */ +#define tRP 12500 /* psec */ +#define tRCD 12500 /* psec */ +#define tRRD 7500 /* psec */ +/* tRRD_TCK is minimum of 2 clk periods, regardless of freq */ +#define tRRD_TCK 2 +#define tWTR 7500 /* psec */ +/* tWTR_TCK is minimum of 2 clk periods, regardless of freq */ +#define tWTR_TCK 2 +#define tRTP 7500 /* psec */ +#define tRTP_TCK (BL / 2) +#define tXP_TCK 2 /* clocks */ +#define tCKE_TCK 3 /* clocks */ +#define tXSNR (tRFC_MIN + 10000) /* psec */ +#define tDLLK 200 /* clocks */ +#define tRAS_MIN 45000 /* psec */ +#define tRC 57500 /* psec */ +#define tFAW 35000 /* psec */ +#define tMRD_TCK 2 /* clocks */ +#define tRFI 7800000 /* psec */
please avoid CamelCase. This should be renamed to something like this:
#define T_RFC_MIN #define T_WR
ack.
+/* DDR Addressing */ +#define COL_BITS 10 +#define BA_BITS 3 +#define ROW_BITS 13 +#define CS_BITS 1
+/* DDR Addressing scheme: {CS, ROW, BA, COL} */ +#define COL_HI_RSHFT 0 +#define COL_HI_MASK 0 +#define COL_LO_MASK ((1 << COL_BITS) - 1)
+#define BA_RSHFT COL_BITS +#define BA_MASK ((1 << BA_BITS) - 1)
+#define ROW_ADDR_RSHIFT (BA_RSHFT + BA_BITS) +#define ROW_ADDR_MASK ((1 << ROW_BITS) - 1)
+#define CS_ADDR_RSHIFT 0 +#define CS_ADDR_MASK 0
+#endif /* __MICROCHIP_DDR2_TIMING_H */
1.8.3.1

Hi,
On 4 January 2016 at 07:01, Purna Chandra Mandal purna.mandal@microchip.com wrote:
Signed-off-by: Paul Thacker paul.thacker@microchip.com Signed-off-by: Purna Chandra Mandal purna.mandal@microchip.com
Please add a commit message.
Changes in v2:
- move ddr2 initialization from board/microchip/ to drivers/ddr/microchip
arch/mips/mach-pic32/include/mach/ddr.h | 32 ++++ drivers/Makefile | 1 + drivers/ddr/microchip/Makefile | 6 + drivers/ddr/microchip/ddr2.c | 277 ++++++++++++++++++++++++++++++++ drivers/ddr/microchip/ddr2_regs.h | 151 +++++++++++++++++ drivers/ddr/microchip/ddr2_timing.h | 65 ++++++++ 6 files changed, 532 insertions(+) create mode 100644 arch/mips/mach-pic32/include/mach/ddr.h create mode 100644 drivers/ddr/microchip/Makefile create mode 100644 drivers/ddr/microchip/ddr2.c create mode 100644 drivers/ddr/microchip/ddr2_regs.h create mode 100644 drivers/ddr/microchip/ddr2_timing.h
Regards, Simon

On 01/11/2016 10:27 PM, Simon Glass wrote:
Hi,
On 4 January 2016 at 07:01, Purna Chandra Mandal purna.mandal@microchip.com wrote:
Signed-off-by: Paul Thacker paul.thacker@microchip.com Signed-off-by: Purna Chandra Mandal purna.mandal@microchip.com
Please add a commit message.
Agreed, Will add.
Changes in v2:
- move ddr2 initialization from board/microchip/ to drivers/ddr/microchip
arch/mips/mach-pic32/include/mach/ddr.h | 32 ++++ drivers/Makefile | 1 + drivers/ddr/microchip/Makefile | 6 + drivers/ddr/microchip/ddr2.c | 277 ++++++++++++++++++++++++++++++++ drivers/ddr/microchip/ddr2_regs.h | 151 +++++++++++++++++ drivers/ddr/microchip/ddr2_timing.h | 65 ++++++++ 6 files changed, 532 insertions(+) create mode 100644 arch/mips/mach-pic32/include/mach/ddr.h create mode 100644 drivers/ddr/microchip/Makefile create mode 100644 drivers/ddr/microchip/ddr2.c create mode 100644 drivers/ddr/microchip/ddr2_regs.h create mode 100644 drivers/ddr/microchip/ddr2_timing.h
Regards, Simon
participants (3)
-
Daniel Schwierzeck
-
Purna Chandra Mandal
-
Simon Glass