summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-02-06 23:18:42 -0500
committerTom Rini <trini@konsulko.com>2020-02-06 23:18:42 -0500
commit457faef262c5c9533c344f25eafb10e757149a34 (patch)
tree9c1b2e777e37d997410b5f4e97b883fa0bb2ad7d /drivers
parent8a6ffeda97dfda5263ef40e1a4efb25b032ce04c (diff)
parent9a5a90ad9b3234c4739427cbe11219c51f0e9bd1 (diff)
downloadu-boot-457faef262c5c9533c344f25eafb10e757149a34.tar.gz
u-boot-457faef262c5c9533c344f25eafb10e757149a34.tar.bz2
u-boot-457faef262c5c9533c344f25eafb10e757149a34.zip
Merge branch 'master' of git://git.denx.de/u-boot-socfpga
- Gen 5 and Watchdog fixes
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ddr/altera/sdram_gen5.c6
-rw-r--r--drivers/ddr/altera/sequencer.c194
-rw-r--r--drivers/ddr/altera/sequencer.h1
-rw-r--r--drivers/reset/reset-socfpga.c6
-rw-r--r--drivers/watchdog/Kconfig7
-rw-r--r--drivers/watchdog/designware_wdt.c150
6 files changed, 301 insertions, 63 deletions
diff --git a/drivers/ddr/altera/sdram_gen5.c b/drivers/ddr/altera/sdram_gen5.c
index a3b914fdfc..3ea5a7c0c0 100644
--- a/drivers/ddr/altera/sdram_gen5.c
+++ b/drivers/ddr/altera/sdram_gen5.c
@@ -434,8 +434,10 @@ static void sdr_load_regs(struct socfpga_sdr_ctrl *sdr_ctrl,
debug("Configuring DRAMODT\n");
writel(cfg->dram_odt, &sdr_ctrl->dram_odt);
- debug("Configuring EXTRATIME1\n");
- writel(cfg->extratime1, &sdr_ctrl->extratime1);
+ if (dram_is_ddr(3)) {
+ debug("Configuring EXTRATIME1\n");
+ writel(cfg->extratime1, &sdr_ctrl->extratime1);
+ }
}
/**
diff --git a/drivers/ddr/altera/sequencer.c b/drivers/ddr/altera/sequencer.c
index b85b56efe5..35bda9b34c 100644
--- a/drivers/ddr/altera/sequencer.c
+++ b/drivers/ddr/altera/sequencer.c
@@ -7,6 +7,7 @@
#include <asm/io.h>
#include <asm/arch/sdram.h>
#include <errno.h>
+#include <hang.h>
#include "sequencer.h"
static const struct socfpga_sdr_rw_load_manager *sdr_rw_load_mgr_regs =
@@ -54,6 +55,21 @@ static const struct socfpga_sdr_ctrl *sdr_ctrl =
#define SKIP_DELAY_LOOP_VALUE_OR_ZERO(non_skip_value) \
((non_skip_value) & seq->skip_delay_mask)
+bool dram_is_ddr(const u8 ddr)
+{
+ const struct socfpga_sdram_config *cfg = socfpga_get_sdram_config();
+ const u8 type = (cfg->ctrl_cfg >> SDR_CTRLGRP_CTRLCFG_MEMTYPE_LSB) &
+ SDR_CTRLGRP_CTRLCFG_MEMTYPE_MASK;
+
+ if (ddr == 2 && type == 1) /* DDR2 */
+ return true;
+
+ if (ddr == 3 && type == 2) /* DDR3 */
+ return true;
+
+ return false;
+}
+
static void set_failing_group_stage(struct socfpga_sdrseq *seq,
u32 group, u32 stage, u32 substage)
{
@@ -164,6 +180,8 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
*/
odt_mask_0 = 0x3 & ~(1 << rank);
odt_mask_1 = 0x3;
+ if (dram_is_ddr(2))
+ odt_mask_1 &= ~(1 << rank);
} else {
/*
* - Single-Slot , Dual-Rank (2 CS per DIMM)
@@ -176,10 +194,11 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
}
break;
case 4: /* 4 Ranks */
- /* Read:
+ /*
+ * DDR3 Read, DDR2 Read/Write:
* ----------+-----------------------+
* | ODT |
- * Read From +-----------------------+
+ * +-----------------------+
* Rank | 3 | 2 | 1 | 0 |
* ----------+-----+-----+-----+-----+
* 0 | 0 | 1 | 0 | 0 |
@@ -188,7 +207,7 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
* 3 | 0 | 0 | 1 | 0 |
* ----------+-----+-----+-----+-----+
*
- * Write:
+ * DDR3 Write:
* ----------+-----------------------+
* | ODT |
* Write To +-----------------------+
@@ -203,19 +222,31 @@ static void set_rank_and_odt_mask(struct socfpga_sdrseq *seq,
switch (rank) {
case 0:
odt_mask_0 = 0x4;
- odt_mask_1 = 0x5;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x4;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0x5;
break;
case 1:
odt_mask_0 = 0x8;
- odt_mask_1 = 0xA;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x8;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0xA;
break;
case 2:
odt_mask_0 = 0x1;
- odt_mask_1 = 0x5;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x1;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0x5;
break;
case 3:
odt_mask_0 = 0x2;
- odt_mask_1 = 0xA;
+ if (dram_is_ddr(2))
+ odt_mask_1 = 0x2;
+ else if (dram_is_ddr(3))
+ odt_mask_1 = 0xA;
break;
}
break;
@@ -839,6 +870,12 @@ static void delay_for_n_mem_clocks(struct socfpga_sdrseq *seq,
debug("%s:%d clocks=%u ... end\n", __func__, __LINE__, clocks);
}
+static void delay_for_n_ns(struct socfpga_sdrseq *seq, const u32 ns)
+{
+ delay_for_n_mem_clocks(seq, (ns * seq->misccfg->afi_clk_freq *
+ seq->misccfg->afi_rate_ratio) / 1000);
+}
+
/**
* rw_mgr_mem_init_load_regs() - Load instruction registers
* @cntr0: Counter 0 value
@@ -872,14 +909,59 @@ static void rw_mgr_mem_init_load_regs(struct socfpga_sdrseq *seq,
}
/**
- * rw_mgr_mem_load_user() - Load user calibration values
+ * rw_mgr_mem_load_user_ddr2() - Load user calibration values for DDR2
+ * @handoff: Indicate whether this is initialization or handoff phase
+ *
+ * Load user calibration values and optionally precharge the banks.
+ */
+static void rw_mgr_mem_load_user_ddr2(struct socfpga_sdrseq *seq,
+ const int handoff)
+{
+ u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
+ RW_MGR_RUN_SINGLE_GROUP_OFFSET;
+ u32 r;
+
+ for (r = 0; r < seq->rwcfg->mem_number_of_ranks; r++) {
+ /* set rank */
+ set_rank_and_odt_mask(seq, r, RW_MGR_ODT_MODE_OFF);
+
+ /* precharge all banks ... */
+ writel(seq->rwcfg->precharge_all, grpaddr);
+
+ writel(seq->rwcfg->emr2, grpaddr);
+ writel(seq->rwcfg->emr3, grpaddr);
+ writel(seq->rwcfg->emr, grpaddr);
+
+ if (handoff) {
+ writel(seq->rwcfg->mr_user, grpaddr);
+ continue;
+ }
+
+ writel(seq->rwcfg->mr_dll_reset, grpaddr);
+
+ writel(seq->rwcfg->precharge_all, grpaddr);
+
+ writel(seq->rwcfg->refresh, grpaddr);
+ delay_for_n_ns(seq, 200);
+ writel(seq->rwcfg->refresh, grpaddr);
+ delay_for_n_ns(seq, 200);
+
+ writel(seq->rwcfg->mr_calib, grpaddr);
+ writel(/*seq->rwcfg->*/0x0b, grpaddr); // EMR_OCD_ENABLE
+ writel(seq->rwcfg->emr, grpaddr);
+ delay_for_n_mem_clocks(seq, 200);
+ }
+}
+
+/**
+ * rw_mgr_mem_load_user_ddr3() - Load user calibration values
* @fin1: Final instruction 1
* @fin2: Final instruction 2
* @precharge: If 1, precharge the banks at the end
*
* Load user calibration values and optionally precharge the banks.
*/
-static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq,
+static void rw_mgr_mem_load_user_ddr3(struct socfpga_sdrseq *seq,
const u32 fin1, const u32 fin2,
const int precharge)
{
@@ -936,6 +1018,25 @@ static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq,
}
/**
+ * rw_mgr_mem_load_user() - Load user calibration values
+ * @fin1: Final instruction 1
+ * @fin2: Final instruction 2
+ * @precharge: If 1, precharge the banks at the end
+ *
+ * Load user calibration values and optionally precharge the banks.
+ */
+static void rw_mgr_mem_load_user(struct socfpga_sdrseq *seq,
+ const u32 fin1, const u32 fin2,
+ const int precharge)
+{
+ if (dram_is_ddr(2))
+ rw_mgr_mem_load_user_ddr2(seq, precharge);
+ else if (dram_is_ddr(3))
+ rw_mgr_mem_load_user_ddr3(seq, fin1, fin2, precharge);
+ else
+ hang();
+}
+/**
* rw_mgr_mem_initialize() - Initialize RW Manager
*
* Initialize RW Manager.
@@ -945,8 +1046,10 @@ static void rw_mgr_mem_initialize(struct socfpga_sdrseq *seq)
debug("%s:%d\n", __func__, __LINE__);
/* The reset / cke part of initialization is broadcasted to all ranks */
- writel(RW_MGR_RANK_ALL, SDR_PHYGRP_RWMGRGRP_ADDRESS |
- RW_MGR_SET_CS_AND_ODT_MASK_OFFSET);
+ if (dram_is_ddr(3)) {
+ writel(RW_MGR_RANK_ALL, SDR_PHYGRP_RWMGRGRP_ADDRESS |
+ RW_MGR_SET_CS_AND_ODT_MASK_OFFSET);
+ }
/*
* Here's how you load register for a loop
@@ -979,29 +1082,38 @@ static void rw_mgr_mem_initialize(struct socfpga_sdrseq *seq)
/* Indicate that memory is stable. */
writel(1, &phy_mgr_cfg->reset_mem_stbl);
- /*
- * transition the RESET to high
- * Wait for 500us
- */
+ if (dram_is_ddr(2)) {
+ writel(seq->rwcfg->nop, SDR_PHYGRP_RWMGRGRP_ADDRESS |
+ RW_MGR_RUN_SINGLE_GROUP_OFFSET);
- /*
- * 500us @ 266MHz (3.75 ns) ~ 134000 clock cycles
- * If a and b are the number of iteration in 2 nested loops
- * it takes the following number of cycles to complete the operation
- * number_of_cycles = ((2 + n) * a + 2) * b
- * where n is the number of instruction in the inner loop
- * One possible solution is n = 2 , a = 131 , b = 256 => a = 83,
- * b = FF
- */
- rw_mgr_mem_init_load_regs(seq, seq->misccfg->treset_cntr0_val,
- seq->misccfg->treset_cntr1_val,
- seq->misccfg->treset_cntr2_val,
- seq->rwcfg->init_reset_1_cke_0);
+ /* Bring up clock enable. */
- /* Bring up clock enable. */
+ /* tXRP < 400 ck cycles */
+ delay_for_n_ns(seq, 400);
+ } else if (dram_is_ddr(3)) {
+ /*
+ * transition the RESET to high
+ * Wait for 500us
+ */
+
+ /*
+ * 500us @ 266MHz (3.75 ns) ~ 134000 clock cycles
+ * If a and b are the number of iteration in 2 nested loops
+ * it takes the following number of cycles to complete the
+ * operation number_of_cycles = ((2 + n) * a + 2) * b
+ * where n is the number of instruction in the inner loop
+ * One possible solution is
+ * n = 2 , a = 131 , b = 256 => a = 83, b = FF
+ */
+ rw_mgr_mem_init_load_regs(seq, seq->misccfg->treset_cntr0_val,
+ seq->misccfg->treset_cntr1_val,
+ seq->misccfg->treset_cntr2_val,
+ seq->rwcfg->init_reset_1_cke_0);
+ /* Bring up clock enable. */
- /* tXRP < 250 ck cycles */
- delay_for_n_mem_clocks(seq, 250);
+ /* tXRP < 250 ck cycles */
+ delay_for_n_mem_clocks(seq, 250);
+ }
rw_mgr_mem_load_user(seq, seq->rwcfg->mrs0_dll_reset_mirr,
seq->rwcfg->mrs0_dll_reset, 0);
@@ -3769,16 +3881,26 @@ static void initialize_tracking(struct socfpga_sdrseq *seq)
&sdr_reg_file->delays);
/* mux delay */
- writel((seq->rwcfg->idle << 24) | (seq->rwcfg->activate_1 << 16) |
- (seq->rwcfg->sgle_read << 8) | (seq->rwcfg->precharge_all << 0),
- &sdr_reg_file->trk_rw_mgr_addr);
+ if (dram_is_ddr(2)) {
+ writel(0, &sdr_reg_file->trk_rw_mgr_addr);
+ } else if (dram_is_ddr(3)) {
+ writel((seq->rwcfg->idle << 24) |
+ (seq->rwcfg->activate_1 << 16) |
+ (seq->rwcfg->sgle_read << 8) |
+ (seq->rwcfg->precharge_all << 0),
+ &sdr_reg_file->trk_rw_mgr_addr);
+ }
writel(seq->rwcfg->mem_if_read_dqs_width,
&sdr_reg_file->trk_read_dqs_width);
/* trefi [7:0] */
- writel((seq->rwcfg->refresh_all << 24) | (1000 << 0),
- &sdr_reg_file->trk_rfsh);
+ if (dram_is_ddr(2)) {
+ writel(1000 << 0, &sdr_reg_file->trk_rfsh);
+ } else if (dram_is_ddr(3)) {
+ writel((seq->rwcfg->refresh_all << 24) | (1000 << 0),
+ &sdr_reg_file->trk_rfsh);
+ }
}
int sdram_calibration_full(struct socfpga_sdr *sdr)
diff --git a/drivers/ddr/altera/sequencer.h b/drivers/ddr/altera/sequencer.h
index 4a03c3fdf9..c72a683ffe 100644
--- a/drivers/ddr/altera/sequencer.h
+++ b/drivers/ddr/altera/sequencer.h
@@ -279,5 +279,6 @@ struct socfpga_sdrseq {
};
int sdram_calibration_full(struct socfpga_sdr *sdr);
+bool dram_is_ddr(const u8 ddr);
#endif /* _SEQUENCER_H_ */
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c
index 93ec9cfdb6..105ce94c71 100644
--- a/drivers/reset/reset-socfpga.c
+++ b/drivers/reset/reset-socfpga.c
@@ -18,6 +18,7 @@
#include <dm/of_access.h>
#include <env.h>
#include <reset-uclass.h>
+#include <wait_bit.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/sizes.h>
@@ -80,7 +81,10 @@ static int socfpga_reset_deassert(struct reset_ctl *reset_ctl)
int offset = id % (reg_width * BITS_PER_BYTE);
clrbits_le32(data->modrst_base + (bank * BANK_INCREMENT), BIT(offset));
- return 0;
+
+ return wait_for_bit_le32(data->modrst_base + (bank * BANK_INCREMENT),
+ BIT(offset),
+ false, 500, false);
}
static int socfpga_reset_request(struct reset_ctl *reset_ctl)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2b8064dfae..77354ad209 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -45,6 +45,13 @@ config ULP_WATCHDOG
help
Say Y here to enable i.MX7ULP watchdog driver.
+config DESIGNWARE_WATCHDOG
+ bool "Designware watchdog timer support"
+ select HW_WATCHDOG if !WDT
+ help
+ Enable this to support Designware Watchdog Timer IP, present e.g.
+ on Altera SoCFPGA SoCs.
+
config WDT
bool "Enable driver model for watchdog timer drivers"
depends on DM
diff --git a/drivers/watchdog/designware_wdt.c b/drivers/watchdog/designware_wdt.c
index c668567c66..1024a04596 100644
--- a/drivers/watchdog/designware_wdt.c
+++ b/drivers/watchdog/designware_wdt.c
@@ -3,8 +3,11 @@
* Copyright (C) 2013 Altera Corporation <www.altera.com>
*/
+#include <clk.h>
#include <common.h>
-#include <watchdog.h>
+#include <dm.h>
+#include <reset.h>
+#include <wdt.h>
#include <asm/io.h>
#include <asm/utils.h>
@@ -14,49 +17,52 @@
#define DW_WDT_CR_EN_OFFSET 0x00
#define DW_WDT_CR_RMOD_OFFSET 0x01
-#define DW_WDT_CR_RMOD_VAL 0x00
#define DW_WDT_CRR_RESTART_VAL 0x76
+struct designware_wdt_priv {
+ void __iomem *base;
+ unsigned int clk_khz;
+};
+
/*
* Set the watchdog time interval.
* Counter is 32 bit.
*/
-static int designware_wdt_settimeout(unsigned int timeout)
+static int designware_wdt_settimeout(void __iomem *base, unsigned int clk_khz,
+ unsigned int timeout)
{
signed int i;
/* calculate the timeout range value */
- i = (log_2_n_round_up(timeout * CONFIG_DW_WDT_CLOCK_KHZ)) - 16;
- if (i > 15)
- i = 15;
- if (i < 0)
- i = 0;
+ i = log_2_n_round_up(timeout * clk_khz) - 16;
+ i = clamp(i, 0, 15);
+
+ writel(i | (i << 4), base + DW_WDT_TORR);
- writel((i | (i << 4)), (CONFIG_DW_WDT_BASE + DW_WDT_TORR));
return 0;
}
-static void designware_wdt_enable(void)
+static void designware_wdt_enable(void __iomem *base)
{
- writel(((DW_WDT_CR_RMOD_VAL << DW_WDT_CR_RMOD_OFFSET) |
- (0x1 << DW_WDT_CR_EN_OFFSET)),
- (CONFIG_DW_WDT_BASE + DW_WDT_CR));
+ writel(BIT(DW_WDT_CR_EN_OFFSET), base + DW_WDT_CR);
}
-static unsigned int designware_wdt_is_enabled(void)
+static unsigned int designware_wdt_is_enabled(void __iomem *base)
{
- unsigned long val;
- val = readl((CONFIG_DW_WDT_BASE + DW_WDT_CR));
- return val & 0x1;
+ return readl(base + DW_WDT_CR) & BIT(0);
}
-#if defined(CONFIG_HW_WATCHDOG)
-void hw_watchdog_reset(void)
+static void designware_wdt_reset_common(void __iomem *base)
{
- if (designware_wdt_is_enabled())
+ if (designware_wdt_is_enabled(base))
/* restart the watchdog counter */
- writel(DW_WDT_CRR_RESTART_VAL,
- (CONFIG_DW_WDT_BASE + DW_WDT_CRR));
+ writel(DW_WDT_CRR_RESTART_VAL, base + DW_WDT_CRR);
+}
+
+#if !CONFIG_IS_ENABLED(WDT)
+void hw_watchdog_reset(void)
+{
+ designware_wdt_reset_common((void __iomem *)CONFIG_DW_WDT_BASE);
}
void hw_watchdog_init(void)
@@ -64,10 +70,106 @@ void hw_watchdog_init(void)
/* reset to disable the watchdog */
hw_watchdog_reset();
/* set timer in miliseconds */
- designware_wdt_settimeout(CONFIG_WATCHDOG_TIMEOUT_MSECS);
+ designware_wdt_settimeout((void __iomem *)CONFIG_DW_WDT_BASE,
+ CONFIG_DW_WDT_CLOCK_KHZ,
+ CONFIG_WATCHDOG_TIMEOUT_MSECS);
/* enable the watchdog */
- designware_wdt_enable();
+ designware_wdt_enable((void __iomem *)CONFIG_DW_WDT_BASE);
/* reset the watchdog */
hw_watchdog_reset();
}
+#else
+static int designware_wdt_reset(struct udevice *dev)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+
+ designware_wdt_reset_common(priv->base);
+
+ return 0;
+}
+
+static int designware_wdt_stop(struct udevice *dev)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+
+ designware_wdt_reset(dev);
+ writel(0, priv->base + DW_WDT_CR);
+
+ return 0;
+}
+
+static int designware_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+
+ designware_wdt_stop(dev);
+
+ /* set timer in miliseconds */
+ designware_wdt_settimeout(priv->base, priv->clk_khz, timeout);
+
+ designware_wdt_enable(priv->base);
+
+ /* reset the watchdog */
+ return designware_wdt_reset(dev);
+}
+
+static int designware_wdt_probe(struct udevice *dev)
+{
+ struct designware_wdt_priv *priv = dev_get_priv(dev);
+ __maybe_unused int ret;
+
+ priv->base = dev_remap_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+#if CONFIG_IS_ENABLED(CLK)
+ struct clk clk;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ priv->clk_khz = clk_get_rate(&clk);
+ if (!priv->clk_khz)
+ return -EINVAL;
+#else
+ priv->clk_khz = CONFIG_DW_WDT_CLOCK_KHZ;
+#endif
+
+#if CONFIG_IS_ENABLED(DM_RESET)
+ struct reset_ctl_bulk resets;
+
+ ret = reset_get_bulk(dev, &resets);
+ if (ret)
+ return ret;
+
+ ret = reset_deassert_bulk(&resets);
+ if (ret)
+ return ret;
+#endif
+
+ /* reset to disable the watchdog */
+ return designware_wdt_stop(dev);
+}
+
+static const struct wdt_ops designware_wdt_ops = {
+ .start = designware_wdt_start,
+ .reset = designware_wdt_reset,
+ .stop = designware_wdt_stop,
+};
+
+static const struct udevice_id designware_wdt_ids[] = {
+ { .compatible = "snps,dw-wdt"},
+ {}
+};
+
+U_BOOT_DRIVER(designware_wdt) = {
+ .name = "designware_wdt",
+ .id = UCLASS_WDT,
+ .of_match = designware_wdt_ids,
+ .priv_auto_alloc_size = sizeof(struct designware_wdt_priv),
+ .probe = designware_wdt_probe,
+ .ops = &designware_wdt_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
#endif