summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/ata/ahci.c1
-rw-r--r--drivers/bus/mvebu-mbus.c2
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/clk-composite.c7
-rw-r--r--drivers/clk/clk-divider.c35
-rw-r--r--drivers/clk/clk-gate.c2
-rw-r--r--drivers/clk/clk-mux.c24
-rw-r--r--drivers/clk/clk-qoric.c665
-rw-r--r--drivers/clk/clk-stm32mp1.c1988
-rw-r--r--drivers/clk/clk.c3
-rw-r--r--drivers/clk/imx/clk-vf610.c2
-rw-r--r--drivers/clk/imx/clk.h33
-rw-r--r--drivers/clk/mvebu/common.c4
-rw-r--r--drivers/clk/mxs/clk-imx23.c2
-rw-r--r--drivers/clk/mxs/clk-imx28.c2
-rw-r--r--drivers/clk/mxs/clk.h2
-rw-r--r--drivers/clk/rockchip/clk-pll.c3
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c3
-rw-r--r--drivers/clk/rockchip/clk-rk3288.c3
-rw-r--r--drivers/clk/rockchip/clk.c16
-rw-r--r--drivers/clk/rockchip/clk.h1
-rw-r--r--drivers/clk/tegra/clk-periph.c2
-rw-r--r--drivers/clocksource/Kconfig2
-rw-r--r--drivers/clocksource/armv8-timer.c1
-rw-r--r--drivers/clocksource/mvebu.c2
-rw-r--r--drivers/crypto/Kconfig2
-rw-r--r--drivers/ddr/Kconfig1
-rw-r--r--drivers/ddr/Makefile1
-rw-r--r--drivers/ddr/fsl/Kconfig16
-rw-r--r--drivers/ddr/fsl/Makefile12
-rw-r--r--drivers/ddr/fsl/arm_ddr_gen3.c204
-rw-r--r--drivers/ddr/fsl/ctrl_regs.c2539
-rw-r--r--drivers/ddr/fsl/ddr1_dimm_params.c319
-rw-r--r--drivers/ddr/fsl/ddr2_dimm_params.c320
-rw-r--r--drivers/ddr/fsl/ddr3_dimm_params.c325
-rw-r--r--drivers/ddr/fsl/ddr4_dimm_params.c352
-rw-r--r--drivers/ddr/fsl/fsl_ddr.h234
-rw-r--r--drivers/ddr/fsl/fsl_ddr_gen4.c501
-rw-r--r--drivers/ddr/fsl/lc_common_dimm_params.c542
-rw-r--r--drivers/ddr/fsl/main.c444
-rw-r--r--drivers/ddr/fsl/options.c1133
-rw-r--r--drivers/ddr/fsl/util.c98
-rw-r--r--drivers/i2c/Kconfig6
-rw-r--r--drivers/i2c/busses/Kconfig2
-rw-r--r--drivers/i2c/busses/i2c-imx.c4
-rw-r--r--drivers/mci/imx-esdhc-pbl.c62
-rw-r--r--drivers/mci/imx-esdhc.c12
-rw-r--r--drivers/mfd/lp3972.c1
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/fsl-fman.c1333
-rw-r--r--drivers/net/mvneta.c2
-rw-r--r--drivers/net/phy/Kconfig5
-rw-r--r--drivers/net/phy/Makefile3
-rw-r--r--drivers/net/phy/phy-core.c175
-rw-r--r--drivers/net/phy/realtek.c172
-rw-r--r--drivers/of/base.c4
-rw-r--r--drivers/pinctrl/Kconfig2
-rw-r--r--drivers/serial/Kconfig4
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_stm32.c246
-rw-r--r--drivers/serial/serial_stm32.h48
-rw-r--r--drivers/usb/Kconfig13
-rw-r--r--drivers/usb/core/hub.c1
-rw-r--r--drivers/usb/core/usb.c24
-rw-r--r--drivers/usb/dwc3/core.c21
-rw-r--r--drivers/usb/otg/twl4030.c2
-rw-r--r--drivers/usb/storage/transport.c87
-rw-r--r--drivers/usb/storage/transport.h2
-rw-r--r--drivers/usb/storage/usb.c484
-rw-r--r--drivers/usb/storage/usb.h19
-rw-r--r--drivers/video/Kconfig2
-rw-r--r--drivers/video/of_display_timing.c15
-rw-r--r--drivers/w1/Kconfig4
-rw-r--r--drivers/w1/slaves/w1_ds2431.c2
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/imxwd.c46
80 files changed, 12200 insertions, 468 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d6fbcbf..f75da26 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -40,5 +40,6 @@ source "drivers/crypto/Kconfig"
source "drivers/memory/Kconfig"
source "drivers/soc/imx/Kconfig"
source "drivers/nvme/Kconfig"
+source "drivers/ddr/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 65fd488..fb7fcd3 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -40,3 +40,4 @@ obj-$(CONFIG_AIODEV) += aiodev/
obj-y += memory/
obj-y += soc/imx/
obj-y += nvme/
+obj-y += ddr/
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index b416105..e6723a3 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -27,7 +27,6 @@
#include <io.h>
#include <of.h>
#include <malloc.h>
-#include <scsi.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <disks.h>
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 965a221..4b7edb5 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -436,7 +436,7 @@ orion5x_4win_mbus_data __maybe_unused = {
};
static const struct mvebu_mbus_soc_data
-orion5x_2win_mbus_data__maybe_unused = {
+orion5x_2win_mbus_data __maybe_unused = {
.num_wins = 8,
.num_remappable_wins = 2,
.win_cfg_offset = orion5x_mbus_win_offset,
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e81e0e7..1bd5f90 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -15,4 +15,6 @@ obj-$(CONFIG_SOC_QCA_AR9331) += clk-ar933x.o
obj-$(CONFIG_SOC_QCA_AR9344) += clk-ar9344.o
obj-$(CONFIG_ARCH_IMX) += imx/
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
+obj-$(CONFIG_ARCH_STM32MP) += clk-stm32mp1.o
obj-$(CONFIG_MACH_VEXPRESS) += vexpress/
+obj-$(CONFIG_ARCH_LAYERSCAPE) += clk-qoric.o
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 5d21a0e..e21ab98 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -54,7 +54,10 @@ static unsigned long clk_composite_recalc_rate(struct clk *clk,
struct clk_composite *composite = to_clk_composite(clk);
struct clk *rate_clk = composite->rate_clk;
- return rate_clk ? rate_clk->ops->recalc_rate(rate_clk, parent_rate) : 0;
+ if (rate_clk)
+ return rate_clk->ops->recalc_rate(rate_clk, parent_rate);
+
+ return parent_rate;
}
static long clk_composite_round_rate(struct clk *clk, unsigned long rate,
@@ -113,7 +116,7 @@ static struct clk_ops clk_composite_ops = {
};
struct clk *clk_register_composite(const char *name,
- const char **parent_names, int num_parents,
+ const char * const *parent_names, int num_parents,
struct clk *mux_clk,
struct clk *rate_clk,
struct clk *gate_clk,
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 7b1bdde..d9d3407 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -203,8 +203,12 @@ static int clk_divider_bestdiv(struct clk *clk, unsigned long rate,
static long clk_divider_round_rate(struct clk *clk, unsigned long rate,
unsigned long *parent_rate)
{
+ struct clk_divider *divider = container_of(clk, struct clk_divider, clk);
int div;
+ if (divider->flags & CLK_DIVIDER_READ_ONLY)
+ return clk_divider_recalc_rate(clk, *parent_rate);
+
div = clk_divider_bestdiv(clk, rate, parent_rate);
return *parent_rate / div;
@@ -217,6 +221,9 @@ static int clk_divider_set_rate(struct clk *clk, unsigned long rate,
unsigned int div, value;
u32 val;
+ if (divider->flags & CLK_DIVIDER_READ_ONLY)
+ return 0;
+
if (clk->flags & CLK_SET_RATE_PARENT) {
unsigned long best_parent_rate = parent_rate;
div = clk_divider_bestdiv(clk, rate, &best_parent_rate);
@@ -249,7 +256,8 @@ struct clk_ops clk_divider_ops = {
};
struct clk *clk_divider_alloc(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width, unsigned flags)
+ unsigned clk_flags, void __iomem *reg, u8 shift,
+ u8 width, unsigned div_flags)
{
struct clk_divider *div = xzalloc(sizeof(*div));
@@ -257,9 +265,10 @@ struct clk *clk_divider_alloc(const char *name, const char *parent,
div->reg = reg;
div->width = width;
div->parent = parent;
+ div->flags = div_flags;
div->clk.ops = &clk_divider_ops;
div->clk.name = name;
- div->clk.flags = flags;
+ div->clk.flags = clk_flags;
div->clk.parent_names = &div->parent;
div->clk.num_parents = 1;
@@ -273,13 +282,14 @@ void clk_divider_free(struct clk *clk)
free(d);
}
-struct clk *clk_divider(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width, unsigned flags)
+struct clk *clk_divider(const char *name, const char *parent, unsigned clk_flags,
+ void __iomem *reg, u8 shift, u8 width, unsigned div_flags)
{
struct clk *d;
int ret;
- d = clk_divider_alloc(name , parent, reg, shift, width, flags);
+ d = clk_divider_alloc(name , parent, clk_flags, reg, shift, width,
+ div_flags);
ret = clk_register(d);
if (ret) {
@@ -291,12 +301,13 @@ struct clk *clk_divider(const char *name, const char *parent,
}
struct clk *clk_divider_one_based(const char *name, const char *parent,
- void __iomem *reg, u8 shift, u8 width, unsigned flags)
+ unsigned clk_flags, void __iomem *reg, u8 shift,
+ u8 width, unsigned div_flags)
{
struct clk_divider *div;
struct clk *clk;
- clk = clk_divider(name, parent, reg, shift, width, flags);
+ clk = clk_divider(name, parent, clk_flags, reg, shift, width, div_flags);
if (IS_ERR(clk))
return clk;
@@ -306,9 +317,10 @@ struct clk *clk_divider_one_based(const char *name, const char *parent,
return clk;
}
-struct clk *clk_divider_table(const char *name,
- const char *parent, void __iomem *reg, u8 shift, u8 width,
- const struct clk_div_table *table, unsigned flags)
+struct clk *clk_divider_table(const char *name, const char *parent,
+ unsigned clk_flags, void __iomem *reg, u8 shift,
+ u8 width, const struct clk_div_table *table,
+ unsigned div_flags)
{
struct clk_divider *div = xzalloc(sizeof(*div));
const struct clk_div_table *clkt;
@@ -318,9 +330,10 @@ struct clk *clk_divider_table(const char *name,
div->reg = reg;
div->width = width;
div->parent = parent;
+ div->flags = div_flags;
div->clk.ops = &clk_divider_ops;
div->clk.name = name;
- div->clk.flags = flags;
+ div->clk.flags = clk_flags;
div->clk.parent_names = &div->parent;
div->clk.num_parents = 1;
div->table = table;
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 89240ff..b2b1605 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -56,7 +56,7 @@ static void clk_gate_disable(struct clk *clk)
clk_gate_endisable(clk, 0);
}
-static int clk_gate_is_enabled(struct clk *clk)
+int clk_gate_is_enabled(struct clk *clk)
{
struct clk_gate *g = container_of(clk, struct clk_gate, clk);
u32 val;
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index d5fe640..e9cb614 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -33,6 +33,13 @@ static int clk_mux_set_parent(struct clk *clk, u8 idx)
struct clk_mux *m = container_of(clk, struct clk_mux, clk);
u32 val;
+ if (m->flags & CLK_MUX_READ_ONLY) {
+ if (clk_mux_get_parent(clk) != idx)
+ return -EPERM;
+ else
+ return 0;
+ }
+
val = readl(m->reg);
val &= ~(((1 << m->width) - 1) << m->shift);
val |= idx << m->shift;
@@ -51,18 +58,19 @@ struct clk_ops clk_mux_ops = {
.set_parent = clk_mux_set_parent,
};
-struct clk *clk_mux_alloc(const char *name, void __iomem *reg,
- u8 shift, u8 width, const char **parents, u8 num_parents,
- unsigned flags)
+struct clk *clk_mux_alloc(const char *name, unsigned clk_flags, void __iomem *reg,
+ u8 shift, u8 width, const char * const *parents, u8 num_parents,
+ unsigned mux_flags)
{
struct clk_mux *m = xzalloc(sizeof(*m));
m->reg = reg;
m->shift = shift;
m->width = width;
+ m->flags = mux_flags;
m->clk.ops = &clk_mux_ops;
m->clk.name = name;
- m->clk.flags = flags;
+ m->clk.flags = clk_flags;
m->clk.parent_names = parents;
m->clk.num_parents = num_parents;
@@ -76,13 +84,15 @@ void clk_mux_free(struct clk *clk_mux)
free(m);
}
-struct clk *clk_mux(const char *name, void __iomem *reg,
- u8 shift, u8 width, const char **parents, u8 num_parents, unsigned flags)
+struct clk *clk_mux(const char *name, unsigned clk_flags, void __iomem *reg,
+ u8 shift, u8 width, const char * const *parents,
+ u8 num_parents, unsigned mux_flags)
{
struct clk *m;
int ret;
- m = clk_mux_alloc(name, reg, shift, width, parents, num_parents, flags);
+ m = clk_mux_alloc(name, clk_flags, reg, shift, width, parents,
+ num_parents, mux_flags);
ret = clk_register(m);
if (ret) {
diff --git a/drivers/clk/clk-qoric.c b/drivers/clk/clk-qoric.c
new file mode 100644
index 0000000..c40c6e9
--- /dev/null
+++ b/drivers/clk/clk-qoric.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * clock driver for Freescale QorIQ SoCs.
+ */
+
+#define pr_fmt(fmt) "clk-qoric: " fmt
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <io.h>
+#include <linux/kernel.h>
+#include <of_address.h>
+#include <of.h>
+#include <asm-generic/div64.h>
+
+#define PLL_DIV1 0
+#define PLL_DIV2 1
+#define PLL_DIV3 2
+#define PLL_DIV4 3
+
+#define PLATFORM_PLL 0
+#define CGA_PLL1 1
+#define CGA_PLL2 2
+#define CGA_PLL3 3
+#define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */
+#define CGB_PLL1 4
+#define CGB_PLL2 5
+
+struct clockgen_pll_div {
+ struct clk *clk;
+ char name[32];
+};
+
+struct clockgen_pll {
+ struct clockgen_pll_div div[8];
+};
+
+#define CLKSEL_VALID 1
+
+struct clockgen_sourceinfo {
+ u32 flags; /* CLKSEL_xxx */
+ int pll; /* CGx_PLLn */
+ int div; /* PLL_DIVn */
+};
+
+#define NUM_MUX_PARENTS 16
+
+struct clockgen_muxinfo {
+ struct clockgen_sourceinfo clksel[NUM_MUX_PARENTS];
+};
+
+#define NUM_HWACCEL 5
+#define NUM_CMUX 8
+
+struct clockgen;
+
+#define CG_PLL_8BIT 2 /* PLLCnGSR[CFG] is 8 bits, not 6 */
+#define CG_VER3 4 /* version 3 cg: reg layout different */
+#define CG_LITTLE_ENDIAN 8
+
+struct clockgen_chipinfo {
+ const char *compat;
+ const struct clockgen_muxinfo *cmux_groups[2];
+ const struct clockgen_muxinfo *hwaccel[NUM_HWACCEL];
+ void (*init_periph)(struct clockgen *cg);
+ int cmux_to_group[NUM_CMUX]; /* -1 terminates if fewer than NUM_CMUX */
+ u32 pll_mask; /* 1 << n bit set if PLL n is valid */
+ u32 flags; /* CG_xxx */
+};
+
+struct clockgen {
+ struct device_node *node;
+ void __iomem *regs;
+ struct clockgen_chipinfo info; /* mutable copy */
+ struct clk *sysclk, *coreclk;
+ struct clockgen_pll pll[6];
+ struct clk *cmux[NUM_CMUX];
+ struct clk *hwaccel[NUM_HWACCEL];
+ struct clk *fman[2];
+};
+
+static struct clockgen clockgen;
+
+static void cg_out(struct clockgen *cg, u32 val, u32 __iomem *reg)
+{
+ if (cg->info.flags & CG_LITTLE_ENDIAN)
+ iowrite32(val, reg);
+ else
+ iowrite32be(val, reg);
+}
+
+static u32 cg_in(struct clockgen *cg, u32 __iomem *reg)
+{
+ u32 val;
+
+ if (cg->info.flags & CG_LITTLE_ENDIAN)
+ val = ioread32(reg);
+ else
+ val = ioread32be(reg);
+
+ return val;
+}
+
+static const struct clockgen_muxinfo t1023_cmux = {
+ {
+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ }
+};
+
+static const struct clockgen_muxinfo t1040_cmux = {
+ {
+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
+ [1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ [4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
+ [5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ }
+};
+
+static const struct clockgen_muxinfo clockgen2_cmux_cga12 = {
+ {
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV4 },
+ },
+};
+
+static const struct clockgen_muxinfo clockgen2_cmux_cgb = {
+ {
+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV1 },
+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGB_PLL1, PLL_DIV4 },
+ {},
+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV1 },
+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGB_PLL2, PLL_DIV4 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1043a_hwa1 = {
+ {
+ {},
+ {},
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
+ {},
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1043a_hwa2 = {
+ {
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1046a_hwa1 = {
+ {
+ {},
+ {},
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
+ { CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1046a_hwa2 = {
+ {
+ {},
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
+ { CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
+ {},
+ {},
+ { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ },
+};
+
+static const struct clockgen_muxinfo ls1012a_cmux = {
+ {
+ [0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
+ {},
+ [2] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
+ }
+};
+
+static void __init t2080_init_periph(struct clockgen *cg)
+{
+ cg->fman[0] = cg->hwaccel[0];
+}
+
+static const struct clockgen_chipinfo chipinfo_ls1021a = {
+ .compat = "fsl,ls1021a-clockgen",
+ .cmux_groups = { &t1023_cmux },
+ .cmux_to_group = { 0, -1 },
+ .pll_mask = 0x03,
+};
+
+static const struct clockgen_chipinfo chipinfo_ls1043a = {
+ .compat = "fsl,ls1043a-clockgen",
+ .init_periph = t2080_init_periph,
+ .cmux_groups = { &t1040_cmux },
+ .hwaccel = { &ls1043a_hwa1, &ls1043a_hwa2 },
+ .cmux_to_group = { 0, -1 },
+ .pll_mask = 0x07,
+ .flags = CG_PLL_8BIT,
+};
+
+static const struct clockgen_chipinfo chipinfo_ls1046a = {
+ .compat = "fsl,ls1046a-clockgen",
+ .init_periph = t2080_init_periph,
+ .cmux_groups = { &t1040_cmux },
+ .hwaccel = { &ls1046a_hwa1, &ls1046a_hwa2 },
+ .cmux_to_group = { 0, -1 },
+ .pll_mask = 0x07,
+ .flags = CG_PLL_8BIT,
+};
+
+static const struct clockgen_chipinfo chipinfo_ls1088a = {
+ .compat = "fsl,ls1088a-clockgen",
+ .cmux_groups = { &clockgen2_cmux_cga12 },
+ .cmux_to_group = { 0, 0, -1 },
+ .pll_mask = 0x07,
+ .flags = CG_VER3 | CG_LITTLE_ENDIAN,
+};
+
+static const struct clockgen_chipinfo chipinfo_ls1012a = {
+ .compat = "fsl,ls1012a-clockgen",
+ .cmux_groups = { &ls1012a_cmux },
+ .cmux_to_group = { 0, -1 },
+ .pll_mask = 0x03,
+};
+
+static const struct clockgen_chipinfo chipinfo_ls2080a = {
+ .compat = "fsl,ls2080a-clockgen",
+ .cmux_groups = { &clockgen2_cmux_cga12, &clockgen2_cmux_cgb },
+ .cmux_to_group = { 0, 0, 1, 1, -1 },
+ .pll_mask = 0x37,
+ .flags = CG_VER3 | CG_LITTLE_ENDIAN,
+};
+
+struct mux_hwclock {
+ struct clk clk;
+ struct clockgen *cg;
+ const struct clockgen_muxinfo *info;
+ u32 __iomem *reg;
+ int num_parents;
+};
+
+#define to_mux_hwclock(p) container_of(p, struct mux_hwclock, clk)
+#define CLKSEL_MASK 0x78000000
+#define CLKSEL_SHIFT 27
+
+static int mux_set_parent(struct clk *clk, u8 idx)
+{
+ struct mux_hwclock *hwc = to_mux_hwclock(clk);
+
+ if (idx >= hwc->num_parents)
+ return -EINVAL;
+
+ cg_out(hwc->cg, (idx << CLKSEL_SHIFT) & CLKSEL_MASK, hwc->reg);
+
+ return 0;
+}
+
+static int mux_get_parent(struct clk *clk)
+{
+ struct mux_hwclock *hwc = to_mux_hwclock(clk);
+
+ return (cg_in(hwc->cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT;
+}
+
+static const struct clk_ops cmux_ops = {
+ .get_parent = mux_get_parent,
+ .set_parent = mux_set_parent,
+};
+
+/*
+ * Don't allow setting for now, as the clock options haven't been
+ * sanitized for additional restrictions.
+ */
+static const struct clk_ops hwaccel_ops = {
+ .get_parent = mux_get_parent,
+};
+
+static const struct clockgen_pll_div *get_pll_div(struct clockgen *cg,
+ struct mux_hwclock *hwc,
+ int idx)
+{
+ const struct clockgen_sourceinfo *clksel = &hwc->info->clksel[idx];
+ int pll, div;
+
+ if (!(clksel->flags & CLKSEL_VALID))
+ return NULL;
+
+ pll = clksel->pll;
+ div = clksel->div;
+
+ return &cg->pll[pll].div[div];
+}
+
+static struct clk * __init create_mux_common(struct clockgen *cg,
+ struct mux_hwclock *hwc,
+ const struct clk_ops *ops,
+ const char *fmt, int idx)
+{
+ struct clk *clk = &hwc->clk;
+ const struct clockgen_pll_div *div;
+ const char **parent_names;
+ int i, ret;
+
+ parent_names = xzalloc(sizeof(char *) * NUM_MUX_PARENTS);
+
+ for (i = 0; i < NUM_MUX_PARENTS; i++) {
+ div = get_pll_div(cg, hwc, i);
+ if (!div)
+ continue;
+
+ parent_names[i] = div->name;
+ }
+
+ clk->name = xasprintf(fmt, idx);;
+ clk->ops = ops;
+ clk->parent_names = parent_names;
+ clk->num_parents = hwc->num_parents = i;
+ hwc->cg = cg;
+
+ ret = clk_register(clk);
+ if (ret) {
+ pr_err("%s: Couldn't register %s: %d\n", __func__, clk->name, ret);
+ kfree(hwc);
+ return NULL;
+ }
+
+ return clk;
+}
+
+static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
+{
+ struct mux_hwclock *hwc;
+ const struct clockgen_pll_div *div;
+ u32 clksel;
+
+ hwc = xzalloc(sizeof(*hwc));
+
+ if (cg->info.flags & CG_VER3)
+ hwc->reg = cg->regs + 0x70000 + 0x20 * idx;
+ else
+ hwc->reg = cg->regs + 0x20 * idx;
+
+ hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]];
+
+ /*
+ * Find the rate for the default clksel, and treat it as the
+ * maximum rated core frequency. If this is an incorrect
+ * assumption, certain clock options (possibly including the
+ * default clksel) may be inappropriately excluded on certain
+ * chips.
+ */
+ clksel = (cg_in(cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT;
+ div = get_pll_div(cg, hwc, clksel);
+ if (!div) {
+ kfree(hwc);
+ return NULL;
+ }
+
+ return create_mux_common(cg, hwc, &cmux_ops, "cg-cmux%d", idx);
+}
+
+static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx)
+{
+ struct mux_hwclock *hwc;
+
+ hwc = xzalloc(sizeof(*hwc));
+
+ hwc->reg = cg->regs + 0x20 * idx + 0x10;
+ hwc->info = cg->info.hwaccel[idx];
+
+ return create_mux_common(cg, hwc, &hwaccel_ops, "cg-hwaccel%d", idx);
+}
+
+static void __init create_muxes(struct clockgen *cg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cg->cmux); i++) {
+ if (cg->info.cmux_to_group[i] < 0)
+ break;
+ if (cg->info.cmux_to_group[i] >=
+ ARRAY_SIZE(cg->info.cmux_groups)) {
+ continue;
+ }
+
+ cg->cmux[i] = create_one_cmux(cg, i);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cg->hwaccel); i++) {
+ if (!cg->info.hwaccel[i])
+ continue;
+
+ cg->hwaccel[i] = create_one_hwaccel(cg, i);
+ }
+}
+
+#define PLL_KILL BIT(31)
+
+static void __init create_one_pll(struct clockgen *cg, int idx)
+{
+ u32 __iomem *reg;
+ u32 mult;
+ struct clockgen_pll *pll = &cg->pll[idx];
+ const char *input = cg->sysclk->name;
+ int i;
+
+ if (!(cg->info.pll_mask & (1 << idx)))
+ return;
+
+ if (cg->coreclk && idx != PLATFORM_PLL) {
+ if (IS_ERR(cg->coreclk))
+ return;
+
+ input = cg->coreclk->name;
+ }
+
+ if (cg->info.flags & CG_VER3) {
+ switch (idx) {
+ case PLATFORM_PLL:
+ reg = cg->regs + 0x60080;
+ break;
+ case CGA_PLL1:
+ reg = cg->regs + 0x80;
+ break;
+ case CGA_PLL2:
+ reg = cg->regs + 0xa0;
+ break;
+ case CGB_PLL1:
+ reg = cg->regs + 0x10080;
+ break;
+ case CGB_PLL2:
+ reg = cg->regs + 0x100a0;
+ break;
+ default:
+ pr_warn("index %d\n", idx);
+ return;
+ }
+ } else {
+ if (idx == PLATFORM_PLL)
+ reg = cg->regs + 0xc00;
+ else
+ reg = cg->regs + 0x800 + 0x20 * (idx - 1);
+ }
+
+ /* Get the multiple of PLL */
+ mult = cg_in(cg, reg);
+
+ /* Check if this PLL is disabled */
+ if (mult & PLL_KILL) {
+ pr_debug("%s(): pll %p disabled\n", __func__, reg);
+ return;
+ }
+
+ if ((cg->info.flags & CG_VER3) ||
+ ((cg->info.flags & CG_PLL_8BIT) && idx != PLATFORM_PLL))
+ mult = (mult & GENMASK(8, 1)) >> 1;
+ else
+ mult = (mult & GENMASK(6, 1)) >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(pll->div); i++) {
+ struct clk *clk;
+
+ /*
+ * For platform PLL, there are 8 divider clocks.
+ * For core PLL, there are 4 divider clocks at most.
+ */
+ if (idx != PLATFORM_PLL && i >= 4)
+ break;
+
+ snprintf(pll->div[i].name, sizeof(pll->div[i].name),
+ "cg-pll%d-div%d", idx, i + 1);
+
+ clk = clk_fixed_factor(pll->div[i].name, input, mult, i + 1, 0);
+ if (IS_ERR(clk)) {
+ pr_err("%s: %s: register failed %ld\n",
+ __func__, pll->div[i].name, PTR_ERR(clk));
+ continue;
+ }
+
+ pll->div[i].clk = clk;
+ }
+}
+
+static void __init create_plls(struct clockgen *cg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cg->pll); i++)
+ create_one_pll(cg, i);
+}
+
+static struct clk *clockgen_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct clockgen *cg = data;
+ struct clk *clk;
+ struct clockgen_pll *pll;
+ u32 type, idx;
+
+ if (clkspec->args_count < 2) {
+ pr_err("%s: insufficient phandle args\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ type = clkspec->args[0];
+ idx = clkspec->args[1];
+
+ switch (type) {
+ case 0:
+ if (idx != 0)
+ goto bad_args;
+ clk = cg->sysclk;
+ break;
+ case 1:
+ if (idx >= ARRAY_SIZE(cg->cmux))
+ goto bad_args;
+ clk = cg->cmux[idx];
+ break;
+ case 2:
+ if (idx >= ARRAY_SIZE(cg->hwaccel))
+ goto bad_args;
+ clk = cg->hwaccel[idx];
+ break;
+ case 3:
+ if (idx >= ARRAY_SIZE(cg->fman))
+ goto bad_args;
+ clk = cg->fman[idx];
+ break;
+ case 4:
+ pll = &cg->pll[PLATFORM_PLL];
+ if (idx >= ARRAY_SIZE(pll->div))
+ goto bad_args;
+ clk = pll->div[idx].clk;
+ break;
+ case 5:
+ if (idx != 0)
+ goto bad_args;
+ clk = cg->coreclk;
+ if (IS_ERR(clk))
+ clk = NULL;
+ break;
+ default:
+ goto bad_args;
+ }
+
+ if (!clk)
+ return ERR_PTR(-ENOENT);
+ return clk;
+
+bad_args:
+ pr_err("%s: Bad phandle args %u %u\n", __func__, type, idx);
+ return ERR_PTR(-EINVAL);
+}
+
+static void __init clockgen_init(struct device_node *np,
+ const struct clockgen_chipinfo *chipinfo)
+{
+ int ret;
+
+ clockgen.node = np;
+ clockgen.regs = of_iomap(np, 0);
+ if (!clockgen.regs) {
+ pr_err("of_iomap failed for %s\n", np->full_name);
+ return;
+ }
+
+ clockgen.info = *chipinfo;
+
+ clockgen.sysclk = of_clk_get(clockgen.node, 0);
+ if (IS_ERR(clockgen.sysclk)) {
+ pr_err("sysclk not found: %s\n", strerrorp(clockgen.sysclk));
+ return;
+ }
+
+ clockgen.coreclk = of_clk_get(clockgen.node, 1);
+ if (IS_ERR(clockgen.coreclk))
+ clockgen.coreclk = NULL;
+
+ create_plls(&clockgen);
+ create_muxes(&clockgen);
+
+ if (clockgen.info.init_periph)
+ clockgen.info.init_periph(&clockgen);
+
+ ret = of_clk_add_provider(np, clockgen_clk_get, &clockgen);
+ if (ret) {
+ pr_err("Couldn't register clk provider for node %s: %d\n",
+ np->full_name, ret);
+ }
+
+ return;
+}
+
+static void __maybe_unused clockgen_init_ls1012a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls1012a);
+}
+
+static void __maybe_unused clockgen_init_ls1021a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls1021a);
+}
+
+static void __maybe_unused clockgen_init_ls1043a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls1043a);
+}
+
+static void __maybe_unused clockgen_init_ls1046a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls1046a);
+}
+
+static void __maybe_unused clockgen_init_ls1088a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls1088a);
+}
+
+static void __maybe_unused clockgen_init_ls2080a(struct device_node *np)
+{
+ clockgen_init(np, &chipinfo_ls2080a);
+}
+
+#ifdef CONFIG_ARCH_LS1012
+CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init_ls1012a);
+#endif
+#ifdef CONFIG_ARCH_LS1021
+CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init_ls1021a);
+#endif
+#ifdef CONFIG_ARCH_LS1043
+CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init_ls1043a);
+#endif
+#ifdef CONFIG_ARCH_LS1046
+CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init_ls1046a);
+#endif
+#ifdef CONFIG_ARCH_LS1088
+CLK_OF_DECLARE(qoriq_clockgen_ls1088a, "fsl,ls1088a-clockgen", clockgen_init_ls1088a);
+#endif
+#ifdef CONFIG_ARCH_LS2080
+CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init_ls2080a);
+#endif
diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c
new file mode 100644
index 0000000..6016c5b
--- /dev/null
+++ b/drivers/clk/clk-stm32mp1.c
@@ -0,0 +1,1988 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Olivier Bideau <olivier.bideau@st.com> for STMicroelectronics.
+ * Author: Gabriel Fernandez <gabriel.fernandez@st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+#include <of.h>
+#include <of_address.h>
+#include <asm-generic/div64.h>
+
+#include <dt-bindings/clock/stm32mp1-clks.h>
+
+#define RCC_OCENSETR 0x0C
+#define RCC_HSICFGR 0x18
+#define RCC_RDLSICR 0x144
+#define RCC_PLL1CR 0x80
+#define RCC_PLL1CFGR1 0x84
+#define RCC_PLL1CFGR2 0x88
+#define RCC_PLL2CR 0x94
+#define RCC_PLL2CFGR1 0x98
+#define RCC_PLL2CFGR2 0x9C
+#define RCC_PLL3CR 0x880
+#define RCC_PLL3CFGR1 0x884
+#define RCC_PLL3CFGR2 0x888
+#define RCC_PLL4CR 0x894
+#define RCC_PLL4CFGR1 0x898
+#define RCC_PLL4CFGR2 0x89C
+#define RCC_APB1ENSETR 0xA00
+#define RCC_APB2ENSETR 0xA08
+#define RCC_APB3ENSETR 0xA10
+#define RCC_APB4ENSETR 0x200
+#define RCC_APB5ENSETR 0x208
+#define RCC_AHB2ENSETR 0xA18
+#define RCC_AHB3ENSETR 0xA20
+#define RCC_AHB4ENSETR 0xA28
+#define RCC_AHB5ENSETR 0x210
+#define RCC_AHB6ENSETR 0x218
+#define RCC_AHB6LPENSETR 0x318
+#define RCC_RCK12SELR 0x28
+#define RCC_RCK3SELR 0x820
+#define RCC_RCK4SELR 0x824
+#define RCC_MPCKSELR 0x20
+#define RCC_ASSCKSELR 0x24
+#define RCC_MSSCKSELR 0x48
+#define RCC_SPI6CKSELR 0xC4
+#define RCC_SDMMC12CKSELR 0x8F4
+#define RCC_SDMMC3CKSELR 0x8F8
+#define RCC_FMCCKSELR 0x904
+#define RCC_I2C46CKSELR 0xC0
+#define RCC_I2C12CKSELR 0x8C0
+#define RCC_I2C35CKSELR 0x8C4
+#define RCC_UART1CKSELR 0xC8
+#define RCC_QSPICKSELR 0x900
+#define RCC_ETHCKSELR 0x8FC
+#define RCC_RNG1CKSELR 0xCC
+#define RCC_RNG2CKSELR 0x920
+#define RCC_GPUCKSELR 0x938
+#define RCC_USBCKSELR 0x91C
+#define RCC_STGENCKSELR 0xD4
+#define RCC_SPDIFCKSELR 0x914
+#define RCC_SPI2S1CKSELR 0x8D8
+#define RCC_SPI2S23CKSELR 0x8DC
+#define RCC_SPI2S45CKSELR 0x8E0
+#define RCC_CECCKSELR 0x918
+#define RCC_LPTIM1CKSELR 0x934
+#define RCC_LPTIM23CKSELR 0x930
+#define RCC_LPTIM45CKSELR 0x92C
+#define RCC_UART24CKSELR 0x8E8
+#define RCC_UART35CKSELR 0x8EC
+#define RCC_UART6CKSELR 0x8E4
+#define RCC_UART78CKSELR 0x8F0
+#define RCC_FDCANCKSELR 0x90C
+#define RCC_SAI1CKSELR 0x8C8
+#define RCC_SAI2CKSELR 0x8CC
+#define RCC_SAI3CKSELR 0x8D0
+#define RCC_SAI4CKSELR 0x8D4
+#define RCC_ADCCKSELR 0x928
+#define RCC_MPCKDIVR 0x2C
+#define RCC_DSICKSELR 0x924
+#define RCC_CPERCKSELR 0xD0
+#define RCC_MCO1CFGR 0x800
+#define RCC_MCO2CFGR 0x804
+#define RCC_BDCR 0x140
+#define RCC_AXIDIVR 0x30
+#define RCC_MCUDIVR 0x830
+#define RCC_APB1DIVR 0x834
+#define RCC_APB2DIVR 0x838
+#define RCC_APB3DIVR 0x83C
+#define RCC_APB4DIVR 0x3C
+#define RCC_APB5DIVR 0x40
+#define RCC_TIMG1PRER 0x828
+#define RCC_TIMG2PRER 0x82C
+#define RCC_RTCDIVR 0x44
+#define RCC_DBGCFGR 0x80C
+
+#define RCC_CLR 0x4
+
+static const char * const ref12_parents[] = {
+ "ck_hsi", "ck_hse"
+};
+
+static const char * const ref3_parents[] = {
+ "ck_hsi", "ck_hse", "ck_csi"
+};
+
+static const char * const ref4_parents[] = {
+ "ck_hsi", "ck_hse", "ck_csi"
+};
+
+static const char * const cpu_src[] = {
+ "ck_hsi", "ck_hse", "pll1_p"
+};
+
+static const char * const axi_src[] = {
+ "ck_hsi", "ck_hse", "pll2_p", "pll3_p"
+};
+
+static const char * const per_src[] = {
+ "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const mcu_src[] = {
+ "ck_hsi", "ck_hse", "ck_csi", "pll3_p"
+};
+
+static const char * const sdmmc12_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_hsi"
+};
+
+static const char * const sdmmc3_src[] = {
+ "ck_mcu", "pll3_r", "pll4_p", "ck_hsi"
+};
+
+static const char * const fmc_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_per"
+};
+
+static const char * const qspi_src[] = {
+ "ck_axi", "pll3_r", "pll4_p", "ck_per"
+};
+
+static const char * const eth_src[] = {
+ "pll4_p", "pll3_q"
+};
+
+static const char * const rng_src[] = {
+ "ck_csi", "pll4_r", "ck_lse", "ck_lsi"
+};
+
+static const char * const usbphy_src[] = {
+ "ck_hse", "pll4_r", "clk-hse-div2"
+};
+
+static const char * const usbo_src[] = {
+ "pll4_r", "ck_usbo_48m"
+};
+
+static const char * const stgen_src[] = {
+ "ck_hsi", "ck_hse"
+};
+
+static const char * const spdif_src[] = {
+ "pll4_p", "pll3_q", "ck_hsi"
+};
+
+static const char * const spi123_src[] = {
+ "pll4_p", "pll3_q", "i2s_ckin", "ck_per", "pll3_r"
+};
+
+static const char * const spi45_src[] = {
+ "pclk2", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const spi6_src[] = {
+ "pclk5", "pll4_q", "ck_hsi", "ck_csi", "ck_hse", "pll3_q"
+};
+
+static const char * const cec_src[] = {
+ "ck_lse", "ck_lsi", "ck_csi"
+};
+
+static const char * const i2c12_src[] = {
+ "pclk1", "pll4_r", "ck_hsi", "ck_csi"
+};
+
+static const char * const i2c35_src[] = {
+ "pclk1", "pll4_r", "ck_hsi", "ck_csi"
+};
+
+static const char * const i2c46_src[] = {
+ "pclk5", "pll3_q", "ck_hsi", "ck_csi"
+};
+
+static const char * const lptim1_src[] = {
+ "pclk1", "pll4_p", "pll3_q", "ck_lse", "ck_lsi", "ck_per"
+};
+
+static const char * const lptim23_src[] = {
+ "pclk3", "pll4_q", "ck_per", "ck_lse", "ck_lsi"
+};
+
+static const char * const lptim45_src[] = {
+ "pclk3", "pll4_p", "pll3_q", "ck_lse", "ck_lsi", "ck_per"
+};
+
+static const char * const usart1_src[] = {
+ "pclk5", "pll3_q", "ck_hsi", "ck_csi", "pll4_q", "ck_hse"
+};
+
+static const char * const usart234578_src[] = {
+ "pclk1", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const usart6_src[] = {
+ "pclk2", "pll4_q", "ck_hsi", "ck_csi", "ck_hse"
+};
+
+static const char * const fdcan_src[] = {
+ "ck_hse", "pll3_q", "pll4_q"
+};
+
+static const char * const sai_src[] = {
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per"
+};
+
+static const char * const sai2_src[] = {
+ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb"
+};
+
+static const char * const adc12_src[] = {
+ "pll4_q", "ck_per"
+};
+
+static const char * const dsi_src[] = {
+ "ck_dsi_phy", "pll4_p"
+};
+
+static const char * const rtc_src[] = {
+ "off", "ck_lse", "ck_lsi", "ck_hse_rtc"
+};
+
+static const char * const mco1_src[] = {
+ "ck_hsi", "ck_hse", "ck_csi", "ck_lsi", "ck_lse"
+};
+
+static const char * const mco2_src[] = {
+ "ck_mpu", "ck_axi", "ck_mcu", "pll4_p", "ck_hse", "ck_hsi"
+};
+
+static const char * const ck_trace_src[] = {
+ "ck_axi"
+};
+
+static const struct clk_div_table axi_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 },
+ { 4, 4 }, { 5, 4 }, { 6, 4 }, { 7, 4 },
+ { 0 },
+};
+
+static const struct clk_div_table mcu_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 },
+ { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 },
+ { 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 },
+ { 0 },
+};
+
+static const struct clk_div_table apb_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 },
+ { 0 },
+};
+
+static const struct clk_div_table ck_trace_div_table[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 },
+ { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 },
+ { 0 },
+};
+
+#define MAX_MUX_CLK 2
+
+struct stm32_mmux {
+ u8 nbr_clk;
+ struct clk *hws[MAX_MUX_CLK];
+};
+
+struct stm32_clk_mmux {
+ struct clk_mux mux;
+ struct stm32_mmux *mmux;
+};
+
+struct stm32_mgate {
+ u8 nbr_clk;
+ u32 flag;
+};
+
+struct stm32_clk_mgate {
+ struct clk_gate gate;
+ struct stm32_mgate *mgate;
+ u32 mask;
+};
+
+struct clock_config {
+ u32 id;
+ const char *name;
+ const char *parent_name;
+ const char * const *parent_names;
+ int num_parents;
+ unsigned long flags;
+ void *cfg;
+ struct clk * (*func)(void __iomem *base, const struct clock_config *cfg);
+};
+
+#define NO_ID ~0
+
+struct gate_cfg {
+ u32 reg_off;
+ u8 bit_idx;
+ u8 gate_flags;
+};
+
+struct fixed_factor_cfg {
+ unsigned int mult;
+ unsigned int div;
+};
+
+struct div_cfg {
+ u32 reg_off;
+ u8 shift;
+ u8 width;
+ u8 div_flags;
+ const struct clk_div_table *table;
+};
+
+struct mux_cfg {
+ u32 reg_off;
+ u8 shift;
+ u8 width;
+ u8 mux_flags;
+ u32 *table;
+};
+
+struct stm32_gate_cfg {
+ struct gate_cfg *gate;
+ struct stm32_mgate *mgate;
+ const struct clk_ops *ops;
+};
+
+struct stm32_div_cfg {
+ struct div_cfg *div;
+ const struct clk_ops *ops;
+};
+
+struct stm32_mux_cfg {
+ struct mux_cfg *mux;
+ struct stm32_mmux *mmux;
+ const struct clk_ops *ops;
+};
+
+/* STM32 Composite clock */
+struct stm32_composite_cfg {
+ const struct stm32_gate_cfg *gate;
+ const struct stm32_div_cfg *div;
+ const struct stm32_mux_cfg *mux;
+};
+
+static struct clk *
+_clk_hw_register_gate(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ struct gate_cfg *gate_cfg = cfg->cfg;
+
+ return clk_gate(cfg->name, cfg->parent_name, gate_cfg->reg_off + base,
+ gate_cfg->bit_idx, cfg->flags, gate_cfg->gate_flags);
+}
+
+static struct clk *
+_clk_hw_register_fixed_factor(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ struct fixed_factor_cfg *ff_cfg = cfg->cfg;
+
+ return clk_fixed_factor(cfg->name, cfg->parent_name, ff_cfg->mult,
+ ff_cfg->div, cfg->flags);
+}
+
+static struct clk *
+_clk_hw_register_divider_table(void __iomem *base,
+ const struct clock_config *cfg)
+{
+
+ struct div_cfg *div_cfg = cfg->cfg;
+
+ if (div_cfg->table)
+ return clk_divider_table(cfg->name, cfg->parent_name, cfg->flags,
+ div_cfg->reg_off + base, div_cfg->shift,
+ div_cfg->width, div_cfg->table,
+ div_cfg->div_flags);
+ else
+ return clk_divider(cfg->name, cfg->parent_name, cfg->flags,
+ div_cfg->reg_off + base, div_cfg->shift,
+ div_cfg->width, div_cfg->div_flags);
+}
+
+static struct clk *
+_clk_hw_register_mux(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ struct mux_cfg *mux_cfg = cfg->cfg;
+
+ return clk_mux(cfg->name,cfg->flags, mux_cfg->reg_off + base, mux_cfg->shift,
+ mux_cfg->width, cfg->parent_names, cfg->num_parents,
+ mux_cfg->mux_flags);
+}
+
+/* MP1 Gate clock with set & clear registers */
+
+static int mp1_gate_clk_enable(struct clk *clk)
+{
+ if (!clk_gate_ops.is_enabled(clk))
+ clk_gate_ops.enable(clk);
+
+ return 0;
+}
+
+static void mp1_gate_clk_disable(struct clk *clk)
+{
+ struct clk_gate *gate = to_clk_gate(clk);
+
+ if (clk_gate_ops.is_enabled(clk)) {
+ writel(BIT(gate->shift), gate->reg + RCC_CLR);
+ }
+}
+
+static const struct clk_ops mp1_gate_clk_ops = {
+ .enable = mp1_gate_clk_enable,
+ .disable = mp1_gate_clk_disable,
+ .is_enabled = clk_gate_is_enabled,
+};
+
+static struct clk *_get_stm32_mux(void __iomem *base,
+ const struct stm32_mux_cfg *cfg)
+{
+ struct stm32_clk_mmux *mmux;
+ struct clk_mux *mux;
+ struct clk *mux_hw;
+
+ if (cfg->mmux) {
+ mmux = kzalloc(sizeof(*mmux), GFP_KERNEL);
+ if (!mmux)
+ return ERR_PTR(-ENOMEM);
+
+ mmux->mux.reg = cfg->mux->reg_off + base;
+ mmux->mux.shift = cfg->mux->shift;
+ mmux->mux.width = cfg->mux->width;
+ mmux->mmux = cfg->mmux;
+ mux_hw = &mmux->mux.clk;
+ cfg->mmux->hws[cfg->mmux->nbr_clk++] = mux_hw;
+ mux = &mmux->mux;
+ } else {
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->reg = cfg->mux->reg_off + base;
+ mux->shift = cfg->mux->shift;
+ mux->width = cfg->mux->width;
+ mux_hw = &mux->clk;
+ }
+
+ if (cfg->ops)
+ mux->clk.ops = cfg->ops;
+ else
+ mux->clk.ops = &clk_mux_ops;
+
+ return mux_hw;
+}
+
+static struct clk *_get_stm32_div(void __iomem *base,
+ const struct stm32_div_cfg *cfg)
+{
+ struct clk_divider *div;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->reg = cfg->div->reg_off + base;
+ div->shift = cfg->div->shift;
+ div->width = cfg->div->width;
+ div->flags = cfg->div->div_flags;
+ div->table = cfg->div->table;
+
+ if (cfg->ops)
+ div->clk.ops = cfg->ops;
+ else
+ div->clk.ops = &clk_divider_ops;
+
+ return &div->clk;
+}
+
+static struct clk_gate *
+_get_stm32_gate(void __iomem *base,
+ const struct stm32_gate_cfg *cfg)
+{
+ struct stm32_clk_mgate *mgate;
+ struct clk_gate *gate;
+
+ if (cfg->mgate) {
+ mgate = kzalloc(sizeof(*mgate), GFP_KERNEL);
+ if (!mgate)
+ return ERR_PTR(-ENOMEM);
+
+ mgate->gate.reg = cfg->gate->reg_off + base;
+ mgate->gate.shift = cfg->gate->bit_idx;
+ mgate->gate.flags = cfg->gate->gate_flags;
+ mgate->mask = BIT(cfg->mgate->nbr_clk++);
+
+ mgate->mgate = cfg->mgate;
+
+ gate = &mgate->gate;
+
+ } else {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = cfg->gate->reg_off + base;
+ gate->shift = cfg->gate->bit_idx;
+ gate->flags = cfg->gate->gate_flags;
+ }
+
+ if (cfg->ops)
+ gate->clk.ops = cfg->ops;
+ else
+ gate->clk.ops = &clk_gate_ops;
+
+ return gate;
+}
+
+static struct clk *
+clk_stm32_register_gate_ops(const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *base,
+ const struct stm32_gate_cfg *cfg)
+{
+ struct clk *clk;
+ struct clk_gate *gate;
+ int ret;
+
+ gate = _get_stm32_gate(base, cfg);
+ if (IS_ERR(gate))
+ return ERR_PTR(-ENOMEM);
+
+ gate->parent = parent_name;
+ clk = &gate->clk;
+ clk->name = name;
+ clk->parent_names = &gate->parent;
+ clk->num_parents = 1;
+ clk->flags = flags;
+
+ ret = clk_register(clk);
+ if (ret)
+ clk = ERR_PTR(ret);
+
+ return clk;
+}
+
+static struct clk *
+clk_stm32_register_composite(const char *name, const char * const *parent_names,
+ int num_parents, void __iomem *base,
+ const struct stm32_composite_cfg *cfg,
+ unsigned long flags)
+{
+ struct clk *mux_hw, *div_hw, *gate_hw;
+ struct clk_gate *gate;
+
+ mux_hw = NULL;
+ div_hw = NULL;
+ gate_hw = NULL;
+
+ if (cfg->mux)
+ mux_hw = _get_stm32_mux(base, cfg->mux);
+
+ if (cfg->div)
+ div_hw = _get_stm32_div(base, cfg->div);
+
+ if (cfg->gate) {
+ gate = _get_stm32_gate(base, cfg->gate);
+ gate_hw = &gate->clk;
+ }
+
+ return clk_register_composite(name, parent_names, num_parents,
+ mux_hw, div_hw, gate_hw, flags);
+}
+
+#define to_clk_mgate(_gate) container_of(_gate, struct stm32_clk_mgate, gate)
+
+static int mp1_mgate_clk_enable(struct clk *clk)
+{
+ struct clk_gate *gate = to_clk_gate(clk);
+ struct stm32_clk_mgate *clk_mgate = to_clk_mgate(gate);
+
+ clk_mgate->mgate->flag |= clk_mgate->mask;
+
+ mp1_gate_clk_enable(clk);
+
+ return 0;
+}
+
+static void mp1_mgate_clk_disable(struct clk *clk)
+{
+ struct clk_gate *gate = to_clk_gate(clk);
+ struct stm32_clk_mgate *clk_mgate = to_clk_mgate(gate);
+
+ clk_mgate->mgate->flag &= ~clk_mgate->mask;
+
+ if (clk_mgate->mgate->flag == 0)
+ mp1_gate_clk_disable(clk);
+}
+
+static const struct clk_ops mp1_mgate_clk_ops = {
+ .enable = mp1_mgate_clk_enable,
+ .disable = mp1_mgate_clk_disable,
+ .is_enabled = clk_gate_is_enabled,
+
+};
+
+#define to_clk_mmux(_mux) container_of(_mux, struct stm32_clk_mmux, mux)
+
+static int clk_mmux_get_parent(struct clk *clk)
+{
+ return clk_mux_ops.get_parent(clk);
+}
+
+static int clk_mmux_set_parent(struct clk *clk, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(clk);
+ struct stm32_clk_mmux *clk_mmux = to_clk_mmux(mux);
+ struct clk *parent;
+ int ret, n;
+
+ ret = clk_mux_ops.set_parent(clk, index);
+ if (ret)
+ return ret;
+
+ parent = clk_get_parent(clk);
+
+ for (n = 0; n < clk_mmux->mmux->nbr_clk; n++)
+ clk_set_parent(clk_mmux->mmux->hws[n], parent);
+
+ return 0;
+}
+
+static const struct clk_ops clk_mmux_ops = {
+ .get_parent = clk_mmux_get_parent,
+ .set_parent = clk_mmux_set_parent,
+};
+
+/* STM32 PLL */
+struct stm32_pll_obj {
+ void __iomem *reg;
+ const char *parent;
+ struct clk clk;
+};
+
+#define to_pll(clk) container_of(clk, struct stm32_pll_obj, clk)
+
+#define PLL_ON BIT(0)
+#define PLL_RDY BIT(1)
+#define DIVN_MASK 0x1FF
+#define DIVM_MASK 0x3F
+#define DIVM_SHIFT 16
+#define DIVN_SHIFT 0
+#define FRAC_OFFSET 0xC
+#define FRAC_MASK 0x1FFF
+#define FRAC_SHIFT 3
+#define FRACLE BIT(16)
+
+static int __pll_is_enabled(struct clk *clk)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(clk);
+
+ return readl(clk_elem->reg) & PLL_ON;
+}
+
+#define TIMEOUT 5
+
+static int pll_enable(struct clk *clk)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(clk);
+ u32 reg;
+ unsigned int timeout = TIMEOUT;
+ int bit_status = 0;
+
+ if (__pll_is_enabled(clk))
+ goto unlock;
+
+ reg = readl(clk_elem->reg);
+ reg |= PLL_ON;
+ writel(reg, clk_elem->reg);
+
+ /* We can't use readl_poll_timeout() because we can be blocked if
+ * someone enables this clock before clocksource changes.
+ * Only jiffies counter is available. Jiffies are incremented by
+ * interruptions and enable op does not allow to be interrupted.
+ */
+ do {
+ bit_status = !(readl(clk_elem->reg) & PLL_RDY);
+
+ if (bit_status)
+ udelay(120);
+
+ } while (bit_status && --timeout);
+
+unlock:
+ return bit_status;
+}
+
+static void pll_disable(struct clk *clk)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(clk);
+ u32 reg;
+
+ reg = readl(clk_elem->reg);
+ reg &= ~PLL_ON;
+ writel(reg, clk_elem->reg);
+}
+
+static u32 pll_frac_val(struct clk *clk)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(clk);
+ u32 reg, frac = 0;
+
+ reg = readl(clk_elem->reg + FRAC_OFFSET);
+ if (reg & FRACLE)
+ frac = (reg >> FRAC_SHIFT) & FRAC_MASK;
+
+ return frac;
+}
+
+static unsigned long pll_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct stm32_pll_obj *clk_elem = to_pll(clk);
+ u32 reg;
+ u32 frac, divm, divn;
+ u64 rate, rate_frac = 0;
+
+ reg = readl(clk_elem->reg + 4);
+
+ divm = ((reg >> DIVM_SHIFT) & DIVM_MASK) + 1;
+ divn = ((reg >> DIVN_SHIFT) & DIVN_MASK) + 1;
+ rate = (u64)parent_rate * divn;
+
+ do_div(rate, divm);
+
+ frac = pll_frac_val(clk);
+ if (frac) {
+ rate_frac = (u64)parent_rate * (u64)frac;
+ do_div(rate_frac, (divm * 8192));
+ }
+
+ return rate + rate_frac;
+}
+
+static int pll_is_enabled(struct clk *clk)
+{
+ int ret;
+
+ ret = __pll_is_enabled(clk);
+
+ return ret;
+}
+
+static const struct clk_ops pll_ops = {
+ .enable = pll_enable,
+ .disable = pll_disable,
+ .recalc_rate = pll_recalc_rate,
+ .is_enabled = pll_is_enabled,
+};
+
+static struct clk *clk_register_pll(const char *name,
+ const char *parent_name,
+ void __iomem *reg,
+ unsigned long flags)
+{
+ struct stm32_pll_obj *element;
+ struct clk *clk;
+ int err;
+
+ element = kzalloc(sizeof(*element), GFP_KERNEL);
+ if (!element)
+ return ERR_PTR(-ENOMEM);
+
+ element->parent = parent_name;
+
+ clk = &element->clk;
+
+ clk->name = name;
+ clk->ops = &pll_ops;
+ clk->flags = flags;
+ clk->parent_names = &element->parent;
+ clk->num_parents = 1;
+
+ element->reg = reg;
+
+ err = clk_register(clk);
+
+ if (err) {
+ kfree(element);
+ return ERR_PTR(err);
+ }
+
+ return clk;
+}
+
+/* Kernel Timer */
+struct timer_cker {
+ void __iomem *apbdiv;
+ void __iomem *timpre;
+ const char *parent;
+ struct clk clk;
+};
+
+#define to_timer_cker(_hw) container_of(_hw, struct timer_cker, clk)
+
+#define APB_DIV_MASK 0x07
+#define TIM_PRE_MASK 0x01
+
+static unsigned long __bestmult(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct timer_cker *tim_ker = to_timer_cker(clk);
+ u32 prescaler;
+ unsigned int mult = 0;
+
+ prescaler = readl(tim_ker->apbdiv) & APB_DIV_MASK;
+ if (prescaler < 2)
+ return 1;
+
+ mult = 2;
+
+ if (rate / parent_rate >= 4)
+ mult = 4;
+
+ return mult;
+}
+
+static long timer_ker_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long factor = __bestmult(clk, rate, *parent_rate);
+
+ return *parent_rate * factor;
+}
+
+static int timer_ker_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct timer_cker *tim_ker = to_timer_cker(clk);
+ unsigned long factor = __bestmult(clk, rate, parent_rate);
+ int ret = 0;
+
+ switch (factor) {
+ case 1:
+ break;
+ case 2:
+ writel(0, tim_ker->timpre);
+ break;
+ case 4:
+ writel(1, tim_ker->timpre);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static unsigned long timer_ker_recalc_rate(struct clk *hw,
+ unsigned long parent_rate)
+{
+ struct timer_cker *tim_ker = to_timer_cker(hw);
+ u32 prescaler, timpre;
+ u32 mul;
+
+ prescaler = readl(tim_ker->apbdiv) & APB_DIV_MASK;
+
+ timpre = readl(tim_ker->timpre) & TIM_PRE_MASK;
+
+ if (!prescaler)
+ return parent_rate;
+
+ mul = (timpre + 1) * 2;
+
+ return parent_rate * mul;
+}
+
+static const struct clk_ops timer_ker_ops = {
+ .recalc_rate = timer_ker_recalc_rate,
+ .round_rate = timer_ker_round_rate,
+ .set_rate = timer_ker_set_rate,
+
+};
+
+static struct clk *clk_register_cktim(const char *name,
+ const char *parent_name,
+ unsigned long flags,
+ void __iomem *apbdiv,
+ void __iomem *timpre)
+{
+ struct timer_cker *tim_ker;
+ struct clk *clk;
+ int err;
+
+ tim_ker = kzalloc(sizeof(*tim_ker), GFP_KERNEL);
+ if (!tim_ker)
+ return ERR_PTR(-ENOMEM);
+
+ clk = &tim_ker->clk;
+ tim_ker->parent = parent_name;
+ clk->name = name;
+ clk->parent_names = &tim_ker->parent;
+ clk->num_parents = 1;
+ clk->ops = &timer_ker_ops;
+ clk->flags = flags;
+
+ tim_ker->apbdiv = apbdiv;
+ tim_ker->timpre = timpre;
+
+ err = clk_register(clk);
+ if (err) {
+ kfree(tim_ker);
+ return ERR_PTR(err);
+ }
+
+ return clk;
+}
+
+struct stm32_pll_cfg {
+ u32 offset;
+};
+
+static struct clk *_clk_register_pll(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ struct stm32_pll_cfg *stm_pll_cfg = cfg->cfg;
+
+ return clk_register_pll(cfg->name, cfg->parent_name,
+ base + stm_pll_cfg->offset, cfg->flags);
+}
+
+struct stm32_cktim_cfg {
+ u32 offset_apbdiv;
+ u32 offset_timpre;
+};
+
+static struct clk *_clk_register_cktim(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ struct stm32_cktim_cfg *cktim_cfg = cfg->cfg;
+
+ return clk_register_cktim(cfg->name, cfg->parent_name, cfg->flags,
+ cktim_cfg->offset_apbdiv + base,
+ cktim_cfg->offset_timpre + base);
+}
+
+static struct clk *
+_clk_stm32_register_gate(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ return clk_stm32_register_gate_ops(cfg->name, cfg->parent_name,
+ cfg->flags, base, cfg->cfg);
+}
+
+static struct clk *
+_clk_stm32_register_composite(void __iomem *base,
+ const struct clock_config *cfg)
+{
+ return clk_stm32_register_composite(cfg->name, cfg->parent_names,
+ cfg->num_parents, base, cfg->cfg,
+ cfg->flags);
+}
+
+#define GATE(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct gate_cfg) {\
+ .reg_off = _offset,\
+ .bit_idx = _bit_idx,\
+ .gate_flags = _gate_flags,\
+ },\
+ .func = _clk_hw_register_gate,\
+}
+
+#define FIXED_FACTOR(_id, _name, _parent, _flags, _mult, _div)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct fixed_factor_cfg) {\
+ .mult = _mult,\
+ .div = _div,\
+ },\
+ .func = _clk_hw_register_fixed_factor,\
+}
+
+#define DIV_TABLE(_id, _name, _parent, _flags, _offset, _shift, _width,\
+ _div_flags, _div_table)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct div_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .div_flags = _div_flags,\
+ .table = _div_table,\
+ },\
+ .func = _clk_hw_register_divider_table,\
+}
+
+#define DIV(_id, _name, _parent, _flags, _offset, _shift, _width, _div_flags)\
+ DIV_TABLE(_id, _name, _parent, _flags, _offset, _shift, _width,\
+ _div_flags, NULL)
+
+#define MUX(_id, _name, _parents, _flags, _offset, _shift, _width, _mux_flags)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_names = _parents,\
+ .num_parents = ARRAY_SIZE(_parents),\
+ .flags = _flags,\
+ .cfg = &(struct mux_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .mux_flags = _mux_flags,\
+ },\
+ .func = _clk_hw_register_mux,\
+}
+
+#define PLL(_id, _name, _parent, _flags, _offset)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct stm32_pll_cfg) {\
+ .offset = _offset,\
+ },\
+ .func = _clk_register_pll,\
+}
+
+#define STM32_CKTIM(_name, _parent, _flags, _offset_apbdiv, _offset_timpre)\
+{\
+ .id = NO_ID,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = &(struct stm32_cktim_cfg) {\
+ .offset_apbdiv = _offset_apbdiv,\
+ .offset_timpre = _offset_timpre,\
+ },\
+ .func = _clk_register_cktim,\
+}
+
+#define STM32_TIM(_id, _name, _parent, _offset_set, _bit_idx)\
+ GATE_MP1(_id, _name, _parent, CLK_SET_RATE_PARENT,\
+ _offset_set, _bit_idx, 0)
+
+/* STM32 GATE */
+#define STM32_GATE(_id, _name, _parent, _flags, _gate)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_name = _parent,\
+ .flags = _flags,\
+ .cfg = (struct stm32_gate_cfg *) {_gate},\
+ .func = _clk_stm32_register_gate,\
+}
+
+#define _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags, _ops)\
+ (&(struct stm32_gate_cfg) {\
+ &(struct gate_cfg) {\
+ .reg_off = _gate_offset,\
+ .bit_idx = _gate_bit_idx,\
+ .gate_flags = _gate_flags,\
+ },\
+ .ops = _ops,\
+ })
+
+#define _STM32_MGATE(_mgate)\
+ (&per_gate_cfg[_mgate])
+
+#define _GATE(_gate_offset, _gate_bit_idx, _gate_flags)\
+ _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\
+ NULL)\
+
+#define _GATE_MP1(_gate_offset, _gate_bit_idx, _gate_flags)\
+ _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\
+ &mp1_gate_clk_ops)\
+
+#define _MGATE_MP1(_mgate)\
+ .gate = &per_gate_cfg[_mgate]
+
+#define GATE_MP1(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\
+ STM32_GATE(_id, _name, _parent, _flags,\
+ _GATE_MP1(_offset, _bit_idx, _gate_flags))
+
+#define MGATE_MP1(_id, _name, _parent, _flags, _mgate)\
+ STM32_GATE(_id, _name, _parent, _flags,\
+ _STM32_MGATE(_mgate))
+
+#define _STM32_DIV(_div_offset, _div_shift, _div_width,\
+ _div_flags, _div_table, _ops)\
+ .div = &(struct stm32_div_cfg) {\
+ &(struct div_cfg) {\
+ .reg_off = _div_offset,\
+ .shift = _div_shift,\
+ .width = _div_width,\
+ .div_flags = _div_flags,\
+ .table = _div_table,\
+ },\
+ .ops = _ops,\
+ }
+
+#define _DIV(_div_offset, _div_shift, _div_width, _div_flags, _div_table)\
+ _STM32_DIV(_div_offset, _div_shift, _div_width,\
+ _div_flags, _div_table, NULL)\
+
+#define _STM32_MUX(_offset, _shift, _width, _mux_flags, _mmux, _ops)\
+ .mux = &(struct stm32_mux_cfg) {\
+ &(struct mux_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .mux_flags = _mux_flags,\
+ .table = NULL,\
+ },\
+ .mmux = _mmux,\
+ .ops = _ops,\
+ }
+
+#define _MUX(_offset, _shift, _width, _mux_flags)\
+ _STM32_MUX(_offset, _shift, _width, _mux_flags, NULL, NULL)\
+
+#define _MMUX(_mmux) .mux = &ker_mux_cfg[_mmux]
+
+#define PARENT(_parent) ((const char *[]) { _parent})
+
+#define _NO_MUX .mux = NULL
+#define _NO_DIV .div = NULL
+#define _NO_GATE .gate = NULL
+
+#define COMPOSITE(_id, _name, _parents, _flags, _gate, _mux, _div)\
+{\
+ .id = _id,\
+ .name = _name,\
+ .parent_names = _parents,\
+ .num_parents = ARRAY_SIZE(_parents),\
+ .flags = _flags,\
+ .cfg = &(struct stm32_composite_cfg) {\
+ _gate,\
+ _mux,\
+ _div,\
+ },\
+ .func = _clk_stm32_register_composite,\
+}
+
+#define PCLK(_id, _name, _parent, _flags, _mgate)\
+ MGATE_MP1(_id, _name, _parent, _flags, _mgate)
+
+#define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\
+ COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\
+ _MGATE_MP1(_mgate),\
+ _MMUX(_mmux),\
+ _NO_DIV)
+
+enum {
+ G_SAI1,
+ G_SAI2,
+ G_SAI3,
+ G_SAI4,
+ G_SPI1,
+ G_SPI2,
+ G_SPI3,
+ G_SPI4,
+ G_SPI5,
+ G_SPI6,
+ G_SPDIF,
+ G_I2C1,
+ G_I2C2,
+ G_I2C3,
+ G_I2C4,
+ G_I2C5,
+ G_I2C6,
+ G_USART2,
+ G_UART4,
+ G_USART3,
+ G_UART5,
+ G_USART1,
+ G_USART6,
+ G_UART7,
+ G_UART8,
+ G_LPTIM1,
+ G_LPTIM2,
+ G_LPTIM3,
+ G_LPTIM4,
+ G_LPTIM5,
+ G_LTDC,
+ G_DSI,
+ G_QSPI,
+ G_FMC,
+ G_SDMMC1,
+ G_SDMMC2,
+ G_SDMMC3,
+ G_USBO,
+ G_USBPHY,
+ G_RNG1,
+ G_RNG2,
+ G_FDCAN,
+ G_DAC12,
+ G_CEC,
+ G_ADC12,
+ G_GPU,
+ G_STGEN,
+ G_DFSDM,
+ G_ADFSDM,
+ G_TIM2,
+ G_TIM3,
+ G_TIM4,
+ G_TIM5,
+ G_TIM6,
+ G_TIM7,
+ G_TIM12,
+ G_TIM13,
+ G_TIM14,
+ G_MDIO,
+ G_TIM1,
+ G_TIM8,
+ G_TIM15,
+ G_TIM16,
+ G_TIM17,
+ G_SYSCFG,
+ G_VREF,
+ G_TMPSENS,
+ G_PMBCTRL,
+ G_HDP,
+ G_IWDG2,
+ G_STGENRO,
+ G_DMA1,
+ G_DMA2,
+ G_DMAMUX,
+ G_DCMI,
+ G_CRYP2,
+ G_HASH2,
+ G_CRC2,
+ G_HSEM,
+ G_IPCC,
+ G_GPIOA,
+ G_GPIOB,
+ G_GPIOC,
+ G_GPIOD,
+ G_GPIOE,
+ G_GPIOF,
+ G_GPIOG,
+ G_GPIOH,
+ G_GPIOI,
+ G_GPIOJ,
+ G_GPIOK,
+ G_MDMA,
+ G_ETHCK,
+ G_ETHTX,
+ G_ETHRX,
+ G_ETHMAC,
+ G_CRC1,
+ G_USBH,
+ G_ETHSTP,
+ G_RTCAPB,
+ G_TZC1,
+ G_TZC2,
+ G_TZPC,
+ G_IWDG1,
+ G_BSEC,
+ G_GPIOZ,
+ G_CRYP1,
+ G_HASH1,
+ G_BKPSRAM,
+
+ G_LAST
+};
+
+static struct stm32_mgate mp1_mgate[G_LAST];
+
+#define _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\
+ _mgate, _ops)\
+ [_id] = {\
+ &(struct gate_cfg) {\
+ .reg_off = _gate_offset,\
+ .bit_idx = _gate_bit_idx,\
+ .gate_flags = _gate_flags,\
+ },\
+ .mgate = _mgate,\
+ .ops = _ops,\
+ }
+
+#define K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags)\
+ _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\
+ NULL, &mp1_gate_clk_ops)
+
+#define K_MGATE(_id, _gate_offset, _gate_bit_idx, _gate_flags)\
+ _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\
+ &mp1_mgate[_id], &mp1_mgate_clk_ops)
+
+/* Peripheral gates */
+static struct stm32_gate_cfg per_gate_cfg[G_LAST] = {
+ /* Multi gates */
+ K_GATE(G_MDIO, RCC_APB1ENSETR, 31, 0),
+ K_MGATE(G_DAC12, RCC_APB1ENSETR, 29, 0),
+ K_MGATE(G_CEC, RCC_APB1ENSETR, 27, 0),
+ K_MGATE(G_SPDIF, RCC_APB1ENSETR, 26, 0),
+ K_MGATE(G_I2C5, RCC_APB1ENSETR, 24, 0),
+ K_MGATE(G_I2C3, RCC_APB1ENSETR, 23, 0),
+ K_MGATE(G_I2C2, RCC_APB1ENSETR, 22, 0),
+ K_MGATE(G_I2C1, RCC_APB1ENSETR, 21, 0),
+ K_MGATE(G_UART8, RCC_APB1ENSETR, 19, 0),
+ K_MGATE(G_UART7, RCC_APB1ENSETR, 18, 0),
+ K_MGATE(G_UART5, RCC_APB1ENSETR, 17, 0),
+ K_MGATE(G_UART4, RCC_APB1ENSETR, 16, 0),
+ K_MGATE(G_USART3, RCC_APB1ENSETR, 15, 0),
+ K_MGATE(G_USART2, RCC_APB1ENSETR, 14, 0),
+ K_MGATE(G_SPI3, RCC_APB1ENSETR, 12, 0),
+ K_MGATE(G_SPI2, RCC_APB1ENSETR, 11, 0),
+ K_MGATE(G_LPTIM1, RCC_APB1ENSETR, 9, 0),
+ K_GATE(G_TIM14, RCC_APB1ENSETR, 8, 0),
+ K_GATE(G_TIM13, RCC_APB1ENSETR, 7, 0),
+ K_GATE(G_TIM12, RCC_APB1ENSETR, 6, 0),
+ K_GATE(G_TIM7, RCC_APB1ENSETR, 5, 0),
+ K_GATE(G_TIM6, RCC_APB1ENSETR, 4, 0),
+ K_GATE(G_TIM5, RCC_APB1ENSETR, 3, 0),
+ K_GATE(G_TIM4, RCC_APB1ENSETR, 2, 0),
+ K_GATE(G_TIM3, RCC_APB1ENSETR, 1, 0),
+ K_GATE(G_TIM2, RCC_APB1ENSETR, 0, 0),
+
+ K_MGATE(G_FDCAN, RCC_APB2ENSETR, 24, 0),
+ K_GATE(G_ADFSDM, RCC_APB2ENSETR, 21, 0),
+ K_GATE(G_DFSDM, RCC_APB2ENSETR, 20, 0),
+ K_MGATE(G_SAI3, RCC_APB2ENSETR, 18, 0),
+ K_MGATE(G_SAI2, RCC_APB2ENSETR, 17, 0),
+ K_MGATE(G_SAI1, RCC_APB2ENSETR, 16, 0),
+ K_MGATE(G_USART6, RCC_APB2ENSETR, 13, 0),
+ K_MGATE(G_SPI5, RCC_APB2ENSETR, 10, 0),
+ K_MGATE(G_SPI4, RCC_APB2ENSETR, 9, 0),
+ K_MGATE(G_SPI1, RCC_APB2ENSETR, 8, 0),
+ K_GATE(G_TIM17, RCC_APB2ENSETR, 4, 0),
+ K_GATE(G_TIM16, RCC_APB2ENSETR, 3, 0),
+ K_GATE(G_TIM15, RCC_APB2ENSETR, 2, 0),
+ K_GATE(G_TIM8, RCC_APB2ENSETR, 1, 0),
+ K_GATE(G_TIM1, RCC_APB2ENSETR, 0, 0),
+
+ K_GATE(G_HDP, RCC_APB3ENSETR, 20, 0),
+ K_GATE(G_PMBCTRL, RCC_APB3ENSETR, 17, 0),
+ K_GATE(G_TMPSENS, RCC_APB3ENSETR, 16, 0),
+ K_GATE(G_VREF, RCC_APB3ENSETR, 13, 0),
+ K_GATE(G_SYSCFG, RCC_APB3ENSETR, 11, 0),
+ K_MGATE(G_SAI4, RCC_APB3ENSETR, 8, 0),
+ K_MGATE(G_LPTIM5, RCC_APB3ENSETR, 3, 0),
+ K_MGATE(G_LPTIM4, RCC_APB3ENSETR, 2, 0),
+ K_MGATE(G_LPTIM3, RCC_APB3ENSETR, 1, 0),
+ K_MGATE(G_LPTIM2, RCC_APB3ENSETR, 0, 0),
+
+ K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0),
+ K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0),
+ K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0),
+ K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0),
+ K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0),
+
+ K_GATE(G_STGEN, RCC_APB5ENSETR, 20, 0),
+ K_GATE(G_BSEC, RCC_APB5ENSETR, 16, 0),
+ K_GATE(G_IWDG1, RCC_APB5ENSETR, 15, 0),
+ K_GATE(G_TZPC, RCC_APB5ENSETR, 13, 0),
+ K_GATE(G_TZC2, RCC_APB5ENSETR, 12, 0),
+ K_GATE(G_TZC1, RCC_APB5ENSETR, 11, 0),
+ K_GATE(G_RTCAPB, RCC_APB5ENSETR, 8, 0),
+ K_MGATE(G_USART1, RCC_APB5ENSETR, 4, 0),
+ K_MGATE(G_I2C6, RCC_APB5ENSETR, 3, 0),
+ K_MGATE(G_I2C4, RCC_APB5ENSETR, 2, 0),
+ K_MGATE(G_SPI6, RCC_APB5ENSETR, 0, 0),
+
+ K_MGATE(G_SDMMC3, RCC_AHB2ENSETR, 16, 0),
+ K_MGATE(G_USBO, RCC_AHB2ENSETR, 8, 0),
+ K_MGATE(G_ADC12, RCC_AHB2ENSETR, 5, 0),
+ K_GATE(G_DMAMUX, RCC_AHB2ENSETR, 2, 0),
+ K_GATE(G_DMA2, RCC_AHB2ENSETR, 1, 0),
+ K_GATE(G_DMA1, RCC_AHB2ENSETR, 0, 0),
+
+ K_GATE(G_IPCC, RCC_AHB3ENSETR, 12, 0),
+ K_GATE(G_HSEM, RCC_AHB3ENSETR, 11, 0),
+ K_GATE(G_CRC2, RCC_AHB3ENSETR, 7, 0),
+ K_MGATE(G_RNG2, RCC_AHB3ENSETR, 6, 0),
+ K_GATE(G_HASH2, RCC_AHB3ENSETR, 5, 0),
+ K_GATE(G_CRYP2, RCC_AHB3ENSETR, 4, 0),
+ K_GATE(G_DCMI, RCC_AHB3ENSETR, 0, 0),
+
+ K_GATE(G_GPIOK, RCC_AHB4ENSETR, 10, 0),
+ K_GATE(G_GPIOJ, RCC_AHB4ENSETR, 9, 0),
+ K_GATE(G_GPIOI, RCC_AHB4ENSETR, 8, 0),
+ K_GATE(G_GPIOH, RCC_AHB4ENSETR, 7, 0),
+ K_GATE(G_GPIOG, RCC_AHB4ENSETR, 6, 0),
+ K_GATE(G_GPIOF, RCC_AHB4ENSETR, 5, 0),
+ K_GATE(G_GPIOE, RCC_AHB4ENSETR, 4, 0),
+ K_GATE(G_GPIOD, RCC_AHB4ENSETR, 3, 0),
+ K_GATE(G_GPIOC, RCC_AHB4ENSETR, 2, 0),
+ K_GATE(G_GPIOB, RCC_AHB4ENSETR, 1, 0),
+ K_GATE(G_GPIOA, RCC_AHB4ENSETR, 0, 0),
+
+ K_GATE(G_BKPSRAM, RCC_AHB5ENSETR, 8, 0),
+ K_MGATE(G_RNG1, RCC_AHB5ENSETR, 6, 0),
+ K_GATE(G_HASH1, RCC_AHB5ENSETR, 5, 0),
+ K_GATE(G_CRYP1, RCC_AHB5ENSETR, 4, 0),
+ K_GATE(G_GPIOZ, RCC_AHB5ENSETR, 0, 0),
+
+ K_GATE(G_USBH, RCC_AHB6ENSETR, 24, 0),
+ K_GATE(G_CRC1, RCC_AHB6ENSETR, 20, 0),
+ K_MGATE(G_SDMMC2, RCC_AHB6ENSETR, 17, 0),
+ K_MGATE(G_SDMMC1, RCC_AHB6ENSETR, 16, 0),
+ K_MGATE(G_QSPI, RCC_AHB6ENSETR, 14, 0),
+ K_MGATE(G_FMC, RCC_AHB6ENSETR, 12, 0),
+ K_GATE(G_ETHMAC, RCC_AHB6ENSETR, 10, 0),
+ K_GATE(G_ETHRX, RCC_AHB6ENSETR, 9, 0),
+ K_GATE(G_ETHTX, RCC_AHB6ENSETR, 8, 0),
+ K_GATE(G_ETHCK, RCC_AHB6ENSETR, 7, 0),
+ K_MGATE(G_GPU, RCC_AHB6ENSETR, 5, 0),
+ K_GATE(G_MDMA, RCC_AHB6ENSETR, 0, 0),
+ K_GATE(G_ETHSTP, RCC_AHB6LPENSETR, 11, 0),
+};
+
+enum {
+ M_SDMMC12,
+ M_SDMMC3,
+ M_FMC,
+ M_QSPI,
+ M_RNG1,
+ M_RNG2,
+ M_USBPHY,
+ M_USBO,
+ M_STGEN,
+ M_SPDIF,
+ M_SPI1,
+ M_SPI23,
+ M_SPI45,
+ M_SPI6,
+ M_CEC,
+ M_I2C12,
+ M_I2C35,
+ M_I2C46,
+ M_LPTIM1,
+ M_LPTIM23,
+ M_LPTIM45,
+ M_USART1,
+ M_UART24,
+ M_UART35,
+ M_USART6,
+ M_UART78,
+ M_SAI1,
+ M_SAI2,
+ M_SAI3,
+ M_SAI4,
+ M_DSI,
+ M_FDCAN,
+ M_ADC12,
+ M_ETHCK,
+ M_CKPER,
+ M_LAST
+};
+
+static struct stm32_mmux ker_mux[M_LAST];
+
+#define _K_MUX(_id, _offset, _shift, _width, _mux_flags, _mmux, _ops)\
+ [_id] = {\
+ &(struct mux_cfg) {\
+ .reg_off = _offset,\
+ .shift = _shift,\
+ .width = _width,\
+ .mux_flags = _mux_flags,\
+ .table = NULL,\
+ },\
+ .mmux = _mmux,\
+ .ops = _ops,\
+ }
+
+#define K_MUX(_id, _offset, _shift, _width, _mux_flags)\
+ _K_MUX(_id, _offset, _shift, _width, _mux_flags,\
+ NULL, NULL)
+
+#define K_MMUX(_id, _offset, _shift, _width, _mux_flags)\
+ _K_MUX(_id, _offset, _shift, _width, _mux_flags,\
+ &ker_mux[_id], &clk_mmux_ops)
+
+static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = {
+ /* Kernel multi mux */
+ K_MMUX(M_SDMMC12, RCC_SDMMC12CKSELR, 0, 3, 0),
+ K_MMUX(M_SPI23, RCC_SPI2S23CKSELR, 0, 3, 0),
+ K_MMUX(M_SPI45, RCC_SPI2S45CKSELR, 0, 3, 0),
+ K_MMUX(M_I2C12, RCC_I2C12CKSELR, 0, 3, 0),
+ K_MMUX(M_I2C35, RCC_I2C35CKSELR, 0, 3, 0),
+ K_MMUX(M_LPTIM23, RCC_LPTIM23CKSELR, 0, 3, 0),
+ K_MMUX(M_LPTIM45, RCC_LPTIM45CKSELR, 0, 3, 0),
+ K_MMUX(M_UART24, RCC_UART24CKSELR, 0, 3, 0),
+ K_MMUX(M_UART35, RCC_UART35CKSELR, 0, 3, 0),
+ K_MMUX(M_UART78, RCC_UART78CKSELR, 0, 3, 0),
+ K_MMUX(M_SAI1, RCC_SAI1CKSELR, 0, 3, 0),
+ K_MMUX(M_ETHCK, RCC_ETHCKSELR, 0, 2, 0),
+ K_MMUX(M_I2C46, RCC_I2C46CKSELR, 0, 3, 0),
+
+ /* Kernel simple mux */
+ K_MUX(M_RNG2, RCC_RNG2CKSELR, 0, 2, 0),
+ K_MUX(M_SDMMC3, RCC_SDMMC3CKSELR, 0, 3, 0),
+ K_MUX(M_FMC, RCC_FMCCKSELR, 0, 2, 0),
+ K_MUX(M_QSPI, RCC_QSPICKSELR, 0, 2, 0),
+ K_MUX(M_USBPHY, RCC_USBCKSELR, 0, 2, 0),
+ K_MUX(M_USBO, RCC_USBCKSELR, 4, 1, 0),
+ K_MUX(M_SPDIF, RCC_SPDIFCKSELR, 0, 2, 0),
+ K_MUX(M_SPI1, RCC_SPI2S1CKSELR, 0, 3, 0),
+ K_MUX(M_CEC, RCC_CECCKSELR, 0, 2, 0),
+ K_MUX(M_LPTIM1, RCC_LPTIM1CKSELR, 0, 3, 0),
+ K_MUX(M_USART6, RCC_UART6CKSELR, 0, 3, 0),
+ K_MUX(M_FDCAN, RCC_FDCANCKSELR, 0, 2, 0),
+ K_MUX(M_SAI2, RCC_SAI2CKSELR, 0, 3, 0),
+ K_MUX(M_SAI3, RCC_SAI3CKSELR, 0, 3, 0),
+ K_MUX(M_SAI4, RCC_SAI4CKSELR, 0, 3, 0),
+ K_MUX(M_ADC12, RCC_ADCCKSELR, 0, 2, 0),
+ K_MUX(M_DSI, RCC_DSICKSELR, 0, 1, 0),
+ K_MUX(M_CKPER, RCC_CPERCKSELR, 0, 2, 0),
+ K_MUX(M_RNG1, RCC_RNG1CKSELR, 0, 2, 0),
+ K_MUX(M_STGEN, RCC_STGENCKSELR, 0, 2, 0),
+ K_MUX(M_USART1, RCC_UART1CKSELR, 0, 3, 0),
+ K_MUX(M_SPI6, RCC_SPI6CKSELR, 0, 3, 0),
+};
+
+static const struct clock_config stm32mp1_clock_cfg[] = {
+ /* Oscillator divider */
+ DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2,
+ CLK_DIVIDER_READ_ONLY),
+
+ /* External / Internal Oscillators */
+ GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0),
+ GATE_MP1(CK_CSI, "ck_csi", "clk-csi", 0, RCC_OCENSETR, 4, 0),
+ GATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0),
+ GATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0),
+ GATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0),
+
+ FIXED_FACTOR(CK_HSE_DIV2, "clk-hse-div2", "ck_hse", 0, 1, 2),
+
+ /* ref clock pll */
+ MUX(NO_ID, "ref1", ref12_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK12SELR,
+ 0, 2, CLK_MUX_READ_ONLY),
+
+ MUX(NO_ID, "ref3", ref3_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK3SELR,
+ 0, 2, CLK_MUX_READ_ONLY),
+
+ MUX(NO_ID, "ref4", ref4_parents, CLK_OPS_PARENT_ENABLE, RCC_RCK4SELR,
+ 0, 2, CLK_MUX_READ_ONLY),
+
+ /* PLLs */
+ PLL(PLL1, "pll1", "ref1", CLK_IGNORE_UNUSED, RCC_PLL1CR),
+ PLL(PLL2, "pll2", "ref1", CLK_IGNORE_UNUSED, RCC_PLL2CR),
+ PLL(PLL3, "pll3", "ref3", CLK_IGNORE_UNUSED, RCC_PLL3CR),
+ PLL(PLL4, "pll4", "ref4", CLK_IGNORE_UNUSED, RCC_PLL4CR),
+
+ /* ODF */
+ COMPOSITE(PLL1_P, "pll1_p", PARENT("pll1"), 0,
+ _GATE(RCC_PLL1CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL1CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL2_P, "pll2_p", PARENT("pll2"), 0,
+ _GATE(RCC_PLL2CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL2CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL2_Q, "pll2_q", PARENT("pll2"), 0,
+ _GATE(RCC_PLL2CR, 5, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL2CFGR2, 8, 7, 0, NULL)),
+
+ COMPOSITE(PLL2_R, "pll2_r", PARENT("pll2"), CLK_IS_CRITICAL,
+ _GATE(RCC_PLL2CR, 6, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL2CFGR2, 16, 7, 0, NULL)),
+
+ COMPOSITE(PLL3_P, "pll3_p", PARENT("pll3"), 0,
+ _GATE(RCC_PLL3CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL3CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL3_Q, "pll3_q", PARENT("pll3"), 0,
+ _GATE(RCC_PLL3CR, 5, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL3CFGR2, 8, 7, 0, NULL)),
+
+ COMPOSITE(PLL3_R, "pll3_r", PARENT("pll3"), 0,
+ _GATE(RCC_PLL3CR, 6, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL3CFGR2, 16, 7, 0, NULL)),
+
+ COMPOSITE(PLL4_P, "pll4_p", PARENT("pll4"), 0,
+ _GATE(RCC_PLL4CR, 4, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL4CFGR2, 0, 7, 0, NULL)),
+
+ COMPOSITE(PLL4_Q, "pll4_q", PARENT("pll4"), 0,
+ _GATE(RCC_PLL4CR, 5, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL4CFGR2, 8, 7, 0, NULL)),
+
+ COMPOSITE(PLL4_R, "pll4_r", PARENT("pll4"), 0,
+ _GATE(RCC_PLL4CR, 6, 0),
+ _NO_MUX,
+ _DIV(RCC_PLL4CFGR2, 16, 7, 0, NULL)),
+
+ /* MUX system clocks */
+ MUX(CK_PER, "ck_per", per_src, CLK_OPS_PARENT_ENABLE,
+ RCC_CPERCKSELR, 0, 2, 0),
+
+ MUX(CK_MPU, "ck_mpu", cpu_src, CLK_OPS_PARENT_ENABLE |
+ CLK_IS_CRITICAL, RCC_MPCKSELR, 0, 2, 0),
+
+ COMPOSITE(CK_AXI, "ck_axi", axi_src, CLK_IS_CRITICAL |
+ CLK_OPS_PARENT_ENABLE,
+ _NO_GATE,
+ _MUX(RCC_ASSCKSELR, 0, 2, 0),
+ _DIV(RCC_AXIDIVR, 0, 3, 0, axi_div_table)),
+
+ COMPOSITE(CK_MCU, "ck_mcu", mcu_src, CLK_IS_CRITICAL |
+ CLK_OPS_PARENT_ENABLE,
+ _NO_GATE,
+ _MUX(RCC_MSSCKSELR, 0, 2, 0),
+ _DIV(RCC_MCUDIVR, 0, 4, 0, mcu_div_table)),
+
+ DIV_TABLE(NO_ID, "pclk1", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB1DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk2", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB2DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk3", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB3DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk4", "ck_axi", CLK_IGNORE_UNUSED, RCC_APB4DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ DIV_TABLE(NO_ID, "pclk5", "ck_axi", CLK_IGNORE_UNUSED, RCC_APB5DIVR, 0,
+ 3, CLK_DIVIDER_READ_ONLY, apb_div_table),
+
+ /* Kernel Timers */
+ STM32_CKTIM("ck1_tim", "pclk1", 0, RCC_APB1DIVR, RCC_TIMG1PRER),
+ STM32_CKTIM("ck2_tim", "pclk2", 0, RCC_APB2DIVR, RCC_TIMG2PRER),
+
+ STM32_TIM(TIM2_K, "tim2_k", "ck1_tim", RCC_APB1ENSETR, 0),
+ STM32_TIM(TIM3_K, "tim3_k", "ck1_tim", RCC_APB1ENSETR, 1),
+ STM32_TIM(TIM4_K, "tim4_k", "ck1_tim", RCC_APB1ENSETR, 2),
+ STM32_TIM(TIM5_K, "tim5_k", "ck1_tim", RCC_APB1ENSETR, 3),
+ STM32_TIM(TIM6_K, "tim6_k", "ck1_tim", RCC_APB1ENSETR, 4),
+ STM32_TIM(TIM7_K, "tim7_k", "ck1_tim", RCC_APB1ENSETR, 5),
+ STM32_TIM(TIM12_K, "tim12_k", "ck1_tim", RCC_APB1ENSETR, 6),
+ STM32_TIM(TIM13_K, "tim13_k", "ck1_tim", RCC_APB1ENSETR, 7),
+ STM32_TIM(TIM14_K, "tim14_k", "ck1_tim", RCC_APB1ENSETR, 8),
+ STM32_TIM(TIM1_K, "tim1_k", "ck2_tim", RCC_APB2ENSETR, 0),
+ STM32_TIM(TIM8_K, "tim8_k", "ck2_tim", RCC_APB2ENSETR, 1),
+ STM32_TIM(TIM15_K, "tim15_k", "ck2_tim", RCC_APB2ENSETR, 2),
+ STM32_TIM(TIM16_K, "tim16_k", "ck2_tim", RCC_APB2ENSETR, 3),
+ STM32_TIM(TIM17_K, "tim17_k", "ck2_tim", RCC_APB2ENSETR, 4),
+
+ /* Peripheral clocks */
+ PCLK(TIM2, "tim2", "pclk1", CLK_IGNORE_UNUSED, G_TIM2),
+ PCLK(TIM3, "tim3", "pclk1", CLK_IGNORE_UNUSED, G_TIM3),
+ PCLK(TIM4, "tim4", "pclk1", CLK_IGNORE_UNUSED, G_TIM4),
+ PCLK(TIM5, "tim5", "pclk1", CLK_IGNORE_UNUSED, G_TIM5),
+ PCLK(TIM6, "tim6", "pclk1", CLK_IGNORE_UNUSED, G_TIM6),
+ PCLK(TIM7, "tim7", "pclk1", CLK_IGNORE_UNUSED, G_TIM7),
+ PCLK(TIM12, "tim12", "pclk1", CLK_IGNORE_UNUSED, G_TIM12),
+ PCLK(TIM13, "tim13", "pclk1", CLK_IGNORE_UNUSED, G_TIM13),
+ PCLK(TIM14, "tim14", "pclk1", CLK_IGNORE_UNUSED, G_TIM14),
+ PCLK(LPTIM1, "lptim1", "pclk1", 0, G_LPTIM1),
+ PCLK(SPI2, "spi2", "pclk1", 0, G_SPI2),
+ PCLK(SPI3, "spi3", "pclk1", 0, G_SPI3),
+ PCLK(USART2, "usart2", "pclk1", 0, G_USART2),
+ PCLK(USART3, "usart3", "pclk1", 0, G_USART3),
+ PCLK(UART4, "uart4", "pclk1", 0, G_UART4),
+ PCLK(UART5, "uart5", "pclk1", 0, G_UART5),
+ PCLK(UART7, "uart7", "pclk1", 0, G_UART7),
+ PCLK(UART8, "uart8", "pclk1", 0, G_UART8),
+ PCLK(I2C1, "i2c1", "pclk1", 0, G_I2C1),
+ PCLK(I2C2, "i2c2", "pclk1", 0, G_I2C2),
+ PCLK(I2C3, "i2c3", "pclk1", 0, G_I2C3),
+ PCLK(I2C5, "i2c5", "pclk1", 0, G_I2C5),
+ PCLK(SPDIF, "spdif", "pclk1", 0, G_SPDIF),
+ PCLK(CEC, "cec", "pclk1", 0, G_CEC),
+ PCLK(DAC12, "dac12", "pclk1", 0, G_DAC12),
+ PCLK(MDIO, "mdio", "pclk1", 0, G_MDIO),
+ PCLK(TIM1, "tim1", "pclk2", CLK_IGNORE_UNUSED, G_TIM1),
+ PCLK(TIM8, "tim8", "pclk2", CLK_IGNORE_UNUSED, G_TIM8),
+ PCLK(TIM15, "tim15", "pclk2", CLK_IGNORE_UNUSED, G_TIM15),
+ PCLK(TIM16, "tim16", "pclk2", CLK_IGNORE_UNUSED, G_TIM16),
+ PCLK(TIM17, "tim17", "pclk2", CLK_IGNORE_UNUSED, G_TIM17),
+ PCLK(SPI1, "spi1", "pclk2", 0, G_SPI1),
+ PCLK(SPI4, "spi4", "pclk2", 0, G_SPI4),
+ PCLK(SPI5, "spi5", "pclk2", 0, G_SPI5),
+ PCLK(USART6, "usart6", "pclk2", 0, G_USART6),
+ PCLK(SAI1, "sai1", "pclk2", 0, G_SAI1),
+ PCLK(SAI2, "sai2", "pclk2", 0, G_SAI2),
+ PCLK(SAI3, "sai3", "pclk2", 0, G_SAI3),
+ PCLK(DFSDM, "dfsdm", "pclk2", 0, G_DFSDM),
+ PCLK(FDCAN, "fdcan", "pclk2", 0, G_FDCAN),
+ PCLK(LPTIM2, "lptim2", "pclk3", 0, G_LPTIM2),
+ PCLK(LPTIM3, "lptim3", "pclk3", 0, G_LPTIM3),
+ PCLK(LPTIM4, "lptim4", "pclk3", 0, G_LPTIM4),
+ PCLK(LPTIM5, "lptim5", "pclk3", 0, G_LPTIM5),
+ PCLK(SAI4, "sai4", "pclk3", 0, G_SAI4),
+ PCLK(SYSCFG, "syscfg", "pclk3", 0, G_SYSCFG),
+ PCLK(VREF, "vref", "pclk3", 13, G_VREF),
+ PCLK(TMPSENS, "tmpsens", "pclk3", 0, G_TMPSENS),
+ PCLK(PMBCTRL, "pmbctrl", "pclk3", 0, G_PMBCTRL),
+ PCLK(HDP, "hdp", "pclk3", 0, G_HDP),
+ PCLK(LTDC, "ltdc", "pclk4", 0, G_LTDC),
+ PCLK(DSI, "dsi", "pclk4", 0, G_DSI),
+ PCLK(IWDG2, "iwdg2", "pclk4", 0, G_IWDG2),
+ PCLK(USBPHY, "usbphy", "pclk4", 0, G_USBPHY),
+ PCLK(STGENRO, "stgenro", "pclk4", 0, G_STGENRO),
+ PCLK(SPI6, "spi6", "pclk5", 0, G_SPI6),
+ PCLK(I2C4, "i2c4", "pclk5", 0, G_I2C4),
+ PCLK(I2C6, "i2c6", "pclk5", 0, G_I2C6),
+ PCLK(USART1, "usart1", "pclk5", 0, G_USART1),
+ PCLK(RTCAPB, "rtcapb", "pclk5", CLK_IGNORE_UNUSED |
+ CLK_IS_CRITICAL, G_RTCAPB),
+ PCLK(TZC1, "tzc1", "ck_axi", CLK_IGNORE_UNUSED, G_TZC1),
+ PCLK(TZC2, "tzc2", "ck_axi", CLK_IGNORE_UNUSED, G_TZC2),
+ PCLK(TZPC, "tzpc", "pclk5", CLK_IGNORE_UNUSED, G_TZPC),
+ PCLK(IWDG1, "iwdg1", "pclk5", 0, G_IWDG1),
+ PCLK(BSEC, "bsec", "pclk5", CLK_IGNORE_UNUSED, G_BSEC),
+ PCLK(STGEN, "stgen", "pclk5", CLK_IGNORE_UNUSED, G_STGEN),
+ PCLK(DMA1, "dma1", "ck_mcu", 0, G_DMA1),
+ PCLK(DMA2, "dma2", "ck_mcu", 0, G_DMA2),
+ PCLK(DMAMUX, "dmamux", "ck_mcu", 0, G_DMAMUX),
+ PCLK(ADC12, "adc12", "ck_mcu", 0, G_ADC12),
+ PCLK(USBO, "usbo", "ck_mcu", 0, G_USBO),
+ PCLK(SDMMC3, "sdmmc3", "ck_mcu", 0, G_SDMMC3),
+ PCLK(DCMI, "dcmi", "ck_mcu", 0, G_DCMI),
+ PCLK(CRYP2, "cryp2", "ck_mcu", 0, G_CRYP2),
+ PCLK(HASH2, "hash2", "ck_mcu", 0, G_HASH2),
+ PCLK(RNG2, "rng2", "ck_mcu", 0, G_RNG2),
+ PCLK(CRC2, "crc2", "ck_mcu", 0, G_CRC2),
+ PCLK(HSEM, "hsem", "ck_mcu", 0, G_HSEM),
+ PCLK(IPCC, "ipcc", "ck_mcu", 0, G_IPCC),
+ PCLK(GPIOA, "gpioa", "ck_mcu", 0, G_GPIOA),
+ PCLK(GPIOB, "gpiob", "ck_mcu", 0, G_GPIOB),
+ PCLK(GPIOC, "gpioc", "ck_mcu", 0, G_GPIOC),
+ PCLK(GPIOD, "gpiod", "ck_mcu", 0, G_GPIOD),
+ PCLK(GPIOE, "gpioe", "ck_mcu", 0, G_GPIOE),
+ PCLK(GPIOF, "gpiof", "ck_mcu", 0, G_GPIOF),
+ PCLK(GPIOG, "gpiog", "ck_mcu", 0, G_GPIOG),
+ PCLK(GPIOH, "gpioh", "ck_mcu", 0, G_GPIOH),
+ PCLK(GPIOI, "gpioi", "ck_mcu", 0, G_GPIOI),
+ PCLK(GPIOJ, "gpioj", "ck_mcu", 0, G_GPIOJ),
+ PCLK(GPIOK, "gpiok", "ck_mcu", 0, G_GPIOK),
+ PCLK(GPIOZ, "gpioz", "ck_axi", CLK_IGNORE_UNUSED, G_GPIOZ),
+ PCLK(CRYP1, "cryp1", "ck_axi", CLK_IGNORE_UNUSED, G_CRYP1),
+ PCLK(HASH1, "hash1", "ck_axi", CLK_IGNORE_UNUSED, G_HASH1),
+ PCLK(RNG1, "rng1", "ck_axi", 0, G_RNG1),
+ PCLK(BKPSRAM, "bkpsram", "ck_axi", CLK_IGNORE_UNUSED, G_BKPSRAM),
+ PCLK(MDMA, "mdma", "ck_axi", 0, G_MDMA),
+ PCLK(GPU, "gpu", "ck_axi", 0, G_GPU),
+ PCLK(ETHTX, "ethtx", "ck_axi", 0, G_ETHTX),
+ PCLK(ETHRX, "ethrx", "ck_axi", 0, G_ETHRX),
+ PCLK(ETHMAC, "ethmac", "ck_axi", 0, G_ETHMAC),
+ PCLK(FMC, "fmc", "ck_axi", CLK_IGNORE_UNUSED, G_FMC),
+ PCLK(QSPI, "qspi", "ck_axi", CLK_IGNORE_UNUSED, G_QSPI),
+ PCLK(SDMMC1, "sdmmc1", "ck_axi", 0, G_SDMMC1),
+ PCLK(SDMMC2, "sdmmc2", "ck_axi", 0, G_SDMMC2),
+ PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1),
+ PCLK(USBH, "usbh", "ck_axi", 0, G_USBH),
+ PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP),
+
+ /* Kernel clocks */
+ KCLK(SDMMC1_K, "sdmmc1_k", sdmmc12_src, 0, G_SDMMC1, M_SDMMC12),
+ KCLK(SDMMC2_K, "sdmmc2_k", sdmmc12_src, 0, G_SDMMC2, M_SDMMC12),
+ KCLK(SDMMC3_K, "sdmmc3_k", sdmmc3_src, 0, G_SDMMC3, M_SDMMC3),
+ KCLK(FMC_K, "fmc_k", fmc_src, 0, G_FMC, M_FMC),
+ KCLK(QSPI_K, "qspi_k", qspi_src, 0, G_QSPI, M_QSPI),
+ KCLK(RNG1_K, "rng1_k", rng_src, 0, G_RNG1, M_RNG1),
+ KCLK(RNG2_K, "rng2_k", rng_src, 0, G_RNG2, M_RNG2),
+ KCLK(USBPHY_K, "usbphy_k", usbphy_src, 0, G_USBPHY, M_USBPHY),
+ KCLK(STGEN_K, "stgen_k", stgen_src, CLK_IS_CRITICAL, G_STGEN, M_STGEN),
+ KCLK(SPDIF_K, "spdif_k", spdif_src, 0, G_SPDIF, M_SPDIF),
+ KCLK(SPI1_K, "spi1_k", spi123_src, 0, G_SPI1, M_SPI1),
+ KCLK(SPI2_K, "spi2_k", spi123_src, 0, G_SPI2, M_SPI23),
+ KCLK(SPI3_K, "spi3_k", spi123_src, 0, G_SPI3, M_SPI23),
+ KCLK(SPI4_K, "spi4_k", spi45_src, 0, G_SPI4, M_SPI45),
+ KCLK(SPI5_K, "spi5_k", spi45_src, 0, G_SPI5, M_SPI45),
+ KCLK(SPI6_K, "spi6_k", spi6_src, 0, G_SPI6, M_SPI6),
+ KCLK(CEC_K, "cec_k", cec_src, 0, G_CEC, M_CEC),
+ KCLK(I2C1_K, "i2c1_k", i2c12_src, 0, G_I2C1, M_I2C12),
+ KCLK(I2C2_K, "i2c2_k", i2c12_src, 0, G_I2C2, M_I2C12),
+ KCLK(I2C3_K, "i2c3_k", i2c35_src, 0, G_I2C3, M_I2C35),
+ KCLK(I2C5_K, "i2c5_k", i2c35_src, 0, G_I2C5, M_I2C35),
+ KCLK(I2C4_K, "i2c4_k", i2c46_src, 0, G_I2C4, M_I2C46),
+ KCLK(I2C6_K, "i2c6_k", i2c46_src, 0, G_I2C6, M_I2C46),
+ KCLK(LPTIM1_K, "lptim1_k", lptim1_src, 0, G_LPTIM1, M_LPTIM1),
+ KCLK(LPTIM2_K, "lptim2_k", lptim23_src, 0, G_LPTIM2, M_LPTIM23),
+ KCLK(LPTIM3_K, "lptim3_k", lptim23_src, 0, G_LPTIM3, M_LPTIM23),
+ KCLK(LPTIM4_K, "lptim4_k", lptim45_src, 0, G_LPTIM4, M_LPTIM45),
+ KCLK(LPTIM5_K, "lptim5_k", lptim45_src, 0, G_LPTIM5, M_LPTIM45),
+ KCLK(USART1_K, "usart1_k", usart1_src, 0, G_USART1, M_USART1),
+ KCLK(USART2_K, "usart2_k", usart234578_src, 0, G_USART2, M_UART24),
+ KCLK(USART3_K, "usart3_k", usart234578_src, 0, G_USART3, M_UART35),
+ KCLK(UART4_K, "uart4_k", usart234578_src, 0, G_UART4, M_UART24),
+ KCLK(UART5_K, "uart5_k", usart234578_src, 0, G_UART5, M_UART35),
+ KCLK(USART6_K, "uart6_k", usart6_src, 0, G_USART6, M_USART6),
+ KCLK(UART7_K, "uart7_k", usart234578_src, 0, G_UART7, M_UART78),
+ KCLK(UART8_K, "uart8_k", usart234578_src, 0, G_UART8, M_UART78),
+ KCLK(FDCAN_K, "fdcan_k", fdcan_src, 0, G_FDCAN, M_FDCAN),
+ KCLK(SAI1_K, "sai1_k", sai_src, 0, G_SAI1, M_SAI1),
+ KCLK(SAI2_K, "sai2_k", sai2_src, 0, G_SAI2, M_SAI2),
+ KCLK(SAI3_K, "sai3_k", sai_src, 0, G_SAI3, M_SAI3),
+ KCLK(SAI4_K, "sai4_k", sai_src, 0, G_SAI4, M_SAI4),
+ KCLK(ADC12_K, "adc12_k", adc12_src, 0, G_ADC12, M_ADC12),
+ KCLK(DSI_K, "dsi_k", dsi_src, 0, G_DSI, M_DSI),
+ KCLK(ADFSDM_K, "adfsdm_k", sai_src, 0, G_ADFSDM, M_SAI1),
+ KCLK(USBO_K, "usbo_k", usbo_src, 0, G_USBO, M_USBO),
+ KCLK(ETHCK_K, "ethck_k", eth_src, 0, G_ETHCK, M_ETHCK),
+
+ /* Particulary Kernel Clocks (no mux or no gate) */
+ MGATE_MP1(DFSDM_K, "dfsdm_k", "ck_mcu", 0, G_DFSDM),
+ MGATE_MP1(DSI_PX, "dsi_px", "pll4_q", CLK_SET_RATE_PARENT, G_DSI),
+ MGATE_MP1(LTDC_PX, "ltdc_px", "pll4_q", CLK_SET_RATE_PARENT, G_LTDC),
+ MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU),
+ MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12),
+
+ COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE,
+ _NO_GATE,
+ _MMUX(M_ETHCK),
+ _DIV(RCC_ETHCKSELR, 4, 4, 0, NULL)),
+
+ /* RTC clock */
+ DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7, 0),
+
+ COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_PARENT,
+ _GATE(RCC_BDCR, 20, 0),
+ _MUX(RCC_BDCR, 16, 2, 0),
+ _NO_DIV),
+
+ /* MCO clocks */
+ COMPOSITE(CK_MCO1, "ck_mco1", mco1_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
+ _GATE(RCC_MCO1CFGR, 12, 0),
+ _MUX(RCC_MCO1CFGR, 0, 3, 0),
+ _DIV(RCC_MCO1CFGR, 4, 4, 0, NULL)),
+
+ COMPOSITE(CK_MCO2, "ck_mco2", mco2_src, CLK_OPS_PARENT_ENABLE |
+ CLK_SET_RATE_NO_REPARENT,
+ _GATE(RCC_MCO2CFGR, 12, 0),
+ _MUX(RCC_MCO2CFGR, 0, 3, 0),
+ _DIV(RCC_MCO2CFGR, 4, 4, 0, NULL)),
+
+ /* Debug clocks */
+ GATE(CK_DBG, "ck_sys_dbg", "ck_axi", CLK_IGNORE_UNUSED,
+ RCC_DBGCFGR, 8, 0),
+
+ COMPOSITE(CK_TRACE, "ck_trace", ck_trace_src, CLK_OPS_PARENT_ENABLE,
+ _GATE(RCC_DBGCFGR, 9, 0),
+ _NO_MUX,
+ _DIV(RCC_DBGCFGR, 0, 3, 0, ck_trace_div_table)),
+};
+
+struct stm32_clock_match_data {
+ const struct clock_config *cfg;
+ unsigned int num;
+ unsigned int maxbinding;
+};
+
+static struct stm32_clock_match_data stm32mp1_data = {
+ .cfg = stm32mp1_clock_cfg,
+ .num = ARRAY_SIZE(stm32mp1_clock_cfg),
+ .maxbinding = STM32MP1_LAST_CLK,
+};
+
+static const struct of_device_id stm32mp1_match_data[] = {
+ {
+ .compatible = "st,stm32mp1-rcc",
+ .data = &stm32mp1_data,
+ },
+ { }
+};
+
+static int stm32_register_hw_clk(struct clk_onecell_data *clk_data,
+ void __iomem *base,
+ const struct clock_config *cfg)
+{
+ struct clk *clk = ERR_PTR(-ENOENT);
+
+ if (cfg->func)
+ clk = (*cfg->func)(base, cfg);
+
+ if (IS_ERR(clk)) {
+ pr_err("Unable to register %s\n", cfg->name);
+ return PTR_ERR(clk);
+ }
+
+ if (cfg->id != NO_ID)
+ clk_data->clks[cfg->id] = clk;
+
+ return 0;
+}
+
+static int stm32_rcc_init(struct device_node *np,
+ void __iomem *base,
+ const struct of_device_id *match_data)
+{
+ struct clk_onecell_data *clk_data;
+ struct clk **clks;
+ const struct of_device_id *match;
+ const struct stm32_clock_match_data *data;
+ int err, n, max_binding;
+
+ match = of_match_node(match_data, np);
+ if (!match) {
+ pr_err("%s: match data not found\n", __func__);
+ return -ENODEV;
+ }
+
+ data = match->data;
+
+ max_binding = data->maxbinding;
+
+ clk_data = xzalloc(sizeof(*clk_data));
+ clk_data->clks = xzalloc(sizeof(void *) * max_binding);
+ clk_data->clk_num = max_binding;
+
+ clks = clk_data->clks;
+
+ for (n = 0; n < max_binding; n++)
+ clks[n] = ERR_PTR(-ENOENT);
+
+ for (n = 0; n < data->num; n++) {
+ err = stm32_register_hw_clk(clk_data, base,
+ &data->cfg[n]);
+ if (err) {
+ pr_err("%s: can't register %s\n", __func__,
+ data->cfg[n].name);
+
+ kfree(clk_data);
+
+ return err;
+ }
+ }
+
+ return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+}
+
+static void stm32mp1_rcc_init(struct device_node *np)
+{
+ void __iomem *base;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("%pOFn: unable to map resource", np);
+ return;
+ }
+
+ stm32_rcc_init(np, base, stm32mp1_match_data);
+}
+
+CLK_OF_DECLARE_DRIVER(stm32mp1_rcc, "st,stm32mp1-rcc", stm32mp1_rcc_init);
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 6a2d8ad..ea3304b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -87,6 +87,9 @@ void clk_disable(struct clk *clk)
clk->enable_count--;
if (!clk->enable_count) {
+ if (clk->flags & CLK_IS_CRITICAL)
+ return;
+
if (clk->ops->disable)
clk->ops->disable(clk);
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 49d66fb..1b1b881 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -252,7 +252,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2);
clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_usb_otg_div", "pll3_usb_otg", CCM_CACRR, 20, 1);
- clk[VF610_CLK_PLL4_MAIN_DIV] = clk_divider_table("pll4_audio_div", "pll4_audio", CCM_CACRR, 6, 3, pll4_audio_div_table, 0);
+ clk[VF610_CLK_PLL4_MAIN_DIV] = clk_divider_table("pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, pll4_audio_div_table, 0);
clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1);
clk[VF610_CLK_DDRMC] = imx_clk_gate2_cgr("ddrmc", "ddr_sel", CCM_CCGR6, CCM_CCGRx_CGn(14), 0x2);
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 945671c..875c76a 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -7,34 +7,36 @@ struct clk *clk_gate2(const char *name, const char *parent, void __iomem *reg,
static inline struct clk *imx_clk_divider(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
{
- return clk_divider(name, parent, reg, shift, width, CLK_SET_RATE_PARENT);
+ return clk_divider(name, parent, CLK_SET_RATE_PARENT, reg, shift, width,
+ 0);
}
static inline struct clk *imx_clk_divider_flags(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 width,
unsigned long flags)
{
- return clk_divider(name, parent, reg, shift, width, flags);
+ return clk_divider(name, parent, flags, reg, shift, width, 0);
}
static inline struct clk *imx_clk_divider_np(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
{
- return clk_divider(name, parent, reg, shift, width, 0);
+ return clk_divider(name, parent, 0, reg, shift, width, 0);
}
static inline struct clk *imx_clk_divider2(const char *name, const char *parent,
void __iomem *reg, u8 shift, u8 width)
{
- return clk_divider(name, parent, reg, shift, width, CLK_OPS_PARENT_ENABLE);
+ return clk_divider(name, parent, CLK_OPS_PARENT_ENABLE, reg, shift,
+ width, 0);
}
static inline struct clk *imx_clk_divider_table(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 width,
const struct clk_div_table *table)
{
- return clk_divider_table(name, parent, reg, shift, width, table,
- CLK_SET_RATE_PARENT);
+ return clk_divider_table(name, parent, CLK_SET_RATE_PARENT, reg, shift,
+ width, table, 0);
}
static inline struct clk *imx_clk_fixed_factor(const char *name,
@@ -46,35 +48,38 @@ static inline struct clk *imx_clk_fixed_factor(const char *name,
static inline struct clk *imx_clk_mux_flags(const char *name, void __iomem *reg,
u8 shift, u8 width,
const char **parents, u8 num_parents,
- unsigned long flags)
+ unsigned long clk_flags)
{
- return clk_mux(name, reg, shift, width, parents, num_parents, flags);
+ return clk_mux(name, clk_flags, reg, shift, width, parents, num_parents,
+ 0);
}
static inline struct clk *imx_clk_mux2_flags(const char *name,
void __iomem *reg, u8 shift, u8 width, const char **parents,
- int num_parents, unsigned long flags)
+ int num_parents, unsigned long clk_flags)
{
- return clk_mux(name, reg, shift, width, parents, num_parents,
- flags | CLK_OPS_PARENT_ENABLE);
+ return clk_mux(name, clk_flags | CLK_OPS_PARENT_ENABLE, reg, shift,
+ width, parents, num_parents, 0);
}
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
{
- return clk_mux(name, reg, shift, width, parents, num_parents, 0);
+ return clk_mux(name, 0, reg, shift, width, parents, num_parents, 0);
}
static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
{
- return clk_mux(name, reg, shift, width, parents, num_parents, CLK_OPS_PARENT_ENABLE);
+ return clk_mux(name, CLK_OPS_PARENT_ENABLE, reg, shift, width, parents,
+ num_parents, 0);
}
static inline struct clk *imx_clk_mux_p(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, u8 num_parents)
{
- return clk_mux(name, reg, shift, width, parents, num_parents, CLK_SET_RATE_PARENT);
+ return clk_mux(name, CLK_SET_RATE_PARENT, reg, shift, width, parents,
+ num_parents, 0);
}
static inline struct clk *imx_clk_gate(const char *name, const char *parent,
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index f6f118f..609e636 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -42,7 +42,7 @@ static struct of_device_id mvebu_coreclk_ids[] = {
{ }
};
-int mvebu_coreclk_probe(struct device_d *dev)
+static int mvebu_coreclk_probe(struct device_d *dev)
{
struct resource *iores;
struct device_node *np = dev->device_node;
@@ -155,7 +155,7 @@ static struct of_device_id mvebu_clk_gating_ids[] = {
{ }
};
-int mvebu_clk_gating_probe(struct device_d *dev)
+static int mvebu_clk_gating_probe(struct device_d *dev)
{
struct resource *iores;
struct device_node *np = dev->device_node;
diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c
index e28dae1..526efc5 100644
--- a/drivers/clk/mxs/clk-imx23.c
+++ b/drivers/clk/mxs/clk-imx23.c
@@ -61,7 +61,7 @@ enum imx23_clk {
static struct clk *clks[clk_max];
-int __init mx23_clocks_init(void __iomem *regs)
+static int __init mx23_clocks_init(void __iomem *regs)
{
clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000);
clks[pll] = mxs_clk_pll("pll", "ref_xtal", PLLCTRL0, 16, 480000000);
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index dd17a74..12bc9dd 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -74,7 +74,7 @@ enum imx28_clk {
static struct clk *clks[clk_max];
-int __init mx28_clocks_init(void __iomem *regs)
+static int __init mx28_clocks_init(void __iomem *regs)
{
clks[ref_xtal] = clk_fixed("ref_xtal", 24000000);
clks[pll0] = mxs_clk_pll("pll0", "ref_xtal", PLL0CTRL0, 17, 480000000);
diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h
index 7bab7b5..00895de 100644
--- a/drivers/clk/mxs/clk.h
+++ b/drivers/clk/mxs/clk.h
@@ -40,7 +40,7 @@ static inline struct clk *mxs_clk_gate(const char *name,
static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parent_names, int num_parents)
{
- return clk_mux(name, reg, shift, width, parent_names, num_parents, 0);
+ return clk_mux(name, 0, reg, shift, width, parent_names, num_parents, 0);
}
static inline struct clk *mxs_clk_fixed_factor(const char *name,
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index 87a3969..39ccf0a 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -352,7 +352,8 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
pll_parents[1] = pll->pll_name;
pll_parents[2] = parent_names[1];
- pll_mux = clk_mux_alloc(name, base + mode_offset, mode_shift, PLL_MODE_MASK, pll_parents, 3, CLK_SET_RATE_PARENT);
+ pll_mux = clk_mux_alloc(name, CLK_SET_RATE_PARENT, base + mode_offset, mode_shift,
+ PLL_MODE_MASK, pll_parents, 3, 0);
pll->pll_mux_ops = pll_mux->ops;
mux_clk = pll_mux;
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
index 7dda296..9340739 100644
--- a/drivers/clk/rockchip/clk-rk3188.c
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -25,9 +25,6 @@
#define RK3066_GRF_SOC_STATUS 0x15c
#define RK3188_GRF_SOC_STATUS 0xac
-#define CLK_SET_RATE_NO_REPARENT 0
-#define CLK_DIVIDER_READ_ONLY 0
-
enum rk3188_plls {
apll, cpll, dpll, gpll,
};
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index bb111e1..dcfaae2 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -25,9 +25,6 @@
#define RK3288_GRF_SOC_CON(x) (0x244 + x * 4)
#define RK3288_GRF_SOC_STATUS1 0x284
-#define CLK_SET_RATE_NO_REPARENT 0
-#define CLK_DIVIDER_READ_ONLY 0
-
enum rk3288_plls {
apll, dpll, cpll, gpll, npll,
};
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 3222a4e..9e0cbad 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -50,7 +50,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
struct clk *div = NULL;
if (num_parents > 1) {
- mux = clk_mux_alloc(name, base + muxdiv_offset, mux_shift,
+ mux = clk_mux_alloc(name, 0, base + muxdiv_offset, mux_shift,
mux_width, parent_names, num_parents, mux_flags);
if (!mux)
return ERR_PTR(-ENOMEM);
@@ -64,7 +64,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
}
if (div_width > 0) {
- div = clk_divider_alloc(name, *parent_names,
+ div = clk_divider_alloc(name, *parent_names, 0,
base + muxdiv_offset, div_shift, div_width, div_flags);
if (!div)
return ERR_PTR(-ENOMEM);
@@ -176,25 +176,21 @@ void __init rockchip_clk_register_branches(
/* catch simple muxes */
switch (list->branch_type) {
case branch_mux:
- /*
- * mux_flags and flags are ored, this is safe,
- * since there is no value clash, but isn't that elegant
- */
- clk = clk_mux(list->name,
+ clk = clk_mux(list->name, flags,
reg_base + list->muxdiv_offset, list->mux_shift,
list->mux_width, list->parent_names,
- list->num_parents, list->mux_flags | flags);
+ list->num_parents, list->mux_flags);
break;
case branch_divider:
if (list->div_table)
clk = clk_divider_table(list->name,
- list->parent_names[0],
+ list->parent_names[0], flags,
reg_base + list->muxdiv_offset,
list->div_shift, list->div_width,
list->div_table, list->div_flags);
else
clk = clk_divider(list->name,
- list->parent_names[0],
+ list->parent_names[0], flags,
reg_base + list->muxdiv_offset,
list->div_shift, list->div_width,
list->div_flags);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index aa52638..d6798a9 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -27,7 +27,6 @@
#include <linux/clk.h>
/* To keep changes from kernel smaller */
-#define CLK_IGNORE_UNUSED 0
#define CLK_GATE_SET_TO_DISABLE CLK_GATE_INVERTED
#define CLK_GET_RATE_NOCACHE 0
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index fd1e2ed..b418286 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -123,7 +123,7 @@ static struct clk *_tegra_clk_register_periph(const char *name,
goto out_periph;
}
- periph->mux = clk_mux_alloc(NULL, clk_base + reg_offset, 32 - mux_size,
+ periph->mux = clk_mux_alloc(NULL, 0, clk_base + reg_offset, 32 - mux_size,
mux_size, parent_names, num_parents, 0);
if (!periph->mux)
goto out_mux;
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index b0502c3..43c5bfc 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -73,7 +73,7 @@ config CLOCKSOURCE_ATMEL_PIT
config CLOCKSOURCE_ARMV8_TIMER
bool
default y
- depends on ARM && CPU_64v8
+ depends on ARM && (CPU_64v8 || CPU_V7)
config CLOCKSOURCE_ARM_GLOBAL_TIMER
bool
diff --git a/drivers/clocksource/armv8-timer.c b/drivers/clocksource/armv8-timer.c
index 918232e..3095f8c 100644
--- a/drivers/clocksource/armv8-timer.c
+++ b/drivers/clocksource/armv8-timer.c
@@ -39,6 +39,7 @@ static int armv8_timer_probe(struct device_d *dev)
}
static struct of_device_id armv8_timer_dt_ids[] = {
+ { .compatible = "arm,armv7-timer", },
{ .compatible = "arm,armv8-timer", },
{ }
};
diff --git a/drivers/clocksource/mvebu.c b/drivers/clocksource/mvebu.c
index 59bbc4b..b55d72a 100644
--- a/drivers/clocksource/mvebu.c
+++ b/drivers/clocksource/mvebu.c
@@ -43,7 +43,7 @@
static __iomem void *timer_base;
-uint64_t mvebu_clocksource_read(void)
+static uint64_t mvebu_clocksource_read(void)
{
return 0 - __raw_readl(timer_base + TIMER0_VAL_OFF);
}
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index d1687e3..b2709f0 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -4,6 +4,6 @@ menuconfig CRYPTO_HW
if CRYPTO_HW
-source drivers/crypto/caam/Kconfig
+source "drivers/crypto/caam/Kconfig"
endif
diff --git a/drivers/ddr/Kconfig b/drivers/ddr/Kconfig
new file mode 100644
index 0000000..4ea7159
--- /dev/null
+++ b/drivers/ddr/Kconfig
@@ -0,0 +1 @@
+source "drivers/ddr/fsl/Kconfig"
diff --git a/drivers/ddr/Makefile b/drivers/ddr/Makefile
new file mode 100644
index 0000000..faf2f9e
--- /dev/null
+++ b/drivers/ddr/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DDR_FSL) += fsl/
diff --git a/drivers/ddr/fsl/Kconfig b/drivers/ddr/fsl/Kconfig
new file mode 100644
index 0000000..9cae902
--- /dev/null
+++ b/drivers/ddr/fsl/Kconfig
@@ -0,0 +1,16 @@
+config DDR_FSL
+ bool
+
+if DDR_FSL
+
+config DDR_FSL_DDR1
+ bool "Enable DDR1 support"
+config DDR_FSL_DDR2
+ bool "Enable DDR2 support"
+config DDR_FSL_DDR3
+ bool "Enable DDR3 support"
+config DDR_FSL_DDR4
+ bool "Enable DDR4 support"
+
+endif
+
diff --git a/drivers/ddr/fsl/Makefile b/drivers/ddr/fsl/Makefile
new file mode 100644
index 0000000..86ac4b8
--- /dev/null
+++ b/drivers/ddr/fsl/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright 2008-2014 Freescale Semiconductor, Inc.
+
+pbl-y += main.o util.o ctrl_regs.o options.o lc_common_dimm_params.o
+
+pbl-y += ddr1_dimm_params.o
+pbl-y += ddr2_dimm_params.o
+pbl-y += ddr3_dimm_params.o
+pbl-y += ddr4_dimm_params.o
+obj-y += arm_ddr_gen3.o
+pbl-y += fsl_ddr_gen4.o
diff --git a/drivers/ddr/fsl/arm_ddr_gen3.c b/drivers/ddr/fsl/arm_ddr_gen3.c
new file mode 100644
index 0000000..c016917
--- /dev/null
+++ b/drivers/ddr/fsl/arm_ddr_gen3.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * Derived from mpc85xx_ddr_gen3.c, removed all workarounds
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <soc/fsl/fsl_immap.h>
+#include <soc/fsl/immap_lsch2.h>
+#include "fsl_ddr.h"
+
+/*
+ * regs has the to-be-set values for DDR controller registers
+ * ctrl_num is the DDR controller number
+ * step: 0 goes through the initialization in one pass
+ * 1 sets registers and returns before enabling controller
+ * 2 resumes from step 1 and continues to initialize
+ * Dividing the initialization to two steps to deassert DDR reset signal
+ * to comply with JEDEC specs for RDIMMs.
+ */
+void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step)
+{
+ struct ccsr_ddr __iomem *ddr = c->base;
+ const fsl_ddr_cfg_regs_t *regs = &c->fsl_ddr_config_reg;
+ unsigned int i, bus_width;
+ u32 temp_sdram_cfg;
+ u32 total_gb_size_per_controller;
+ int timeout;
+
+ if (step == 2)
+ goto step2;
+
+ if (regs->ddr_eor)
+ ddr_out32(&ddr->eor, regs->ddr_eor);
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (i == 0) {
+ ddr_out32(&ddr->cs0_bnds, regs->cs[i].bnds);
+ ddr_out32(&ddr->cs0_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs0_config_2, regs->cs[i].config_2);
+
+ } else if (i == 1) {
+ ddr_out32(&ddr->cs1_bnds, regs->cs[i].bnds);
+ ddr_out32(&ddr->cs1_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs1_config_2, regs->cs[i].config_2);
+
+ } else if (i == 2) {
+ ddr_out32(&ddr->cs2_bnds, regs->cs[i].bnds);
+ ddr_out32(&ddr->cs2_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs2_config_2, regs->cs[i].config_2);
+
+ } else if (i == 3) {
+ ddr_out32(&ddr->cs3_bnds, regs->cs[i].bnds);
+ ddr_out32(&ddr->cs3_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs3_config_2, regs->cs[i].config_2);
+ }
+ }
+
+ ddr_out32(&ddr->timing_cfg_3, regs->timing_cfg_3);
+ ddr_out32(&ddr->timing_cfg_0, regs->timing_cfg_0);
+ ddr_out32(&ddr->timing_cfg_1, regs->timing_cfg_1);
+ ddr_out32(&ddr->timing_cfg_2, regs->timing_cfg_2);
+ ddr_out32(&ddr->sdram_mode, regs->ddr_sdram_mode);
+ ddr_out32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2);
+ ddr_out32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3);
+ ddr_out32(&ddr->sdram_mode_4, regs->ddr_sdram_mode_4);
+ ddr_out32(&ddr->sdram_mode_5, regs->ddr_sdram_mode_5);
+ ddr_out32(&ddr->sdram_mode_6, regs->ddr_sdram_mode_6);
+ ddr_out32(&ddr->sdram_mode_7, regs->ddr_sdram_mode_7);
+ ddr_out32(&ddr->sdram_mode_8, regs->ddr_sdram_mode_8);
+ ddr_out32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl);
+ ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval);
+ ddr_out32(&ddr->sdram_data_init, regs->ddr_data_init);
+ ddr_out32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl);
+ ddr_out32(&ddr->timing_cfg_4, regs->timing_cfg_4);
+ ddr_out32(&ddr->timing_cfg_5, regs->timing_cfg_5);
+ ddr_out32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl);
+ ddr_out32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl);
+ if (regs->ddr_wrlvl_cntl_2)
+ ddr_out32(&ddr->ddr_wrlvl_cntl_2, regs->ddr_wrlvl_cntl_2);
+ if (regs->ddr_wrlvl_cntl_3)
+ ddr_out32(&ddr->ddr_wrlvl_cntl_3, regs->ddr_wrlvl_cntl_3);
+
+ ddr_out32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr);
+ ddr_out32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1);
+ ddr_out32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2);
+ ddr_out32(&ddr->ddr_cdr1, regs->ddr_cdr1);
+
+ if (is_warm_boot()) {
+ ddr_out32(&ddr->sdram_cfg_2,
+ regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT);
+ ddr_out32(&ddr->init_addr, c->common_timing_params.base_address);
+ ddr_out32(&ddr->init_ext_addr, DDR_INIT_ADDR_EXT_UIA);
+
+ /* DRAM VRef will not be trained */
+ ddr_out32(&ddr->ddr_cdr2,
+ regs->ddr_cdr2 & ~DDR_CDR2_VREF_TRAIN_EN);
+ } else {
+ ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2);
+ ddr_out32(&ddr->init_addr, regs->ddr_init_addr);
+ ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr);
+ ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2);
+ }
+ ddr_out32(&ddr->err_disable, regs->err_disable);
+ ddr_out32(&ddr->err_int_en, regs->err_int_en);
+ for (i = 0; i < 32; i++) {
+ if (regs->debug[i]) {
+ debug("Write to debug_%d as %08x\n", i + 1,
+ regs->debug[i]);
+ ddr_out32(&ddr->debug[i], regs->debug[i]);
+ }
+ }
+
+ /*
+ * For RDIMMs, JEDEC spec requires clocks to be stable before reset is
+ * deasserted. Clocks start when any chip select is enabled and clock
+ * control register is set. Because all DDR components are connected to
+ * one reset signal, this needs to be done in two steps. Step 1 is to
+ * get the clocks started. Step 2 resumes after reset signal is
+ * deasserted.
+ */
+ if (step == 1) {
+ udelay(200);
+ return;
+ }
+
+step2:
+ /* Set, but do not enable the memory */
+ temp_sdram_cfg = regs->ddr_sdram_cfg;
+ temp_sdram_cfg &= ~(SDRAM_CFG_MEM_EN);
+ ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg);
+
+ /*
+ * 500 painful micro-seconds must elapse between
+ * the DDR clock setup and the DDR config enable.
+ * DDR2 need 200 us, and DDR3 need 500 us from spec,
+ * we choose the max, that is 500 us for all of case.
+ */
+ udelay(500);
+ asm volatile("dsb sy;isb");
+
+ if (is_warm_boot()) {
+ /* enter self-refresh */
+ temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg_2);
+ temp_sdram_cfg |= SDRAM_CFG2_FRC_SR;
+ ddr_out32(&ddr->sdram_cfg_2, temp_sdram_cfg);
+
+ temp_sdram_cfg = (ddr_in32(&ddr->sdram_cfg) | SDRAM_CFG_BI);
+ } else {
+ temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI;
+ }
+ /* Let the controller go */
+ ddr_out32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN);
+ asm volatile("dsb sy;isb");
+
+ total_gb_size_per_controller = 0;
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (!(regs->cs[i].config & 0x80000000))
+ continue;
+ total_gb_size_per_controller += 1 << (
+ ((regs->cs[i].config >> 14) & 0x3) + 2 +
+ ((regs->cs[i].config >> 8) & 0x7) + 12 +
+ ((regs->cs[i].config >> 0) & 0x7) + 8 +
+ 3 - ((regs->ddr_sdram_cfg >> 19) & 0x3) -
+ 26); /* minus 26 (count of 64M) */
+ }
+ if (regs->cs[0].config & 0x20000000) {
+ /* 2-way interleaving */
+ total_gb_size_per_controller <<= 1;
+ }
+ /*
+ * total memory / bus width = transactions needed
+ * transactions needed / data rate = seconds
+ * to add plenty of buffer, double the time
+ * For example, 2GB on 666MT/s 64-bit bus takes about 402ms
+ * Let's wait for 800ms
+ */
+ bus_width = 3 - ((ddr_in32(&ddr->sdram_cfg) & SDRAM_CFG_DBW_MASK)
+ >> SDRAM_CFG_DBW_SHIFT);
+ timeout = ((total_gb_size_per_controller << (6 - bus_width)) * 100 /
+ (c->ddr_freq >> 20)) << 1;
+ total_gb_size_per_controller >>= 4; /* shift down to gb size */
+ debug("total %d GB\n", total_gb_size_per_controller);
+ debug("Need to wait up to %d * 10ms\n", timeout);
+
+ /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */
+ while ((ddr_in32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) &&
+ (timeout >= 0)) {
+ udelay(10000); /* throttle polling rate */
+ timeout--;
+ }
+
+ if (timeout <= 0)
+ printf("Waiting for D_INIT timeout. Memory may not work.\n");
+
+ if (is_warm_boot()) {
+ /* exit self-refresh */
+ temp_sdram_cfg = ddr_in32(&ddr->sdram_cfg_2);
+ temp_sdram_cfg &= ~SDRAM_CFG2_FRC_SR;
+ ddr_out32(&ddr->sdram_cfg_2, temp_sdram_cfg);
+ }
+}
diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c
new file mode 100644
index 0000000..4957320
--- /dev/null
+++ b/drivers/ddr/fsl/ctrl_regs.c
@@ -0,0 +1,2539 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP Semiconductor
+ */
+
+/*
+ * Generic driver for Freescale DDR/DDR2/DDR3/DDR4 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <soc/fsl/fsl_immap.h>
+#include <io.h>
+#include <soc/fsl/immap_lsch2.h>
+#include <linux/log2.h>
+#include "fsl_ddr.h"
+
+/*
+ * Determine Rtt value.
+ *
+ * This should likely be either board or controller specific.
+ *
+ * Rtt(nominal) - DDR2:
+ * 0 = Rtt disabled
+ * 1 = 75 ohm
+ * 2 = 150 ohm
+ * 3 = 50 ohm
+ * Rtt(nominal) - DDR3:
+ * 0 = Rtt disabled
+ * 1 = 60 ohm
+ * 2 = 120 ohm
+ * 3 = 40 ohm
+ * 4 = 20 ohm
+ * 5 = 30 ohm
+ *
+ */
+static inline int fsl_ddr_get_rtt(const memctl_options_t *popts)
+{
+ if (is_ddr2(popts))
+ return 3;
+ else
+ return 0;
+}
+
+/*
+ * compute CAS write latency according to DDR4 spec
+ * CWL = 9 for <= 1600MT/s
+ * 10 for <= 1866MT/s
+ * 11 for <= 2133MT/s
+ * 12 for <= 2400MT/s
+ * 14 for <= 2667MT/s
+ * 16 for <= 2933MT/s
+ * 18 for higher
+ */
+static inline unsigned int compute_cas_write_latency_ddr4(struct fsl_ddr_controller *c)
+{
+ unsigned int cwl;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+ if (mclk_ps >= 1250)
+ cwl = 9;
+ else if (mclk_ps >= 1070)
+ cwl = 10;
+ else if (mclk_ps >= 935)
+ cwl = 11;
+ else if (mclk_ps >= 833)
+ cwl = 12;
+ else if (mclk_ps >= 750)
+ cwl = 14;
+ else if (mclk_ps >= 681)
+ cwl = 16;
+ else
+ cwl = 18;
+
+ return cwl;
+}
+
+/*
+ * compute the CAS write latency according to DDR3 spec
+ * CWL = 5 if tCK >= 2.5ns
+ * 6 if 2.5ns > tCK >= 1.875ns
+ * 7 if 1.875ns > tCK >= 1.5ns
+ * 8 if 1.5ns > tCK >= 1.25ns
+ * 9 if 1.25ns > tCK >= 1.07ns
+ * 10 if 1.07ns > tCK >= 0.935ns
+ * 11 if 0.935ns > tCK >= 0.833ns
+ * 12 if 0.833ns > tCK >= 0.75ns
+ */
+static inline unsigned int compute_cas_write_latency_ddr3(struct fsl_ddr_controller *c)
+{
+ unsigned int cwl;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+
+ if (mclk_ps >= 2500)
+ cwl = 5;
+ else if (mclk_ps >= 1875)
+ cwl = 6;
+ else if (mclk_ps >= 1500)
+ cwl = 7;
+ else if (mclk_ps >= 1250)
+ cwl = 8;
+ else if (mclk_ps >= 1070)
+ cwl = 9;
+ else if (mclk_ps >= 935)
+ cwl = 10;
+ else if (mclk_ps >= 833)
+ cwl = 11;
+ else if (mclk_ps >= 750)
+ cwl = 12;
+ else {
+ cwl = 12;
+ printf("Warning: CWL is out of range\n");
+ }
+ return cwl;
+}
+
+/* Chip Select Configuration (CSn_CONFIG) */
+static void set_csn_config(int dimm_number, int i, fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const struct dimm_params *dimm_params)
+{
+ unsigned int cs_n_en = 0; /* Chip Select enable */
+ unsigned int intlv_en = 0; /* Memory controller interleave enable */
+ unsigned int intlv_ctl = 0; /* Interleaving control */
+ unsigned int ap_n_en = 0; /* Chip select n auto-precharge enable */
+ unsigned int odt_rd_cfg = 0; /* ODT for reads configuration */
+ unsigned int odt_wr_cfg = 0; /* ODT for writes configuration */
+ unsigned int ba_bits_cs_n = 0; /* Num of bank bits for SDRAM on CSn */
+ unsigned int row_bits_cs_n = 0; /* Num of row bits for SDRAM on CSn */
+ unsigned int col_bits_cs_n = 0; /* Num of ocl bits for SDRAM on CSn */
+ int go_config = 0;
+ unsigned int bg_bits_cs_n = 0; /* Num of bank group bits */
+ unsigned int n_banks_per_sdram_device;
+
+ /* Compute CS_CONFIG only for existing ranks of each DIMM. */
+ switch (i) {
+ case 0:
+ if (dimm_params[dimm_number].n_ranks > 0) {
+ go_config = 1;
+ /* These fields only available in CS0_CONFIG */
+ if (!popts->memctl_interleaving)
+ break;
+ switch (popts->memctl_interleaving_mode) {
+ case FSL_DDR_256B_INTERLEAVING:
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ intlv_en = popts->memctl_interleaving;
+ intlv_ctl = popts->memctl_interleaving_mode;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case 1:
+ if ((dimm_number == 0 && dimm_params[0].n_ranks > 1) || \
+ (dimm_number == 1 && dimm_params[1].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 2:
+ if ((dimm_number == 0 && dimm_params[0].n_ranks > 2) || \
+ (dimm_number >= 1 && dimm_params[dimm_number].n_ranks > 0))
+ go_config = 1;
+ break;
+ case 3:
+ if ((dimm_number == 0 && dimm_params[0].n_ranks > 3) || \
+ (dimm_number == 1 && dimm_params[1].n_ranks > 1) || \
+ (dimm_number == 3 && dimm_params[3].n_ranks > 0))
+ go_config = 1;
+ break;
+ default:
+ break;
+ }
+ if (go_config) {
+ cs_n_en = 1;
+ ap_n_en = popts->cs_local_opts[i].auto_precharge;
+ odt_rd_cfg = popts->cs_local_opts[i].odt_rd_cfg;
+ odt_wr_cfg = popts->cs_local_opts[i].odt_wr_cfg;
+ if (is_ddr4(popts)) {
+ ba_bits_cs_n = dimm_params[dimm_number].bank_addr_bits;
+ bg_bits_cs_n = dimm_params[dimm_number].bank_group_bits;
+ } else {
+ n_banks_per_sdram_device
+ = dimm_params[dimm_number].n_banks_per_sdram_device;
+ ba_bits_cs_n = ilog2(n_banks_per_sdram_device) - 2;
+ }
+ row_bits_cs_n = dimm_params[dimm_number].n_row_addr - 12;
+ col_bits_cs_n = dimm_params[dimm_number].n_col_addr - 8;
+ }
+ ddr->cs[i].config = (0
+ | ((cs_n_en & 0x1) << 31)
+ | ((intlv_en & 0x3) << 29)
+ | ((intlv_ctl & 0xf) << 24)
+ | ((ap_n_en & 0x1) << 23)
+
+ /* XXX: some implementation only have 1 bit starting at left */
+ | ((odt_rd_cfg & 0x7) << 20)
+
+ /* XXX: Some implementation only have 1 bit starting at left */
+ | ((odt_wr_cfg & 0x7) << 16)
+
+ | ((ba_bits_cs_n & 0x3) << 14)
+ | ((row_bits_cs_n & 0x7) << 8)
+ | ((bg_bits_cs_n & 0x3) << 4)
+ | ((col_bits_cs_n & 0x7) << 0)
+ );
+ debug("FSLDDR: cs[%d]_config = 0x%08x\n", i,ddr->cs[i].config);
+}
+
+/* Chip Select Configuration 2 (CSn_CONFIG_2) */
+/* FIXME: 8572 */
+static void set_csn_config_2(int i, fsl_ddr_cfg_regs_t *ddr)
+{
+ unsigned int pasr_cfg = 0; /* Partial array self refresh config */
+
+ ddr->cs[i].config_2 = ((pasr_cfg & 7) << 24);
+ debug("FSLDDR: cs[%d]_config_2 = 0x%08x\n", i, ddr->cs[i].config_2);
+}
+
+/* -3E = 667 CL5, -25 = CL6 800, -25E = CL5 800 */
+
+/*
+ * Check DIMM configuration, return 2 if quad-rank or two dual-rank
+ * Return 1 if other two slots configuration. Return 0 if single slot.
+ */
+static inline int avoid_odt_overlap(struct fsl_ddr_controller *c,
+ const struct dimm_params *dimm_params)
+{
+ if (c->dimm_slots_per_ctrl == 1)
+ if (dimm_params[0].n_ranks == 4)
+ return 2;
+
+ if (c->dimm_slots_per_ctrl == 2) {
+ if ((dimm_params[0].n_ranks == 2) &&
+ (dimm_params[1].n_ranks == 2))
+ return 2;
+
+ if ((dimm_params[0].n_ranks != 0) &&
+ (dimm_params[2].n_ranks != 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * DDR SDRAM Timing Configuration 0 (TIMING_CFG_0)
+ *
+ * Avoid writing for DDR I. The new PQ38 DDR controller
+ * dreams up non-zero default values to be backwards compatible.
+ */
+static void set_timing_cfg_0(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct dimm_params *dimm_params = c->dimm_params;
+ unsigned char trwt_mclk = 0; /* Read-to-write turnaround */
+ unsigned char twrt_mclk = 0; /* Write-to-read turnaround */
+ /* 7.5 ns on -3E; 0 means WL - CL + BL/2 + 1 */
+ unsigned char trrt_mclk = 0; /* Read-to-read turnaround */
+ unsigned char twwt_mclk = 0; /* Write-to-write turnaround */
+
+ /* Active powerdown exit timing (tXARD and tXARDS). */
+ unsigned char act_pd_exit_mclk;
+ /* Precharge powerdown exit timing (tXP). */
+ unsigned char pre_pd_exit_mclk;
+ /* ODT powerdown exit timing (tAXPD). */
+ unsigned char taxpd_mclk = 0;
+ /* Mode register set cycle time (tMRD). */
+ unsigned char tmrd_mclk;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+
+ if (is_ddr4(popts)) {
+ /* tXP=max(4nCK, 6ns) */
+ int txp = max((int)mclk_ps * 4, 6000); /* unit=ps */
+ unsigned int data_rate = c->ddr_freq;
+
+ /* for faster clock, need more time for data setup */
+ trwt_mclk = (data_rate/1000000 > 1900) ? 3 : 2;
+
+ /*
+ * for single quad-rank DIMM and two-slot DIMMs
+ * to avoid ODT overlap
+ */
+ switch (avoid_odt_overlap(c, dimm_params)) {
+ case 2:
+ twrt_mclk = 2;
+ twwt_mclk = 2;
+ trrt_mclk = 2;
+ break;
+ default:
+ twrt_mclk = 1;
+ twwt_mclk = 1;
+ trrt_mclk = 0;
+ break;
+ }
+
+ act_pd_exit_mclk = picos_to_mclk(c, txp);
+ pre_pd_exit_mclk = act_pd_exit_mclk;
+ /*
+ * MRS_CYC = max(tMRD, tMOD)
+ * tMRD = 8nCK, tMOD = max(24nCK, 15ns)
+ */
+ tmrd_mclk = max(24U, picos_to_mclk(c, 15000));
+ } else if (is_ddr3(popts)) {
+ unsigned int data_rate = c->ddr_freq;
+ int txp;
+ unsigned int ip_rev;
+ int odt_overlap;
+ /*
+ * (tXARD and tXARDS). Empirical?
+ * The DDR3 spec has not tXARD,
+ * we use the tXP instead of it.
+ * tXP=max(3nCK, 7.5ns) for DDR3-800, 1066
+ * max(3nCK, 6ns) for DDR3-1333, 1600, 1866, 2133
+ * spec has not the tAXPD, we use
+ * tAXPD=1, need design to confirm.
+ */
+ txp = max((int)mclk_ps * 3, (mclk_ps > 1540 ? 7500 : 6000));
+
+ ip_rev = fsl_ddr_get_version(c);
+ if (ip_rev >= 0x40700) {
+ /*
+ * MRS_CYC = max(tMRD, tMOD)
+ * tMRD = 4nCK (8nCK for RDIMM)
+ * tMOD = max(12nCK, 15ns)
+ */
+ tmrd_mclk = max((unsigned int)12, picos_to_mclk(c, 15000));
+ } else {
+ /*
+ * MRS_CYC = tMRD
+ * tMRD = 4nCK (8nCK for RDIMM)
+ */
+ if (popts->registered_dimm_en)
+ tmrd_mclk = 8;
+ else
+ tmrd_mclk = 4;
+ }
+
+ /* set the turnaround time */
+
+ /*
+ * for single quad-rank DIMM and two-slot DIMMs
+ * to avoid ODT overlap
+ */
+ odt_overlap = avoid_odt_overlap(c, dimm_params);
+ switch (odt_overlap) {
+ case 2:
+ twwt_mclk = 2;
+ trrt_mclk = 1;
+ break;
+ case 1:
+ twwt_mclk = 1;
+ trrt_mclk = 0;
+ break;
+ default:
+ break;
+ }
+
+ /* for faster clock, need more time for data setup */
+ trwt_mclk = (data_rate/1000000 > 1800) ? 2 : 1;
+
+ if ((data_rate/1000000 > 1150) || (popts->memctl_interleaving))
+ twrt_mclk = 1;
+
+ if (popts->dynamic_power == 0) { /* powerdown is not used */
+ act_pd_exit_mclk = 1;
+ pre_pd_exit_mclk = 1;
+ taxpd_mclk = 1;
+ } else {
+ /* act_pd_exit_mclk = tXARD, see above */
+ act_pd_exit_mclk = picos_to_mclk(c, txp);
+ /* Mode register MR0[A12] is '1' - fast exit */
+ pre_pd_exit_mclk = act_pd_exit_mclk;
+ taxpd_mclk = 1;
+ }
+ } else if (is_ddr2(popts)) {
+ /*
+ * (tXARD and tXARDS). Empirical?
+ * tXARD = 2 for DDR2
+ * tXP=2
+ * tAXPD=8
+ */
+ act_pd_exit_mclk = 2;
+ pre_pd_exit_mclk = 2;
+ taxpd_mclk = 8;
+ tmrd_mclk = 2;
+ } else {
+ return;
+ }
+
+ if (popts->trwt_override)
+ trwt_mclk = popts->trwt;
+
+ ddr->timing_cfg_0 = (0
+ | ((trwt_mclk & 0x3) << 30) /* RWT */
+ | ((twrt_mclk & 0x3) << 28) /* WRT */
+ | ((trrt_mclk & 0x3) << 26) /* RRT */
+ | ((twwt_mclk & 0x3) << 24) /* WWT */
+ | ((act_pd_exit_mclk & 0xf) << 20) /* ACT_PD_EXIT */
+ | ((pre_pd_exit_mclk & 0xF) << 16) /* PRE_PD_EXIT */
+ | ((taxpd_mclk & 0xf) << 8) /* ODT_PD_EXIT */
+ | ((tmrd_mclk & 0x1f) << 0) /* MRS_CYC */
+ );
+ debug("FSLDDR: timing_cfg_0 = 0x%08x\n", ddr->timing_cfg_0);
+}
+
+/* DDR SDRAM Timing Configuration 3 (TIMING_CFG_3) */
+static void set_timing_cfg_3(struct fsl_ddr_controller *c,
+ unsigned int cas_latency,
+ unsigned int additive_latency)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ /* Extended precharge to activate interval (tRP) */
+ unsigned int ext_pretoact = 0;
+ /* Extended Activate to precharge interval (tRAS) */
+ unsigned int ext_acttopre = 0;
+ /* Extended activate to read/write interval (tRCD) */
+ unsigned int ext_acttorw = 0;
+ /* Extended refresh recovery time (tRFC) */
+ unsigned int ext_refrec;
+ /* Extended MCAS latency from READ cmd */
+ unsigned int ext_caslat = 0;
+ /* Extended additive latency */
+ unsigned int ext_add_lat = 0;
+ /* Extended last data to precharge interval (tWR) */
+ unsigned int ext_wrrec = 0;
+ /* Control Adjust */
+ unsigned int cntl_adj = 0;
+
+ ext_pretoact = picos_to_mclk(c, common_dimm->trp_ps) >> 4;
+ ext_acttopre = picos_to_mclk(c, common_dimm->tras_ps) >> 4;
+ ext_acttorw = picos_to_mclk(c, common_dimm->trcd_ps) >> 4;
+ ext_caslat = (2 * cas_latency - 1) >> 4;
+ ext_add_lat = additive_latency >> 4;
+
+ if (is_ddr4(popts))
+ ext_refrec = (picos_to_mclk(c, common_dimm->trfc1_ps) - 8) >> 4;
+ else
+ ext_refrec = (picos_to_mclk(c, common_dimm->trfc_ps) - 8) >> 4;
+ /* ext_wrrec only deals with 16 clock and above, or 14 with OTF */
+
+ ext_wrrec = (picos_to_mclk(c, common_dimm->twr_ps) +
+ (popts->otf_burst_chop_en ? 2 : 0)) >> 4;
+
+ ddr->timing_cfg_3 = (0
+ | ((ext_pretoact & 0x1) << 28)
+ | ((ext_acttopre & 0x3) << 24)
+ | ((ext_acttorw & 0x1) << 22)
+ | ((ext_refrec & 0x3F) << 16)
+ | ((ext_caslat & 0x3) << 12)
+ | ((ext_add_lat & 0x1) << 10)
+ | ((ext_wrrec & 0x1) << 8)
+ | ((cntl_adj & 0x7) << 0)
+ );
+ debug("FSLDDR: timing_cfg_3 = 0x%08x\n", ddr->timing_cfg_3);
+}
+
+/* DDR SDRAM Timing Configuration 1 (TIMING_CFG_1) */
+static void set_timing_cfg_1(struct fsl_ddr_controller *c, unsigned int cas_latency)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ /* Precharge-to-activate interval (tRP) */
+ unsigned char pretoact_mclk;
+ /* Activate to precharge interval (tRAS) */
+ unsigned char acttopre_mclk;
+ /* Activate to read/write interval (tRCD) */
+ unsigned char acttorw_mclk;
+ /* CASLAT */
+ unsigned char caslat_ctrl;
+ /* Refresh recovery time (tRFC) ; trfc_low */
+ unsigned char refrec_ctrl;
+ /* Last data to precharge minimum interval (tWR) */
+ unsigned char wrrec_mclk;
+ /* Activate-to-activate interval (tRRD) */
+ unsigned char acttoact_mclk;
+ /* Last write data pair to read command issue interval (tWTR) */
+ unsigned char wrtord_mclk;
+
+ pretoact_mclk = picos_to_mclk(c, common_dimm->trp_ps);
+ acttopre_mclk = picos_to_mclk(c, common_dimm->tras_ps);
+ acttorw_mclk = picos_to_mclk(c, common_dimm->trcd_ps);
+
+ /*
+ * Translate CAS Latency to a DDR controller field value:
+ *
+ * CAS Lat DDR I DDR II Ctrl
+ * Clocks SPD Bit SPD Bit Value
+ * ------- ------- ------- -----
+ * 1.0 0 0001
+ * 1.5 1 0010
+ * 2.0 2 2 0011
+ * 2.5 3 0100
+ * 3.0 4 3 0101
+ * 3.5 5 0110
+ * 4.0 4 0111
+ * 4.5 1000
+ * 5.0 5 1001
+ */
+ if (is_ddr1(popts)) {
+ caslat_ctrl = (cas_latency + 1) & 0x07;
+ } else if (is_ddr2(popts)) {
+ caslat_ctrl = 2 * cas_latency - 1;
+ } else {
+ /*
+ * if the CAS latency more than 8 cycle,
+ * we need set extend bit for it at
+ * TIMING_CFG_3[EXT_CASLAT]
+ */
+ if (fsl_ddr_get_version(c) <= 0x40400)
+ caslat_ctrl = 2 * cas_latency - 1;
+ else
+ caslat_ctrl = (cas_latency - 1) << 1;
+ }
+
+ if (is_ddr4(popts)) {
+ /* DDR4 supports 10, 12, 14, 16, 18, 20, 24 */
+ static const u8 wrrec_table[] = {
+ 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10,
+ 12, 12, 14, 14, 16,
+ 16, 18, 18, 20, 20,
+ 24, 24, 24, 24
+ };
+
+ refrec_ctrl = picos_to_mclk(c, common_dimm->trfc1_ps) - 8;
+ wrrec_mclk = picos_to_mclk(c, common_dimm->twr_ps);
+ acttoact_mclk = max(picos_to_mclk(c, common_dimm->trrds_ps), 4U);
+ wrtord_mclk = max(2U, picos_to_mclk(c, 2500));
+ if ((wrrec_mclk < 1) || (wrrec_mclk > 24))
+ printf("Error: WRREC doesn't support %d clocks\n", wrrec_mclk);
+ else
+ wrrec_mclk = wrrec_table[wrrec_mclk - 1];
+ } else {
+ /* DDR_SDRAM_MODE doesn't support 9,11,13,15 */
+ static const u8 wrrec_table[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 10, 10, 12, 12, 14, 14, 0, 0
+ };
+
+ refrec_ctrl = picos_to_mclk(c, common_dimm->trfc_ps) - 8;
+ wrrec_mclk = picos_to_mclk(c, common_dimm->twr_ps);
+ acttoact_mclk = picos_to_mclk(c, common_dimm->trrd_ps);
+ wrtord_mclk = picos_to_mclk(c, common_dimm->twtr_ps);
+ if ((wrrec_mclk < 1) || (wrrec_mclk > 16))
+ printf("Error: WRREC doesn't support %d clocks\n", wrrec_mclk);
+ else
+ wrrec_mclk = wrrec_table[wrrec_mclk - 1];
+ }
+
+ if (popts->otf_burst_chop_en)
+ wrrec_mclk += 2;
+
+ /*
+ * JEDEC has min requirement for tRRD
+ */
+ if (is_ddr3(popts) && acttoact_mclk < 4)
+ acttoact_mclk = 4;
+
+ /*
+ * JEDEC has some min requirements for tWTR
+ */
+ if (is_ddr2(popts) && wrtord_mclk < 2)
+ wrtord_mclk = 2;
+
+ if (is_ddr3(popts) && wrtord_mclk < 4)
+ wrtord_mclk = 4;
+
+ if (popts->otf_burst_chop_en)
+ wrtord_mclk += 2;
+
+ ddr->timing_cfg_1 = (0
+ | ((pretoact_mclk & 0x0F) << 28)
+ | ((acttopre_mclk & 0x0F) << 24)
+ | ((acttorw_mclk & 0xF) << 20)
+ | ((caslat_ctrl & 0xF) << 16)
+ | ((refrec_ctrl & 0xF) << 12)
+ | ((wrrec_mclk & 0x0F) << 8)
+ | ((acttoact_mclk & 0x0F) << 4)
+ | ((wrtord_mclk & 0x0F) << 0)
+ );
+ debug("FSLDDR: timing_cfg_1 = 0x%08x\n", ddr->timing_cfg_1);
+}
+
+/* DDR SDRAM Timing Configuration 2 (TIMING_CFG_2) */
+static void set_timing_cfg_2(struct fsl_ddr_controller *c,
+ unsigned int cas_latency,
+ unsigned int additive_latency)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ /* Additive latency */
+ unsigned char add_lat_mclk;
+ /* CAS-to-preamble override */
+ unsigned short cpo;
+ /* Write latency */
+ unsigned char wr_lat;
+ /* Read to precharge (tRTP) */
+ unsigned char rd_to_pre;
+ /* Write command to write data strobe timing adjustment */
+ unsigned char wr_data_delay;
+ /* Minimum CKE pulse width (tCKE) */
+ unsigned char cke_pls;
+ /* Window for four activates (tFAW) */
+ unsigned short four_act;
+ unsigned int mclk_ps;
+
+ /* FIXME add check that this must be less than acttorw_mclk */
+ add_lat_mclk = additive_latency;
+ cpo = popts->cpo_override;
+
+ if (is_ddr1(popts)) {
+ /*
+ * This is a lie. It should really be 1, but if it is
+ * set to 1, bits overlap into the old controller's
+ * otherwise unused ACSM field. If we leave it 0, then
+ * the HW will magically treat it as 1 for DDR 1. Oh Yea.
+ */
+ wr_lat = 0;
+ } else if (is_ddr2(popts)) {
+ wr_lat = cas_latency - 1;
+ } else if (is_ddr3(popts)) {
+ wr_lat = compute_cas_write_latency_ddr3(c);
+ } else {
+ wr_lat = compute_cas_write_latency_ddr4(c);
+ }
+
+ if (is_ddr4(popts))
+ rd_to_pre = picos_to_mclk(c, 7500);
+ else
+ rd_to_pre = picos_to_mclk(c, common_dimm->trtp_ps);
+
+ /*
+ * JEDEC has some min requirements for tRTP
+ */
+ if (is_ddr2(popts) && rd_to_pre < 2)
+ rd_to_pre = 2;
+
+ if (is_ddr3_4(popts) && rd_to_pre < 4)
+ rd_to_pre = 4;
+
+ if (popts->otf_burst_chop_en)
+ rd_to_pre += 2; /* according to UM */
+
+ wr_data_delay = popts->write_data_delay;
+
+ if (is_ddr4(popts)) {
+ cpo = 0;
+ cke_pls = max(3U, picos_to_mclk(c, 5000));
+ } else if (is_ddr3(popts)) {
+ mclk_ps = get_memory_clk_period_ps(c);
+
+ /*
+ * cke pulse = max(3nCK, 7.5ns) for DDR3-800
+ * max(3nCK, 5.625ns) for DDR3-1066, 1333
+ * max(3nCK, 5ns) for DDR3-1600, 1866, 2133
+ */
+ cke_pls = max(3U, picos_to_mclk(c, mclk_ps > 1870 ? 7500 :
+ (mclk_ps > 1245 ? 5625 : 5000)));
+ } else if (is_ddr2(popts)) {
+ cke_pls = FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR2;
+ } else {
+ cke_pls = FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR1;
+ }
+
+ four_act = picos_to_mclk(c, popts->tfaw_window_four_activates_ps);
+
+ ddr->timing_cfg_2 = (0
+ | ((add_lat_mclk & 0xf) << 28)
+ | ((cpo & 0x1f) << 23)
+ | ((wr_lat & 0xf) << 19)
+ | (((wr_lat & 0x10) >> 4) << 18)
+ | ((rd_to_pre & RD_TO_PRE_MASK) << RD_TO_PRE_SHIFT)
+ | ((wr_data_delay & WR_DATA_DELAY_MASK) << WR_DATA_DELAY_SHIFT)
+ | ((cke_pls & 0x7) << 6)
+ | ((four_act & 0x3f) << 0)
+ );
+ debug("FSLDDR: timing_cfg_2 = 0x%08x\n", ddr->timing_cfg_2);
+}
+
+/* DDR SDRAM Register Control Word */
+static void set_ddr_sdram_rcw(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned int ddr_freq = c->ddr_freq / 1000000;
+ unsigned int rc0a, rc0f;
+
+ if (common_dimm->all_dimms_registered &&
+ !common_dimm->all_dimms_unbuffered) {
+ if (popts->rcw_override) {
+ ddr->ddr_sdram_rcw_1 = popts->rcw_1;
+ ddr->ddr_sdram_rcw_2 = popts->rcw_2;
+ ddr->ddr_sdram_rcw_3 = popts->rcw_3;
+ } else {
+ rc0a = ddr_freq > 3200 ? 0x7 :
+ (ddr_freq > 2933 ? 0x6 :
+ (ddr_freq > 2666 ? 0x5 :
+ (ddr_freq > 2400 ? 0x4 :
+ (ddr_freq > 2133 ? 0x3 :
+ (ddr_freq > 1866 ? 0x2 :
+ (ddr_freq > 1600 ? 1 : 0))))));
+ rc0f = ddr_freq > 3200 ? 0x3 :
+ (ddr_freq > 2400 ? 0x2 :
+ (ddr_freq > 2133 ? 0x1 : 0));
+ ddr->ddr_sdram_rcw_1 =
+ common_dimm->rcw[0] << 28 | \
+ common_dimm->rcw[1] << 24 | \
+ common_dimm->rcw[2] << 20 | \
+ common_dimm->rcw[3] << 16 | \
+ common_dimm->rcw[4] << 12 | \
+ common_dimm->rcw[5] << 8 | \
+ common_dimm->rcw[6] << 4 | \
+ common_dimm->rcw[7];
+ ddr->ddr_sdram_rcw_2 =
+ common_dimm->rcw[8] << 28 | \
+ common_dimm->rcw[9] << 24 | \
+ rc0a << 20 | \
+ common_dimm->rcw[11] << 16 | \
+ common_dimm->rcw[12] << 12 | \
+ common_dimm->rcw[13] << 8 | \
+ common_dimm->rcw[14] << 4 | \
+ rc0f;
+ ddr->ddr_sdram_rcw_3 =
+ ((ddr_freq - 1260 + 19) / 20) << 8;
+ }
+ debug("FSLDDR: ddr_sdram_rcw_1 = 0x%08x\n",
+ ddr->ddr_sdram_rcw_1);
+ debug("FSLDDR: ddr_sdram_rcw_2 = 0x%08x\n",
+ ddr->ddr_sdram_rcw_2);
+ debug("FSLDDR: ddr_sdram_rcw_3 = 0x%08x\n",
+ ddr->ddr_sdram_rcw_3);
+ }
+}
+
+/* DDR SDRAM control configuration (DDR_SDRAM_CFG) */
+static void set_ddr_sdram_cfg(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned int mem_en; /* DDR SDRAM interface logic enable */
+ unsigned int sren; /* Self refresh enable (during sleep) */
+ unsigned int ecc_en = 0; /* ECC enable. */
+ unsigned int rd_en; /* Registered DIMM enable */
+ unsigned int sdram_type; /* Type of SDRAM */
+ unsigned int dyn_pwr; /* Dynamic power management mode */
+ unsigned int dbw; /* DRAM dta bus width */
+ unsigned int eight_be = 0; /* 8-beat burst enable, DDR2 is zero */
+ unsigned int ncap = 0; /* Non-concurrent auto-precharge */
+ unsigned int threet_en; /* Enable 3T timing */
+ unsigned int twot_en; /* Enable 2T timing */
+ unsigned int ba_intlv_ctl; /* Bank (CS) interleaving control */
+ unsigned int x32_en = 0; /* x32 enable */
+ unsigned int pchb8 = 0; /* precharge bit 8 enable */
+ unsigned int hse; /* Global half strength override */
+ unsigned int acc_ecc_en = 0; /* Accumulated ECC enable */
+ unsigned int mem_halt = 0; /* memory controller halt */
+ unsigned int bi = 0; /* Bypass initialization */
+
+ mem_en = 1;
+ sren = popts->self_refresh_in_sleep;
+ if (common_dimm->all_dimms_ecc_capable)
+ ecc_en = 1;
+
+ if (common_dimm->all_dimms_registered &&
+ !common_dimm->all_dimms_unbuffered) {
+ rd_en = 1;
+ twot_en = 0;
+ } else {
+ rd_en = 0;
+ twot_en = popts->twot_en;
+ }
+
+ sdram_type = popts->ddrtype;
+
+ dyn_pwr = popts->dynamic_power;
+ dbw = popts->data_bus_width;
+ /* 8-beat burst enable DDR-III case
+ * we must clear it when use the on-the-fly mode,
+ * must set it when use the 32-bits bus mode.
+ */
+ if (is_ddr3_4(popts)) {
+ if (popts->burst_length == DDR_BL8)
+ eight_be = 1;
+ if (popts->burst_length == DDR_OTF)
+ eight_be = 0;
+ if (dbw == 0x1)
+ eight_be = 1;
+ }
+
+ threet_en = popts->threet_en;
+ ba_intlv_ctl = popts->ba_intlv_ctl;
+ hse = popts->half_strength_driver_enable;
+
+ /* set when ddr bus width < 64 */
+ acc_ecc_en = (dbw != 0 && ecc_en == 1) ? 1 : 0;
+
+ ddr->ddr_sdram_cfg = (0
+ | ((mem_en & 0x1) << 31)
+ | ((sren & 0x1) << 30)
+ | ((ecc_en & 0x1) << 29)
+ | ((rd_en & 0x1) << 28)
+ | ((sdram_type & 0x7) << 24)
+ | ((dyn_pwr & 0x1) << 21)
+ | ((dbw & 0x3) << 19)
+ | ((eight_be & 0x1) << 18)
+ | ((ncap & 0x1) << 17)
+ | ((threet_en & 0x1) << 16)
+ | ((twot_en & 0x1) << 15)
+ | ((ba_intlv_ctl & 0x7F) << 8)
+ | ((x32_en & 0x1) << 5)
+ | ((pchb8 & 0x1) << 4)
+ | ((hse & 0x1) << 3)
+ | ((acc_ecc_en & 0x1) << 2)
+ | ((mem_halt & 0x1) << 1)
+ | ((bi & 0x1) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_cfg = 0x%08x\n", ddr->ddr_sdram_cfg);
+}
+
+/* DDR SDRAM control configuration 2 (DDR_SDRAM_CFG_2) */
+static void set_ddr_sdram_cfg_2(struct fsl_ddr_controller *c,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned int frc_sr = 0; /* Force self refresh */
+ unsigned int sr_ie = 0; /* Self-refresh interrupt enable */
+ unsigned int odt_cfg = 0; /* ODT configuration */
+ unsigned int num_pr; /* Number of posted refreshes */
+ unsigned int slow = 0; /* DDR will be run less than 1250 */
+ unsigned int x4_en = 0; /* x4 DRAM enable */
+ unsigned int obc_cfg; /* On-The-Fly Burst Chop Cfg */
+ unsigned int ap_en; /* Address Parity Enable */
+ unsigned int d_init; /* DRAM data initialization */
+ unsigned int rcw_en = 0; /* Register Control Word Enable */
+ unsigned int md_en = 0; /* Mirrored DIMM Enable */
+ unsigned int qd_en = 0; /* quad-rank DIMM Enable */
+ int i;
+ unsigned int dll_rst_dis; /* DLL reset disable */
+ unsigned int dqs_cfg; /* DQS configuration */
+
+ if (is_ddr4(popts)) {
+ dll_rst_dis = 0;
+ dqs_cfg = 0;
+ } else {
+ dqs_cfg = popts->dqs_config;
+ dll_rst_dis = 1;
+ }
+
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (popts->cs_local_opts[i].odt_rd_cfg
+ || popts->cs_local_opts[i].odt_wr_cfg) {
+ odt_cfg = SDRAM_CFG2_ODT_ONLY_READ;
+ break;
+ }
+ }
+ sr_ie = popts->self_refresh_interrupt_en;
+ num_pr = popts->package_3ds + 1;
+
+ /*
+ * 8572 manual says
+ * {TIMING_CFG_1[PRETOACT]
+ * + [DDR_SDRAM_CFG_2[NUM_PR]
+ * * ({EXT_REFREC || REFREC} + 8 + 2)]}
+ * << DDR_SDRAM_INTERVAL[REFINT]
+ */
+ if (is_ddr3_4(popts))
+ obc_cfg = popts->otf_burst_chop_en;
+ else
+ obc_cfg = 0;
+
+ slow = c->ddr_freq < 1249000000;
+
+ if (popts->registered_dimm_en)
+ rcw_en = 1;
+
+ /* DDR4 can have address parity for UDIMM and discrete */
+ if (!is_ddr4(popts) && !popts->registered_dimm_en) {
+ ap_en = 0;
+ } else {
+ ap_en = popts->ap_en;
+ }
+
+ x4_en = popts->x4_en ? 1 : 0;
+
+ /* Use the DDR controller to auto initialize memory. */
+ d_init = common_dimm->all_dimms_ecc_capable ? 1 : 0;;
+ ddr->ddr_data_init = 0xdeadbeef;
+
+ if (is_ddr3_4(popts))
+ md_en = popts->mirrored_dimm;
+
+ qd_en = popts->quad_rank_present ? 1 : 0;
+ ddr->ddr_sdram_cfg_2 = (0
+ | ((frc_sr & 0x1) << 31)
+ | ((sr_ie & 0x1) << 30)
+ | ((dll_rst_dis & 0x1) << 29)
+ | ((dqs_cfg & 0x3) << 26)
+ | ((odt_cfg & 0x3) << 21)
+ | ((num_pr & 0xf) << 12)
+ | ((slow & 1) << 11)
+ | (x4_en << 10)
+ | (qd_en << 9)
+ | (unq_mrs_en << 8)
+ | ((obc_cfg & 0x1) << 6)
+ | ((ap_en & 0x1) << 5)
+ | ((d_init & 0x1) << 4)
+ | ((rcw_en & 0x1) << 2)
+ | ((md_en & 0x1) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_cfg_2 = 0x%08x\n", ddr->ddr_sdram_cfg_2);
+}
+
+/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */
+static void set_ddr4_sdram_mode_2(struct fsl_ddr_controller *c,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */
+ unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */
+ int i;
+ unsigned int wr_crc = 0; /* Disable */
+ unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */
+ unsigned int srt = 0; /* self-refresh temerature, normal range */
+ unsigned int cwl = compute_cas_write_latency_ddr4(c) - 9;
+ unsigned int mpr = 0; /* serial */
+ unsigned int wc_lat;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+
+ if (popts->rtt_override)
+ rtt_wr = popts->rtt_wr_override_value;
+ else
+ rtt_wr = popts->cs_local_opts[0].odt_rtt_wr;
+
+ if (common_dimm->extended_op_srt)
+ srt = common_dimm->extended_op_srt;
+
+ esdmode2 = (0
+ | ((wr_crc & 0x1) << 12)
+ | ((rtt_wr & 0x3) << 9)
+ | ((srt & 0x3) << 6)
+ | ((cwl & 0x7) << 3));
+
+ if (mclk_ps >= 1250)
+ wc_lat = 0;
+ else if (mclk_ps >= 833)
+ wc_lat = 1;
+ else
+ wc_lat = 2;
+
+ esdmode3 = (0
+ | ((mpr & 0x3) << 11)
+ | ((wc_lat & 0x3) << 9));
+
+ ddr->ddr_sdram_mode_2 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2);
+
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < c->chip_selects_per_ctrl; i++) {
+ if (popts->rtt_override)
+ rtt_wr = popts->rtt_wr_override_value;
+ else
+ rtt_wr = popts->cs_local_opts[i].odt_rtt_wr;
+
+ esdmode2 &= 0xF9FF; /* clear bit 10, 9 */
+ esdmode2 |= (rtt_wr & 0x3) << 9;
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_4 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_6 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_8 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_4 = 0x%08x\n",
+ ddr->ddr_sdram_mode_4);
+ debug("FSLDDR: ddr_sdram_mode_6 = 0x%08x\n",
+ ddr->ddr_sdram_mode_6);
+ debug("FSLDDR: ddr_sdram_mode_8 = 0x%08x\n",
+ ddr->ddr_sdram_mode_8);
+ }
+}
+
+/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */
+static void set_ddr3_sdram_mode_2(struct fsl_ddr_controller *c,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */
+ unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */
+ int i;
+ unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */
+ unsigned int srt = 0; /* self-refresh temerature, normal range */
+ unsigned int asr = 0; /* auto self-refresh disable */
+ unsigned int cwl = compute_cas_write_latency_ddr3(c) - 5;
+ unsigned int pasr = 0; /* partial array self refresh disable */
+
+ if (popts->rtt_override)
+ rtt_wr = popts->rtt_wr_override_value;
+ else
+ rtt_wr = popts->cs_local_opts[0].odt_rtt_wr;
+
+ if (common_dimm->extended_op_srt)
+ srt = common_dimm->extended_op_srt;
+
+ esdmode2 = (0
+ | ((rtt_wr & 0x3) << 9)
+ | ((srt & 0x1) << 7)
+ | ((asr & 0x1) << 6)
+ | ((cwl & 0x7) << 3)
+ | ((pasr & 0x7) << 0));
+ ddr->ddr_sdram_mode_2 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2);
+
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < c->chip_selects_per_ctrl; i++) {
+ if (popts->rtt_override)
+ rtt_wr = popts->rtt_wr_override_value;
+ else
+ rtt_wr = popts->cs_local_opts[i].odt_rtt_wr;
+
+ esdmode2 &= 0xF9FF; /* clear bit 10, 9 */
+ esdmode2 |= (rtt_wr & 0x3) << 9;
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_4 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_6 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_8 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_4 = 0x%08x\n",
+ ddr->ddr_sdram_mode_4);
+ debug("FSLDDR: ddr_sdram_mode_6 = 0x%08x\n",
+ ddr->ddr_sdram_mode_6);
+ debug("FSLDDR: ddr_sdram_mode_8 = 0x%08x\n",
+ ddr->ddr_sdram_mode_8);
+ }
+}
+
+/* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */
+static void set_ddr1_2_sdram_mode_2(struct fsl_ddr_controller *c,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */
+ unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */
+
+ ddr->ddr_sdram_mode_2 = (0
+ | ((esdmode2 & 0xFFFF) << 16)
+ | ((esdmode3 & 0xFFFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2);
+}
+
+/* DDR SDRAM Mode configuration 9 (DDR_SDRAM_MODE_9) */
+static void set_ddr_sdram_mode_9(struct fsl_ddr_controller *c,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ int i;
+ unsigned short esdmode4 = 0; /* Extended SDRAM mode 4 */
+ unsigned short esdmode5; /* Extended SDRAM mode 5 */
+ int rtt_park = 0;
+ bool four_cs = false;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(0);
+
+ if ((ddr->cs[0].config & SDRAM_CS_CONFIG_EN) &&
+ (ddr->cs[1].config & SDRAM_CS_CONFIG_EN) &&
+ (ddr->cs[2].config & SDRAM_CS_CONFIG_EN) &&
+ (ddr->cs[3].config & SDRAM_CS_CONFIG_EN))
+ four_cs = true;
+
+ if (ddr->cs[0].config & SDRAM_CS_CONFIG_EN) {
+ esdmode5 = 0x00000500; /* Data mask enable, RTT_PARK CS0 */
+ rtt_park = four_cs ? 0 : 1;
+ } else {
+ esdmode5 = 0x00000400; /* Data mask enabled */
+ }
+
+ /*
+ * For DDR3, set C/A latency if address parity is enabled.
+ * For DDR4, set C/A latency for UDIMM only. For RDIMM the delay is
+ * handled by register chip and RCW settings.
+ */
+ if ((ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) &&
+ (!is_ddr4(popts) || !popts->registered_dimm_en)) {
+ if (mclk_ps >= 935) {
+ /* for DDR4-1600/1866/2133 */
+ esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK;
+ } else if (mclk_ps >= 833) {
+ /* for DDR4-2400 */
+ esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK;
+ } else {
+ printf("parity: mclk_ps = %d not supported\n", mclk_ps);
+ }
+ }
+
+ ddr->ddr_sdram_mode_9 = (0
+ | ((esdmode4 & 0xffff) << 16)
+ | ((esdmode5 & 0xffff) << 0)
+ );
+
+ /* Normally only the first enabled CS use 0x500, others use 0x400
+ * But when four chip-selects are all enabled, all mode registers
+ * need 0x500 to park.
+ */
+
+ debug("FSLDDR: ddr_sdram_mode_9 = 0x%08x\n", ddr->ddr_sdram_mode_9);
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < c->chip_selects_per_ctrl; i++) {
+ if (!rtt_park &&
+ (ddr->cs[i].config & SDRAM_CS_CONFIG_EN)) {
+ esdmode5 |= 0x00000500; /* RTT_PARK */
+ rtt_park = four_cs ? 0 : 1;
+ } else {
+ esdmode5 = 0x00000400;
+ }
+
+ if ((ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) &&
+ (!is_ddr4(popts) || !popts->registered_dimm_en)) {
+ if (mclk_ps >= 935) {
+ /* for DDR4-1600/1866/2133 */
+ esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK;
+ } else if (mclk_ps >= 833) {
+ /* for DDR4-2400 */
+ esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK;
+ } else {
+ printf("parity: mclk_ps = %d not supported\n",
+ mclk_ps);
+ }
+ }
+
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_11 = (0
+ | ((esdmode4 & 0xFFFF) << 16)
+ | ((esdmode5 & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_13 = (0
+ | ((esdmode4 & 0xFFFF) << 16)
+ | ((esdmode5 & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_15 = (0
+ | ((esdmode4 & 0xFFFF) << 16)
+ | ((esdmode5 & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_11 = 0x%08x\n",
+ ddr->ddr_sdram_mode_11);
+ debug("FSLDDR: ddr_sdram_mode_13 = 0x%08x\n",
+ ddr->ddr_sdram_mode_13);
+ debug("FSLDDR: ddr_sdram_mode_15 = 0x%08x\n",
+ ddr->ddr_sdram_mode_15);
+ }
+}
+
+/* DDR SDRAM Mode configuration 10 (DDR_SDRAM_MODE_10) */
+static void set_ddr_sdram_mode_10(struct fsl_ddr_controller *c,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ int i;
+ unsigned short esdmode6 = 0; /* Extended SDRAM mode 6 */
+ unsigned short esdmode7 = 0; /* Extended SDRAM mode 7 */
+ unsigned int tccdl_min = picos_to_mclk(c, common_dimm->tccdl_ps);
+
+ esdmode6 = ((tccdl_min - 4) & 0x7) << 10;
+
+ if (popts->ddr_cdr2 & DDR_CDR2_VREF_RANGE_2)
+ esdmode6 |= 1 << 6; /* Range 2 */
+
+ ddr->ddr_sdram_mode_10 = (0
+ | ((esdmode6 & 0xffff) << 16)
+ | ((esdmode7 & 0xffff) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_mode_10 = 0x%08x\n", ddr->ddr_sdram_mode_10);
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < c->chip_selects_per_ctrl; i++) {
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_12 = (0
+ | ((esdmode6 & 0xFFFF) << 16)
+ | ((esdmode7 & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_14 = (0
+ | ((esdmode6 & 0xFFFF) << 16)
+ | ((esdmode7 & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_16 = (0
+ | ((esdmode6 & 0xFFFF) << 16)
+ | ((esdmode7 & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_12 = 0x%08x\n",
+ ddr->ddr_sdram_mode_12);
+ debug("FSLDDR: ddr_sdram_mode_14 = 0x%08x\n",
+ ddr->ddr_sdram_mode_14);
+ debug("FSLDDR: ddr_sdram_mode_16 = 0x%08x\n",
+ ddr->ddr_sdram_mode_16);
+ }
+}
+
+/* DDR SDRAM Interval Configuration (DDR_SDRAM_INTERVAL) */
+static void set_ddr_sdram_interval(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned int refint; /* Refresh interval */
+ unsigned int bstopre; /* Precharge interval */
+
+ refint = picos_to_mclk(c, common_dimm->refresh_rate_ps);
+
+ bstopre = popts->bstopre;
+
+ /* refint field used 0x3FFF in earlier controllers */
+ ddr->ddr_sdram_interval = (0
+ | ((refint & 0xFFFF) << 16)
+ | ((bstopre & 0x3FFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_interval = 0x%08x\n", ddr->ddr_sdram_interval);
+}
+
+/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */
+static void set_ddr_sdram_mode_ddr4(struct fsl_ddr_controller *c,
+ unsigned int cas_latency,
+ unsigned int additive_latency,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ int i;
+ unsigned short esdmode; /* Extended SDRAM mode */
+ unsigned short sdmode; /* SDRAM mode */
+
+ /* Mode Register - MR1 */
+ unsigned int qoff = 0; /* Output buffer enable 0=yes, 1=no */
+ unsigned int tdqs_en = 0; /* TDQS Enable: 0=no, 1=yes */
+ unsigned int rtt;
+ unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */
+ unsigned int al = 0; /* Posted CAS# additive latency (AL) */
+ unsigned int dic = 0; /* Output driver impedance, 40ohm */
+ unsigned int dll_en = 1; /* DLL Enable 1=Enable (Normal),
+ 0=Disable (Test/Debug) */
+
+ /* Mode Register - MR0 */
+ unsigned int wr = 0; /* Write Recovery */
+ unsigned int dll_rst; /* DLL Reset */
+ unsigned int mode; /* Normal=0 or Test=1 */
+ unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */
+ /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */
+ unsigned int bt;
+ unsigned int bl; /* BL: Burst Length */
+
+ unsigned int wr_mclk;
+ /* DDR4 support WR 10, 12, 14, 16, 18, 20, 24 */
+ static const u8 wr_table[] = {
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6};
+ /* DDR4 support CAS 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24 */
+ static const u8 cas_latency_table[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8,
+ 9, 9, 10, 10, 11, 11};
+
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[0].odt_rtt_norm;
+
+ if (additive_latency == (cas_latency - 1))
+ al = 1;
+ if (additive_latency == (cas_latency - 2))
+ al = 2;
+
+ if (popts->quad_rank_present)
+ dic = 1; /* output driver impedance 240/7 ohm */
+
+ /*
+ * The esdmode value will also be used for writing
+ * MR1 during write leveling for DDR3, although the
+ * bits specifically related to the write leveling
+ * scheme will be handled automatically by the DDR
+ * controller. so we set the wrlvl_en = 0 here.
+ */
+ esdmode = (0
+ | ((qoff & 0x1) << 12)
+ | ((tdqs_en & 0x1) << 11)
+ | ((rtt & 0x7) << 8)
+ | ((wrlvl_en & 0x1) << 7)
+ | ((al & 0x3) << 3)
+ | ((dic & 0x3) << 1) /* DIC field is split */
+ | ((dll_en & 0x1) << 0)
+ );
+
+ /*
+ * DLL control for precharge PD
+ * 0=slow exit DLL off (tXPDLL)
+ * 1=fast exit DLL on (tXP)
+ */
+
+ wr_mclk = picos_to_mclk(c, common_dimm->twr_ps);
+ if (wr_mclk <= 24) {
+ wr = wr_table[wr_mclk - 10];
+ } else {
+ printf("Error: unsupported write recovery for mode register wr_mclk = %d\n",
+ wr_mclk);
+ }
+
+ dll_rst = 0; /* dll no reset */
+ mode = 0; /* normal mode */
+
+ /* look up table to get the cas latency bits */
+ if (cas_latency >= 9 && cas_latency <= 24)
+ caslat = cas_latency_table[cas_latency - 9];
+ else
+ printf("Error: unsupported cas latency for mode register\n");
+
+ bt = 0; /* Nibble sequential */
+
+ switch (popts->burst_length) {
+ case DDR_BL8:
+ bl = 0;
+ break;
+ case DDR_OTF:
+ bl = 1;
+ break;
+ case DDR_BC4:
+ bl = 2;
+ break;
+ default:
+ printf("Error: invalid burst length of %u specified. ",
+ popts->burst_length);
+ printf("Defaulting to on-the-fly BC4 or BL8 beats.\n");
+ bl = 1;
+ break;
+ }
+
+ sdmode = (0
+ | ((wr & 0x7) << 9)
+ | ((dll_rst & 0x1) << 8)
+ | ((mode & 0x1) << 7)
+ | (((caslat >> 1) & 0x7) << 4)
+ | ((bt & 0x1) << 3)
+ | ((caslat & 1) << 2)
+ | ((bl & 0x3) << 0)
+ );
+
+ ddr->ddr_sdram_mode = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+
+ debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode);
+
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < c->chip_selects_per_ctrl; i++) {
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[i].odt_rtt_norm;
+
+ esdmode &= 0xF8FF; /* clear bit 10,9,8 for rtt */
+ esdmode |= (rtt & 0x7) << 8;
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_3 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_5 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_7 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_3 = 0x%08x\n",
+ ddr->ddr_sdram_mode_3);
+ debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n",
+ ddr->ddr_sdram_mode_5);
+ debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n",
+ ddr->ddr_sdram_mode_5);
+ }
+}
+
+/* DDR SDRAM Mode configuration set (DDR_SDRAM_MODE) */
+static void set_ddr_sdram_mode_ddr3(struct fsl_ddr_controller *c,
+ unsigned int cas_latency,
+ unsigned int additive_latency,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ int i;
+ unsigned short esdmode; /* Extended SDRAM mode */
+ unsigned short sdmode; /* SDRAM mode */
+
+ /* Mode Register - MR1 */
+ unsigned int qoff = 0; /* Output buffer enable 0=yes, 1=no */
+ unsigned int tdqs_en = 0; /* TDQS Enable: 0=no, 1=yes */
+ unsigned int rtt;
+ unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */
+ unsigned int al = 0; /* Posted CAS# additive latency (AL) */
+ unsigned int dic = 0; /* Output driver impedance, 40ohm */
+ unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal),
+ 1=Disable (Test/Debug) */
+
+ /* Mode Register - MR0 */
+ unsigned int dll_on; /* DLL control for precharge PD, 0=off, 1=on */
+ unsigned int wr = 0; /* Write Recovery */
+ unsigned int dll_rst; /* DLL Reset */
+ unsigned int mode; /* Normal=0 or Test=1 */
+ unsigned int caslat = 4;/* CAS# latency, default set as 6 cycles */
+ /* BT: Burst Type (0=Nibble Sequential, 1=Interleaved) */
+ unsigned int bt;
+ unsigned int bl; /* BL: Burst Length */
+
+ unsigned int wr_mclk;
+ /*
+ * DDR_SDRAM_MODE doesn't support 9,11,13,15
+ * Please refer JEDEC Standard No. 79-3E for Mode Register MR0
+ * for this table
+ */
+ static const u8 wr_table[] = {1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0};
+
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[0].odt_rtt_norm;
+
+ if (additive_latency == (cas_latency - 1))
+ al = 1;
+ if (additive_latency == (cas_latency - 2))
+ al = 2;
+
+ if (popts->quad_rank_present)
+ dic = 1; /* output driver impedance 240/7 ohm */
+
+ /*
+ * The esdmode value will also be used for writing
+ * MR1 during write leveling for DDR3, although the
+ * bits specifically related to the write leveling
+ * scheme will be handled automatically by the DDR
+ * controller. so we set the wrlvl_en = 0 here.
+ */
+ esdmode = (0
+ | ((qoff & 0x1) << 12)
+ | ((tdqs_en & 0x1) << 11)
+ | ((rtt & 0x4) << 7) /* rtt field is split */
+ | ((wrlvl_en & 0x1) << 7)
+ | ((rtt & 0x2) << 5) /* rtt field is split */
+ | ((dic & 0x2) << 4) /* DIC field is split */
+ | ((al & 0x3) << 3)
+ | ((rtt & 0x1) << 2) /* rtt field is split */
+ | ((dic & 0x1) << 1) /* DIC field is split */
+ | ((dll_en & 0x1) << 0)
+ );
+
+ /*
+ * DLL control for precharge PD
+ * 0=slow exit DLL off (tXPDLL)
+ * 1=fast exit DLL on (tXP)
+ */
+ dll_on = 1;
+
+ wr_mclk = picos_to_mclk(c, common_dimm->twr_ps);
+ if (wr_mclk <= 16) {
+ wr = wr_table[wr_mclk - 5];
+ } else {
+ printf("Error: unsupported write recovery for mode register "
+ "wr_mclk = %d\n", wr_mclk);
+ }
+
+ dll_rst = 0; /* dll no reset */
+ mode = 0; /* normal mode */
+
+ /* look up table to get the cas latency bits */
+ if (cas_latency >= 5 && cas_latency <= 16) {
+ unsigned char cas_latency_table[] = {
+ 0x2, /* 5 clocks */
+ 0x4, /* 6 clocks */
+ 0x6, /* 7 clocks */
+ 0x8, /* 8 clocks */
+ 0xa, /* 9 clocks */
+ 0xc, /* 10 clocks */
+ 0xe, /* 11 clocks */
+ 0x1, /* 12 clocks */
+ 0x3, /* 13 clocks */
+ 0x5, /* 14 clocks */
+ 0x7, /* 15 clocks */
+ 0x9, /* 16 clocks */
+ };
+ caslat = cas_latency_table[cas_latency - 5];
+ } else {
+ printf("Error: unsupported cas latency for mode register\n");
+ }
+
+ bt = 0; /* Nibble sequential */
+
+ switch (popts->burst_length) {
+ case DDR_BL8:
+ bl = 0;
+ break;
+ case DDR_OTF:
+ bl = 1;
+ break;
+ case DDR_BC4:
+ bl = 2;
+ break;
+ default:
+ printf("Error: invalid burst length of %u specified. "
+ " Defaulting to on-the-fly BC4 or BL8 beats.\n",
+ popts->burst_length);
+ bl = 1;
+ break;
+ }
+
+ sdmode = (0
+ | ((dll_on & 0x1) << 12)
+ | ((wr & 0x7) << 9)
+ | ((dll_rst & 0x1) << 8)
+ | ((mode & 0x1) << 7)
+ | (((caslat >> 1) & 0x7) << 4)
+ | ((bt & 0x1) << 3)
+ | ((caslat & 1) << 2)
+ | ((bl & 0x3) << 0)
+ );
+
+ ddr->ddr_sdram_mode = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+
+ debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode);
+
+ if (unq_mrs_en) { /* unique mode registers are supported */
+ for (i = 1; i < c->chip_selects_per_ctrl; i++) {
+ if (popts->rtt_override)
+ rtt = popts->rtt_override_value;
+ else
+ rtt = popts->cs_local_opts[i].odt_rtt_norm;
+
+ esdmode &= 0xFDBB; /* clear bit 9,6,2 */
+ esdmode |= (0
+ | ((rtt & 0x4) << 7) /* rtt field is split */
+ | ((rtt & 0x2) << 5) /* rtt field is split */
+ | ((rtt & 0x1) << 2) /* rtt field is split */
+ );
+ switch (i) {
+ case 1:
+ ddr->ddr_sdram_mode_3 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ case 2:
+ ddr->ddr_sdram_mode_5 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ case 3:
+ ddr->ddr_sdram_mode_7 = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ break;
+ }
+ }
+ debug("FSLDDR: ddr_sdram_mode_3 = 0x%08x\n",
+ ddr->ddr_sdram_mode_3);
+ debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n",
+ ddr->ddr_sdram_mode_5);
+ debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n",
+ ddr->ddr_sdram_mode_5);
+ }
+}
+
+static void set_ddr_sdram_mode_ddr12(struct fsl_ddr_controller *c,
+ unsigned int cas_latency,
+ unsigned int additive_latency,
+ const unsigned int unq_mrs_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned short esdmode; /* Extended SDRAM mode */
+ unsigned short sdmode; /* SDRAM mode */
+
+ /*
+ * FIXME: This ought to be pre-calculated in a
+ * technology-specific routine,
+ * e.g. compute_DDR2_mode_register(), and then the
+ * sdmode and esdmode passed in as part of common_dimm.
+ */
+
+ /* Extended Mode Register */
+ unsigned int mrs = 0; /* Mode Register Set */
+ unsigned int outputs = 0; /* 0=Enabled, 1=Disabled */
+ unsigned int rdqs_en = 0; /* RDQS Enable: 0=no, 1=yes */
+ unsigned int dqs_en = 0; /* DQS# Enable: 0=enable, 1=disable */
+ unsigned int ocd = 0; /* 0x0=OCD not supported,
+ 0x7=OCD default state */
+ unsigned int rtt;
+ unsigned int al; /* Posted CAS# additive latency (AL) */
+ unsigned int ods = 0; /* Output Drive Strength:
+ 0 = Full strength (18ohm)
+ 1 = Reduced strength (4ohm) */
+ unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal),
+ 1=Disable (Test/Debug) */
+
+ /* Mode Register (MR) */
+ unsigned int mr; /* Mode Register Definition */
+ unsigned int pd; /* Power-Down Mode */
+ unsigned int wr; /* Write Recovery */
+ unsigned int dll_res; /* DLL Reset */
+ unsigned int mode; /* Normal=0 or Test=1 */
+ unsigned int caslat = 0;/* CAS# latency */
+ /* BT: Burst Type (0=Sequential, 1=Interleaved) */
+ unsigned int bt;
+ unsigned int bl; /* BL: Burst Length */
+
+ dqs_en = !popts->dqs_config;
+ rtt = fsl_ddr_get_rtt(popts);
+
+ al = additive_latency;
+
+ esdmode = (0
+ | ((mrs & 0x3) << 14)
+ | ((outputs & 0x1) << 12)
+ | ((rdqs_en & 0x1) << 11)
+ | ((dqs_en & 0x1) << 10)
+ | ((ocd & 0x7) << 7)
+ | ((rtt & 0x2) << 5) /* rtt field is split */
+ | ((al & 0x7) << 3)
+ | ((rtt & 0x1) << 2) /* rtt field is split */
+ | ((ods & 0x1) << 1)
+ | ((dll_en & 0x1) << 0)
+ );
+
+ mr = 0; /* FIXME: CHECKME */
+
+ /*
+ * 0 = Fast Exit (Normal)
+ * 1 = Slow Exit (Low Power)
+ */
+ pd = 0;
+
+ if (is_ddr1(popts))
+ wr = 0; /* Historical */
+ else
+ wr = picos_to_mclk(c, common_dimm->twr_ps);
+
+ dll_res = 0;
+ mode = 0;
+
+ if (is_ddr1(popts)) {
+ if (1 <= cas_latency && cas_latency <= 4) {
+ unsigned char mode_caslat_table[4] = {
+ 0x5, /* 1.5 clocks */
+ 0x2, /* 2.0 clocks */
+ 0x6, /* 2.5 clocks */
+ 0x3 /* 3.0 clocks */
+ };
+ caslat = mode_caslat_table[cas_latency - 1];
+ } else {
+ printf("Warning: unknown cas_latency %d\n", cas_latency);
+ }
+ } else if (is_ddr2(popts)) {
+ caslat = cas_latency;
+ }
+
+ bt = 0;
+
+ switch (popts->burst_length) {
+ case DDR_BL4:
+ bl = 2;
+ break;
+ case DDR_BL8:
+ bl = 3;
+ break;
+ default:
+ printf("Error: invalid burst length of %u specified. "
+ " Defaulting to 4 beats.\n",
+ popts->burst_length);
+ bl = 2;
+ break;
+ }
+
+ sdmode = (0
+ | ((mr & 0x3) << 14)
+ | ((pd & 0x1) << 12)
+ | ((wr & 0x7) << 9)
+ | ((dll_res & 0x1) << 8)
+ | ((mode & 0x1) << 7)
+ | ((caslat & 0x7) << 4)
+ | ((bt & 0x1) << 3)
+ | ((bl & 0x7) << 0)
+ );
+
+ ddr->ddr_sdram_mode = (0
+ | ((esdmode & 0xFFFF) << 16)
+ | ((sdmode & 0xFFFF) << 0)
+ );
+ debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode);
+}
+
+/*
+ * DDR SDRAM Clock Control (DDR_SDRAM_CLK_CNTL)
+ * The old controller on the 8540/60 doesn't have this register.
+ * Hope it's OK to set it (to 0) anyway.
+ */
+static void set_ddr_sdram_clk_cntl(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ unsigned int clk_adjust; /* Clock adjust */
+ unsigned int ss_en = 0; /* Source synchronous enable */
+
+ if (fsl_ddr_get_version(c) >= 0x40701) {
+ /* clk_adjust in 5-bits on T-series and LS-series */
+ clk_adjust = (popts->clk_adjust & 0x1F) << 22;
+ } else {
+ /* clk_adjust in 4-bits on earlier MPC85xx and P-series */
+ clk_adjust = (popts->clk_adjust & 0xF) << 23;
+ }
+
+ ddr->ddr_sdram_clk_cntl = (0
+ | ((ss_en & 0x1) << 31)
+ | clk_adjust
+ );
+ debug("FSLDDR: clk_cntl = 0x%08x\n", ddr->ddr_sdram_clk_cntl);
+}
+
+/* DDR Initialization Address (DDR_INIT_ADDR) */
+static void set_ddr_init_addr(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ unsigned int init_addr = 0; /* Initialization address */
+
+ ddr->ddr_init_addr = init_addr;
+}
+
+/* DDR Initialization Address (DDR_INIT_EXT_ADDR) */
+static void set_ddr_init_ext_addr(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ unsigned int uia = 0; /* Use initialization address */
+ unsigned int init_ext_addr = 0; /* Initialization address */
+
+ ddr->ddr_init_ext_addr = (0
+ | ((uia & 0x1) << 31)
+ | (init_ext_addr & 0xF)
+ );
+}
+
+/* DDR SDRAM Timing Configuration 4 (TIMING_CFG_4) */
+static void set_timing_cfg_4(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ unsigned int rwt = 0; /* Read-to-write turnaround for same CS */
+ unsigned int wrt = 0; /* Write-to-read turnaround for same CS */
+ unsigned int rrt = 0; /* Read-to-read turnaround for same CS */
+ unsigned int wwt = 0; /* Write-to-write turnaround for same CS */
+ unsigned int trwt_mclk = 0; /* ext_rwt */
+ unsigned int dll_lock = 0; /* DDR SDRAM DLL Lock Time */
+
+ if (is_ddr3_4(popts)) {
+ if (popts->burst_length == DDR_BL8) {
+ /* We set BL/2 for fixed BL8 */
+ rrt = 0; /* BL/2 clocks */
+ wwt = 0; /* BL/2 clocks */
+ } else {
+ /* We need to set BL/2 + 2 to BC4 and OTF */
+ rrt = 2; /* BL/2 + 2 clocks */
+ wwt = 2; /* BL/2 + 2 clocks */
+ }
+ }
+
+ if (is_ddr4(popts))
+ dll_lock = 2; /* tDLLK = 1024 clocks */
+ else if (is_ddr3(popts))
+ dll_lock = 1; /* tDLLK = 512 clocks from spec */
+
+ if (popts->trwt_override)
+ trwt_mclk = popts->trwt;
+
+ ddr->timing_cfg_4 = (0
+ | ((rwt & 0xf) << 28)
+ | ((wrt & 0xf) << 24)
+ | ((rrt & 0xf) << 20)
+ | ((wwt & 0xf) << 16)
+ | ((trwt_mclk & 0xc) << 12)
+ | (dll_lock & 0x3)
+ );
+ debug("FSLDDR: timing_cfg_4 = 0x%08x\n", ddr->timing_cfg_4);
+}
+
+/* DDR SDRAM Timing Configuration 5 (TIMING_CFG_5) */
+static void set_timing_cfg_5(struct fsl_ddr_controller *c, unsigned int cas_latency)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ unsigned int rodt_on = 0; /* Read to ODT on */
+ unsigned int rodt_off = 0; /* Read to ODT off */
+ unsigned int wodt_on = 0; /* Write to ODT on */
+ unsigned int wodt_off = 0; /* Write to ODT off */
+
+ if (is_ddr3_4(popts)) {
+ unsigned int wr_lat = ((ddr->timing_cfg_2 & 0x00780000) >> 19) +
+ ((ddr->timing_cfg_2 & 0x00040000) >> 14);
+ /* rodt_on = timing_cfg_1[caslat] - timing_cfg_2[wrlat] + 1 */
+ if (cas_latency >= wr_lat)
+ rodt_on = cas_latency - wr_lat + 1;
+ rodt_off = 4; /* 4 clocks */
+ wodt_on = 1; /* 1 clocks */
+ wodt_off = 4; /* 4 clocks */
+ }
+
+ ddr->timing_cfg_5 = (0
+ | ((rodt_on & 0x1f) << 24)
+ | ((rodt_off & 0x7) << 20)
+ | ((wodt_on & 0x1f) << 12)
+ | ((wodt_off & 0x7) << 8)
+ );
+ debug("FSLDDR: timing_cfg_5 = 0x%08x\n", ddr->timing_cfg_5);
+}
+
+static void set_timing_cfg_6(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ unsigned int hs_caslat = 0;
+ unsigned int hs_wrlat = 0;
+ unsigned int hs_wrrec = 0;
+ unsigned int hs_clkadj = 0;
+ unsigned int hs_wrlvl_start = 0;
+
+ ddr->timing_cfg_6 = (0
+ | ((hs_caslat & 0x1f) << 24)
+ | ((hs_wrlat & 0x1f) << 19)
+ | ((hs_wrrec & 0x1f) << 12)
+ | ((hs_clkadj & 0x1f) << 6)
+ | ((hs_wrlvl_start & 0x1f) << 0)
+ );
+ debug("FSLDDR: timing_cfg_6 = 0x%08x\n", ddr->timing_cfg_6);
+}
+
+static void set_timing_cfg_7(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned int txpr, tcksre, tcksrx;
+ unsigned int cke_rst, cksre, cksrx, par_lat = 0, cs_to_cmd;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+
+ txpr = max(5U, picos_to_mclk(c, common_dimm->trfc1_ps + 10000));
+ tcksre = max(5U, picos_to_mclk(c, 10000));
+ tcksrx = max(5U, picos_to_mclk(c, 10000));
+
+ if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN && is_ddr4(popts)) {
+ /* for DDR4 only */
+ par_lat = (ddr->ddr_sdram_rcw_2 & 0xf) + 1;
+ debug("PAR_LAT = %u for mclk_ps = %d\n", par_lat, mclk_ps);
+ }
+
+ cs_to_cmd = 0;
+
+ if (txpr <= 200)
+ cke_rst = 0;
+ else if (txpr <= 256)
+ cke_rst = 1;
+ else if (txpr <= 512)
+ cke_rst = 2;
+ else
+ cke_rst = 3;
+
+ if (tcksre <= 19)
+ cksre = tcksre - 5;
+ else
+ cksre = 15;
+
+ if (tcksrx <= 19)
+ cksrx = tcksrx - 5;
+ else
+ cksrx = 15;
+
+ ddr->timing_cfg_7 = (0
+ | ((cke_rst & 0x3) << 28)
+ | ((cksre & 0xf) << 24)
+ | ((cksrx & 0xf) << 20)
+ | ((par_lat & 0xf) << 16)
+ | ((cs_to_cmd & 0xf) << 4)
+ );
+ debug("FSLDDR: timing_cfg_7 = 0x%08x\n", ddr->timing_cfg_7);
+}
+
+static void set_timing_cfg_8(struct fsl_ddr_controller *c, unsigned int cas_latency)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ int rwt_bg, wrt_bg, rrt_bg, wwt_bg;
+ unsigned int acttoact_bg, wrtord_bg, pre_all_rec;
+ int tccdl = picos_to_mclk(c, common_dimm->tccdl_ps);
+ int wr_lat = ((ddr->timing_cfg_2 & 0x00780000) >> 19) +
+ ((ddr->timing_cfg_2 & 0x00040000) >> 14);
+
+ rwt_bg = cas_latency + 2 + 4 - wr_lat;
+ if (rwt_bg < tccdl)
+ rwt_bg = tccdl - rwt_bg;
+ else
+ rwt_bg = 0;
+
+ wrt_bg = wr_lat + 4 + 1 - cas_latency;
+ if (wrt_bg < tccdl)
+ wrt_bg = tccdl - wrt_bg;
+ else
+ wrt_bg = 0;
+
+ if (popts->burst_length == DDR_BL8) {
+ rrt_bg = tccdl - 4;
+ wwt_bg = tccdl - 4;
+ } else {
+ rrt_bg = tccdl - 2;
+ wwt_bg = tccdl - 2;
+ }
+
+ acttoact_bg = picos_to_mclk(c, common_dimm->trrdl_ps);
+ wrtord_bg = max(4U, picos_to_mclk(c, 7500));
+ if (popts->otf_burst_chop_en)
+ wrtord_bg += 2;
+
+ pre_all_rec = 0;
+
+ ddr->timing_cfg_8 = (0
+ | ((rwt_bg & 0xf) << 28)
+ | ((wrt_bg & 0xf) << 24)
+ | ((rrt_bg & 0xf) << 20)
+ | ((wwt_bg & 0xf) << 16)
+ | ((acttoact_bg & 0xf) << 12)
+ | ((wrtord_bg & 0xf) << 8)
+ | ((pre_all_rec & 0x1f) << 0)
+ );
+
+ debug("FSLDDR: timing_cfg_8 = 0x%08x\n", ddr->timing_cfg_8);
+}
+
+static void set_timing_cfg_9(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ unsigned int refrec_cid_mclk = 0;
+ unsigned int acttoact_cid_mclk = 0;
+
+ if (popts->package_3ds) {
+ refrec_cid_mclk =
+ picos_to_mclk(c, common_dimm->trfc_slr_ps);
+ acttoact_cid_mclk = 4U; /* tRRDS_slr */
+ }
+
+ ddr->timing_cfg_9 = (refrec_cid_mclk & 0x3ff) << 16 |
+ (acttoact_cid_mclk & 0xf) << 8;
+
+ debug("FSLDDR: timing_cfg_9 = 0x%08x\n", ddr->timing_cfg_9);
+}
+
+/* This function needs to be called after set_ddr_sdram_cfg() is called */
+static void set_ddr_dq_mapping(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const struct dimm_params *dimm_params = c->dimm_params;
+ unsigned int acc_ecc_en = (ddr->ddr_sdram_cfg >> 2) & 0x1;
+ int i;
+
+ for (i = 0; i < c->dimm_slots_per_ctrl; i++) {
+ if (dimm_params[i].n_ranks)
+ break;
+ }
+ if (i >= c->dimm_slots_per_ctrl) {
+ printf("DDR error: no DIMM found!\n");
+ return;
+ }
+
+ ddr->dq_map_0 = ((dimm_params[i].dq_mapping[0] & 0x3F) << 26) |
+ ((dimm_params[i].dq_mapping[1] & 0x3F) << 20) |
+ ((dimm_params[i].dq_mapping[2] & 0x3F) << 14) |
+ ((dimm_params[i].dq_mapping[3] & 0x3F) << 8) |
+ ((dimm_params[i].dq_mapping[4] & 0x3F) << 2);
+
+ ddr->dq_map_1 = ((dimm_params[i].dq_mapping[5] & 0x3F) << 26) |
+ ((dimm_params[i].dq_mapping[6] & 0x3F) << 20) |
+ ((dimm_params[i].dq_mapping[7] & 0x3F) << 14) |
+ ((dimm_params[i].dq_mapping[10] & 0x3F) << 8) |
+ ((dimm_params[i].dq_mapping[11] & 0x3F) << 2);
+
+ ddr->dq_map_2 = ((dimm_params[i].dq_mapping[12] & 0x3F) << 26) |
+ ((dimm_params[i].dq_mapping[13] & 0x3F) << 20) |
+ ((dimm_params[i].dq_mapping[14] & 0x3F) << 14) |
+ ((dimm_params[i].dq_mapping[15] & 0x3F) << 8) |
+ ((dimm_params[i].dq_mapping[16] & 0x3F) << 2);
+
+ /* dq_map for ECC[4:7] is set to 0 if accumulated ECC is enabled */
+ ddr->dq_map_3 = ((dimm_params[i].dq_mapping[17] & 0x3F) << 26) |
+ ((dimm_params[i].dq_mapping[8] & 0x3F) << 20) |
+ (acc_ecc_en ? 0 :
+ (dimm_params[i].dq_mapping[9] & 0x3F) << 14) |
+ dimm_params[i].dq_mapping_ors;
+
+ debug("FSLDDR: dq_map_0 = 0x%08x\n", ddr->dq_map_0);
+ debug("FSLDDR: dq_map_1 = 0x%08x\n", ddr->dq_map_1);
+ debug("FSLDDR: dq_map_2 = 0x%08x\n", ddr->dq_map_2);
+ debug("FSLDDR: dq_map_3 = 0x%08x\n", ddr->dq_map_3);
+}
+static void set_ddr_sdram_cfg_3(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ int rd_pre;
+
+ rd_pre = popts->quad_rank_present ? 1 : 0;
+
+ ddr->ddr_sdram_cfg_3 = (rd_pre & 0x1) << 16;
+ /* Disable MRS on parity error for RDIMMs */
+ ddr->ddr_sdram_cfg_3 |= popts->registered_dimm_en ? 1 : 0;
+
+ if (popts->package_3ds) { /* only 2,4,8 are supported */
+ if ((popts->package_3ds + 1) & 0x1) {
+ printf("Error: Unsupported 3DS DIMM with %d die\n",
+ popts->package_3ds + 1);
+ } else {
+ ddr->ddr_sdram_cfg_3 |= ((popts->package_3ds + 1) >> 1)
+ << 4;
+ }
+ }
+
+ debug("FSLDDR: ddr_sdram_cfg_3 = 0x%08x\n", ddr->ddr_sdram_cfg_3);
+}
+
+/* DDR ZQ Calibration Control (DDR_ZQ_CNTL) */
+static void set_ddr_zq_cntl(struct fsl_ddr_controller *c,
+ unsigned int zq_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+ unsigned int zqinit; /* POR ZQ Calibration Time (tZQinit) */
+ unsigned int zqoper; /* Normal Operation Full Calibration Time (tZQoper) */
+ unsigned int zqcs; /* Normal Operation Short Calibration Time (tZQCS) */
+ unsigned int zqcs_init;
+
+ if (!zq_en) {
+ ddr->ddr_zq_cntl = 0;
+ goto out;
+ }
+
+ if (is_ddr4(popts)) {
+ zqinit = 10; /* 1024 clocks */
+ zqoper = 9; /* 512 clocks */
+ zqcs = 7; /* 128 clocks */
+ zqcs_init = 5; /* 1024 refresh sequences */
+ } else {
+ zqinit = 9; /* 512 clocks */
+ zqoper = 8; /* 256 clocks */
+ zqcs = 6; /* 64 clocks */
+ zqcs_init = 0;
+ }
+
+ ddr->ddr_zq_cntl = ((zq_en & 0x1) << 31)
+ | ((zqinit & 0xF) << 24)
+ | ((zqoper & 0xF) << 16)
+ | ((zqcs & 0xF) << 8)
+ | (zqcs_init & 0xF);
+
+out:
+ debug("FSLDDR: zq_cntl = 0x%08x\n", ddr->ddr_zq_cntl);
+}
+
+/* DDR Write Leveling Control (DDR_WRLVL_CNTL) */
+static void set_ddr_wrlvl_cntl(struct fsl_ddr_controller *c, unsigned int wrlvl_en)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+
+ /*
+ * First DQS pulse rising edge after margining mode
+ * is programmed (tWL_MRD)
+ */
+ unsigned int wrlvl_mrd = 0;
+ /* ODT delay after margining mode is programmed (tWL_ODTEN) */
+ unsigned int wrlvl_odten = 0;
+ /* DQS/DQS_ delay after margining mode is programmed (tWL_DQSEN) */
+ unsigned int wrlvl_dqsen = 0;
+ /* WRLVL_SMPL: Write leveling sample time */
+ unsigned int wrlvl_smpl = 0;
+ /* WRLVL_WLR: Write leveling repeition time */
+ unsigned int wrlvl_wlr = 0;
+ /* WRLVL_START: Write leveling start time */
+ unsigned int wrlvl_start = 0;
+
+ /* suggest enable write leveling for DDR3 due to fly-by topology */
+ if (wrlvl_en) {
+ /* tWL_MRD min = 40 nCK, we set it 64 */
+ wrlvl_mrd = 0x6;
+ /* tWL_ODTEN 128 */
+ wrlvl_odten = 0x7;
+ /* tWL_DQSEN min = 25 nCK, we set it 32 */
+ wrlvl_dqsen = 0x5;
+ /*
+ * Write leveling sample time at least need 6 clocks
+ * higher than tWLO to allow enough time for progagation
+ * delay and sampling the prime data bits.
+ */
+ wrlvl_smpl = 0xf;
+ /*
+ * Write leveling repetition time
+ * at least tWLO + 6 clocks clocks
+ * we set it 64
+ */
+ wrlvl_wlr = 0x6;
+ /*
+ * Write leveling start time
+ * The value use for the DQS_ADJUST for the first sample
+ * when write leveling is enabled. It probably needs to be
+ * overridden per platform.
+ */
+ wrlvl_start = 0x8;
+ /*
+ * Override the write leveling sample and start time
+ * according to specific board
+ */
+ if (popts->wrlvl_override) {
+ wrlvl_smpl = popts->wrlvl_sample;
+ wrlvl_start = popts->wrlvl_start;
+ }
+ }
+
+ ddr->ddr_wrlvl_cntl = (0
+ | ((wrlvl_en & 0x1) << 31)
+ | ((wrlvl_mrd & 0x7) << 24)
+ | ((wrlvl_odten & 0x7) << 20)
+ | ((wrlvl_dqsen & 0x7) << 16)
+ | ((wrlvl_smpl & 0xf) << 12)
+ | ((wrlvl_wlr & 0x7) << 8)
+ | ((wrlvl_start & 0x1F) << 0)
+ );
+ debug("FSLDDR: wrlvl_cntl = 0x%08x\n", ddr->ddr_wrlvl_cntl);
+ ddr->ddr_wrlvl_cntl_2 = popts->wrlvl_ctl_2;
+ debug("FSLDDR: wrlvl_cntl_2 = 0x%08x\n", ddr->ddr_wrlvl_cntl_2);
+ ddr->ddr_wrlvl_cntl_3 = popts->wrlvl_ctl_3;
+ debug("FSLDDR: wrlvl_cntl_3 = 0x%08x\n", ddr->ddr_wrlvl_cntl_3);
+
+}
+
+/* DDR Self Refresh Counter (DDR_SR_CNTR) */
+static void set_ddr_sr_cntr(struct fsl_ddr_controller *c, unsigned int sr_it)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+
+ /* Self Refresh Idle Threshold */
+ ddr->ddr_sr_cntr = (sr_it & 0xF) << 16;
+}
+
+static void set_ddr_eor(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+
+ if (popts->addr_hash) {
+ ddr->ddr_eor = 0x40000000; /* address hash enable */
+ printf("Address hashing enabled.\n");
+ }
+}
+
+static void set_ddr_cdr1(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+
+ ddr->ddr_cdr1 = popts->ddr_cdr1;
+ debug("FSLDDR: ddr_cdr1 = 0x%08x\n", ddr->ddr_cdr1);
+}
+
+static void set_ddr_cdr2(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const memctl_options_t *popts = &c->memctl_opts;
+
+ ddr->ddr_cdr2 = popts->ddr_cdr2;
+ debug("FSLDDR: ddr_cdr2 = 0x%08x\n", ddr->ddr_cdr2);
+}
+
+static unsigned int
+check_fsl_memctl_config_regs(struct fsl_ddr_controller *c)
+{
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ unsigned int res = 0;
+
+ /*
+ * Check that DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] are
+ * not set at the same time.
+ */
+ if (ddr->ddr_sdram_cfg & 0x10000000
+ && ddr->ddr_sdram_cfg & 0x00008000) {
+ printf("Error: DDR_SDRAM_CFG[RD_EN] and DDR_SDRAM_CFG[2T_EN] "
+ " should not be set at the same time.\n");
+ res++;
+ }
+
+ return res;
+}
+
+unsigned int
+compute_fsl_memctl_config_regs(struct fsl_ddr_controller *c)
+{
+ const memctl_options_t *popts = &c->memctl_opts;
+ fsl_ddr_cfg_regs_t *ddr = &c->fsl_ddr_config_reg;
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ const struct dimm_params *dimm_params = c->dimm_params;
+ unsigned int i;
+ unsigned int cas_latency;
+ unsigned int additive_latency;
+ unsigned int sr_it;
+ unsigned int wrlvl_en;
+ unsigned int ip_rev = 0;
+ unsigned int unq_mrs_en = 0;
+ int cs_en = 1;
+ unsigned int ddr_freq;
+ struct ccsr_ddr __iomem *ddrc = c->base;
+
+ memset(ddr, 0, sizeof(fsl_ddr_cfg_regs_t));
+
+ if (common_dimm == NULL) {
+ printf("Error: subset DIMM params struct null pointer\n");
+ return 1;
+ }
+
+ /*
+ * Process overrides first.
+ *
+ * FIXME: somehow add dereated caslat to this
+ */
+ cas_latency = (popts->cas_latency_override)
+ ? popts->cas_latency_override_value
+ : common_dimm->lowest_common_spd_caslat;
+
+ additive_latency = (popts->additive_latency_override)
+ ? popts->additive_latency_override_value
+ : common_dimm->additive_latency;
+
+ sr_it = (popts->auto_self_refresh_en)
+ ? popts->sr_it
+ : 0;
+ /* write leveling */
+ wrlvl_en = (popts->wrlvl_en) ? 1 : 0;
+
+ /* Chip Select Memory Bounds (CSn_BNDS) */
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ unsigned long long ea, sa;
+ unsigned int cs_per_dimm
+ = c->chip_selects_per_ctrl / c->dimm_slots_per_ctrl;
+ unsigned int dimm_number
+ = i / cs_per_dimm;
+ unsigned long long rank_density
+ = dimm_params[dimm_number].rank_density >> c->dbw_capacity_adjust;
+
+ if (dimm_params[dimm_number].n_ranks == 0) {
+ debug("Skipping setup of CS%u "
+ "because n_ranks on DIMM %u is 0\n", i, dimm_number);
+ continue;
+ }
+ if (popts->memctl_interleaving) {
+ switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ break;
+ case FSL_DDR_CS0_CS1:
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ if (i > 1)
+ cs_en = 0;
+ break;
+ case FSL_DDR_CS2_CS3:
+ default:
+ if (i > 0)
+ cs_en = 0;
+ break;
+ }
+ sa = common_dimm->base_address;
+ ea = sa + common_dimm->total_mem - 1;
+ } else if (!popts->memctl_interleaving) {
+ /*
+ * If memory interleaving between controllers is NOT
+ * enabled, the starting address for each memory
+ * controller is distinct. However, because rank
+ * interleaving is enabled, the starting and ending
+ * addresses of the total memory on that memory
+ * controller needs to be programmed into its
+ * respective CS0_BNDS.
+ */
+ switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ sa = common_dimm->base_address;
+ ea = sa + common_dimm->total_mem - 1;
+ break;
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ if ((i >= 2) && (dimm_number == 0)) {
+ sa = dimm_params[dimm_number].base_address +
+ 2 * rank_density;
+ ea = sa + 2 * rank_density - 1;
+ } else {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + 2 * rank_density - 1;
+ }
+ break;
+ case FSL_DDR_CS0_CS1:
+ if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (i != 1)
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ if (i == 0)
+ ea += rank_density;
+ break;
+ case FSL_DDR_CS2_CS3:
+ if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (i != 3)
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ if (i == 2)
+ ea += (rank_density >> c->dbw_capacity_adjust);
+ break;
+ default: /* No bank(chip-select) interleaving */
+ sa = dimm_params[dimm_number].base_address;
+ ea = sa + rank_density - 1;
+ if (dimm_params[dimm_number].n_ranks > (i % cs_per_dimm)) {
+ sa += (i % cs_per_dimm) * rank_density;
+ ea += (i % cs_per_dimm) * rank_density;
+ } else {
+ sa = 0;
+ ea = 0;
+ }
+ break;
+ }
+ }
+
+ sa >>= 24;
+ ea >>= 24;
+
+ if (cs_en) {
+ ddr->cs[i].bnds = (0
+ | ((sa & 0xffff) << 16) /* starting address */
+ | ((ea & 0xffff) << 0) /* ending address */
+ );
+ } else {
+ /* setting bnds to 0xffffffff for inactive CS */
+ ddr->cs[i].bnds = 0xffffffff;
+ }
+
+ debug("FSLDDR: cs[%d]_bnds = 0x%08x\n", i, ddr->cs[i].bnds);
+ set_csn_config(dimm_number, i, ddr, popts, dimm_params);
+ set_csn_config_2(i, ddr);
+ }
+
+ set_ddr_eor(c);
+
+ if (!is_ddr1(popts))
+ set_timing_cfg_0(c);
+
+ set_timing_cfg_3(c, cas_latency,
+ additive_latency);
+ set_timing_cfg_1(c, cas_latency);
+ set_timing_cfg_2(c, cas_latency, additive_latency);
+
+ set_ddr_cdr1(c);
+ set_ddr_cdr2(c);
+ set_ddr_sdram_cfg(c);
+ ip_rev = fsl_ddr_get_version(c);
+ if (ip_rev > 0x40400)
+ unq_mrs_en = 1;
+
+ if ((ip_rev > 0x40700) && (popts->cswl_override != 0))
+ ddr->debug[18] = popts->cswl_override;
+
+ set_ddr_sdram_cfg_2(c, unq_mrs_en);
+ if (is_ddr4(popts)) {
+ set_ddr_sdram_mode_ddr4(c, cas_latency, additive_latency, unq_mrs_en);
+ set_ddr4_sdram_mode_2(c, unq_mrs_en);
+ set_ddr_sdram_mode_9(c, unq_mrs_en);
+ set_ddr_sdram_mode_10(c, unq_mrs_en);
+ } else if (is_ddr3(popts)) {
+ set_ddr_sdram_mode_ddr3(c, cas_latency, additive_latency, unq_mrs_en);
+ set_ddr3_sdram_mode_2(c, unq_mrs_en);
+ } else {
+ set_ddr_sdram_mode_ddr12(c, cas_latency, additive_latency, unq_mrs_en);
+ set_ddr1_2_sdram_mode_2(c, unq_mrs_en);
+ }
+
+ set_ddr_sdram_rcw(c);
+
+ set_ddr_sdram_interval(c);
+
+ ddr->ddr_data_init = 0xdeadbeef;
+
+ set_ddr_sdram_clk_cntl(c);
+ set_ddr_init_addr(c);
+ set_ddr_init_ext_addr(c);
+ set_timing_cfg_4(c);
+ set_timing_cfg_5(c, cas_latency);
+
+ if (is_ddr4(popts)) {
+ set_ddr_sdram_cfg_3(c);
+ set_timing_cfg_6(c);
+ set_timing_cfg_7(c);
+ set_timing_cfg_8(c, cas_latency);
+ set_timing_cfg_9(c);
+ set_ddr_dq_mapping(c);
+ }
+
+ set_ddr_zq_cntl(c, popts->zq_en);
+ set_ddr_wrlvl_cntl(c, wrlvl_en);
+
+ set_ddr_sr_cntr(c, sr_it);
+
+ if (c->erratum_A004508 && ip_rev >= 0x40000 && ip_rev < 0x40400)
+ ddr->debug[2] |= 0x00000200; /* set bit 22 */
+
+ /* Erratum applies when accumulated ECC is used, or DBI is enabled */
+#define IS_ACC_ECC_EN(v) ((v) & 0x4)
+#define IS_DBI(v) ((((v) >> 12) & 0x3) == 0x2)
+ if (c->erratum_A008378) {
+ if (IS_ACC_ECC_EN(ddr->ddr_sdram_cfg) ||
+ IS_DBI(ddr->ddr_sdram_cfg_3)) {
+ ddr->debug[28] = ddr_in32(&ddrc->debug[28]);
+ ddr->debug[28] |= (0x9 << 20);
+ }
+ }
+
+ if (c->erratum_A009942) {
+ ddr_freq = c->ddr_freq / 1000000;
+ ddr->debug[28] |= ddr_in32(&ddrc->debug[28]);
+ ddr->debug[28] &= 0xff0fff00;
+ if (ddr_freq <= 1333)
+ ddr->debug[28] |= 0x0080006a;
+ else if (ddr_freq <= 1600)
+ ddr->debug[28] |= 0x0070006f;
+ else if (ddr_freq <= 1867)
+ ddr->debug[28] |= 0x00700076;
+ else if (ddr_freq <= 2133)
+ ddr->debug[28] |= 0x0060007b;
+ if (popts->cpo_sample)
+ ddr->debug[28] = (ddr->debug[28] & 0xffffff00) |
+ popts->cpo_sample;
+ }
+
+ return check_fsl_memctl_config_regs(c);
+}
diff --git a/drivers/ddr/fsl/ddr1_dimm_params.c b/drivers/ddr/fsl/ddr1_dimm_params.c
new file mode 100644
index 0000000..268bf5b
--- /dev/null
+++ b/drivers/ddr/fsl/ddr1_dimm_params.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ */
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <linux/log2.h>
+#include "fsl_ddr.h"
+
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * Study these table from Byte 31 of JEDEC SPD Spec.
+ *
+ * DDR I DDR II
+ * Bit Size Size
+ * --- ----- ------
+ * 7 high 512MB 512MB
+ * 6 256MB 256MB
+ * 5 128MB 128MB
+ * 4 64MB 16GB
+ * 3 32MB 8GB
+ * 2 16MB 4GB
+ * 1 2GB 2GB
+ * 0 low 1GB 1GB
+ *
+ * Reorder Table to be linear by stripping the bottom
+ * 2 or 5 bits off and shifting them up to the top.
+ */
+
+static unsigned long long
+compute_ranksize(unsigned int mem_type, unsigned char row_dens)
+{
+ unsigned long long bsize;
+
+ /* Bottom 2 bits up to the top. */
+ bsize = ((row_dens >> 2) | ((row_dens & 3) << 6));
+ bsize <<= 24ULL;
+ debug("DDR: DDR I rank density = 0x%16llx\n", bsize);
+
+ return bsize;
+}
+
+/*
+ * Convert a two-nibble BCD value into a cycle time.
+ * While the spec calls for nano-seconds, picos are returned.
+ *
+ * This implements the tables for bytes 9, 23 and 25 for both
+ * DDR I and II. No allowance for distinguishing the invalid
+ * fields absent for DDR I yet present in DDR II is made.
+ * (That is, cycle times of .25, .33, .66 and .75 ns are
+ * allowed for both DDR II and I.)
+ */
+static unsigned int
+convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val)
+{
+ /* Table look up the lower nibble, allow DDR I & II. */
+ unsigned int tenths_ps[16] = {
+ 0,
+ 100,
+ 200,
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 250, /* This and the next 3 entries valid ... */
+ 330, /* ... only for tCK calculations. */
+ 660,
+ 750,
+ 0, /* undefined */
+ 0 /* undefined */
+ };
+
+ unsigned int whole_ns = (spd_val & 0xF0) >> 4;
+ unsigned int tenth_ns = spd_val & 0x0F;
+ unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns];
+
+ return ps;
+}
+
+static unsigned int
+convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val)
+{
+ unsigned int tenth_ns = (spd_val & 0xF0) >> 4;
+ unsigned int hundredth_ns = spd_val & 0x0F;
+ unsigned int ps = tenth_ns * 100 + hundredth_ns * 10;
+
+ return ps;
+}
+
+static unsigned int byte40_table_ps[8] = {
+ 0,
+ 250,
+ 330,
+ 500,
+ 660,
+ 750,
+ 0, /* supposed to be RFC, but not sure what that means */
+ 0 /* Undefined */
+};
+
+static unsigned int
+compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc)
+{
+ return ((trctrfc_ext & 0x1) * 256 + trfc) * 1000
+ + byte40_table_ps[(trctrfc_ext >> 1) & 0x7];
+}
+
+static unsigned int
+compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc)
+{
+ return trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7];
+}
+
+/*
+ * tCKmax from DDR I SPD Byte 43
+ *
+ * Bits 7:2 == whole ns
+ * Bits 1:0 == quarter ns
+ * 00 == 0.00 ns
+ * 01 == 0.25 ns
+ * 10 == 0.50 ns
+ * 11 == 0.75 ns
+ *
+ * Returns picoseconds.
+ */
+static unsigned int
+compute_tckmax_from_spd_ps(unsigned int byte43)
+{
+ return (byte43 >> 2) * 1000 + (byte43 & 0x3) * 250;
+}
+
+/*
+ * Determine Refresh Rate. Ignore self refresh bit on DDR I.
+ * Table from SPD Spec, Byte 12, converted to picoseconds and
+ * filled in with "default" normal values.
+ */
+static unsigned int
+determine_refresh_rate_ps(const unsigned int spd_refresh)
+{
+ unsigned int refresh_time_ps[8] = {
+ 15625000, /* 0 Normal 1.00x */
+ 3900000, /* 1 Reduced .25x */
+ 7800000, /* 2 Extended .50x */
+ 31300000, /* 3 Extended 2.00x */
+ 62500000, /* 4 Extended 4.00x */
+ 125000000, /* 5 Extended 8.00x */
+ 15625000, /* 6 Normal 1.00x filler */
+ 15625000, /* 7 Normal 1.00x filler */
+ };
+
+ return refresh_time_ps[spd_refresh & 0x7];
+}
+
+/*
+ * The purpose of this function is to compute a suitable
+ * CAS latency given the DRAM clock period. The SPD only
+ * defines at most 3 CAS latencies. Typically the slower in
+ * frequency the DIMM runs at, the shorter its CAS latency can be.
+ * If the DIMM is operating at a sufficiently low frequency,
+ * it may be able to run at a CAS latency shorter than the
+ * shortest SPD-defined CAS latency.
+ *
+ * If a CAS latency is not found, 0 is returned.
+ *
+ * Do this by finding in the standard speed bin table the longest
+ * tCKmin that doesn't exceed the value of mclk_ps (tCK).
+ *
+ * An assumption made is that the SDRAM device allows the
+ * CL to be programmed for a value that is lower than those
+ * advertised by the SPD. This is not always the case,
+ * as those modes not defined in the SPD are optional.
+ *
+ * CAS latency de-rating based upon values JEDEC Standard No. 79-E
+ * Table 11.
+ *
+ * ordinal 2, ddr1_speed_bins[1] contains tCK for CL=2
+ */
+ /* CL2.0 CL2.5 CL3.0 */
+unsigned short ddr1_speed_bins[] = {0, 7500, 6000, 5000 };
+
+static unsigned int
+compute_derated_DDR1_CAS_latency(unsigned int mclk_ps)
+{
+ const unsigned int num_speed_bins = ARRAY_SIZE(ddr1_speed_bins);
+ unsigned int lowest_tCKmin_found = 0;
+ unsigned int lowest_tCKmin_CL = 0;
+ unsigned int i;
+
+ debug("mclk_ps = %u\n", mclk_ps);
+
+ for (i = 0; i < num_speed_bins; i++) {
+ unsigned int x = ddr1_speed_bins[i];
+ debug("i=%u, x = %u, lowest_tCKmin_found = %u\n",
+ i, x, lowest_tCKmin_found);
+ if (x && lowest_tCKmin_found <= x && x <= mclk_ps) {
+ lowest_tCKmin_found = x;
+ lowest_tCKmin_CL = i + 1;
+ }
+ }
+
+ debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL);
+
+ return lowest_tCKmin_CL;
+}
+
+/*
+ * ddr1_compute_dimm_parameters for DDR1 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in spd.
+ * Writes the results to the struct dimm_params structure pointed by pdimm.
+ *
+ * FIXME: use #define for the retvals
+ */
+unsigned int ddr1_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr1_spd_eeprom *spd,
+ struct dimm_params *pdimm)
+{
+ int ret;
+
+ ret = ddr1_spd_check(spd);
+ if (ret) {
+ printf("DIMM: failed checksum\n");
+ return 2;
+ }
+
+ /*
+ * The part name in ASCII in the SPD EEPROM is not null terminated.
+ * Guarantee null termination here by presetting all bytes to 0
+ * and copying the part name in ASCII from the SPD onto it
+ */
+ memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = spd->nrows;
+ pdimm->rank_density = compute_ranksize(spd->mem_type, spd->bank_dens);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->data_width = spd->dataw_lsb;
+ pdimm->primary_sdram_width = spd->primw;
+ pdimm->ec_sdram_width = spd->ecw;
+
+ /*
+ * FIXME: Need to determine registered_dimm status.
+ * 1 == register buffered
+ * 0 == unbuffered
+ */
+ pdimm->registered_dimm = 0; /* unbuffered */
+
+ /* SDRAM device parameters */
+ pdimm->n_row_addr = spd->nrow_addr;
+ pdimm->n_col_addr = spd->ncol_addr;
+ pdimm->n_banks_per_sdram_device = spd->nbanks;
+ pdimm->edc_config = spd->config;
+ pdimm->burst_lengths_bitmask = spd->burstl;
+
+ /*
+ * Calculate the Maximum Data Rate based on the Minimum Cycle time.
+ * The SPD clk_cycle field (tCKmin) is measured in tenths of
+ * nanoseconds and represented as BCD.
+ */
+ pdimm->tckmin_x_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle);
+ pdimm->tckmin_x_minus_1_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2);
+ pdimm->tckmin_x_minus_2_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3);
+
+ pdimm->tckmax_ps = compute_tckmax_from_spd_ps(spd->tckmax);
+
+ /*
+ * Compute CAS latencies defined by SPD
+ * The SPD caslat_x should have at least 1 and at most 3 bits set.
+ *
+ * If cas_lat after masking is 0, the __ilog2 function returns
+ * 255 into the variable. This behavior is abused once.
+ */
+ pdimm->caslat_x = ilog2(spd->cas_lat);
+ pdimm->caslat_x_minus_1 = ilog2(spd->cas_lat
+ & ~(1 << pdimm->caslat_x));
+ pdimm->caslat_x_minus_2 = ilog2(spd->cas_lat
+ & ~(1 << pdimm->caslat_x)
+ & ~(1 << pdimm->caslat_x_minus_1));
+
+ /* Compute CAS latencies below that defined by SPD */
+ pdimm->caslat_lowest_derated = compute_derated_DDR1_CAS_latency(
+ get_memory_clk_period_ps(c));
+
+ /* Compute timing parameters */
+ pdimm->trcd_ps = spd->trcd * 250;
+ pdimm->trp_ps = spd->trp * 250;
+ pdimm->tras_ps = spd->tras * 1000;
+
+ pdimm->twr_ps = mclk_to_picos(c, 3);
+ pdimm->twtr_ps = mclk_to_picos(c, 1);
+ pdimm->trfc_ps = compute_trfc_ps_from_spd(0, spd->trfc);
+
+ pdimm->trrd_ps = spd->trrd * 250;
+ pdimm->trc_ps = compute_trc_ps_from_spd(0, spd->trc);
+
+ pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh);
+
+ pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
+ pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
+ pdimm->tds_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
+ pdimm->tdh_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
+
+ pdimm->trtp_ps = mclk_to_picos(c, 2); /* By the book. */
+ pdimm->tdqsq_max_ps = spd->tdqsq * 10;
+ pdimm->tqhs_ps = spd->tqhs * 10;
+
+ return 0;
+}
diff --git a/drivers/ddr/fsl/ddr2_dimm_params.c b/drivers/ddr/fsl/ddr2_dimm_params.c
new file mode 100644
index 0000000..3f8b563
--- /dev/null
+++ b/drivers/ddr/fsl/ddr2_dimm_params.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ */
+
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <linux/log2.h>
+#include "fsl_ddr.h"
+
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * Study these table from Byte 31 of JEDEC SPD Spec.
+ *
+ * DDR I DDR II
+ * Bit Size Size
+ * --- ----- ------
+ * 7 high 512MB 512MB
+ * 6 256MB 256MB
+ * 5 128MB 128MB
+ * 4 64MB 16GB
+ * 3 32MB 8GB
+ * 2 16MB 4GB
+ * 1 2GB 2GB
+ * 0 low 1GB 1GB
+ *
+ * Reorder Table to be linear by stripping the bottom
+ * 2 or 5 bits off and shifting them up to the top.
+ *
+ */
+static unsigned long long
+compute_ranksize(unsigned int mem_type, unsigned char row_dens)
+{
+ unsigned long long bsize;
+
+ /* Bottom 5 bits up to the top. */
+ bsize = ((row_dens >> 5) | ((row_dens & 31) << 3));
+ bsize <<= 27ULL;
+ debug("DDR: DDR II rank density = 0x%16llx\n", bsize);
+
+ return bsize;
+}
+
+/*
+ * Convert a two-nibble BCD value into a cycle time.
+ * While the spec calls for nano-seconds, picos are returned.
+ *
+ * This implements the tables for bytes 9, 23 and 25 for both
+ * DDR I and II. No allowance for distinguishing the invalid
+ * fields absent for DDR I yet present in DDR II is made.
+ * (That is, cycle times of .25, .33, .66 and .75 ns are
+ * allowed for both DDR II and I.)
+ */
+static unsigned int
+convert_bcd_tenths_to_cycle_time_ps(unsigned int spd_val)
+{
+ /* Table look up the lower nibble, allow DDR I & II. */
+ unsigned int tenths_ps[16] = {
+ 0,
+ 100,
+ 200,
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 250, /* This and the next 3 entries valid ... */
+ 330, /* ... only for tCK calculations. */
+ 660,
+ 750,
+ 0, /* undefined */
+ 0 /* undefined */
+ };
+
+ unsigned int whole_ns = (spd_val & 0xF0) >> 4;
+ unsigned int tenth_ns = spd_val & 0x0F;
+ unsigned int ps = whole_ns * 1000 + tenths_ps[tenth_ns];
+
+ return ps;
+}
+
+static unsigned int
+convert_bcd_hundredths_to_cycle_time_ps(unsigned int spd_val)
+{
+ unsigned int tenth_ns = (spd_val & 0xF0) >> 4;
+ unsigned int hundredth_ns = spd_val & 0x0F;
+ unsigned int ps = tenth_ns * 100 + hundredth_ns * 10;
+
+ return ps;
+}
+
+static unsigned int byte40_table_ps[8] = {
+ 0,
+ 250,
+ 330,
+ 500,
+ 660,
+ 750,
+ 0, /* supposed to be RFC, but not sure what that means */
+ 0 /* Undefined */
+};
+
+static unsigned int
+compute_trfc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trfc)
+{
+ return (((trctrfc_ext & 0x1) * 256) + trfc) * 1000
+ + byte40_table_ps[(trctrfc_ext >> 1) & 0x7];
+}
+
+static unsigned int
+compute_trc_ps_from_spd(unsigned char trctrfc_ext, unsigned char trc)
+{
+ return trc * 1000 + byte40_table_ps[(trctrfc_ext >> 4) & 0x7];
+}
+
+/*
+ * Determine Refresh Rate. Ignore self refresh bit on DDR I.
+ * Table from SPD Spec, Byte 12, converted to picoseconds and
+ * filled in with "default" normal values.
+ */
+static unsigned int
+determine_refresh_rate_ps(const unsigned int spd_refresh)
+{
+ unsigned int refresh_time_ps[8] = {
+ 15625000, /* 0 Normal 1.00x */
+ 3900000, /* 1 Reduced .25x */
+ 7800000, /* 2 Extended .50x */
+ 31300000, /* 3 Extended 2.00x */
+ 62500000, /* 4 Extended 4.00x */
+ 125000000, /* 5 Extended 8.00x */
+ 15625000, /* 6 Normal 1.00x filler */
+ 15625000, /* 7 Normal 1.00x filler */
+ };
+
+ return refresh_time_ps[spd_refresh & 0x7];
+}
+
+/*
+ * The purpose of this function is to compute a suitable
+ * CAS latency given the DRAM clock period. The SPD only
+ * defines at most 3 CAS latencies. Typically the slower in
+ * frequency the DIMM runs at, the shorter its CAS latency can.
+ * be. If the DIMM is operating at a sufficiently low frequency,
+ * it may be able to run at a CAS latency shorter than the
+ * shortest SPD-defined CAS latency.
+ *
+ * If a CAS latency is not found, 0 is returned.
+ *
+ * Do this by finding in the standard speed bin table the longest
+ * tCKmin that doesn't exceed the value of mclk_ps (tCK).
+ *
+ * An assumption made is that the SDRAM device allows the
+ * CL to be programmed for a value that is lower than those
+ * advertised by the SPD. This is not always the case,
+ * as those modes not defined in the SPD are optional.
+ *
+ * CAS latency de-rating based upon values JEDEC Standard No. 79-2C
+ * Table 40, "DDR2 SDRAM stanadard speed bins and tCK, tRCD, tRP, tRAS,
+ * and tRC for corresponding bin"
+ *
+ * ordinal 2, ddr2_speed_bins[1] contains tCK for CL=3
+ * Not certain if any good value exists for CL=2
+ */
+ /* CL2 CL3 CL4 CL5 CL6 CL7*/
+unsigned short ddr2_speed_bins[] = { 0, 5000, 3750, 3000, 2500, 1875 };
+
+static unsigned int
+compute_derated_DDR2_CAS_latency(unsigned int mclk_ps)
+{
+ const unsigned int num_speed_bins = ARRAY_SIZE(ddr2_speed_bins);
+ unsigned int lowest_tCKmin_found = 0;
+ unsigned int lowest_tCKmin_CL = 0;
+ unsigned int i;
+
+ debug("mclk_ps = %u\n", mclk_ps);
+
+ for (i = 0; i < num_speed_bins; i++) {
+ unsigned int x = ddr2_speed_bins[i];
+ debug("i=%u, x = %u, lowest_tCKmin_found = %u\n",
+ i, x, lowest_tCKmin_found);
+ if (x && x <= mclk_ps && x >= lowest_tCKmin_found ) {
+ lowest_tCKmin_found = x;
+ lowest_tCKmin_CL = i + 2;
+ }
+ }
+
+ debug("lowest_tCKmin_CL = %u\n", lowest_tCKmin_CL);
+
+ return lowest_tCKmin_CL;
+}
+
+/*
+ * ddr2_compute_dimm_parameters for DDR2 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in spd.
+ * Writes the results to the struct dimm_params structure pointed by pdimm.
+ *
+ * FIXME: use #define for the retvals
+ */
+unsigned int ddr2_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr2_spd_eeprom *spd,
+ struct dimm_params *pdimm)
+{
+ int ret;
+
+ ret = ddr2_spd_check(spd);
+ if (ret) {
+ printf("DIMM: failed checksum\n");
+ return 2;
+ }
+
+ /*
+ * The part name in ASCII in the SPD EEPROM is not null terminated.
+ * Guarantee null termination here by presetting all bytes to 0
+ * and copying the part name in ASCII from the SPD onto it
+ */
+ memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = (spd->mod_ranks & 0x7) + 1;
+ pdimm->rank_density = compute_ranksize(spd->mem_type, spd->rank_dens);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->data_width = spd->dataw;
+ pdimm->primary_sdram_width = spd->primw;
+ pdimm->ec_sdram_width = spd->ecw;
+
+ /* These are all the types defined by the JEDEC DDR2 SPD 1.3 spec */
+ switch (spd->dimm_type) {
+ case DDR2_SPD_DIMMTYPE_RDIMM:
+ case DDR2_SPD_DIMMTYPE_72B_SO_RDIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_UDIMM:
+ case DDR2_SPD_DIMMTYPE_SO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MICRO_DIMM:
+ case DDR2_SPD_DIMMTYPE_MINI_UDIMM:
+ /* Unbuffered DIMMs */
+ pdimm->registered_dimm = 0;
+ break;
+
+ case DDR2_SPD_DIMMTYPE_72B_SO_CDIMM:
+ default:
+ printf("unknown dimm_type 0x%02X\n", spd->dimm_type);
+ return 1;
+ }
+
+ /* SDRAM device parameters */
+ pdimm->n_row_addr = spd->nrow_addr;
+ pdimm->n_col_addr = spd->ncol_addr;
+ pdimm->n_banks_per_sdram_device = spd->nbanks;
+ pdimm->edc_config = spd->config;
+ pdimm->burst_lengths_bitmask = spd->burstl;
+
+ /*
+ * Calculate the Maximum Data Rate based on the Minimum Cycle time.
+ * The SPD clk_cycle field (tCKmin) is measured in tenths of
+ * nanoseconds and represented as BCD.
+ */
+ pdimm->tckmin_x_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle);
+ pdimm->tckmin_x_minus_1_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle2);
+ pdimm->tckmin_x_minus_2_ps
+ = convert_bcd_tenths_to_cycle_time_ps(spd->clk_cycle3);
+
+ pdimm->tckmax_ps = convert_bcd_tenths_to_cycle_time_ps(spd->tckmax);
+
+ /*
+ * Compute CAS latencies defined by SPD
+ * The SPD caslat_x should have at least 1 and at most 3 bits set.
+ *
+ * If cas_lat after masking is 0, the __ilog2 function returns
+ * 255 into the variable. This behavior is abused once.
+ */
+ pdimm->caslat_x = ilog2(spd->cas_lat);
+ pdimm->caslat_x_minus_1 = ilog2(spd->cas_lat
+ & ~(1 << pdimm->caslat_x));
+ pdimm->caslat_x_minus_2 = ilog2(spd->cas_lat
+ & ~(1 << pdimm->caslat_x)
+ & ~(1 << pdimm->caslat_x_minus_1));
+
+ /* Compute CAS latencies below that defined by SPD */
+ pdimm->caslat_lowest_derated = compute_derated_DDR2_CAS_latency(
+ get_memory_clk_period_ps(c));
+
+ /* Compute timing parameters */
+ pdimm->trcd_ps = spd->trcd * 250;
+ pdimm->trp_ps = spd->trp * 250;
+ pdimm->tras_ps = spd->tras * 1000;
+
+ pdimm->twr_ps = spd->twr * 250;
+ pdimm->twtr_ps = spd->twtr * 250;
+ pdimm->trfc_ps = compute_trfc_ps_from_spd(spd->trctrfc_ext, spd->trfc);
+
+ pdimm->trrd_ps = spd->trrd * 250;
+ pdimm->trc_ps = compute_trc_ps_from_spd(spd->trctrfc_ext, spd->trc);
+
+ pdimm->refresh_rate_ps = determine_refresh_rate_ps(spd->refresh);
+
+ pdimm->tis_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_setup);
+ pdimm->tih_ps = convert_bcd_hundredths_to_cycle_time_ps(spd->ca_hold);
+ pdimm->tds_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_setup);
+ pdimm->tdh_ps
+ = convert_bcd_hundredths_to_cycle_time_ps(spd->data_hold);
+
+ pdimm->trtp_ps = spd->trtp * 250;
+ pdimm->tdqsq_max_ps = spd->tdqsq * 10;
+ pdimm->tqhs_ps = spd->tqhs * 10;
+
+ return 0;
+}
diff --git a/drivers/ddr/fsl/ddr3_dimm_params.c b/drivers/ddr/fsl/ddr3_dimm_params.c
new file mode 100644
index 0000000..1665e79
--- /dev/null
+++ b/drivers/ddr/fsl/ddr3_dimm_params.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008-2012 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ *
+ * calculate the organization and timing parameter
+ * from ddr3 spd, please refer to the spec
+ * JEDEC standard No.21-C 4_01_02_11R18.pdf
+ */
+
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include "fsl_ddr.h"
+
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * each rank size =
+ * sdram capacity(bit) / 8 * primary bus width / sdram width
+ *
+ * where: sdram capacity = spd byte4[3:0]
+ * primary bus width = spd byte8[2:0]
+ * sdram width = spd byte7[2:0]
+ *
+ * SPD byte4 - sdram density and banks
+ * bit[3:0] size(bit) size(byte)
+ * 0000 256Mb 32MB
+ * 0001 512Mb 64MB
+ * 0010 1Gb 128MB
+ * 0011 2Gb 256MB
+ * 0100 4Gb 512MB
+ * 0101 8Gb 1GB
+ * 0110 16Gb 2GB
+ *
+ * SPD byte8 - module memory bus width
+ * bit[2:0] primary bus width
+ * 000 8bits
+ * 001 16bits
+ * 010 32bits
+ * 011 64bits
+ *
+ * SPD byte7 - module organiztion
+ * bit[2:0] sdram device width
+ * 000 4bits
+ * 001 8bits
+ * 010 16bits
+ * 011 32bits
+ *
+ */
+static unsigned long long
+compute_ranksize(const struct ddr3_spd_eeprom *spd)
+{
+ unsigned long long bsize;
+
+ int nbit_sdram_cap_bsize = 0;
+ int nbit_primary_bus_width = 0;
+ int nbit_sdram_width = 0;
+
+ if ((spd->density_banks & 0xf) < 7)
+ nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28;
+ if ((spd->bus_width & 0x7) < 4)
+ nbit_primary_bus_width = (spd->bus_width & 0x7) + 3;
+ if ((spd->organization & 0x7) < 4)
+ nbit_sdram_width = (spd->organization & 0x7) + 2;
+
+ bsize = 1ULL << (nbit_sdram_cap_bsize - 3
+ + nbit_primary_bus_width - nbit_sdram_width);
+
+ debug("DDR: DDR III rank density = 0x%16llx\n", bsize);
+
+ return bsize;
+}
+
+/*
+ * ddr3_compute_dimm_parameters for DDR3 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in spd.
+ * Writes the results to the struct dimm_params structure pointed by pdimm.
+ *
+ */
+unsigned int ddr3_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr3_spd_eeprom *spd,
+ struct dimm_params *pdimm)
+{
+ int ret;
+ unsigned int mtb_ps;
+ int ftb_10th_ps;
+ int i;
+
+ ret = ddr3_spd_check(spd);
+ if (ret) {
+ printf("DIMM: failed checksum\n");
+ return 2;
+ }
+
+ /*
+ * The part name in ASCII in the SPD EEPROM is not null terminated.
+ * Guarantee null termination here by presetting all bytes to 0
+ * and copying the part name in ASCII from the SPD onto it
+ */
+ memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
+ if ((spd->info_size_crc & 0xF) > 1)
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1;
+ pdimm->rank_density = compute_ranksize(spd);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7));
+ if ((spd->bus_width >> 3) & 0x3)
+ pdimm->ec_sdram_width = 8;
+ else
+ pdimm->ec_sdram_width = 0;
+ pdimm->data_width = pdimm->primary_sdram_width
+ + pdimm->ec_sdram_width;
+ pdimm->device_width = 1 << ((spd->organization & 0x7) + 2);
+
+ /* These are the types defined by the JEDEC DDR3 SPD spec */
+ pdimm->mirrored_dimm = 0;
+ pdimm->registered_dimm = 0;
+ switch (spd->module_type & DDR3_SPD_MODULETYPE_MASK) {
+ case DDR3_SPD_MODULETYPE_RDIMM:
+ case DDR3_SPD_MODULETYPE_MINI_RDIMM:
+ case DDR3_SPD_MODULETYPE_72B_SO_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ for (i = 0; i < 16; i += 2) {
+ u8 rcw = spd->mod_section.registered.rcw[i/2];
+ pdimm->rcw[i] = (rcw >> 0) & 0x0F;
+ pdimm->rcw[i+1] = (rcw >> 4) & 0x0F;
+ }
+ break;
+
+ case DDR3_SPD_MODULETYPE_UDIMM:
+ case DDR3_SPD_MODULETYPE_SO_DIMM:
+ case DDR3_SPD_MODULETYPE_MICRO_DIMM:
+ case DDR3_SPD_MODULETYPE_MINI_UDIMM:
+ case DDR3_SPD_MODULETYPE_MINI_CDIMM:
+ case DDR3_SPD_MODULETYPE_72B_SO_UDIMM:
+ case DDR3_SPD_MODULETYPE_72B_SO_CDIMM:
+ case DDR3_SPD_MODULETYPE_LRDIMM:
+ case DDR3_SPD_MODULETYPE_16B_SO_DIMM:
+ case DDR3_SPD_MODULETYPE_32B_SO_DIMM:
+ /* Unbuffered DIMMs */
+ if (spd->mod_section.unbuffered.addr_mapping & 0x1)
+ pdimm->mirrored_dimm = 1;
+ break;
+
+ default:
+ printf("unknown module_type 0x%02X\n", spd->module_type);
+ return 1;
+ }
+
+ /* SDRAM device parameters */
+ pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12;
+ pdimm->n_col_addr = (spd->addressing & 0x7) + 9;
+ pdimm->n_banks_per_sdram_device = 8 << ((spd->density_banks >> 4) & 0x7);
+
+ /*
+ * The SPD spec has not the ECC bit,
+ * We consider the DIMM as ECC capability
+ * when the extension bus exist
+ */
+ if (pdimm->ec_sdram_width)
+ pdimm->edc_config = 0x02;
+ else
+ pdimm->edc_config = 0x00;
+
+ /*
+ * The SPD spec has not the burst length byte
+ * but DDR3 spec has nature BL8 and BC4,
+ * BL8 -bit3, BC4 -bit2
+ */
+ pdimm->burst_lengths_bitmask = 0x0c;
+
+ /* MTB - medium timebase
+ * The unit in the SPD spec is ns,
+ * We convert it to ps.
+ * eg: MTB = 0.125ns (125ps)
+ */
+ mtb_ps = (spd->mtb_dividend * 1000) /spd->mtb_divisor;
+ pdimm->mtb_ps = mtb_ps;
+
+ /*
+ * FTB - fine timebase
+ * use 1/10th of ps as our unit to avoid floating point
+ * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps
+ */
+ ftb_10th_ps =
+ ((spd->ftb_div & 0xf0) >> 4) * 10 / (spd->ftb_div & 0x0f);
+ pdimm->ftb_10th_ps = ftb_10th_ps;
+ /*
+ * sdram minimum cycle time
+ * we assume the MTB is 0.125ns
+ * eg:
+ * tck_min=15 MTB (1.875ns) ->DDR3-1066
+ * =12 MTB (1.5ns) ->DDR3-1333
+ * =10 MTB (1.25ns) ->DDR3-1600
+ */
+ pdimm->tckmin_x_ps = spd->tck_min * mtb_ps +
+ (spd->fine_tck_min * ftb_10th_ps) / 10;
+
+ /*
+ * CAS latency supported
+ * bit4 - CL4
+ * bit5 - CL5
+ * bit18 - CL18
+ */
+ pdimm->caslat_x = ((spd->caslat_msb << 8) | spd->caslat_lsb) << 4;
+
+ /*
+ * min CAS latency time
+ * eg: taa_min =
+ * DDR3-800D 100 MTB (12.5ns)
+ * DDR3-1066F 105 MTB (13.125ns)
+ * DDR3-1333H 108 MTB (13.5ns)
+ * DDR3-1600H 90 MTB (11.25ns)
+ */
+ pdimm->taa_ps = spd->taa_min * mtb_ps +
+ (spd->fine_taa_min * ftb_10th_ps) / 10;
+
+ /*
+ * min write recovery time
+ * eg:
+ * twr_min = 120 MTB (15ns) -> all speed grades.
+ */
+ pdimm->twr_ps = spd->twr_min * mtb_ps;
+
+ /*
+ * min RAS to CAS delay time
+ * eg: trcd_min =
+ * DDR3-800 100 MTB (12.5ns)
+ * DDR3-1066F 105 MTB (13.125ns)
+ * DDR3-1333H 108 MTB (13.5ns)
+ * DDR3-1600H 90 MTB (11.25)
+ */
+ pdimm->trcd_ps = spd->trcd_min * mtb_ps +
+ (spd->fine_trcd_min * ftb_10th_ps) / 10;
+
+ /*
+ * min row active to row active delay time
+ * eg: trrd_min =
+ * DDR3-800(1KB page) 80 MTB (10ns)
+ * DDR3-1333(1KB page) 48 MTB (6ns)
+ */
+ pdimm->trrd_ps = spd->trrd_min * mtb_ps;
+
+ /*
+ * min row precharge delay time
+ * eg: trp_min =
+ * DDR3-800D 100 MTB (12.5ns)
+ * DDR3-1066F 105 MTB (13.125ns)
+ * DDR3-1333H 108 MTB (13.5ns)
+ * DDR3-1600H 90 MTB (11.25ns)
+ */
+ pdimm->trp_ps = spd->trp_min * mtb_ps +
+ (spd->fine_trp_min * ftb_10th_ps) / 10;
+
+ /* min active to precharge delay time
+ * eg: tRAS_min =
+ * DDR3-800D 300 MTB (37.5ns)
+ * DDR3-1066F 300 MTB (37.5ns)
+ * DDR3-1333H 288 MTB (36ns)
+ * DDR3-1600H 280 MTB (35ns)
+ */
+ pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) | spd->tras_min_lsb)
+ * mtb_ps;
+ /*
+ * min active to actice/refresh delay time
+ * eg: tRC_min =
+ * DDR3-800D 400 MTB (50ns)
+ * DDR3-1066F 405 MTB (50.625ns)
+ * DDR3-1333H 396 MTB (49.5ns)
+ * DDR3-1600H 370 MTB (46.25ns)
+ */
+ pdimm->trc_ps = (((spd->tras_trc_ext & 0xf0) << 4) | spd->trc_min_lsb)
+ * mtb_ps + (spd->fine_trc_min * ftb_10th_ps) / 10;
+ /*
+ * min refresh recovery delay time
+ * eg: tRFC_min =
+ * 512Mb 720 MTB (90ns)
+ * 1Gb 880 MTB (110ns)
+ * 2Gb 1280 MTB (160ns)
+ */
+ pdimm->trfc_ps = ((spd->trfc_min_msb << 8) | spd->trfc_min_lsb)
+ * mtb_ps;
+ /*
+ * min internal write to read command delay time
+ * eg: twtr_min = 40 MTB (7.5ns) - all speed bins.
+ * tWRT is at least 4 mclk independent of operating freq.
+ */
+ pdimm->twtr_ps = spd->twtr_min * mtb_ps;
+
+ /*
+ * min internal read to precharge command delay time
+ * eg: trtp_min = 40 MTB (7.5ns) - all speed bins.
+ * tRTP is at least 4 mclk independent of operating freq.
+ */
+ pdimm->trtp_ps = spd->trtp_min * mtb_ps;
+
+ /*
+ * Average periodic refresh interval
+ * tREFI = 7.8 us at normal temperature range
+ * = 3.9 us at ext temperature range
+ */
+ pdimm->refresh_rate_ps = 7800000;
+ if ((spd->therm_ref_opt & 0x1) && !(spd->therm_ref_opt & 0x2)) {
+ pdimm->refresh_rate_ps = 3900000;
+ pdimm->extended_op_srt = 1;
+ }
+
+ /*
+ * min four active window delay time
+ * eg: tfaw_min =
+ * DDR3-800(1KB page) 320 MTB (40ns)
+ * DDR3-1066(1KB page) 300 MTB (37.5ns)
+ * DDR3-1333(1KB page) 240 MTB (30ns)
+ * DDR3-1600(1KB page) 240 MTB (30ns)
+ */
+ pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min)
+ * mtb_ps;
+
+ return 0;
+}
diff --git a/drivers/ddr/fsl/ddr4_dimm_params.c b/drivers/ddr/fsl/ddr4_dimm_params.c
new file mode 100644
index 0000000..f39b6e2
--- /dev/null
+++ b/drivers/ddr/fsl/ddr4_dimm_params.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2014-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP Semiconductor
+ *
+ * calculate the organization and timing parameter
+ * from ddr3 spd, please refer to the spec
+ * JEDEC standard No.21-C 4_01_02_12R23A.pdf
+ *
+ *
+ */
+
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include "fsl_ddr.h"
+
+/*
+ * Calculate the Density of each Physical Rank.
+ * Returned size is in bytes.
+ *
+ * Total DIMM size =
+ * sdram capacity(bit) / 8 * primary bus width / sdram width
+ * * Logical Ranks per DIMM
+ *
+ * where: sdram capacity = spd byte4[3:0]
+ * primary bus width = spd byte13[2:0]
+ * sdram width = spd byte12[2:0]
+ * Logical Ranks per DIMM = spd byte12[5:3] for SDP, DDP, QDP
+ * spd byte12{5:3] * spd byte6[6:4] for 3DS
+ *
+ * To simplify each rank size = total DIMM size / Number of Package Ranks
+ * where Number of Package Ranks = spd byte12[5:3]
+ *
+ * SPD byte4 - sdram density and banks
+ * bit[3:0] size(bit) size(byte)
+ * 0000 256Mb 32MB
+ * 0001 512Mb 64MB
+ * 0010 1Gb 128MB
+ * 0011 2Gb 256MB
+ * 0100 4Gb 512MB
+ * 0101 8Gb 1GB
+ * 0110 16Gb 2GB
+ * 0111 32Gb 4GB
+ *
+ * SPD byte13 - module memory bus width
+ * bit[2:0] primary bus width
+ * 000 8bits
+ * 001 16bits
+ * 010 32bits
+ * 011 64bits
+ *
+ * SPD byte12 - module organization
+ * bit[2:0] sdram device width
+ * 000 4bits
+ * 001 8bits
+ * 010 16bits
+ * 011 32bits
+ *
+ * SPD byte12 - module organization
+ * bit[5:3] number of package ranks per DIMM
+ * 000 1
+ * 001 2
+ * 010 3
+ * 011 4
+ *
+ * SPD byte6 - SDRAM package type
+ * bit[6:4] Die count
+ * 000 1
+ * 001 2
+ * 010 3
+ * 011 4
+ * 100 5
+ * 101 6
+ * 110 7
+ * 111 8
+ *
+ * SPD byte6 - SRAM package type
+ * bit[1:0] Signal loading
+ * 00 Not specified
+ * 01 Multi load stack
+ * 10 Sigle load stack (3DS)
+ * 11 Reserved
+ */
+static unsigned long long
+compute_ranksize(const struct ddr4_spd_eeprom *spd)
+{
+ unsigned long long bsize;
+
+ int nbit_sdram_cap_bsize = 0;
+ int nbit_primary_bus_width = 0;
+ int nbit_sdram_width = 0;
+ int die_count = 0;
+ bool package_3ds;
+
+ if ((spd->density_banks & 0xf) <= 7)
+ nbit_sdram_cap_bsize = (spd->density_banks & 0xf) + 28;
+ if ((spd->bus_width & 0x7) < 4)
+ nbit_primary_bus_width = (spd->bus_width & 0x7) + 3;
+ if ((spd->organization & 0x7) < 4)
+ nbit_sdram_width = (spd->organization & 0x7) + 2;
+ package_3ds = (spd->package_type & 0x3) == 0x2;
+ if ((spd->package_type & 0x80) && !package_3ds) { /* other than 3DS */
+ printf("Warning: not supported SDRAM package type\n");
+ return 0;
+ }
+ if (package_3ds)
+ die_count = (spd->package_type >> 4) & 0x7;
+
+ bsize = 1ULL << (nbit_sdram_cap_bsize - 3 +
+ nbit_primary_bus_width - nbit_sdram_width +
+ die_count);
+
+ debug("DDR: DDR rank density = 0x%16llx\n", bsize);
+
+ return bsize;
+}
+
+#define spd_to_ps(mtb, ftb) \
+ (mtb * pdimm->mtb_ps + (ftb * pdimm->ftb_10th_ps) / 10)
+/*
+ * ddr4_compute_dimm_parameters for DDR4 SPD
+ *
+ * Compute DIMM parameters based upon the SPD information in spd.
+ * Writes the results to the struct dimm_params structure pointed by pdimm.
+ *
+ */
+unsigned int ddr4_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr4_spd_eeprom *spd,
+ struct dimm_params *pdimm)
+{
+ int ret;
+ int i;
+ const u8 udimm_rc_e_dq[18] = {
+ 0x0c, 0x2c, 0x15, 0x35, 0x15, 0x35, 0x0b, 0x2c, 0x15,
+ 0x35, 0x0b, 0x35, 0x0b, 0x2c, 0x0b, 0x35, 0x15, 0x36
+ };
+ int spd_error = 0;
+ u8 *ptr;
+ u8 val;
+
+ ret = ddr4_spd_check(spd);
+ if (ret) {
+ printf("DIMM: failed checksum\n");
+ return 2;
+ }
+
+ /*
+ * The part name in ASCII in the SPD EEPROM is not null terminated.
+ * Guarantee null termination here by presetting all bytes to 0
+ * and copying the part name in ASCII from the SPD onto it
+ */
+ memset(pdimm->mpart, 0, sizeof(pdimm->mpart));
+ if ((spd->info_size_crc & 0xF) > 2)
+ memcpy(pdimm->mpart, spd->mpart, sizeof(pdimm->mpart) - 1);
+
+ /* DIMM organization parameters */
+ pdimm->n_ranks = ((spd->organization >> 3) & 0x7) + 1;
+ pdimm->rank_density = compute_ranksize(spd);
+ pdimm->capacity = pdimm->n_ranks * pdimm->rank_density;
+ pdimm->die_density = spd->density_banks & 0xf;
+ pdimm->primary_sdram_width = 1 << (3 + (spd->bus_width & 0x7));
+ if ((spd->bus_width >> 3) & 0x3)
+ pdimm->ec_sdram_width = 8;
+ else
+ pdimm->ec_sdram_width = 0;
+ pdimm->data_width = pdimm->primary_sdram_width
+ + pdimm->ec_sdram_width;
+ pdimm->device_width = 1 << ((spd->organization & 0x7) + 2);
+ pdimm->package_3ds = (spd->package_type & 0x3) == 0x2 ?
+ (spd->package_type >> 4) & 0x7 : 0;
+
+ /* These are the types defined by the JEDEC SPD spec */
+ pdimm->mirrored_dimm = 0;
+ pdimm->registered_dimm = 0;
+ switch (spd->module_type & DDR4_SPD_MODULETYPE_MASK) {
+ case DDR4_SPD_MODULETYPE_RDIMM:
+ /* Registered/buffered DIMMs */
+ pdimm->registered_dimm = 1;
+ if (spd->mod_section.registered.reg_map & 0x1)
+ pdimm->mirrored_dimm = 1;
+ val = spd->mod_section.registered.ca_stren;
+ pdimm->rcw[3] = val >> 4;
+ pdimm->rcw[4] = ((val & 0x3) << 2) | ((val & 0xc) >> 2);
+ val = spd->mod_section.registered.clk_stren;
+ pdimm->rcw[5] = ((val & 0x3) << 2) | ((val & 0xc) >> 2);
+ /* Not all in SPD. For convience only. Boards may overwrite. */
+ pdimm->rcw[6] = 0xf;
+ /*
+ * A17 only used for 16Gb and above devices.
+ * C[2:0] only used for 3DS.
+ */
+ pdimm->rcw[8] = pdimm->die_density >= 0x6 ? 0x0 : 0x8 |
+ (pdimm->package_3ds > 0x3 ? 0x0 :
+ (pdimm->package_3ds > 0x1 ? 0x1 :
+ (pdimm->package_3ds > 0 ? 0x2 : 0x3)));
+ if (pdimm->package_3ds || pdimm->n_ranks != 4)
+ pdimm->rcw[13] = 0xc;
+ else
+ pdimm->rcw[13] = 0xd; /* Fix encoded by board */
+
+ break;
+
+ case DDR4_SPD_MODULETYPE_UDIMM:
+ case DDR4_SPD_MODULETYPE_SO_DIMM:
+ /* Unbuffered DIMMs */
+ if (spd->mod_section.unbuffered.addr_mapping & 0x1)
+ pdimm->mirrored_dimm = 1;
+ if ((spd->mod_section.unbuffered.mod_height & 0xe0) == 0 &&
+ (spd->mod_section.unbuffered.ref_raw_card == 0x04)) {
+ /* Fix SPD error found on DIMMs with raw card E0 */
+ for (i = 0; i < 18; i++) {
+ if (spd->mapping[i] == udimm_rc_e_dq[i])
+ continue;
+ spd_error = 1;
+ debug("SPD byte %d: 0x%x, should be 0x%x\n",
+ 60 + i, spd->mapping[i],
+ udimm_rc_e_dq[i]);
+ ptr = (u8 *)&spd->mapping[i];
+ *ptr = udimm_rc_e_dq[i];
+ }
+ if (spd_error)
+ printf("SPD DQ mapping error fixed\n");
+ }
+ break;
+
+ default:
+ printf("unknown module_type 0x%02X\n", spd->module_type);
+ return 1;
+ }
+
+ /* SDRAM device parameters */
+ pdimm->n_row_addr = ((spd->addressing >> 3) & 0x7) + 12;
+ pdimm->n_col_addr = (spd->addressing & 0x7) + 9;
+ pdimm->bank_addr_bits = (spd->density_banks >> 4) & 0x3;
+ pdimm->bank_group_bits = (spd->density_banks >> 6) & 0x3;
+
+ /*
+ * The SPD spec has not the ECC bit,
+ * We consider the DIMM as ECC capability
+ * when the extension bus exist
+ */
+ if (pdimm->ec_sdram_width)
+ pdimm->edc_config = 0x02;
+ else
+ pdimm->edc_config = 0x00;
+
+ /*
+ * The SPD spec has not the burst length byte
+ * but DDR4 spec has nature BL8 and BC4,
+ * BL8 -bit3, BC4 -bit2
+ */
+ pdimm->burst_lengths_bitmask = 0x0c;
+
+ /* MTB - medium timebase
+ * The MTB in the SPD spec is 125ps,
+ *
+ * FTB - fine timebase
+ * use 1/10th of ps as our unit to avoid floating point
+ * eg, 10 for 1ps, 25 for 2.5ps, 50 for 5ps
+ */
+ if ((spd->timebases & 0xf) == 0x0) {
+ pdimm->mtb_ps = 125;
+ pdimm->ftb_10th_ps = 10;
+
+ } else {
+ printf("Unknown Timebases\n");
+ }
+
+ /* sdram minimum cycle time */
+ pdimm->tckmin_x_ps = spd_to_ps(spd->tck_min, spd->fine_tck_min);
+
+ /* sdram max cycle time */
+ pdimm->tckmax_ps = spd_to_ps(spd->tck_max, spd->fine_tck_max);
+
+ /*
+ * CAS latency supported
+ * bit0 - CL7
+ * bit4 - CL11
+ * bit8 - CL15
+ * bit12- CL19
+ * bit16- CL23
+ */
+ pdimm->caslat_x = (spd->caslat_b1 << 7) |
+ (spd->caslat_b2 << 15) |
+ (spd->caslat_b3 << 23);
+
+ BUG_ON(spd->caslat_b4 != 0);
+
+ /*
+ * min CAS latency time
+ */
+ pdimm->taa_ps = spd_to_ps(spd->taa_min, spd->fine_taa_min);
+
+ /*
+ * min RAS to CAS delay time
+ */
+ pdimm->trcd_ps = spd_to_ps(spd->trcd_min, spd->fine_trcd_min);
+
+ /*
+ * Min Row Precharge Delay Time
+ */
+ pdimm->trp_ps = spd_to_ps(spd->trp_min, spd->fine_trp_min);
+
+ /* min active to precharge delay time */
+ pdimm->tras_ps = (((spd->tras_trc_ext & 0xf) << 8) +
+ spd->tras_min_lsb) * pdimm->mtb_ps;
+
+ /* min active to actice/refresh delay time */
+ pdimm->trc_ps = spd_to_ps((((spd->tras_trc_ext & 0xf0) << 4) +
+ spd->trc_min_lsb), spd->fine_trc_min);
+ /* Min Refresh Recovery Delay Time */
+ pdimm->trfc1_ps = ((spd->trfc1_min_msb << 8) | (spd->trfc1_min_lsb)) *
+ pdimm->mtb_ps;
+ pdimm->trfc2_ps = ((spd->trfc2_min_msb << 8) | (spd->trfc2_min_lsb)) *
+ pdimm->mtb_ps;
+ pdimm->trfc4_ps = ((spd->trfc4_min_msb << 8) | (spd->trfc4_min_lsb)) *
+ pdimm->mtb_ps;
+ /* min four active window delay time */
+ pdimm->tfaw_ps = (((spd->tfaw_msb & 0xf) << 8) | spd->tfaw_min) *
+ pdimm->mtb_ps;
+
+ /* min row active to row active delay time, different bank group */
+ pdimm->trrds_ps = spd_to_ps(spd->trrds_min, spd->fine_trrds_min);
+ /* min row active to row active delay time, same bank group */
+ pdimm->trrdl_ps = spd_to_ps(spd->trrdl_min, spd->fine_trrdl_min);
+ /* min CAS to CAS Delay Time (tCCD_Lmin), same bank group */
+ pdimm->tccdl_ps = spd_to_ps(spd->tccdl_min, spd->fine_tccdl_min);
+
+ if (pdimm->package_3ds) {
+ if (pdimm->die_density <= 0x4) {
+ pdimm->trfc_slr_ps = 260000;
+ } else if (pdimm->die_density <= 0x5) {
+ pdimm->trfc_slr_ps = 350000;
+ } else {
+ printf("WARN: Unsupported logical rank density 0x%x\n",
+ pdimm->die_density);
+ }
+ }
+
+ /*
+ * Average periodic refresh interval
+ * tREFI = 7.8 us at normal temperature range
+ */
+ pdimm->refresh_rate_ps = 7800000;
+
+ for (i = 0; i < 18; i++)
+ pdimm->dq_mapping[i] = spd->mapping[i];
+
+ pdimm->dq_mapping_ors = ((spd->mapping[0] >> 6) & 0x3) == 0 ? 1 : 0;
+
+ return 0;
+}
diff --git a/drivers/ddr/fsl/fsl_ddr.h b/drivers/ddr/fsl/fsl_ddr.h
new file mode 100644
index 0000000..ee6069d
--- /dev/null
+++ b/drivers/ddr/fsl/fsl_ddr.h
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP Semiconductor
+ */
+
+#ifndef FSL_DDR_H
+#define FSL_DDR_H
+
+#include <ddr_spd.h>
+#include <soc/fsl/fsl_immap.h>
+
+#define DDR_BL4 4 /* burst length 4 */
+#define DDR_BC4 DDR_BL4 /* burst chop for ddr3 */
+#define DDR_OTF 6 /* on-the-fly BC4 and BL8 */
+#define DDR_BL8 8 /* burst length 8 */
+
+#define DDR3_RTT_OFF 0
+#define DDR3_RTT_60_OHM 1 /* RTT_Nom = RZQ/4 */
+#define DDR3_RTT_120_OHM 2 /* RTT_Nom = RZQ/2 */
+#define DDR3_RTT_40_OHM 3 /* RTT_Nom = RZQ/6 */
+#define DDR3_RTT_20_OHM 4 /* RTT_Nom = RZQ/12 */
+#define DDR3_RTT_30_OHM 5 /* RTT_Nom = RZQ/8 */
+
+#define DDR4_RTT_OFF 0
+#define DDR4_RTT_60_OHM 1 /* RZQ/4 */
+#define DDR4_RTT_120_OHM 2 /* RZQ/2 */
+#define DDR4_RTT_40_OHM 3 /* RZQ/6 */
+#define DDR4_RTT_240_OHM 4 /* RZQ/1 */
+#define DDR4_RTT_48_OHM 5 /* RZQ/5 */
+#define DDR4_RTT_80_OHM 6 /* RZQ/3 */
+#define DDR4_RTT_34_OHM 7 /* RZQ/7 */
+
+#define DDR2_RTT_OFF 0
+#define DDR2_RTT_75_OHM 1
+#define DDR2_RTT_150_OHM 2
+#define DDR2_RTT_50_OHM 3
+
+#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR1 1
+#define FSL_DDR_MIN_TCKE_PULSE_WIDTH_DDR2 3
+
+#define FSL_DDR_ODT_NEVER 0x0
+#define FSL_DDR_ODT_CS 0x1
+#define FSL_DDR_ODT_ALL_OTHER_CS 0x2
+#define FSL_DDR_ODT_OTHER_DIMM 0x3
+#define FSL_DDR_ODT_ALL 0x4
+#define FSL_DDR_ODT_SAME_DIMM 0x5
+#define FSL_DDR_ODT_CS_AND_OTHER_DIMM 0x6
+#define FSL_DDR_ODT_OTHER_CS_ONSAMEDIMM 0x7
+
+/* define bank(chip select) interleaving mode */
+#define FSL_DDR_CS0_CS1 0x40
+#define FSL_DDR_CS2_CS3 0x20
+#define FSL_DDR_CS0_CS1_AND_CS2_CS3 (FSL_DDR_CS0_CS1 | FSL_DDR_CS2_CS3)
+#define FSL_DDR_CS0_CS1_CS2_CS3 (FSL_DDR_CS0_CS1_AND_CS2_CS3 | 0x04)
+
+/* define memory controller interleaving mode */
+#define FSL_DDR_CACHE_LINE_INTERLEAVING 0x0
+#define FSL_DDR_PAGE_INTERLEAVING 0x1
+#define FSL_DDR_BANK_INTERLEAVING 0x2
+#define FSL_DDR_SUPERBANK_INTERLEAVING 0x3
+#define FSL_DDR_256B_INTERLEAVING 0x8
+#define FSL_DDR_3WAY_1KB_INTERLEAVING 0xA
+#define FSL_DDR_3WAY_4KB_INTERLEAVING 0xC
+#define FSL_DDR_3WAY_8KB_INTERLEAVING 0xD
+/* placeholder for 4-way interleaving */
+#define FSL_DDR_4WAY_1KB_INTERLEAVING 0x1A
+#define FSL_DDR_4WAY_4KB_INTERLEAVING 0x1C
+#define FSL_DDR_4WAY_8KB_INTERLEAVING 0x1D
+
+#define SDRAM_CS_CONFIG_EN 0x80000000
+
+/* DDR_SDRAM_CFG - DDR SDRAM Control Configuration
+ */
+#define SDRAM_CFG_MEM_EN 0x80000000
+#define SDRAM_CFG_SREN 0x40000000
+#define SDRAM_CFG_ECC_EN 0x20000000
+#define SDRAM_CFG_RD_EN 0x10000000
+#define SDRAM_CFG_SDRAM_TYPE_DDR1 0x02000000
+#define SDRAM_CFG_SDRAM_TYPE_DDR2 0x03000000
+#define SDRAM_CFG_SDRAM_TYPE_MASK 0x07000000
+#define SDRAM_CFG_SDRAM_TYPE_SHIFT 24
+#define SDRAM_CFG_DYN_PWR 0x00200000
+#define SDRAM_CFG_DBW_MASK 0x00180000
+#define SDRAM_CFG_DBW_SHIFT 19
+#define SDRAM_CFG_32_BE 0x00080000
+#define SDRAM_CFG_16_BE 0x00100000
+#define SDRAM_CFG_8_BE 0x00040000
+#define SDRAM_CFG_NCAP 0x00020000
+#define SDRAM_CFG_2T_EN 0x00008000
+#define SDRAM_CFG_BI 0x00000001
+
+#define SDRAM_CFG2_FRC_SR 0x80000000
+#define SDRAM_CFG2_D_INIT 0x00000010
+#define SDRAM_CFG2_AP_EN 0x00000020
+#define SDRAM_CFG2_ODT_CFG_MASK 0x00600000
+#define SDRAM_CFG2_ODT_NEVER 0
+#define SDRAM_CFG2_ODT_ONLY_WRITE 1
+#define SDRAM_CFG2_ODT_ONLY_READ 2
+#define SDRAM_CFG2_ODT_ALWAYS 3
+
+#define SDRAM_INTERVAL_BSTOPRE 0x3FFF
+#define TIMING_CFG_2_CPO_MASK 0x0F800000
+
+#define RD_TO_PRE_MASK 0xf
+#define RD_TO_PRE_SHIFT 13
+#define WR_DATA_DELAY_MASK 0xf
+#define WR_DATA_DELAY_SHIFT 9
+
+/* DDR_EOR register */
+#define DDR_EOR_RD_REOD_DIS 0x07000000
+#define DDR_EOR_WD_REOD_DIS 0x00100000
+
+/* DDR_MD_CNTL */
+#define MD_CNTL_MD_EN 0x80000000
+#define MD_CNTL_CS_SEL_CS0 0x00000000
+#define MD_CNTL_CS_SEL_CS1 0x10000000
+#define MD_CNTL_CS_SEL_CS2 0x20000000
+#define MD_CNTL_CS_SEL_CS3 0x30000000
+#define MD_CNTL_CS_SEL_CS0_CS1 0x40000000
+#define MD_CNTL_CS_SEL_CS2_CS3 0x50000000
+#define MD_CNTL_MD_SEL_MR 0x00000000
+#define MD_CNTL_MD_SEL_EMR 0x01000000
+#define MD_CNTL_MD_SEL_EMR2 0x02000000
+#define MD_CNTL_MD_SEL_EMR3 0x03000000
+#define MD_CNTL_SET_REF 0x00800000
+#define MD_CNTL_SET_PRE 0x00400000
+#define MD_CNTL_CKE_CNTL_LOW 0x00100000
+#define MD_CNTL_CKE_CNTL_HIGH 0x00200000
+#define MD_CNTL_WRCW 0x00080000
+#define MD_CNTL_MD_VALUE(x) (x & 0x0000FFFF)
+#define MD_CNTL_CS_SEL(x) (((x) & 0x7) << 28)
+#define MD_CNTL_MD_SEL(x) (((x) & 0xf) << 24)
+
+/* DDR_CDR1 */
+#define DDR_CDR1_DHC_EN 0x80000000
+#define DDR_CDR1_V0PT9_EN 0x40000000
+#define DDR_CDR1_ODT_SHIFT 17
+#define DDR_CDR1_ODT_MASK 0x6
+#define DDR_CDR2_ODT_MASK 0x1
+#define DDR_CDR1_ODT(x) ((x & DDR_CDR1_ODT_MASK) << DDR_CDR1_ODT_SHIFT)
+#define DDR_CDR2_ODT(x) (x & DDR_CDR2_ODT_MASK)
+#define DDR_CDR2_VREF_OVRD(x) (0x00008080 | ((((x) - 37) & 0x3F) << 8))
+#define DDR_CDR2_VREF_TRAIN_EN 0x00000080
+#define DDR_CDR2_VREF_RANGE_2 0x00000040
+
+/* DDR ERR_DISABLE */
+#define DDR_ERR_DISABLE_APED (1 << 8) /* Address parity error disable */
+
+/* Mode Registers */
+#define DDR_MR5_CA_PARITY_LAT_4_CLK 0x1 /* for DDR4-1600/1866/2133 */
+#define DDR_MR5_CA_PARITY_LAT_5_CLK 0x2 /* for DDR4-2400 */
+
+/* DEBUG_26 register */
+#define DDR_CAS_TO_PRE_SUB_MASK 0x0000f000 /* CAS to preamble subtract value */
+#define DDR_CAS_TO_PRE_SUB_SHIFT 12
+
+/* DEBUG_29 register */
+#define DDR_TX_BD_DIS (1 << 10) /* Transmit Bit Deskew Disable */
+
+static inline int is_ddr1(const memctl_options_t *popts)
+{
+ return IS_ENABLED(CONFIG_DDR_FSL_DDR1) &&
+ popts->ddrtype == SDRAM_TYPE_DDR1;
+}
+
+static inline int is_ddr2(const memctl_options_t *popts)
+{
+ return IS_ENABLED(CONFIG_DDR_FSL_DDR2) &&
+ popts->ddrtype == SDRAM_TYPE_DDR2;
+}
+
+static inline int is_ddr3(const memctl_options_t *popts)
+{
+ return IS_ENABLED(CONFIG_DDR_FSL_DDR3) &&
+ popts->ddrtype == SDRAM_TYPE_DDR3;
+}
+
+static inline int is_ddr4(const memctl_options_t *popts)
+{
+ return IS_ENABLED(CONFIG_DDR_FSL_DDR4) &&
+ popts->ddrtype == SDRAM_TYPE_DDR4;
+}
+
+static inline int is_ddr3_4(const memctl_options_t *popts)
+{
+ return is_ddr3(popts) || is_ddr4(popts);
+}
+
+struct fsl_ddr_info;
+
+phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo);
+u32 fsl_ddr_get_intl3r(void);
+
+void board_mem_sleep_setup(void);
+static inline bool is_warm_boot(void)
+{
+ return false;
+}
+
+int fsl_dp_resume(void);
+
+struct fsl_ddr_controller;
+
+u32 fsl_ddr_get_version(struct fsl_ddr_controller *c);
+
+unsigned int ddr1_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr1_spd_eeprom *spd,
+ struct dimm_params *pdimm);
+unsigned int ddr2_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr2_spd_eeprom *spd,
+ struct dimm_params *pdimm);
+unsigned int ddr3_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr3_spd_eeprom *spd,
+ struct dimm_params *pdimm);
+unsigned int ddr4_compute_dimm_parameters(struct fsl_ddr_controller *c,
+ const struct ddr4_spd_eeprom *spd,
+ struct dimm_params *pdimm);
+void fsl_ddr_set_intl3r(const unsigned int granule_size);
+
+unsigned int compute_fsl_memctl_config_regs(struct fsl_ddr_controller *c);
+unsigned int compute_lowest_common_dimm_parameters(struct fsl_ddr_controller *c);
+unsigned int populate_memctl_options(struct fsl_ddr_controller *c);
+void check_interleaving_options(struct fsl_ddr_info *pinfo);
+
+unsigned int mclk_to_picos(struct fsl_ddr_controller *c, unsigned int mclk);
+unsigned int get_memory_clk_period_ps(struct fsl_ddr_controller *c);
+unsigned int picos_to_mclk(struct fsl_ddr_controller *c, unsigned int picos);
+
+void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step);
+
+void erratum_a009942_check_cpo(void);
+
+#endif
diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c
new file mode 100644
index 0000000..ac68e4f
--- /dev/null
+++ b/drivers/ddr/fsl/fsl_ddr_gen4.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2014-2015 Freescale Semiconductor, Inc.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <soc/fsl/fsl_immap.h>
+#include <soc/fsl/immap_lsch2.h>
+#include <asm/system.h>
+#include "fsl_ddr.h"
+
+#define CTLR_INTLV_MASK 0x20000000
+
+static void set_wait_for_bits_clear(void *ptr, u32 value, u32 bits)
+{
+ int timeout = 1000;
+
+ ddr_out32(ptr, value);
+
+ while (ddr_in32(ptr) & bits) {
+ udelay(100);
+ timeout--;
+ }
+ if (timeout <= 0)
+ printf("Error: wait for clear timeout.\n");
+}
+
+/*
+ * regs has the to-be-set values for DDR controller registers
+ * ctrl_num is the DDR controller number
+ * step: 0 goes through the initialization in one pass
+ * 1 sets registers and returns before enabling controller
+ * 2 resumes from step 1 and continues to initialize
+ * Dividing the initialization to two steps to deassert DDR reset signal
+ * to comply with JEDEC specs for RDIMMs.
+ */
+void fsl_ddr_set_memctl_regs(struct fsl_ddr_controller *c, int step)
+{
+ struct ccsr_ddr __iomem *ddr = c->base;
+ const fsl_ddr_cfg_regs_t *regs = &c->fsl_ddr_config_reg;
+ unsigned int i, bus_width;
+ u32 temp32;
+ u32 total_gb_size_per_controller;
+ int timeout;
+ int mod_bnds = 0;
+ u32 mr6;
+ u32 vref_seq1[3] = {0x80, 0x96, 0x16}; /* for range 1 */
+ u32 vref_seq2[3] = {0xc0, 0xf0, 0x70}; /* for range 2 */
+ u32 *vref_seq = vref_seq1;
+ u32 mtcr, err_detect, err_sbe;
+ u32 cs0_bnds, cs1_bnds, cs2_bnds, cs3_bnds, cs0_config;
+ mod_bnds = regs->cs[0].config & CTLR_INTLV_MASK;
+
+ if (step == 2)
+ goto step2;
+
+ /* Set cdr1 first in case 0.9v VDD is enabled for some SoCs*/
+ ddr_out32(&ddr->ddr_cdr1, regs->ddr_cdr1);
+
+ if (regs->ddr_eor)
+ ddr_out32(&ddr->eor, regs->ddr_eor);
+
+ ddr_out32(&ddr->sdram_clk_cntl, regs->ddr_sdram_clk_cntl);
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (i == 0) {
+ if (mod_bnds) {
+ debug("modified bnds\n");
+ ddr_out32(&ddr->cs0_bnds,
+ (regs->cs[i].bnds & 0xfffefffe) >> 1);
+ ddr_out32(&ddr->cs0_config,
+ (regs->cs[i].config &
+ ~CTLR_INTLV_MASK));
+ } else {
+ ddr_out32(&ddr->cs0_bnds, regs->cs[i].bnds);
+ ddr_out32(&ddr->cs0_config, regs->cs[i].config);
+ }
+ ddr_out32(&ddr->cs0_config_2, regs->cs[i].config_2);
+
+ } else if (i == 1) {
+ if (mod_bnds) {
+ ddr_out32(&ddr->cs1_bnds,
+ (regs->cs[i].bnds & 0xfffefffe) >> 1);
+ } else {
+ ddr_out32(&ddr->cs1_bnds, regs->cs[i].bnds);
+ }
+ ddr_out32(&ddr->cs1_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs1_config_2, regs->cs[i].config_2);
+
+ } else if (i == 2) {
+ if (mod_bnds) {
+ ddr_out32(&ddr->cs2_bnds,
+ (regs->cs[i].bnds & 0xfffefffe) >> 1);
+ } else {
+ ddr_out32(&ddr->cs2_bnds, regs->cs[i].bnds);
+ }
+ ddr_out32(&ddr->cs2_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs2_config_2, regs->cs[i].config_2);
+
+ } else if (i == 3) {
+ if (mod_bnds) {
+ ddr_out32(&ddr->cs3_bnds,
+ (regs->cs[i].bnds & 0xfffefffe) >> 1);
+ } else {
+ ddr_out32(&ddr->cs3_bnds, regs->cs[i].bnds);
+ }
+ ddr_out32(&ddr->cs3_config, regs->cs[i].config);
+ ddr_out32(&ddr->cs3_config_2, regs->cs[i].config_2);
+ }
+ }
+
+ ddr_out32(&ddr->timing_cfg_3, regs->timing_cfg_3);
+ ddr_out32(&ddr->timing_cfg_0, regs->timing_cfg_0);
+ ddr_out32(&ddr->timing_cfg_1, regs->timing_cfg_1);
+ ddr_out32(&ddr->timing_cfg_2, regs->timing_cfg_2);
+ ddr_out32(&ddr->timing_cfg_4, regs->timing_cfg_4);
+ ddr_out32(&ddr->timing_cfg_5, regs->timing_cfg_5);
+ ddr_out32(&ddr->timing_cfg_6, regs->timing_cfg_6);
+ ddr_out32(&ddr->timing_cfg_7, regs->timing_cfg_7);
+ ddr_out32(&ddr->timing_cfg_8, regs->timing_cfg_8);
+ ddr_out32(&ddr->timing_cfg_9, regs->timing_cfg_9);
+ ddr_out32(&ddr->ddr_zq_cntl, regs->ddr_zq_cntl);
+ ddr_out32(&ddr->dq_map_0, regs->dq_map_0);
+ ddr_out32(&ddr->dq_map_1, regs->dq_map_1);
+ ddr_out32(&ddr->dq_map_2, regs->dq_map_2);
+ ddr_out32(&ddr->dq_map_3, regs->dq_map_3);
+ ddr_out32(&ddr->sdram_cfg_3, regs->ddr_sdram_cfg_3);
+ ddr_out32(&ddr->sdram_mode, regs->ddr_sdram_mode);
+ ddr_out32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2);
+ ddr_out32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3);
+ ddr_out32(&ddr->sdram_mode_4, regs->ddr_sdram_mode_4);
+ ddr_out32(&ddr->sdram_mode_5, regs->ddr_sdram_mode_5);
+ ddr_out32(&ddr->sdram_mode_6, regs->ddr_sdram_mode_6);
+ ddr_out32(&ddr->sdram_mode_7, regs->ddr_sdram_mode_7);
+ ddr_out32(&ddr->sdram_mode_8, regs->ddr_sdram_mode_8);
+ ddr_out32(&ddr->sdram_mode_9, regs->ddr_sdram_mode_9);
+ ddr_out32(&ddr->sdram_mode_10, regs->ddr_sdram_mode_10);
+ ddr_out32(&ddr->sdram_mode_11, regs->ddr_sdram_mode_11);
+ ddr_out32(&ddr->sdram_mode_12, regs->ddr_sdram_mode_12);
+ ddr_out32(&ddr->sdram_mode_13, regs->ddr_sdram_mode_13);
+ ddr_out32(&ddr->sdram_mode_14, regs->ddr_sdram_mode_14);
+ ddr_out32(&ddr->sdram_mode_15, regs->ddr_sdram_mode_15);
+ ddr_out32(&ddr->sdram_mode_16, regs->ddr_sdram_mode_16);
+ ddr_out32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl);
+
+ if (c->erratum_A009663)
+ ddr_out32(&ddr->sdram_interval,
+ regs->ddr_sdram_interval & ~SDRAM_INTERVAL_BSTOPRE);
+ else
+ ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval);
+
+ ddr_out32(&ddr->sdram_data_init, regs->ddr_data_init);
+ ddr_out32(&ddr->ddr_wrlvl_cntl, regs->ddr_wrlvl_cntl);
+ if (regs->ddr_wrlvl_cntl_2)
+ ddr_out32(&ddr->ddr_wrlvl_cntl_2, regs->ddr_wrlvl_cntl_2);
+ if (regs->ddr_wrlvl_cntl_3)
+ ddr_out32(&ddr->ddr_wrlvl_cntl_3, regs->ddr_wrlvl_cntl_3);
+
+ ddr_out32(&ddr->ddr_sr_cntr, regs->ddr_sr_cntr);
+ ddr_out32(&ddr->ddr_sdram_rcw_1, regs->ddr_sdram_rcw_1);
+ ddr_out32(&ddr->ddr_sdram_rcw_2, regs->ddr_sdram_rcw_2);
+ ddr_out32(&ddr->ddr_sdram_rcw_3, regs->ddr_sdram_rcw_3);
+ ddr_out32(&ddr->ddr_sdram_rcw_4, regs->ddr_sdram_rcw_4);
+ ddr_out32(&ddr->ddr_sdram_rcw_5, regs->ddr_sdram_rcw_5);
+ ddr_out32(&ddr->ddr_sdram_rcw_6, regs->ddr_sdram_rcw_6);
+
+ if (is_warm_boot()) {
+ ddr_out32(&ddr->sdram_cfg_2,
+ regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT);
+ ddr_out32(&ddr->init_addr, 0x80000000); /* FIXME */
+ ddr_out32(&ddr->init_ext_addr, DDR_INIT_ADDR_EXT_UIA);
+
+ /* DRAM VRef will not be trained */
+ ddr_out32(&ddr->ddr_cdr2,
+ regs->ddr_cdr2 & ~DDR_CDR2_VREF_TRAIN_EN);
+ } else {
+ ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2);
+ ddr_out32(&ddr->init_addr, regs->ddr_init_addr);
+ ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr);
+ ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2);
+ }
+
+ /* part 1 of 2 */
+ if (c->erratum_A009803) {
+ if (regs->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) {
+ if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) { /* for RDIMM */
+ ddr_out32(&ddr->ddr_sdram_rcw_2,
+ regs->ddr_sdram_rcw_2 & ~0xf0);
+ }
+ ddr_out32(&ddr->err_disable, regs->err_disable |
+ DDR_ERR_DISABLE_APED);
+ }
+ } else {
+ ddr_out32(&ddr->err_disable, regs->err_disable);
+ }
+ ddr_out32(&ddr->err_int_en, regs->err_int_en);
+ for (i = 0; i < 64; i++) {
+ if (regs->debug[i]) {
+ debug("Write to debug_%d as %08x\n",
+ i+1, regs->debug[i]);
+ ddr_out32(&ddr->debug[i], regs->debug[i]);
+ }
+ }
+
+ if (c->erratum_A008511) {
+ /* Part 1 of 2 */
+ if (fsl_ddr_get_version(c) == 0x50200) {
+ /* Disable DRAM VRef training */
+ ddr_out32(&ddr->ddr_cdr2,
+ regs->ddr_cdr2 & ~DDR_CDR2_VREF_TRAIN_EN);
+ /* disable transmit bit deskew */
+ temp32 = ddr_in32(&ddr->debug[28]);
+ temp32 |= DDR_TX_BD_DIS;
+ ddr_out32(&ddr->debug[28], temp32);
+ ddr_out32(&ddr->debug[25], 0x9000);
+ } else if (fsl_ddr_get_version(c) == 0x50201) {
+ /* Output enable forced off */
+ ddr_out32(&ddr->debug[37], 1 << 31);
+ /* Enable Vref training */
+ ddr_out32(&ddr->ddr_cdr2,
+ regs->ddr_cdr2 | DDR_CDR2_VREF_TRAIN_EN);
+ } else {
+ debug("Erratum A008511 doesn't apply.\n");
+ }
+ }
+
+ if (c->erratum_A009803 || c->erratum_A008511)
+ /* Disable D_INIT */
+ ddr_out32(&ddr->sdram_cfg_2,
+ regs->ddr_sdram_cfg_2 & ~SDRAM_CFG2_D_INIT);
+
+ if (c->erratum_A009801) {
+ temp32 = ddr_in32(&ddr->debug[25]);
+ temp32 &= ~DDR_CAS_TO_PRE_SUB_MASK;
+ temp32 |= 9 << DDR_CAS_TO_PRE_SUB_SHIFT;
+ ddr_out32(&ddr->debug[25], temp32);
+ }
+
+ if (c->erratum_A010165) {
+ temp32 = c->ddr_freq / 1000000;
+ if ((temp32 > 1900) && (temp32 < 2300)) {
+ temp32 = ddr_in32(&ddr->debug[28]);
+ ddr_out32(&ddr->debug[28], temp32 | 0x000a0000);
+ }
+ }
+
+ /*
+ * For RDIMMs, JEDEC spec requires clocks to be stable before reset is
+ * deasserted. Clocks start when any chip select is enabled and clock
+ * control register is set. Because all DDR components are connected to
+ * one reset signal, this needs to be done in two steps. Step 1 is to
+ * get the clocks started. Step 2 resumes after reset signal is
+ * deasserted.
+ */
+ if (step == 1) {
+ udelay(200);
+ return;
+ }
+
+step2:
+ /* Set, but do not enable the memory */
+ temp32 = regs->ddr_sdram_cfg;
+ temp32 &= ~(SDRAM_CFG_MEM_EN);
+ ddr_out32(&ddr->sdram_cfg, temp32);
+
+ /*
+ * 500 painful micro-seconds must elapse between
+ * the DDR clock setup and the DDR config enable.
+ * DDR2 need 200 us, and DDR3 need 500 us from spec,
+ * we choose the max, that is 500 us for all of case.
+ */
+ udelay(500);
+ dsb();
+ isb();
+
+ if (is_warm_boot()) {
+ /* enter self-refresh */
+ temp32 = ddr_in32(&ddr->sdram_cfg_2);
+ temp32 |= SDRAM_CFG2_FRC_SR;
+ ddr_out32(&ddr->sdram_cfg_2, temp32);
+ /* do board specific memory setup */
+ board_mem_sleep_setup();
+
+ temp32 = (ddr_in32(&ddr->sdram_cfg) | SDRAM_CFG_BI);
+ } else {
+ temp32 = ddr_in32(&ddr->sdram_cfg) & ~SDRAM_CFG_BI;
+ }
+
+ /* Let the controller go */
+ ddr_out32(&ddr->sdram_cfg, temp32 | SDRAM_CFG_MEM_EN);
+ dsb();
+ isb();
+
+ if (c->erratum_A008511 || c->erratum_A009803) {
+ /* Part 2 of 2 */
+ timeout = 40;
+ /* Wait for idle. D_INIT needs to be cleared earlier, or timeout */
+ while (!(ddr_in32(&ddr->debug[1]) & 0x2) && timeout > 0) {
+ udelay(1000);
+ timeout--;
+ }
+ if (timeout <= 0) {
+ printf("Controler %d timeout, debug_2 = %x\n",
+ c->num, ddr_in32(&ddr->debug[1]));
+ }
+ }
+
+ if (c->erratum_A008511) {
+ /* This erraum only applies to verion 5.2.0 */
+ if (fsl_ddr_get_version(c) == 0x50200) {
+ /* The vref setting sequence is different for range 2 */
+ if (regs->ddr_cdr2 & DDR_CDR2_VREF_RANGE_2)
+ vref_seq = vref_seq2;
+
+ /* Set VREF */
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (!(regs->cs[i].config & SDRAM_CS_CONFIG_EN))
+ continue;
+
+ mr6 = (regs->ddr_sdram_mode_10 >> 16) |
+ MD_CNTL_MD_EN |
+ MD_CNTL_CS_SEL(i) |
+ MD_CNTL_MD_SEL(6) |
+ 0x00200000;
+ temp32 = mr6 | vref_seq[0];
+ set_wait_for_bits_clear(&ddr->sdram_md_cntl,
+ temp32, MD_CNTL_MD_EN);
+ udelay(1);
+ debug("MR6 = 0x%08x\n", temp32);
+ temp32 = mr6 | vref_seq[1];
+ set_wait_for_bits_clear(&ddr->sdram_md_cntl,
+ temp32, MD_CNTL_MD_EN);
+ udelay(1);
+ debug("MR6 = 0x%08x\n", temp32);
+ temp32 = mr6 | vref_seq[2];
+ set_wait_for_bits_clear(&ddr->sdram_md_cntl,
+ temp32, MD_CNTL_MD_EN);
+ udelay(1);
+ debug("MR6 = 0x%08x\n", temp32);
+ }
+ ddr_out32(&ddr->sdram_md_cntl, 0);
+ temp32 = ddr_in32(&ddr->debug[28]);
+ temp32 &= ~DDR_TX_BD_DIS; /* Enable deskew */
+ ddr_out32(&ddr->debug[28], temp32);
+ ddr_out32(&ddr->debug[1], 0x400); /* restart deskew */
+ /* wait for idle */
+ timeout = 40;
+ while (!(ddr_in32(&ddr->debug[1]) & 0x2) && timeout > 0) {
+ udelay(1000);
+ timeout--;
+ }
+ if (timeout <= 0) {
+ printf("Controler %d timeout, debug_2 = %x\n",
+ c->num, ddr_in32(&ddr->debug[1]));
+ }
+ }
+ }
+
+ if (c->erratum_A009803 && regs->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) {
+ /* if it's RDIMM */
+ if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) {
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (!(regs->cs[i].config & SDRAM_CS_CONFIG_EN))
+ continue;
+ set_wait_for_bits_clear(&ddr->sdram_md_cntl,
+ MD_CNTL_MD_EN |
+ MD_CNTL_CS_SEL(i) |
+ 0x070000ed,
+ MD_CNTL_MD_EN);
+ udelay(1);
+ }
+ }
+
+ ddr_out32(&ddr->err_disable,
+ regs->err_disable & ~DDR_ERR_DISABLE_APED);
+ }
+
+ /* Restore D_INIT */
+ ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2);
+
+ total_gb_size_per_controller = 0;
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (!(regs->cs[i].config & 0x80000000))
+ continue;
+ total_gb_size_per_controller += 1 << (
+ ((regs->cs[i].config >> 14) & 0x3) + 2 +
+ ((regs->cs[i].config >> 8) & 0x7) + 12 +
+ ((regs->cs[i].config >> 4) & 0x3) + 0 +
+ ((regs->cs[i].config >> 0) & 0x7) + 8 +
+ ((regs->ddr_sdram_cfg_3 >> 4) & 0x3) +
+ 3 - ((regs->ddr_sdram_cfg >> 19) & 0x3) -
+ 26); /* minus 26 (count of 64M) */
+ }
+ /*
+ * total memory / bus width = transactions needed
+ * transactions needed / data rate = seconds
+ * to add plenty of buffer, double the time
+ * For example, 2GB on 666MT/s 64-bit bus takes about 402ms
+ * Let's wait for 800ms
+ */
+ bus_width = 3 - ((ddr_in32(&ddr->sdram_cfg) & SDRAM_CFG_DBW_MASK)
+ >> SDRAM_CFG_DBW_SHIFT);
+ timeout = ((total_gb_size_per_controller << (6 - bus_width)) * 100 /
+ (c->ddr_freq >> 20)) << 2;
+ total_gb_size_per_controller >>= 4; /* shift down to gb size */
+ debug("total %d GB\n", total_gb_size_per_controller);
+ debug("Need to wait up to %d * 10ms\n", timeout);
+
+ /* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done. */
+ while ((ddr_in32(&ddr->sdram_cfg_2) & SDRAM_CFG2_D_INIT) &&
+ (timeout >= 0)) {
+ udelay(10000); /* throttle polling rate */
+ timeout--;
+ }
+
+ if (timeout <= 0)
+ printf("Waiting for D_INIT timeout. Memory may not work.\n");
+
+ if (mod_bnds) {
+ debug("Reset to original bnds\n");
+ ddr_out32(&ddr->cs0_bnds, regs->cs[0].bnds);
+ ddr_out32(&ddr->cs1_bnds, regs->cs[1].bnds);
+ ddr_out32(&ddr->cs2_bnds, regs->cs[2].bnds);
+ ddr_out32(&ddr->cs3_bnds, regs->cs[3].bnds);
+ ddr_out32(&ddr->cs0_config, regs->cs[0].config);
+ }
+
+ if (c->erratum_A009663)
+ ddr_out32(&ddr->sdram_interval, regs->ddr_sdram_interval);
+
+ if (is_warm_boot()) {
+ /* exit self-refresh */
+ temp32 = ddr_in32(&ddr->sdram_cfg_2);
+ temp32 &= ~SDRAM_CFG2_FRC_SR;
+ ddr_out32(&ddr->sdram_cfg_2, temp32);
+ }
+
+#define BIST_PATTERN1 0xFFFFFFFF
+#define BIST_PATTERN2 0x0
+#define BIST_CR 0x80010000
+#define BIST_CR_EN 0x80000000
+#define BIST_CR_STAT 0x00000001
+ /* Perform build-in test on memory. Three-way interleaving is not yet
+ * supported by this code. */
+ if (0) {
+ printf("Running BIST test. This will take a while...");
+ cs0_config = ddr_in32(&ddr->cs0_config);
+ cs0_bnds = ddr_in32(&ddr->cs0_bnds);
+ cs1_bnds = ddr_in32(&ddr->cs1_bnds);
+ cs2_bnds = ddr_in32(&ddr->cs2_bnds);
+ cs3_bnds = ddr_in32(&ddr->cs3_bnds);
+ if (cs0_config & CTLR_INTLV_MASK) {
+ /* set bnds to non-interleaving */
+ ddr_out32(&ddr->cs0_bnds, (cs0_bnds & 0xfffefffe) >> 1);
+ ddr_out32(&ddr->cs1_bnds, (cs1_bnds & 0xfffefffe) >> 1);
+ ddr_out32(&ddr->cs2_bnds, (cs2_bnds & 0xfffefffe) >> 1);
+ ddr_out32(&ddr->cs3_bnds, (cs3_bnds & 0xfffefffe) >> 1);
+ }
+ ddr_out32(&ddr->mtp1, BIST_PATTERN1);
+ ddr_out32(&ddr->mtp2, BIST_PATTERN1);
+ ddr_out32(&ddr->mtp3, BIST_PATTERN2);
+ ddr_out32(&ddr->mtp4, BIST_PATTERN2);
+ ddr_out32(&ddr->mtp5, BIST_PATTERN1);
+ ddr_out32(&ddr->mtp6, BIST_PATTERN1);
+ ddr_out32(&ddr->mtp7, BIST_PATTERN2);
+ ddr_out32(&ddr->mtp8, BIST_PATTERN2);
+ ddr_out32(&ddr->mtp9, BIST_PATTERN1);
+ ddr_out32(&ddr->mtp10, BIST_PATTERN2);
+ mtcr = BIST_CR;
+ ddr_out32(&ddr->mtcr, mtcr);
+ timeout = 100;
+ while (timeout > 0 && (mtcr & BIST_CR_EN)) {
+ mdelay(1000);
+ timeout--;
+ mtcr = ddr_in32(&ddr->mtcr);
+ }
+ if (timeout <= 0)
+ printf("Timeout\n");
+ else
+ printf("Done\n");
+ err_detect = ddr_in32(&ddr->err_detect);
+ err_sbe = ddr_in32(&ddr->err_sbe);
+ if (mtcr & BIST_CR_STAT) {
+ printf("BIST test failed on controller %d.\n",
+ c->num);
+ }
+ if (err_detect || (err_sbe & 0xffff)) {
+ printf("ECC error detected on controller %d.\n",
+ c->num);
+ }
+
+ if (cs0_config & CTLR_INTLV_MASK) {
+ /* restore bnds registers */
+ ddr_out32(&ddr->cs0_bnds, cs0_bnds);
+ ddr_out32(&ddr->cs1_bnds, cs1_bnds);
+ ddr_out32(&ddr->cs2_bnds, cs2_bnds);
+ ddr_out32(&ddr->cs3_bnds, cs3_bnds);
+ }
+ }
+}
diff --git a/drivers/ddr/fsl/lc_common_dimm_params.c b/drivers/ddr/fsl/lc_common_dimm_params.c
new file mode 100644
index 0000000..2de4cca
--- /dev/null
+++ b/drivers/ddr/fsl/lc_common_dimm_params.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP Semiconductor
+ */
+
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <linux/log2.h>
+#include "fsl_ddr.h"
+
+static unsigned int
+compute_cas_latency_ddr34(memctl_options_t *popts,
+ struct fsl_ddr_controller *c,
+ const struct dimm_params *dimm_params,
+ struct common_timing_params *outpdimm,
+ unsigned int number_of_dimms)
+{
+ unsigned int i;
+ unsigned int common_caslat;
+ unsigned int caslat_actual;
+ unsigned int retry = 16;
+ unsigned int tmp = ~0;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+ unsigned int taamax;
+
+ if (is_ddr3(popts))
+ taamax = 20000;
+ else
+ taamax = 18000;
+
+ /* compute the common CAS latency supported between slots */
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm_params[i].n_ranks)
+ tmp &= dimm_params[i].caslat_x;
+ }
+ common_caslat = tmp;
+
+ /* validate if the memory clk is in the range of dimms */
+ if (mclk_ps < outpdimm->tckmin_x_ps) {
+ printf("DDR clock (MCLK cycle %u ps) is faster than "
+ "the slowest DIMM(s) (tCKmin %u ps) can support.\n",
+ mclk_ps, outpdimm->tckmin_x_ps);
+ }
+
+ if (is_ddr4(popts) && mclk_ps > outpdimm->tckmax_ps) {
+ printf("DDR clock (MCLK cycle %u ps) is slower than DIMM(s) (tCKmax %u ps) can support.\n",
+ mclk_ps, outpdimm->tckmax_ps);
+ }
+
+ /* determine the acutal cas latency */
+ caslat_actual = (outpdimm->taamin_ps + mclk_ps - 1) / mclk_ps;
+ /* check if the dimms support the CAS latency */
+ while (!(common_caslat & (1 << caslat_actual)) && retry > 0) {
+ caslat_actual++;
+ retry--;
+ }
+ /* once the caculation of caslat_actual is completed
+ * we must verify that this CAS latency value does not
+ * exceed tAAmax, which is 20 ns for all DDR3 speed grades,
+ * 18ns for all DDR4 speed grades.
+ */
+ if (caslat_actual * mclk_ps > taamax) {
+ printf("The chosen cas latency %d is too large\n",
+ caslat_actual);
+ }
+ outpdimm->lowest_common_spd_caslat = caslat_actual;
+ debug("lowest_common_spd_caslat is 0x%x\n", caslat_actual);
+
+ return 0;
+}
+
+static unsigned int
+compute_cas_latency_ddr12(memctl_options_t *popts,
+ struct fsl_ddr_controller *c,
+ const struct dimm_params *dimm_params,
+ struct common_timing_params *outpdimm,
+ unsigned int number_of_dimms)
+{
+ int i;
+ const unsigned int mclk_ps = get_memory_clk_period_ps(c);
+ unsigned int lowest_good_caslat;
+ unsigned int not_ok;
+ unsigned int temp1, temp2;
+
+ debug("using mclk_ps = %u\n", mclk_ps);
+ if (mclk_ps > outpdimm->tckmax_ps) {
+ printf("Warning: DDR clock (%u ps) is slower than DIMM(s) (tCKmax %u ps)\n",
+ mclk_ps, outpdimm->tckmax_ps);
+ }
+
+ /*
+ * Compute a CAS latency suitable for all DIMMs
+ *
+ * Strategy for SPD-defined latencies: compute only
+ * CAS latency defined by all DIMMs.
+ */
+
+ /*
+ * Step 1: find CAS latency common to all DIMMs using bitwise
+ * operation.
+ */
+ temp1 = 0xFF;
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm_params[i].n_ranks) {
+ temp2 = 0;
+ temp2 |= 1 << dimm_params[i].caslat_x;
+ temp2 |= 1 << dimm_params[i].caslat_x_minus_1;
+ temp2 |= 1 << dimm_params[i].caslat_x_minus_2;
+ /*
+ * If there was no entry for X-2 (X-1) in
+ * the SPD, then caslat_x_minus_2
+ * (caslat_x_minus_1) contains either 255 or
+ * 0xFFFFFFFF because that's what the glorious
+ * __ilog2 function returns for an input of 0.
+ * On 32-bit PowerPC, left shift counts with bit
+ * 26 set (that the value of 255 or 0xFFFFFFFF
+ * will have), cause the destination register to
+ * be 0. That is why this works.
+ */
+ temp1 &= temp2;
+ }
+ }
+
+ /*
+ * Step 2: check each common CAS latency against tCK of each
+ * DIMM's SPD.
+ */
+ lowest_good_caslat = 0;
+ temp2 = 0;
+ while (temp1) {
+ not_ok = 0;
+ temp2 = ilog2(temp1);
+ debug("checking common caslat = %u\n", temp2);
+
+ /* Check if this CAS latency will work on all DIMMs at tCK. */
+ for (i = 0; i < number_of_dimms; i++) {
+ if (!dimm_params[i].n_ranks)
+ continue;
+
+ if (dimm_params[i].caslat_x == temp2) {
+ if (mclk_ps >= dimm_params[i].tckmin_x_ps) {
+ debug("CL = %u ok on DIMM %u at tCK=%u ps with tCKmin_X_ps of %u\n",
+ temp2, i, mclk_ps,
+ dimm_params[i].tckmin_x_ps);
+ continue;
+ } else {
+ not_ok++;
+ }
+ }
+
+ if (dimm_params[i].caslat_x_minus_1 == temp2) {
+ unsigned int tckmin_x_minus_1_ps
+ = dimm_params[i].tckmin_x_minus_1_ps;
+ if (mclk_ps >= tckmin_x_minus_1_ps) {
+ debug("CL = %u ok on DIMM %u at tCK=%u ps with tckmin_x_minus_1_ps of %u\n",
+ temp2, i, mclk_ps,
+ tckmin_x_minus_1_ps);
+ continue;
+ } else {
+ not_ok++;
+ }
+ }
+
+ if (dimm_params[i].caslat_x_minus_2 == temp2) {
+ unsigned int tckmin_x_minus_2_ps
+ = dimm_params[i].tckmin_x_minus_2_ps;
+ if (mclk_ps >= tckmin_x_minus_2_ps) {
+ debug("CL = %u ok on DIMM %u at tCK=%u ps with tckmin_x_minus_2_ps of %u\n",
+ temp2, i, mclk_ps,
+ tckmin_x_minus_2_ps);
+ continue;
+ } else {
+ not_ok++;
+ }
+ }
+ }
+
+ if (!not_ok)
+ lowest_good_caslat = temp2;
+
+ temp1 &= ~(1 << temp2);
+ }
+
+ debug("lowest common SPD-defined CAS latency = %u\n",
+ lowest_good_caslat);
+ outpdimm->lowest_common_spd_caslat = lowest_good_caslat;
+
+
+ /*
+ * Compute a common 'de-rated' CAS latency.
+ *
+ * The strategy here is to find the *highest* dereated cas latency
+ * with the assumption that all of the DIMMs will support a dereated
+ * CAS latency higher than or equal to their lowest dereated value.
+ */
+ temp1 = 0;
+ for (i = 0; i < number_of_dimms; i++)
+ temp1 = max(temp1, dimm_params[i].caslat_lowest_derated);
+
+ outpdimm->highest_common_derated_caslat = temp1;
+ debug("highest common dereated CAS latency = %u\n", temp1);
+
+ return 0;
+}
+
+/*
+ * compute_lowest_common_dimm_parameters()
+ *
+ * Determine the worst-case DIMM timing parameters from the set of DIMMs
+ * whose parameters have been computed into the array pointed to
+ * by dimm_params.
+ */
+unsigned int
+compute_lowest_common_dimm_parameters(struct fsl_ddr_controller *c)
+{
+ int number_of_dimms = c->dimm_slots_per_ctrl;
+ memctl_options_t *popts = &c->memctl_opts;
+ const struct dimm_params *dimm_params = c->dimm_params;
+ struct common_timing_params *outpdimm = &c->common_timing_params;
+ unsigned int i, j;
+
+ unsigned int tckmin_x_ps = 0;
+ unsigned int tckmax_ps = 0xFFFFFFFF;
+ unsigned int trcd_ps = 0;
+ unsigned int trp_ps = 0;
+ unsigned int tras_ps = 0;
+ unsigned int taamin_ps = 0;
+ unsigned int twr_ps = 0;
+ unsigned int trfc1_ps = 0;
+ unsigned int trfc2_ps = 0;
+ unsigned int trfc4_ps = 0;
+ unsigned int trrds_ps = 0;
+ unsigned int trrdl_ps = 0;
+ unsigned int tccdl_ps = 0;
+ unsigned int trfc_slr_ps = 0;
+ unsigned int twtr_ps = 0;
+ unsigned int trfc_ps = 0;
+ unsigned int trrd_ps = 0;
+ unsigned int trtp_ps = 0;
+ unsigned int trc_ps = 0;
+ unsigned int refresh_rate_ps = 0;
+ unsigned int extended_op_srt = 1;
+ unsigned int tis_ps = 0;
+ unsigned int tih_ps = 0;
+ unsigned int tds_ps = 0;
+ unsigned int tdh_ps = 0;
+ unsigned int tdqsq_max_ps = 0;
+ unsigned int tqhs_ps = 0;
+ unsigned int temp1, temp2;
+ unsigned int additive_latency = 0;
+
+ temp1 = 0;
+ for (i = 0; i < number_of_dimms; i++) {
+ /*
+ * If there are no ranks on this DIMM,
+ * it probably doesn't exist, so skip it.
+ */
+ if (dimm_params[i].n_ranks == 0) {
+ temp1++;
+ continue;
+ }
+ if (dimm_params[i].n_ranks == 4 && i != 0) {
+ printf("Found Quad-rank DIMM in wrong bank, ignored."
+ " Software may not run as expected.\n");
+ temp1++;
+ continue;
+ }
+
+ /*
+ * check if quad-rank DIMM is plugged if
+ * CONFIG_CHIP_SELECT_QUAD_CAPABLE is not defined
+ * Only the board with proper design is capable
+ */
+ if (dimm_params[i].n_ranks == 4 && \
+ c->chip_selects_per_ctrl / c->dimm_slots_per_ctrl < 4) {
+ printf("Found Quad-rank DIMM, not able to support.");
+ temp1++;
+ continue;
+ }
+
+ /*
+ * Find minimum tckmax_ps to find fastest slow speed,
+ * i.e., this is the slowest the whole system can go.
+ */
+ outpdimm->tckmax_ps = min(tckmax_ps,
+ (unsigned int)dimm_params[i].tckmax_ps);
+ if (is_ddr3_4(popts))
+ outpdimm->taamin_ps = max(taamin_ps,
+ (unsigned int)dimm_params[i].taa_ps);
+ outpdimm->tckmin_x_ps = max(tckmin_x_ps,
+ (unsigned int)dimm_params[i].tckmin_x_ps);
+ outpdimm->trcd_ps = max(trcd_ps, (unsigned int)dimm_params[i].trcd_ps);
+ outpdimm->trp_ps = max(trp_ps, (unsigned int)dimm_params[i].trp_ps);
+ outpdimm->tras_ps = max(tras_ps, (unsigned int)dimm_params[i].tras_ps);
+
+ if (is_ddr4(popts)) {
+ outpdimm->twr_ps = 15000;
+ outpdimm->trfc1_ps = max(trfc1_ps,
+ (unsigned int)dimm_params[i].trfc1_ps);
+ outpdimm->trfc2_ps = max(trfc2_ps,
+ (unsigned int)dimm_params[i].trfc2_ps);
+ outpdimm->trfc4_ps = max(trfc4_ps,
+ (unsigned int)dimm_params[i].trfc4_ps);
+ outpdimm->trrds_ps = max(trrds_ps,
+ (unsigned int)dimm_params[i].trrds_ps);
+ outpdimm->trrdl_ps = max(trrdl_ps,
+ (unsigned int)dimm_params[i].trrdl_ps);
+ outpdimm->tccdl_ps = max(tccdl_ps,
+ (unsigned int)dimm_params[i].tccdl_ps);
+ outpdimm->trfc_slr_ps = max(trfc_slr_ps,
+ (unsigned int)dimm_params[i].trfc_slr_ps);
+ } else {
+ twr_ps = max(twr_ps, (unsigned int)dimm_params[i].twr_ps);
+ outpdimm->twtr_ps = max(twtr_ps, (unsigned int)dimm_params[i].twtr_ps);
+ outpdimm->trfc_ps = max(trfc_ps, (unsigned int)dimm_params[i].trfc_ps);
+ outpdimm->trrd_ps = max(trrd_ps, (unsigned int)dimm_params[i].trrd_ps);
+ outpdimm->trtp_ps = max(trtp_ps, (unsigned int)dimm_params[i].trtp_ps);
+ }
+ outpdimm->trc_ps = max(trc_ps, (unsigned int)dimm_params[i].trc_ps);
+ if (is_ddr1(popts) || is_ddr2(popts)) {
+ outpdimm->tis_ps = max(tis_ps, (unsigned int)dimm_params[i].tis_ps);
+ outpdimm->tih_ps = max(tih_ps, (unsigned int)dimm_params[i].tih_ps);
+ outpdimm->tds_ps = max(tds_ps, (unsigned int)dimm_params[i].tds_ps);
+ outpdimm->tdh_ps = max(tdh_ps, (unsigned int)dimm_params[i].tdh_ps);
+ outpdimm->tqhs_ps = max(tqhs_ps, (unsigned int)dimm_params[i].tqhs_ps);
+ /*
+ * Find maximum tdqsq_max_ps to find slowest.
+ *
+ * FIXME: is finding the slowest value the correct
+ * strategy for this parameter?
+ */
+ outpdimm->tdqsq_max_ps = max(tdqsq_max_ps,
+ (unsigned int)dimm_params[i].tdqsq_max_ps);
+ }
+ outpdimm->refresh_rate_ps = max(refresh_rate_ps,
+ (unsigned int)dimm_params[i].refresh_rate_ps);
+ /* extended_op_srt is either 0 or 1, 0 having priority */
+ outpdimm->extended_op_srt = min(extended_op_srt,
+ (unsigned int)dimm_params[i].extended_op_srt);
+ }
+
+ outpdimm->ndimms_present = number_of_dimms - temp1;
+
+ if (temp1 == number_of_dimms) {
+ debug("no dimms this memory controller\n");
+ return 0;
+ }
+
+ /* Determine common burst length for all DIMMs. */
+ temp1 = 0xff;
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm_params[i].n_ranks) {
+ temp1 &= dimm_params[i].burst_lengths_bitmask;
+ }
+ }
+ outpdimm->all_dimms_burst_lengths_bitmask = temp1;
+
+ /* Determine if all DIMMs registered buffered. */
+ temp1 = temp2 = 0;
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm_params[i].n_ranks) {
+ if (dimm_params[i].registered_dimm) {
+ temp1 = 1;
+ printf("Detected RDIMM %s\n",
+ dimm_params[i].mpart);
+ } else {
+ temp2 = 1;
+ printf("Detected UDIMM %s\n",
+ dimm_params[i].mpart);
+ }
+ }
+ }
+
+ outpdimm->all_dimms_registered = 0;
+ outpdimm->all_dimms_unbuffered = 0;
+ if (temp1 && !temp2) {
+ outpdimm->all_dimms_registered = 1;
+ } else if (!temp1 && temp2) {
+ outpdimm->all_dimms_unbuffered = 1;
+ } else {
+ printf("ERROR: Mix of registered buffered and unbuffered "
+ "DIMMs detected!\n");
+ }
+
+ temp1 = 0;
+ if (outpdimm->all_dimms_registered)
+ for (j = 0; j < 16; j++) {
+ outpdimm->rcw[j] = dimm_params[0].rcw[j];
+ for (i = 1; i < number_of_dimms; i++) {
+ if (!dimm_params[i].n_ranks)
+ continue;
+ if (dimm_params[i].rcw[j] != dimm_params[0].rcw[j]) {
+ temp1 = 1;
+ break;
+ }
+ }
+ }
+
+ if (temp1 != 0)
+ printf("ERROR: Mix different RDIMM detected!\n");
+
+ /* calculate cas latency for all DDR types */
+ if (is_ddr3_4(popts)) {
+ if (compute_cas_latency_ddr34(popts, c, dimm_params,
+ outpdimm, number_of_dimms))
+ return 1;
+ } else {
+ if (compute_cas_latency_ddr12(popts, c, dimm_params,
+ outpdimm, number_of_dimms))
+ return 1;
+ }
+
+ /* Determine if all DIMMs ECC capable. */
+ temp1 = 1;
+ for (i = 0; i < number_of_dimms; i++) {
+ if (dimm_params[i].n_ranks &&
+ !(dimm_params[i].edc_config & EDC_ECC)) {
+ temp1 = 0;
+ break;
+ }
+ }
+ if (temp1) {
+ debug("all DIMMs ECC capable\n");
+ } else {
+ debug("Warning: not all DIMMs ECC capable, cant enable ECC\n");
+ }
+ outpdimm->all_dimms_ecc_capable = temp1;
+
+ /*
+ * Compute additive latency.
+ *
+ * For DDR1, additive latency should be 0.
+ *
+ * For DDR2, with ODT enabled, use "a value" less than ACTTORW,
+ * which comes from Trcd, and also note that:
+ * add_lat + caslat must be >= 4
+ *
+ * For DDR3, we use the AL=0
+ *
+ * When to use additive latency for DDR2:
+ *
+ * I. Because you are using CL=3 and need to do ODT on writes and
+ * want functionality.
+ * 1. Are you going to use ODT? (Does your board not have
+ * additional termination circuitry for DQ, DQS, DQS_,
+ * DM, RDQS, RDQS_ for x4/x8 configs?)
+ * 2. If so, is your lowest supported CL going to be 3?
+ * 3. If so, then you must set AL=1 because
+ *
+ * WL >= 3 for ODT on writes
+ * RL = AL + CL
+ * WL = RL - 1
+ * ->
+ * WL = AL + CL - 1
+ * AL + CL - 1 >= 3
+ * AL + CL >= 4
+ * QED
+ *
+ * RL >= 3 for ODT on reads
+ * RL = AL + CL
+ *
+ * Since CL aren't usually less than 2, AL=0 is a minimum,
+ * so the WL-derived AL should be the -- FIXME?
+ *
+ * II. Because you are using auto-precharge globally and want to
+ * use additive latency (posted CAS) to get more bandwidth.
+ * 1. Are you going to use auto-precharge mode globally?
+ *
+ * Use addtivie latency and compute AL to be 1 cycle less than
+ * tRCD, i.e. the READ or WRITE command is in the cycle
+ * immediately following the ACTIVATE command..
+ *
+ * III. Because you feel like it or want to do some sort of
+ * degraded-performance experiment.
+ * 1. Do you just want to use additive latency because you feel
+ * like it?
+ *
+ * Validation: AL is less than tRCD, and within the other
+ * read-to-precharge constraints.
+ */
+
+ additive_latency = 0;
+
+ if (is_ddr2(popts) && outpdimm->lowest_common_spd_caslat < 4 &&
+ picos_to_mclk(c, trcd_ps > outpdimm->lowest_common_spd_caslat)) {
+ additive_latency = picos_to_mclk(c, trcd_ps) -
+ outpdimm->lowest_common_spd_caslat;
+ if (mclk_to_picos(c, additive_latency) > trcd_ps) {
+ additive_latency = picos_to_mclk(c, trcd_ps);
+ debug("setting additive_latency to %u because it was "
+ " greater than tRCD_ps\n", additive_latency);
+ }
+ }
+
+ /*
+ * Validate additive latency
+ *
+ * AL <= tRCD(min)
+ */
+ if (mclk_to_picos(c, additive_latency) > trcd_ps) {
+ printf("Error: invalid additive latency exceeds tRCD(min).\n");
+ return 1;
+ }
+
+ /*
+ * RL = CL + AL; RL >= 3 for ODT_RD_CFG to be enabled
+ * WL = RL - 1; WL >= 3 for ODT_WL_CFG to be enabled
+ * ADD_LAT (the register) must be set to a value less
+ * than ACTTORW if WL = 1, then AL must be set to 1
+ * RD_TO_PRE (the register) must be set to a minimum
+ * tRTP + AL if AL is nonzero
+ */
+
+ /*
+ * Additive latency will be applied only if the memctl option to
+ * use it.
+ */
+ outpdimm->additive_latency = additive_latency;
+
+ debug("tCKmin_ps = %u\n", outpdimm->tckmin_x_ps);
+ debug("trcd_ps = %u\n", outpdimm->trcd_ps);
+ debug("trp_ps = %u\n", outpdimm->trp_ps);
+ debug("tras_ps = %u\n", outpdimm->tras_ps);
+ if (is_ddr4(popts)) {
+ debug("trfc1_ps = %u\n", trfc1_ps);
+ debug("trfc2_ps = %u\n", trfc2_ps);
+ debug("trfc4_ps = %u\n", trfc4_ps);
+ debug("trrds_ps = %u\n", trrds_ps);
+ debug("trrdl_ps = %u\n", trrdl_ps);
+ debug("tccdl_ps = %u\n", tccdl_ps);
+ debug("trfc_slr_ps = %u\n", trfc_slr_ps);
+ } else {
+ debug("twtr_ps = %u\n", outpdimm->twtr_ps);
+ debug("trfc_ps = %u\n", outpdimm->trfc_ps);
+ debug("trrd_ps = %u\n", outpdimm->trrd_ps);
+ }
+ debug("twr_ps = %u\n", outpdimm->twr_ps);
+ debug("trc_ps = %u\n", outpdimm->trc_ps);
+
+ return 0;
+}
diff --git a/drivers/ddr/fsl/main.c b/drivers/ddr/fsl/main.c
new file mode 100644
index 0000000..b0df34c
--- /dev/null
+++ b/drivers/ddr/fsl/main.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008-2014 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * Generic driver for Freescale DDR/DDR2/DDR3 memory controller.
+ * Based on code from spd_sdram.c
+ * Author: James Yang [at freescale.com]
+ */
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <linux/log2.h>
+#include "fsl_ddr.h"
+
+/*
+ * ASSUMPTIONS:
+ * - Same number of CONFIG_DIMM_SLOTS_PER_CTLR on each controller
+ * - Same memory data bus width on all controllers
+ *
+ * NOTES:
+ *
+ * The memory controller and associated documentation use confusing
+ * terminology when referring to the orgranization of DRAM.
+ *
+ * Here is a terminology translation table:
+ *
+ * memory controller/documention |industry |this code |signals
+ * -------------------------------|-----------|-----------|-----------------
+ * physical bank/bank |rank |rank |chip select (CS)
+ * logical bank/sub-bank |bank |bank |bank address (BA)
+ * page/row |row |page |row address
+ * ??? |column |column |column address
+ *
+ * The naming confusion is further exacerbated by the descriptions of the
+ * memory controller interleaving feature, where accesses are interleaved
+ * _BETWEEN_ two seperate memory controllers. This is configured only in
+ * CS0_CONFIG[INTLV_CTL] of each memory controller.
+ *
+ * memory controller documentation | number of chip selects
+ * | per memory controller supported
+ * --------------------------------|-----------------------------------------
+ * cache line interleaving | 1 (CS0 only)
+ * page interleaving | 1 (CS0 only)
+ * bank interleaving | 1 (CS0 only)
+ * superbank interleraving | depends on bank (chip select)
+ * | interleraving [rank interleaving]
+ * | mode used on every memory controller
+ *
+ * Even further confusing is the existence of the interleaving feature
+ * _WITHIN_ each memory controller. The feature is referred to in
+ * documentation as chip select interleaving or bank interleaving,
+ * although it is configured in the DDR_SDRAM_CFG field.
+ *
+ * Name of field | documentation name | this code
+ * -----------------------------|-----------------------|------------------
+ * DDR_SDRAM_CFG[BA_INTLV_CTL] | Bank (chip select) | rank interleaving
+ * | interleaving
+ */
+
+static unsigned long long step_assign_addresses_linear(struct fsl_ddr_info *pinfo,
+ unsigned long long current_mem_base)
+{
+ int i, j;
+ unsigned long long total_mem = 0;
+
+ /*
+ * Simple linear assignment if memory
+ * controllers are not interleaved.
+ */
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+ unsigned long long total_ctlr_mem = 0;
+
+ c->common_timing_params.base_address = current_mem_base;
+
+ for (j = 0; j < c->dimm_slots_per_ctrl; j++) {
+ /* Compute DIMM base addresses. */
+ unsigned long long cap = c->dimm_params[j].capacity >>
+ pinfo->c[i].dbw_capacity_adjust;
+
+ c->dimm_params[j].base_address = current_mem_base;
+ debug("ctrl %d dimm %d base 0x%llx\n", i, j, current_mem_base);
+ current_mem_base += cap;
+ total_ctlr_mem += cap;
+ }
+ debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem);
+ c->common_timing_params.total_mem = total_ctlr_mem;
+ total_mem += total_ctlr_mem;
+ }
+
+ return total_mem;
+}
+
+static unsigned long long step_assign_addresses_interleaved(struct fsl_ddr_info *pinfo,
+ unsigned long long current_mem_base)
+{
+ unsigned long long total_mem, total_ctlr_mem;
+ unsigned long long rank_density, ctlr_density = 0;
+ int i;
+
+ rank_density = pinfo->c[0].dimm_params[0].rank_density >>
+ pinfo->c[0].dbw_capacity_adjust;
+
+ switch (pinfo->c[0].memctl_opts.ba_intlv_ctl &
+ FSL_DDR_CS0_CS1_CS2_CS3) {
+ case FSL_DDR_CS0_CS1_CS2_CS3:
+ ctlr_density = 4 * rank_density;
+ break;
+ case FSL_DDR_CS0_CS1:
+ case FSL_DDR_CS0_CS1_AND_CS2_CS3:
+ ctlr_density = 2 * rank_density;
+ break;
+ case FSL_DDR_CS2_CS3:
+ default:
+ ctlr_density = rank_density;
+ break;
+ }
+
+ debug("rank density is 0x%llx, ctlr density is 0x%llx\n",
+ rank_density, ctlr_density);
+
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ if (c->memctl_opts.memctl_interleaving) {
+ switch (c->memctl_opts.memctl_interleaving_mode) {
+ case FSL_DDR_256B_INTERLEAVING:
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ total_ctlr_mem = 2 * ctlr_density;
+ break;
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ total_ctlr_mem = 3 * ctlr_density;
+ break;
+ case FSL_DDR_4WAY_1KB_INTERLEAVING:
+ case FSL_DDR_4WAY_4KB_INTERLEAVING:
+ case FSL_DDR_4WAY_8KB_INTERLEAVING:
+ total_ctlr_mem = 4 * ctlr_density;
+ break;
+ default:
+ panic("Unknown interleaving mode");
+ }
+ c->common_timing_params.base_address = current_mem_base;
+ c->common_timing_params.total_mem = total_ctlr_mem;
+ total_mem = current_mem_base + total_ctlr_mem;
+ debug("ctrl %d base 0x%llx\n", i, current_mem_base);
+ debug("ctrl %d total 0x%llx\n", i, total_ctlr_mem);
+ } else {
+ total_mem += step_assign_addresses_linear(pinfo, current_mem_base);
+ }
+ }
+
+ return total_mem;
+}
+
+static unsigned long long step_assign_addresses(struct fsl_ddr_info *pinfo)
+{
+ unsigned int i, j;
+ unsigned long long total_mem;
+
+ /*
+ * If a reduced data width is requested, but the SPD
+ * specifies a physically wider device, adjust the
+ * computed dimm capacities accordingly before
+ * assigning addresses.
+ */
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+ unsigned int found = 0;
+
+ switch (c->memctl_opts.data_bus_width) {
+ case 2:
+ /* 16-bit */
+ for (j = 0; j < c->dimm_slots_per_ctrl; j++) {
+ unsigned int dw;
+ if (!c->dimm_params[j].n_ranks)
+ continue;
+ dw = c->dimm_params[j].primary_sdram_width;
+ if ((dw == 72 || dw == 64)) {
+ pinfo->c[i].dbw_capacity_adjust = 2;
+ break;
+ } else if ((dw == 40 || dw == 32)) {
+ pinfo->c[i].dbw_capacity_adjust = 1;
+ break;
+ }
+ }
+ break;
+
+ case 1:
+ /* 32-bit */
+ for (j = 0; j < c->dimm_slots_per_ctrl; j++) {
+ unsigned int dw;
+ dw = c->dimm_params[j].data_width;
+ if (c->dimm_params[j].n_ranks
+ && (dw == 72 || dw == 64)) {
+ /*
+ * FIXME: can't really do it
+ * like this because this just
+ * further reduces the memory
+ */
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ pinfo->c[i].dbw_capacity_adjust = 1;
+
+ break;
+
+ case 0:
+ /* 64-bit */
+ break;
+
+ default:
+ printf("unexpected data bus width "
+ "specified controller %u\n", i);
+ return 1;
+ }
+ debug("dbw_cap_adj[%d]=%d\n", i, pinfo->c[i].dbw_capacity_adjust);
+ }
+
+ if (pinfo->c[0].memctl_opts.memctl_interleaving)
+ total_mem = step_assign_addresses_interleaved(pinfo, pinfo->mem_base);
+ else
+ total_mem = step_assign_addresses_linear(pinfo, pinfo->mem_base);
+
+ debug("Total mem by %s is 0x%llx\n", __func__, total_mem);
+
+ return total_mem;
+}
+
+static int compute_dimm_parameters(struct fsl_ddr_controller *c,
+ struct spd_eeprom *spd,
+ struct dimm_params *pdimm)
+{
+ const memctl_options_t *popts = &c->memctl_opts;
+ int ret = -EINVAL;
+
+ memset(pdimm, 0, sizeof(*pdimm));
+
+ if (is_ddr1(popts))
+ ret = ddr1_compute_dimm_parameters(c, (void *)spd, pdimm);
+ else if (is_ddr2(popts))
+ ret = ddr2_compute_dimm_parameters(c, (void *)spd, pdimm);
+ else if (is_ddr3(popts))
+ ret = ddr3_compute_dimm_parameters(c, (void *)spd, pdimm);
+ else if (is_ddr4(popts))
+ ret = ddr4_compute_dimm_parameters(c, (void *)spd, pdimm);
+
+ return ret;
+}
+
+static unsigned long long fsl_ddr_compute(struct fsl_ddr_info *pinfo)
+{
+ unsigned int i, j;
+ unsigned long long total_mem = 0;
+ int assert_reset = 0;
+ int retval;
+ unsigned int max_end = 0;
+
+ /* STEP 2: Compute DIMM parameters from SPD data */
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ if (!c->spd_installed_dimms)
+ continue;
+
+ for (j = 0; j < c->dimm_slots_per_ctrl; j++) {
+ struct spd_eeprom *spd = &c->spd_installed_dimms[j];
+ struct dimm_params *pdimm = &c->dimm_params[j];
+
+ retval = compute_dimm_parameters(c, spd, pdimm);
+ if (retval == 2) {
+ printf("Error: compute_dimm_parameters"
+ " non-zero returned FATAL value "
+ "for memctl=%u dimm=%u\n", i, j);
+ return 0;
+ }
+ if (retval) {
+ debug("Warning: compute_dimm_parameters"
+ " non-zero return value for memctl=%u "
+ "dimm=%u\n", i, j);
+ }
+ }
+ }
+
+ /*
+ * STEP 3: Compute a common set of timing parameters
+ * suitable for all of the DIMMs on each memory controller
+ */
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ debug("Computing lowest common DIMM parameters for memctl=%u\n",
+ i);
+ compute_lowest_common_dimm_parameters(c);
+ }
+
+ /* STEP 4: Gather configuration requirements from user */
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ debug("Reloading memory controller "
+ "configuration options for memctl=%u\n", i);
+ /*
+ * This "reloads" the memory controller options
+ * to defaults. If the user "edits" an option,
+ * next_step points to the step after this,
+ * which is currently STEP_ASSIGN_ADDRESSES.
+ */
+ populate_memctl_options(c);
+ /*
+ * For RDIMMs, JEDEC spec requires clocks to be stable
+ * before reset signal is deasserted. For the boards
+ * using fixed parameters, this function should be
+ * be called from board init file.
+ */
+ if (c->common_timing_params.all_dimms_registered)
+ assert_reset = 1;
+ }
+
+ /* STEP 5: Assign addresses to chip selects */
+ check_interleaving_options(pinfo);
+ total_mem = step_assign_addresses(pinfo);
+ debug("Total mem %llu assigned\n", total_mem);
+
+ /* STEP 6: compute controller register values */
+ debug("FSL Memory ctrl register computation\n");
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ if (c->common_timing_params.ndimms_present == 0) {
+ memset(&c->fsl_ddr_config_reg, 0,
+ sizeof(fsl_ddr_cfg_regs_t));
+ continue;
+ }
+
+ compute_fsl_memctl_config_regs(c);
+ }
+
+ /*
+ * Compute the amount of memory available just by
+ * looking for the highest valid CSn_BNDS value.
+ * This allows us to also experiment with using
+ * only CS0 when using dual-rank DIMMs.
+ */
+
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ for (j = 0; j < c->chip_selects_per_ctrl; j++) {
+ fsl_ddr_cfg_regs_t *reg = &c->fsl_ddr_config_reg;
+ if (reg->cs[j].config & 0x80000000) {
+ unsigned int end;
+ /*
+ * 0xfffffff is a special value we put
+ * for unused bnds
+ */
+ if (reg->cs[j].bnds == 0xffffffff)
+ continue;
+ end = reg->cs[j].bnds & 0xffff;
+ if (end > max_end) {
+ max_end = end;
+ }
+ }
+ }
+ }
+
+ total_mem = 1 + (((unsigned long long)max_end << 24ULL) |
+ 0xFFFFFFULL) - pinfo->mem_base;
+
+ return total_mem;
+}
+
+phys_size_t fsl_ddr_sdram(struct fsl_ddr_info *pinfo)
+{
+ unsigned int i;
+ unsigned long long total_memory;
+ int deassert_reset = 0;
+
+ total_memory = fsl_ddr_compute(pinfo);
+
+ /* setup 3-way interleaving before enabling DDRC */
+ switch (pinfo->c[0].memctl_opts.memctl_interleaving_mode) {
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ fsl_ddr_set_intl3r(
+ pinfo->c[0].memctl_opts.
+ memctl_interleaving_mode);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Program configuration registers.
+ * JEDEC specs requires clocks to be stable before deasserting reset
+ * for RDIMMs. Clocks start after chip select is enabled and clock
+ * control register is set. During step 1, all controllers have their
+ * registers set but not enabled. Step 2 proceeds after deasserting
+ * reset through board FPGA or GPIO.
+ * For non-registered DIMMs, initialization can go through but it is
+ * also OK to follow the same flow.
+ */
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ if (c->common_timing_params.all_dimms_registered)
+ deassert_reset = 1;
+ }
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ debug("Programming controller %u\n", i);
+ if (c->common_timing_params.ndimms_present == 0) {
+ debug("No dimms present on controller %u; "
+ "skipping programming\n", i);
+ continue;
+ }
+ /*
+ * The following call with step = 1 returns before enabling
+ * the controller. It has to finish with step = 2 later.
+ */
+ fsl_ddr_set_memctl_regs(c, deassert_reset ? 1 : 0);
+ }
+ if (deassert_reset) {
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ struct fsl_ddr_controller *c = &pinfo->c[i];
+
+ /* Call with step = 2 to continue initialization */
+ fsl_ddr_set_memctl_regs(c, 2);
+ }
+ }
+
+ debug("total_memory by %s = %llu\n", __func__, total_memory);
+
+ return total_memory;
+}
diff --git a/drivers/ddr/fsl/options.c b/drivers/ddr/fsl/options.c
new file mode 100644
index 0000000..73e9ab0
--- /dev/null
+++ b/drivers/ddr/fsl/options.c
@@ -0,0 +1,1133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008, 2010-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2018 NXP Semiconductor
+ */
+
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include "fsl_ddr.h"
+
+struct dynamic_odt {
+ unsigned int odt_rd_cfg;
+ unsigned int odt_wr_cfg;
+ unsigned int odt_rtt_norm;
+ unsigned int odt_rtt_wr;
+};
+
+/* Quad rank is not verified yet due availability.
+ * Replacing 20 OHM with 34 OHM since DDR4 doesn't have 20 OHM option
+ */
+static const struct dynamic_odt single_Q_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS_AND_OTHER_DIMM,
+ DDR4_RTT_34_OHM, /* unverified */
+ DDR4_RTT_120_OHM
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR4_RTT_OFF,
+ DDR4_RTT_120_OHM
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS_AND_OTHER_DIMM,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_120_OHM
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER, /* tied high */
+ DDR4_RTT_OFF,
+ DDR4_RTT_120_OHM
+ }
+};
+
+static const struct dynamic_odt single_D_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR4_RTT_40_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR4_RTT_OFF,
+ DDR4_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_S_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR4_RTT_40_OHM,
+ DDR4_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+};
+
+static const struct dynamic_odt dual_DD_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_DS_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_120_OHM
+ },
+ {0, 0, 0, 0}
+};
+static const struct dynamic_odt dual_SD_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_120_OHM
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_SS_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_120_OHM
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR4_RTT_34_OHM,
+ DDR4_RTT_120_OHM
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_D0_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR4_RTT_40_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR4_RTT_OFF,
+ DDR4_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_0D_ddr4[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR4_RTT_40_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR4_RTT_OFF,
+ DDR4_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_S0_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR4_RTT_40_OHM,
+ DDR4_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt dual_0S_ddr4[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR4_RTT_40_OHM,
+ DDR4_RTT_OFF
+ },
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt odt_unknown_ddr4[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR4_RTT_120_OHM,
+ DDR4_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt single_Q_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS_AND_OTHER_DIMM,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER, /* tied high */
+ DDR3_RTT_OFF,
+ DDR3_RTT_120_OHM
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS_AND_OTHER_DIMM,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER, /* tied high */
+ DDR3_RTT_OFF,
+ DDR3_RTT_120_OHM
+ }
+};
+
+static const struct dynamic_odt single_D_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR3_RTT_OFF,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_S_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+};
+
+static const struct dynamic_odt dual_DD_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_DS_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0}
+};
+static const struct dynamic_odt dual_SD_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR3_RTT_20_OHM,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_SS_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_ALL,
+ DDR3_RTT_30_OHM,
+ DDR3_RTT_120_OHM
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_D0_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR3_RTT_OFF,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_0D_ddr3[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_SAME_DIMM,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR3_RTT_OFF,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_S0_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt dual_0S_ddr3[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_40_OHM,
+ DDR3_RTT_OFF
+ },
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt odt_unknown_ddr3[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR3_RTT_120_OHM,
+ DDR3_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt single_Q_ddr12[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_D_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt single_S_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+};
+
+static const struct dynamic_odt dual_DD_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_DS_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_SD_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_SS_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_OTHER_DIMM,
+ FSL_DDR_ODT_OTHER_DIMM,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_D0_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static const struct dynamic_odt dual_0D_ddr12[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_ALL,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+static const struct dynamic_odt dual_S0_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt dual_0S_ddr12[4] = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_150_OHM,
+ DDR2_RTT_OFF
+ },
+ {0, 0, 0, 0}
+
+};
+
+static const struct dynamic_odt odt_unknown_ddr12[4] = {
+ { /* cs0 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs1 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ },
+ { /* cs2 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_CS,
+ DDR2_RTT_75_OHM,
+ DDR2_RTT_OFF
+ },
+ { /* cs3 */
+ FSL_DDR_ODT_NEVER,
+ FSL_DDR_ODT_NEVER,
+ DDR2_RTT_OFF,
+ DDR2_RTT_OFF
+ }
+};
+
+/*
+ * Automatically seleect bank interleaving mode based on DIMMs
+ * in this order: cs0_cs1_cs2_cs3, cs0_cs1, null.
+ * This function only deal with one or two slots per controller.
+ */
+static inline unsigned int auto_bank_intlv(struct fsl_ddr_controller *c,
+ struct dimm_params *pdimm)
+{
+ if (c->dimm_slots_per_ctrl == 1) {
+ if (pdimm[0].n_ranks == 4)
+ return FSL_DDR_CS0_CS1_CS2_CS3;
+ else if (pdimm[0].n_ranks == 2)
+ return FSL_DDR_CS0_CS1;
+ }
+
+ if (c->dimm_slots_per_ctrl == 2) {
+ if (pdimm[0].n_ranks == 2) {
+ if (pdimm[1].n_ranks == 2)
+ return FSL_DDR_CS0_CS1_CS2_CS3;
+ else
+ return FSL_DDR_CS0_CS1;
+ }
+ }
+
+ return 0;
+}
+
+unsigned int populate_memctl_options(struct fsl_ddr_controller *c)
+{
+ const struct common_timing_params *common_dimm = &c->common_timing_params;
+ memctl_options_t *popts = &c->memctl_opts;
+ struct dimm_params *pdimm = c->dimm_params;
+ unsigned int i;
+ const struct dynamic_odt *pdodt;
+ const struct dynamic_odt *single_Q, *single_S, *single_D;
+ const struct dynamic_odt *dual_DD, *dual_DS, *dual_0S;
+ const struct dynamic_odt *dual_D0, *dual_SD, *dual_SS, *dual_S0, *dual_0D;
+
+ if (is_ddr1(popts) || is_ddr2(popts)) {
+ pdodt = odt_unknown_ddr12;
+ single_Q = single_Q_ddr12;
+ single_D = single_D_ddr12;
+ single_S = single_S_ddr12;
+ dual_DD = dual_DD_ddr12;
+ dual_DS = dual_DS_ddr12;
+ dual_0S = dual_0S_ddr12;
+ dual_D0 = dual_D0_ddr12;
+ dual_SD = dual_SD_ddr12;
+ dual_SS = dual_SS_ddr12;
+ dual_S0 = dual_S0_ddr12;
+ dual_0D = dual_0D_ddr12;
+ } else if (is_ddr3(popts)) {
+ pdodt = odt_unknown_ddr3;
+ single_Q = single_Q_ddr3;
+ single_D = single_D_ddr3;
+ single_S = single_S_ddr3;
+ dual_DD = dual_DD_ddr3;
+ dual_DS = dual_DS_ddr3;
+ dual_0S = dual_0S_ddr3;
+ dual_D0 = dual_D0_ddr3;
+ dual_SD = dual_SD_ddr3;
+ dual_SS = dual_SS_ddr3;
+ dual_S0 = dual_S0_ddr3;
+ dual_0D = dual_0D_ddr3;
+ } else if (is_ddr4(popts)) {
+ pdodt = odt_unknown_ddr4;
+ single_Q = single_Q_ddr4;
+ single_D = single_D_ddr4;
+ single_S = single_S_ddr4;
+ dual_DD = dual_DD_ddr4;
+ dual_DS = dual_DS_ddr4;
+ dual_0S = dual_0S_ddr4;
+ dual_D0 = dual_D0_ddr4;
+ dual_SD = dual_SD_ddr4;
+ dual_SS = dual_SS_ddr4;
+ dual_S0 = dual_S0_ddr4;
+ dual_0D = dual_0D_ddr4;
+ } else {
+ return -EINVAL;
+ }
+
+ if (!is_ddr1(popts)) {
+ /* Chip select options. */
+ if (c->dimm_slots_per_ctrl == 1) {
+ switch (pdimm[0].n_ranks) {
+ case 1:
+ pdodt = single_S;
+ break;
+ case 2:
+ pdodt = single_D;
+ break;
+ case 4:
+ pdodt = single_Q;
+ break;
+ }
+ } else if (c->dimm_slots_per_ctrl == 2) {
+ switch (pdimm[0].n_ranks) {
+ case 4:
+ pdodt = single_Q;
+ if (pdimm[1].n_ranks)
+ printf("Error: Quad- and Dual-rank DIMMs cannot be used together\n");
+ break;
+ case 2:
+ switch (pdimm[1].n_ranks) {
+ case 2:
+ pdodt = dual_DD;
+ break;
+ case 1:
+ pdodt = dual_DS;
+ break;
+ case 0:
+ pdodt = dual_D0;
+ break;
+ }
+ break;
+ case 1:
+ switch (pdimm[1].n_ranks) {
+ case 2:
+ pdodt = dual_SD;
+ break;
+ case 1:
+ pdodt = dual_SS;
+ break;
+ case 0:
+ pdodt = dual_S0;
+ break;
+ }
+ break;
+ case 0:
+ switch (pdimm[1].n_ranks) {
+ case 2:
+ pdodt = dual_0D;
+ break;
+ case 1:
+ pdodt = dual_0S;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Pick chip-select local options. */
+ for (i = 0; i < c->chip_selects_per_ctrl; i++) {
+ if (is_ddr1(popts)) {
+ popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER;
+ popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_CS;
+ } else {
+ popts->cs_local_opts[i].odt_rd_cfg = pdodt[i].odt_rd_cfg;
+ popts->cs_local_opts[i].odt_wr_cfg = pdodt[i].odt_wr_cfg;
+ popts->cs_local_opts[i].odt_rtt_norm = pdodt[i].odt_rtt_norm;
+ popts->cs_local_opts[i].odt_rtt_wr = pdodt[i].odt_rtt_wr;
+ }
+ popts->cs_local_opts[i].auto_precharge = 0;
+ }
+
+ /* Pick interleaving mode. */
+
+ /*
+ * 0 = no interleaving
+ * 1 = interleaving between 2 controllers
+ */
+ popts->memctl_interleaving = 0;
+
+ /*
+ * 0 = cacheline
+ * 1 = page
+ * 2 = (logical) bank
+ * 3 = superbank (only if CS interleaving is enabled)
+ */
+ popts->memctl_interleaving_mode = 0;
+
+ /*
+ * 0: cacheline: bit 30 of the 36-bit physical addr selects the memctl
+ * 1: page: bit to the left of the column bits selects the memctl
+ * 2: bank: bit to the left of the bank bits selects the memctl
+ * 3: superbank: bit to the left of the chip select selects the memctl
+ *
+ * NOTE: ba_intlv (rank interleaving) is independent of memory
+ * controller interleaving; it is only within a memory controller.
+ * Must use superbank interleaving if rank interleaving is used and
+ * memory controller interleaving is enabled.
+ */
+
+ /*
+ * 0 = no
+ * 0x40 = CS0,CS1
+ * 0x20 = CS2,CS3
+ * 0x60 = CS0,CS1 + CS2,CS3
+ * 0x04 = CS0,CS1,CS2,CS3
+ */
+ popts->ba_intlv_ctl = 0;
+
+ /* Memory Organization Parameters */
+ popts->registered_dimm_en = common_dimm->all_dimms_registered;
+
+ /*
+ * Choose DQS config
+ * 0 for DDR1
+ * 1 for DDR2
+ */
+ if (is_ddr2(popts) || is_ddr3(popts))
+ popts->dqs_config = 1;
+
+ /* Choose self-refresh during sleep. */
+ popts->self_refresh_in_sleep = 1;
+
+ /* Choose dynamic power management mode. */
+ popts->dynamic_power = 0;
+
+ popts->x4_en = (pdimm[0].device_width == 4) ? 1 : 0;
+
+ /* Choose ddr controller address mirror mode */
+ if (is_ddr3_4(popts)) {
+ if (pdimm[0].n_ranks != 0) {
+ if (pdimm[0].primary_sdram_width == 64)
+ popts->data_bus_width = 0;
+ else if (pdimm[0].primary_sdram_width == 32)
+ popts->data_bus_width = 1;
+ else if (pdimm[0].primary_sdram_width == 16)
+ popts->data_bus_width = 2;
+ else {
+ panic("Error: primary sdram width %u is invalid!\n",
+ pdimm[0].primary_sdram_width);
+ }
+ }
+
+ if ((popts->data_bus_width == 1) || (popts->data_bus_width == 2)) {
+ /* 32-bit or 16-bit bus */
+ popts->otf_burst_chop_en = 0;
+ popts->burst_length = DDR_BL8;
+ } else {
+ popts->otf_burst_chop_en = 1; /* on-the-fly burst chop */
+ popts->burst_length = DDR_OTF; /* on-the-fly BC4 and BL8 */
+ }
+
+ for (i = 0; i < c->dimm_slots_per_ctrl; i++) {
+ if (pdimm[i].n_ranks) {
+ popts->mirrored_dimm = pdimm[i].mirrored_dimm;
+ break;
+ }
+ }
+ } else {
+ if (pdimm[0].n_ranks != 0) {
+ if ((pdimm[0].data_width >= 64) && \
+ (pdimm[0].data_width <= 72))
+ popts->data_bus_width = 0;
+ else if ((pdimm[0].data_width >= 32) && \
+ (pdimm[0].data_width <= 40))
+ popts->data_bus_width = 1;
+ else {
+ panic("Error: data width %u is invalid!\n",
+ pdimm[0].data_width);
+ }
+ }
+
+ popts->burst_length = DDR_BL4; /* has to be 4 for DDR2 */
+ }
+
+
+ /* Global Timing Parameters. */
+ debug("mclk_ps = %u ps\n", get_memory_clk_period_ps(c));
+
+ /* Pick a caslat override. */
+ popts->cas_latency_override = 0;
+ popts->cas_latency_override_value = 3;
+ if (popts->cas_latency_override) {
+ debug("using caslat override value = %u\n",
+ popts->cas_latency_override_value);
+ }
+
+ /* Decide whether to use the computed derated latency */
+ popts->use_derated_caslat = 0;
+
+ /* Choose an additive latency. */
+ popts->additive_latency_override = 0;
+ popts->additive_latency_override_value = 3;
+ if (popts->additive_latency_override) {
+ debug("using additive latency override value = %u\n",
+ popts->additive_latency_override_value);
+ }
+
+ /*
+ * 2T_EN setting
+ *
+ * Factors to consider for 2T_EN:
+ * - number of DIMMs installed
+ * - number of components, number of active ranks
+ * - how much time you want to spend playing around
+ */
+ popts->twot_en = 0;
+ popts->threet_en = 0;
+
+ /* for RDIMM and DDR4 UDIMM/discrete memory, address parity enable */
+ if (popts->registered_dimm_en)
+ popts->ap_en = 1; /* 0 = disable, 1 = enable */
+ else
+ popts->ap_en = 0; /* disabled for DDR4 UDIMM/discrete default */
+
+ /*
+ * BSTTOPRE precharge interval
+ *
+ * Set this to 0 for global auto precharge
+ * The value of 0x100 has been used for DDR1, DDR2, DDR3.
+ * It is not wrong. Any value should be OK. The performance depends on
+ * applications. There is no one good value for all. One way to set
+ * is to use 1/4 of refint value.
+ */
+ popts->bstopre = picos_to_mclk(c, common_dimm->refresh_rate_ps)
+ >> 2;
+
+ /*
+ * Window for four activates -- tFAW
+ *
+ * FIXME: UM: applies only to DDR2/DDR3 with eight logical banks only
+ * FIXME: varies depending upon number of column addresses or data
+ * FIXME: width, was considering looking at pdimm->primary_sdram_width
+ */
+ if (is_ddr1(popts))
+ popts->tfaw_window_four_activates_ps = mclk_to_picos(c, 1);
+ else if (is_ddr2(popts))
+ /*
+ * x4/x8; some datasheets have 35000
+ * x16 wide columns only? Use 50000?
+ */
+ popts->tfaw_window_four_activates_ps = 37500;
+ else
+ popts->tfaw_window_four_activates_ps = pdimm[0].tfaw_ps;
+
+ popts->zq_en = 0;
+ popts->wrlvl_en = 0;
+
+ if (is_ddr3_4(popts)) {
+ /*
+ * due to ddr3 dimm is fly-by topology
+ * we suggest to enable write leveling to
+ * meet the tQDSS under different loading.
+ */
+ popts->wrlvl_en = 1;
+ popts->zq_en = 1;
+ popts->wrlvl_override = 0;
+ }
+
+ if (pdimm[0].n_ranks == 4)
+ popts->quad_rank_present = 1;
+
+ popts->package_3ds = pdimm->package_3ds;
+
+ if (!is_ddr4(popts)) {
+ ulong ddr_freq = c->ddr_freq / 1000000;
+ if (popts->registered_dimm_en) {
+ popts->rcw_override = 1;
+ popts->rcw_1 = 0x000a5a00;
+ if (ddr_freq <= 800)
+ popts->rcw_2 = 0x00000000;
+ else if (ddr_freq <= 1066)
+ popts->rcw_2 = 0x00100000;
+ else if (ddr_freq <= 1333)
+ popts->rcw_2 = 0x00200000;
+ else
+ popts->rcw_2 = 0x00300000;
+ }
+ }
+
+ if (c->board_options)
+ c->board_options(popts, pdimm, c);
+
+ return 0;
+}
+
+void check_interleaving_options(struct fsl_ddr_info *pinfo)
+{
+ int i, j, k, check_n_ranks, intlv_invalid = 0;
+ unsigned int check_intlv, check_n_row_addr, check_n_col_addr;
+ unsigned long long check_rank_density;
+ struct dimm_params *dimm;
+
+ /*
+ * Check if all controllers are configured for memory
+ * controller interleaving. Identical dimms are recommended. At least
+ * the size, row and col address should be checked.
+ */
+ j = 0;
+ check_n_ranks = pinfo->c[0].dimm_params[0].n_ranks;
+ check_rank_density = pinfo->c[0].dimm_params[0].rank_density;
+ check_n_row_addr = pinfo->c[0].dimm_params[0].n_row_addr;
+ check_n_col_addr = pinfo->c[0].dimm_params[0].n_col_addr;
+ check_intlv = pinfo->c[0].memctl_opts.memctl_interleaving_mode;
+ for (i = 0; i < pinfo->num_ctrls; i++) {
+ dimm = &pinfo->c[i].dimm_params[0];
+ if (!pinfo->c[i].memctl_opts.memctl_interleaving) {
+ continue;
+ } else if (((check_rank_density != dimm->rank_density) ||
+ (check_n_ranks != dimm->n_ranks) ||
+ (check_n_row_addr != dimm->n_row_addr) ||
+ (check_n_col_addr != dimm->n_col_addr) ||
+ (check_intlv !=
+ pinfo->c[i].memctl_opts.memctl_interleaving_mode))){
+ intlv_invalid = 1;
+ break;
+ } else {
+ j++;
+ }
+
+ }
+ if (intlv_invalid) {
+ for (i = 0; i < pinfo->num_ctrls; i++)
+ pinfo->c[i].memctl_opts.memctl_interleaving = 0;
+ printf("Not all DIMMs are identical. "
+ "Memory controller interleaving disabled.\n");
+ } else {
+ switch (check_intlv) {
+ case FSL_DDR_256B_INTERLEAVING:
+ case FSL_DDR_CACHE_LINE_INTERLEAVING:
+ case FSL_DDR_PAGE_INTERLEAVING:
+ case FSL_DDR_BANK_INTERLEAVING:
+ case FSL_DDR_SUPERBANK_INTERLEAVING:
+ if (pinfo->num_ctrls == 3)
+ k = 2;
+ else
+ k = pinfo->num_ctrls;
+ break;
+ case FSL_DDR_3WAY_1KB_INTERLEAVING:
+ case FSL_DDR_3WAY_4KB_INTERLEAVING:
+ case FSL_DDR_3WAY_8KB_INTERLEAVING:
+ case FSL_DDR_4WAY_1KB_INTERLEAVING:
+ case FSL_DDR_4WAY_4KB_INTERLEAVING:
+ case FSL_DDR_4WAY_8KB_INTERLEAVING:
+ default:
+ k = pinfo->num_ctrls;
+ break;
+ }
+ debug("%d of %d controllers are interleaving.\n", j, k);
+ if (j && (j != k)) {
+ for (i = 0; i < pinfo->num_ctrls; i++)
+ pinfo->c[i].memctl_opts.memctl_interleaving = 0;
+ if (pinfo->num_ctrls > 1)
+ printf("Not all controllers have compatible interleaving mode. All disabled.\n");
+ }
+ }
+ debug("Checking interleaving options completed\n");
+}
diff --git a/drivers/ddr/fsl/util.c b/drivers/ddr/fsl/util.c
new file mode 100644
index 0000000..977d22d
--- /dev/null
+++ b/drivers/ddr/fsl/util.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008-2014 Freescale Semiconductor, Inc.
+ */
+
+#include <common.h>
+#include <soc/fsl/fsl_ddr_sdram.h>
+#include <soc/fsl/fsl_immap.h>
+#include <io.h>
+#include <soc/fsl/immap_lsch2.h>
+#include <asm-generic/div64.h>
+#include "fsl_ddr.h"
+
+/* To avoid 64-bit full-divides, we factor this here */
+#define ULL_2E12 2000000000000ULL
+#define UL_5POW12 244140625UL
+#define UL_2POW13 (1UL << 13)
+
+#define ULL_8FS 0xFFFFFFFFULL
+
+u32 fsl_ddr_get_version(struct fsl_ddr_controller *c)
+{
+ struct ccsr_ddr __iomem *ddr = c->base;
+ u32 ver_major_minor_errata;
+
+ ver_major_minor_errata = (ddr_in32(&ddr->ip_rev1) & 0xFFFF) << 8;
+ ver_major_minor_errata |= (ddr_in32(&ddr->ip_rev2) & 0xFF00) >> 8;
+
+ return ver_major_minor_errata;
+}
+
+/*
+ * Round up mclk_ps to nearest 1 ps in memory controller code
+ * if the error is 0.5ps or more.
+ *
+ * If an imprecise data rate is too high due to rounding error
+ * propagation, compute a suitably rounded mclk_ps to compute
+ * a working memory controller configuration.
+ */
+unsigned int get_memory_clk_period_ps(struct fsl_ddr_controller *c)
+{
+ unsigned int data_rate = c->ddr_freq;
+ unsigned int result;
+
+ /* Round to nearest 10ps, being careful about 64-bit multiply/divide */
+ unsigned long long rem, mclk_ps = ULL_2E12;
+
+ /* Now perform the big divide, the result fits in 32-bits */
+ rem = do_div(mclk_ps, data_rate);
+ result = (rem >= (data_rate >> 1)) ? mclk_ps + 1 : mclk_ps;
+
+ return result;
+}
+
+/* Convert picoseconds into DRAM clock cycles (rounding up if needed). */
+unsigned int picos_to_mclk(struct fsl_ddr_controller *c, unsigned int picos)
+{
+ unsigned long long clks, clks_rem;
+ unsigned int data_rate = c->ddr_freq;
+
+ /* Short circuit for zero picos */
+ if (!picos)
+ return 0;
+
+ /* First multiply the time by the data rate (32x32 => 64) */
+ clks = picos * (unsigned long long)data_rate;
+ /*
+ * Now divide by 5^12 and track the 32-bit remainder, then divide
+ * by 2*(2^12) using shifts (and updating the remainder).
+ */
+ clks_rem = do_div(clks, UL_5POW12);
+ clks_rem += (clks & (UL_2POW13-1)) * UL_5POW12;
+ clks >>= 13;
+
+ /* If we had a remainder greater than the 1ps error, then round up */
+ if (clks_rem > data_rate)
+ clks++;
+
+ /* Clamp to the maximum representable value */
+ if (clks > ULL_8FS)
+ clks = ULL_8FS;
+ return (unsigned int) clks;
+}
+
+unsigned int mclk_to_picos(struct fsl_ddr_controller *c, unsigned int mclk)
+{
+ return get_memory_clk_period_ps(c) * mclk;
+}
+
+void fsl_ddr_set_intl3r(const unsigned int granule_size)
+{
+}
+
+u32 fsl_ddr_get_intl3r(void)
+{
+ u32 val = 0;
+ return val;
+}
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index fc314ec..0bd8be0 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -3,8 +3,8 @@ menuconfig I2C
if I2C
-source drivers/i2c/algos/Kconfig
-source drivers/i2c/busses/Kconfig
+source "drivers/i2c/algos/Kconfig"
+source "drivers/i2c/busses/Kconfig"
config I2C_MUX
tristate "I2C bus multiplexing support"
@@ -13,7 +13,7 @@ config I2C_MUX
handle multiplexed I2C bus topologies, by presenting each
multiplexed segment as a I2C adapter.
-source drivers/i2c/muxes/Kconfig
+source "drivers/i2c/muxes/Kconfig"
endif
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index a25a871..6d87435 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -18,7 +18,7 @@ config I2C_AT91
config I2C_IMX
bool "MPC85xx/MPC5200/i.MX I2C Master driver"
- depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX || ARCH_MPC5200
+ depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX || ARCH_MPC5200 || ARCH_LAYERSCAPE
help
If you say yes to this option, support will be included for many
built-in I2C master controllers found in Freescale SoCs. This is true
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 4c73460..6911f80 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -47,7 +47,6 @@
#include <io.h>
#include <i2c/i2c.h>
-#include <mach/clock.h>
#include "i2c-imx.h"
@@ -276,6 +275,9 @@ static void i2c_fsl_stop(struct i2c_adapter *adapter)
}
#ifdef CONFIG_PPC
+
+#include <mach/clock.h>
+
static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
unsigned int rate)
{
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index f77530d..f7f8c33 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -15,11 +15,12 @@
#include <io.h>
#include <mci.h>
#include <linux/sizes.h>
+#include <asm-generic/sections.h>
+#include <mach/xload.h>
#ifdef CONFIG_ARCH_IMX
#include <mach/atf.h>
#include <mach/imx6-regs.h>
#include <mach/imx8mq-regs.h>
-#include <mach/xload.h>
#include <mach/imx-header.h>
#endif
#include "sdhci.h"
@@ -366,6 +367,7 @@ int imx6_esdhc_start_image(int instance)
return -EINVAL;
}
+ esdhc.is_be = 0;
esdhc.is_mx6 = 1;
return esdhc_start_image(&esdhc, 0x10000000, 0x10000000, 0);
@@ -398,9 +400,67 @@ int imx8_esdhc_start_image(int instance)
return -EINVAL;
}
+ esdhc.is_be = 0;
esdhc.is_mx6 = 1;
return esdhc_start_image(&esdhc, MX8MQ_DDR_CSD1_BASE_ADDR,
MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K);
}
#endif
+
+#ifdef CONFIG_ARCH_LS1046
+
+/*
+ * The image on the SD card starts at 0x1000. We reserved 128KiB for the PBL,
+ * so the 2nd stage image starts here:
+ */
+#define LS1046A_SD_IMAGE_OFFSET (SZ_4K + SZ_128K)
+
+/**
+ * ls1046a_esdhc_start_image - Load and start a 2nd stage from the ESDHC controller
+ *
+ * This loads and starts a 2nd stage barebox from an SD card and starts it. We
+ * assume the image has been generated with scripts/pblimage.c which puts the
+ * second stage to an offset of 128KiB in the image.
+ *
+ * Return: If successful, this function does not return. A negative error
+ * code is returned when this function fails.
+ */
+int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long r2)
+{
+ int ret;
+ uint32_t val;
+ struct esdhc esdhc = {
+ .regs = IOMEM(0x01560000),
+ .is_be = true,
+ };
+ unsigned long sdram = 0x80000000;
+ void (*barebox)(unsigned long, unsigned long, unsigned long) =
+ (void *)(sdram + LS1046A_SD_IMAGE_OFFSET);
+
+ /*
+ * The ROM leaves us here with a clock frequency of around 400kHz. Speed
+ * this up a bit. FIXME: The resulting frequency has not yet been verified
+ * to work on all cards.
+ */
+ val = esdhc_read32(&esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
+ val &= ~0x0000fff0;
+ val |= (2 << 8) | (6 << 4);
+ esdhc_write32(&esdhc, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+
+ esdhc_write32(&esdhc, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
+
+ ret = esdhc_read_blocks(&esdhc, (void *)sdram,
+ ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512));
+ if (ret) {
+ pr_err("%s: reading blocks failed with: %d\n", __func__, ret);
+ return ret;
+ }
+
+ printf("Starting barebox\n");
+
+ barebox(r0, r1, r2);
+
+ return -EINVAL;
+}
+#endif
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index cedfb3d..f71ca53 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -67,10 +67,6 @@
#define ESDHC_FLAG_STD_TUNING BIT(5)
/* The IP has SDHCI_CAPABILITIES_1 register */
#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
-/* Need to access registers in bigendian mode */
-#define ESDHC_FLAG_BIGENDIAN BIT(7)
-/* Enable cache snooping */
-#define ESDHC_FLAG_CACHE_SNOOPING BIT(8)
/*
* The IP has errata ERR004536
@@ -83,6 +79,11 @@
/* The IP supports HS400 mode */
#define ESDHC_FLAG_HS400 BIT(9)
+/* Need to access registers in bigendian mode */
+#define ESDHC_FLAG_BIGENDIAN BIT(10)
+/* Enable cache snooping */
+#define ESDHC_FLAG_CACHE_SNOOPING BIT(11)
+
struct esdhc_soc_data {
u32 flags;
const char *clkidx;
@@ -740,7 +741,8 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
};
static struct esdhc_soc_data esdhc_ls_data = {
- .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN,
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN |
+ ESDHC_FLAG_CACHE_SNOOPING,
};
static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = {
diff --git a/drivers/mfd/lp3972.c b/drivers/mfd/lp3972.c
index 3ae9d1a..ca73671 100644
--- a/drivers/mfd/lp3972.c
+++ b/drivers/mfd/lp3972.c
@@ -20,6 +20,7 @@
#include <driver.h>
#include <xfuncs.h>
#include <errno.h>
+#include <mfd/lp3972.h>
#include <i2c/i2c.h>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 1aa096f..3e3de5a 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -137,6 +137,14 @@ config DRIVER_NET_FEC_IMX
depends on ARCH_HAS_FEC_IMX
select PHYLIB
+config DRIVER_NET_FSL_FMAN
+ bool "Freescale fman ethernet driver"
+ select PHYLIB
+ select FSL_QE_FIRMWARE
+ help
+ This option enabled support for the Freescale fman core found
+ on Layerscape SoCs.
+
config DRIVER_NET_GIANFAR
bool "Gianfar Ethernet"
depends on ARCH_MPC85XX
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 304bbba..6ccd22c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o
obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o
obj-$(CONFIG_DRIVER_NET_ETHOC) += ethoc.o
obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o
+obj-$(CONFIG_DRIVER_NET_FSL_FMAN) += fsl-fman.o
obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o
obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o
obj-$(CONFIG_DRIVER_NET_MACB) += macb.o
diff --git a/drivers/net/fsl-fman.c b/drivers/net/fsl-fman.c
new file mode 100644
index 0000000..1a11ca4
--- /dev/null
+++ b/drivers/net/fsl-fman.c
@@ -0,0 +1,1333 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2009-2012 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ */
+
+#include <common.h>
+#include <io.h>
+#include <malloc.h>
+#include <net.h>
+#include <init.h>
+#include <of_net.h>
+#include <dma.h>
+#include <firmware.h>
+#include <soc/fsl/qe.h>
+#include <soc/fsl/fsl_fman.h>
+#include <soc/fsl/fsl_memac.h>
+
+/* Port ID */
+#define OH_PORT_ID_BASE 0x01
+#define MAX_NUM_OH_PORT 7
+#define RX_PORT_1G_BASE 0x08
+#define MAX_NUM_RX_PORT_1G 8
+#define RX_PORT_10G_BASE 0x10
+#define RX_PORT_10G_BASE2 0x08
+#define TX_PORT_1G_BASE 0x28
+#define MAX_NUM_TX_PORT_1G 8
+#define TX_PORT_10G_BASE 0x30
+#define TX_PORT_10G_BASE2 0x28
+#define MIIM_TIMEOUT 0xFFFF
+
+struct fm_muram {
+ void *base;
+ void *top;
+ size_t size;
+ void *alloc;
+};
+#define FM_MURAM_RES_SIZE 0x01000
+
+/* Rx/Tx buffer descriptor */
+struct fm_port_bd {
+ u16 status;
+ u16 len;
+ u32 res0;
+ u16 res1;
+ u16 buf_ptr_hi;
+ u32 buf_ptr_lo;
+};
+
+/* Common BD flags */
+#define BD_LAST 0x0800
+
+/* Rx BD status flags */
+#define RxBD_EMPTY 0x8000
+#define RxBD_LAST BD_LAST
+#define RxBD_FIRST 0x0400
+#define RxBD_PHYS_ERR 0x0008
+#define RxBD_SIZE_ERR 0x0004
+#define RxBD_ERROR (RxBD_PHYS_ERR | RxBD_SIZE_ERR)
+
+/* Tx BD status flags */
+#define TxBD_READY 0x8000
+#define TxBD_LAST BD_LAST
+
+/* Rx/Tx queue descriptor */
+struct fm_port_qd {
+ u16 gen;
+ u16 bd_ring_base_hi;
+ u32 bd_ring_base_lo;
+ u16 bd_ring_size;
+ u16 offset_in;
+ u16 offset_out;
+ u16 res0;
+ u32 res1[0x4];
+};
+
+/* IM global parameter RAM */
+struct fm_port_global_pram {
+ u32 mode; /* independent mode register */
+ u32 rxqd_ptr; /* Rx queue descriptor pointer */
+ u32 txqd_ptr; /* Tx queue descriptor pointer */
+ u16 mrblr; /* max Rx buffer length */
+ u16 rxqd_bsy_cnt; /* RxQD busy counter, should be cleared */
+ u32 res0[0x4];
+ struct fm_port_qd rxqd; /* Rx queue descriptor */
+ struct fm_port_qd txqd; /* Tx queue descriptor */
+ u32 res1[0x28];
+};
+
+#define FM_PRAM_SIZE sizeof(struct fm_port_global_pram)
+#define FM_PRAM_ALIGN 256
+#define PRAM_MODE_GLOBAL 0x20000000
+#define PRAM_MODE_GRACEFUL_STOP 0x00800000
+
+#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */
+#define FM_FREE_POOL_ALIGN 256
+
+/* Fman ethernet private struct */
+struct fm_eth {
+ struct fm_bmi_tx_port *tx_port;
+ struct fm_bmi_rx_port *rx_port;
+ phy_interface_t enet_if;
+ struct eth_device edev;
+ struct device_d *dev;
+ struct fm_port_global_pram *rx_pram; /* Rx parameter table */
+ struct fm_port_global_pram *tx_pram; /* Tx parameter table */
+ void *rx_bd_ring; /* Rx BD ring base */
+ void *cur_rxbd; /* current Rx BD */
+ void *rx_buf; /* Rx buffer base */
+ void *tx_bd_ring; /* Tx BD ring base */
+ void *cur_txbd; /* current Tx BD */
+ struct memac *regs;
+};
+
+#define RX_BD_RING_SIZE 8
+#define TX_BD_RING_SIZE 8
+#define MAX_RXBUF_LOG2 11
+#define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2)
+
+struct fsl_fman_mdio {
+ struct mii_bus bus;
+ struct memac_mdio_controller *regs;
+};
+
+enum fman_port_type {
+ FMAN_PORT_TYPE_RX = 0, /* RX Port */
+ FMAN_PORT_TYPE_TX, /* TX Port */
+};
+
+struct fsl_fman_port {
+ void *regs;
+ enum fman_port_type type;
+ struct fm_bmi_rx_port *rxport;
+ struct fm_bmi_tx_port *txport;
+};
+
+static struct fm_eth *to_fm_eth(struct eth_device *edev)
+{
+ return container_of(edev, struct fm_eth, edev);
+}
+
+static struct fm_muram muram;
+
+static void *fm_muram_base(void)
+{
+ return muram.base;
+}
+
+static void *fm_muram_alloc(size_t size, unsigned long align)
+{
+ void *ret;
+ unsigned long align_mask;
+ size_t off;
+ void *save;
+
+ align_mask = align - 1;
+ save = muram.alloc;
+
+ off = (unsigned long)save & align_mask;
+ if (off != 0)
+ muram.alloc += (align - off);
+ off = size & align_mask;
+ if (off != 0)
+ size += (align - off);
+ if ((muram.alloc + size) >= muram.top) {
+ muram.alloc = save;
+ printf("%s: run out of ram.\n", __func__);
+ return NULL;
+ }
+
+ ret = muram.alloc;
+ muram.alloc += size;
+
+ return ret;
+}
+
+/*
+ * fm_upload_ucode - Fman microcode upload worker function
+ *
+ * This function does the actual uploading of an Fman microcode
+ * to an Fman.
+ */
+static int fm_upload_ucode(struct fm_imem *imem,
+ u32 *ucode, unsigned int size)
+{
+ unsigned int i;
+ unsigned int timeout = 1000000;
+
+ /* enable address auto increase */
+ out_be32(&imem->iadd, IRAM_IADD_AIE);
+ /* write microcode to IRAM */
+ for (i = 0; i < size / 4; i++)
+ out_be32(&imem->idata, (be32_to_cpu(ucode[i])));
+
+ /* verify if the writing is over */
+ out_be32(&imem->iadd, 0);
+ while ((in_be32(&imem->idata) != be32_to_cpu(ucode[0])) && --timeout)
+ ;
+ if (!timeout) {
+ printf("microcode upload timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* enable microcode from IRAM */
+ out_be32(&imem->iready, IRAM_READY);
+
+ return 0;
+}
+
+static int fman_upload_firmware(struct device_d *dev, struct fm_imem *fm_imem)
+{
+ int i, size, ret;
+ const struct qe_firmware *firmware;
+
+ get_builtin_firmware(fsl_fman_ucode_ls1046_r1_0_106_4_18_bin, &firmware, &size);
+
+ ret = qe_validate_firmware(firmware, size);
+ if (ret)
+ return ret;
+
+ if (firmware->count != 1) {
+ dev_err(dev, "Invalid data in firmware header\n");
+ return -EINVAL;
+ }
+
+ /* Loop through each microcode. */
+ for (i = 0; i < firmware->count; i++) {
+ const struct qe_microcode *ucode = &firmware->microcode[i];
+
+ /* Upload a microcode if it's present */
+ if (be32_to_cpu(ucode->code_offset)) {
+ u32 ucode_size;
+ u32 *code;
+ dev_info(dev, "Uploading microcode version %u.%u.%u\n",
+ ucode->major, ucode->minor,
+ ucode->revision);
+ code = (void *)firmware +
+ be32_to_cpu(ucode->code_offset);
+ ucode_size = sizeof(u32) * be32_to_cpu(ucode->count);
+ ret = fm_upload_ucode(fm_imem, code, ucode_size);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static u32 fm_assign_risc(int port_id)
+{
+ u32 risc_sel, val;
+ risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1;
+ val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK;
+ val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel);
+
+ return val;
+}
+
+static void fm_init_fpm(struct fm_fpm *fpm)
+{
+ int i, port_id;
+ u32 val;
+
+ setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC |
+ FMFPEE_CER | FMFPEE_DER);
+
+ /* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */
+
+ /* offline/parser port */
+ for (i = 0; i < MAX_NUM_OH_PORT; i++) {
+ port_id = OH_PORT_ID_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Rx 1G port */
+ for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
+ port_id = RX_PORT_1G_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Tx 1G port */
+ for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
+ port_id = TX_PORT_1G_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Rx 10G port */
+ port_id = RX_PORT_10G_BASE;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ /* Tx 10G port */
+ port_id = TX_PORT_10G_BASE;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+
+ /* disable the dispatch limit in IM case */
+ out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE);
+ /* clear events */
+ out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT);
+
+ /* clear risc events */
+ for (i = 0; i < 4; i++)
+ out_be32(&fpm->fpmcev[i], 0xffffffff);
+
+ /* clear error */
+ out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC);
+}
+
+static int fm_init_bmi(struct fm_bmi_common *bmi)
+{
+ int blk, i, port_id;
+ u32 val;
+ size_t offset;
+ void *base;
+
+ /* alloc free buffer pool in MURAM */
+ base = fm_muram_alloc(FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN);
+ if (!base) {
+ printf("%s: no muram for free buffer pool\n", __func__);
+ return -ENOMEM;
+ }
+ offset = base - fm_muram_base();
+
+ /* Need 128KB total free buffer pool size */
+ val = offset / 256;
+ blk = FM_FREE_POOL_SIZE / 256;
+ /* in IM, we must not begin from offset 0 in MURAM */
+ val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT);
+ out_be32(&bmi->fmbm_cfg1, val);
+
+ /* disable all BMI interrupt */
+ out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL);
+
+ /* clear all events */
+ out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL);
+
+ /*
+ * set port parameters - FMBM_PP_x
+ * max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1
+ * max dma 10G Rx/Tx=3, others is 1
+ * set port FIFO size - FMBM_PFS_x
+ * 4KB for all Rx and Tx ports
+ */
+ /* offline/parser port */
+ for (i = 0; i < MAX_NUM_OH_PORT; i++) {
+ port_id = OH_PORT_ID_BASE + i - 1;
+ /* max tasks=1, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], 0);
+ /* port FIFO size - 256 bytes, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], 0);
+ }
+ /* Rx 1G port */
+ for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
+ port_id = RX_PORT_1G_BASE + i - 1;
+ /* max tasks=4, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+ }
+ /* Tx 1G port FIFO size - 4KB, no extra */
+ for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
+ port_id = TX_PORT_1G_BASE + i - 1;
+ /* max tasks=4, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+ }
+ /* Rx 10G port */
+ port_id = RX_PORT_10G_BASE - 1;
+ /* max tasks=12, max dma=3, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+
+ /* Tx 10G port */
+ port_id = TX_PORT_10G_BASE - 1;
+ /* max tasks=12, max dma=3, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+
+ /* initialize internal buffers data base (linked list) */
+ out_be32(&bmi->fmbm_init, FMBM_INIT_START);
+
+ return 0;
+}
+
+static void fm_init_qmi(struct fm_qmi_common *qmi)
+{
+ /* disable all error interrupts */
+ out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL);
+ /* clear all error events */
+ out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL);
+
+ /* disable all interrupts */
+ out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL);
+ /* clear all interrupts */
+ out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL);
+}
+
+static int fm_init_common(struct device_d *dev, struct ccsr_fman *reg)
+{
+ int ret;
+
+ /* Upload the Fman microcode if it's present */
+ ret = fman_upload_firmware(dev, &reg->fm_imem);
+ if (ret)
+ return ret;
+
+ fm_init_qmi(&reg->fm_qmi_common);
+ fm_init_fpm(&reg->fm_fpm);
+
+ /* clear DMA status */
+ setbits_be32(&reg->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL);
+
+ /* set DMA mode */
+ setbits_be32(&reg->fm_dma.fmdmmr, FMDMMR_SBER);
+
+ return fm_init_bmi(&reg->fm_bmi_common);
+}
+
+#define memac_out_32(a, v) out_be32(a, v)
+#define memac_in_32(a) in_be32(a)
+#define memac_clrbits_32(a, v) clrbits_be32(a, v)
+#define memac_setbits_32(a, v) setbits_be32(a, v)
+
+static int memac_mdio_write(struct mii_bus *bus, int port_addr, int regnum, u16 value)
+{
+ struct fsl_fman_mdio *priv = container_of(bus, struct fsl_fman_mdio, bus);
+ struct memac_mdio_controller *regs = priv->regs;
+ u32 mdio_ctl;
+
+ memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Set the port and dev addr */
+ mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(regnum);
+ memac_out_32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Write the value to the register */
+ memac_out_32(&regs->mdio_data, MDIO_DATA(value));
+
+ /* Wait till the MDIO write is complete */
+ while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
+ ;
+
+ return 0;
+}
+
+static int memac_mdio_read(struct mii_bus *bus, int port_addr, int regnum)
+{
+ struct fsl_fman_mdio *priv = container_of(bus, struct fsl_fman_mdio, bus);
+ struct memac_mdio_controller *regs = priv->regs;
+ u32 mdio_ctl;
+
+ memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Set the Port and Device Addrs */
+ mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(regnum);
+ memac_out_32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Initiate the read */
+ mdio_ctl |= MDIO_CTL_READ;
+ memac_out_32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the MDIO write is complete */
+ while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
+ ;
+
+ /* Return all Fs if nothing was there */
+ if (memac_in_32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
+ return 0xffff;
+
+ return memac_in_32(&regs->mdio_data) & 0xffff;
+}
+
+static u16 muram_readw(u16 *addr)
+{
+ unsigned long base = (unsigned long)addr & ~0x3UL;
+ u32 val32 = in_be32((void *)base);
+ int byte_pos;
+ u16 ret;
+
+ byte_pos = (unsigned long)addr & 0x3UL;
+ if (byte_pos)
+ ret = (u16)(val32 & 0x0000ffff);
+ else
+ ret = (u16)((val32 & 0xffff0000) >> 16);
+
+ return ret;
+}
+
+static void muram_writew(u16 *addr, u16 val)
+{
+ unsigned long base = (unsigned long)addr & ~0x3UL;
+ u32 org32 = in_be32((void *)base);
+ u32 val32;
+ int byte_pos;
+
+ byte_pos = (unsigned long)addr & 0x3UL;
+ if (byte_pos)
+ val32 = (org32 & 0xffff0000) | val;
+ else
+ val32 = (org32 & 0x0000ffff) | ((u32)val << 16);
+
+ out_be32((void *)base, val32);
+}
+
+static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port)
+{
+ int timeout = 1000000;
+
+ clrbits_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_EN);
+
+ /* wait until the rx port is not busy */
+ while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--)
+ ;
+}
+
+static void bmi_rx_port_init(struct fm_bmi_rx_port *rx_port)
+{
+ /* set BMI to independent mode, Rx port disable */
+ out_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_IM);
+ /* clear FOF in IM case */
+ out_be32(&rx_port->fmbm_rim, 0);
+ /* Rx frame next engine -RISC */
+ out_be32(&rx_port->fmbm_rfne, NIA_ENG_RISC | NIA_RISC_AC_IM_RX);
+ /* Rx command attribute - no order, MR[3] = 1 */
+ clrbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_ORDER | FMBM_RFCA_MR_MASK);
+ setbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_MR(4));
+ /* enable Rx statistic counters */
+ out_be32(&rx_port->fmbm_rstc, FMBM_RSTC_EN);
+ /* disable Rx performance counters */
+ out_be32(&rx_port->fmbm_rpc, 0);
+}
+
+static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port)
+{
+ int timeout = 1000000;
+
+ clrbits_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_EN);
+
+ /* wait until the tx port is not busy */
+ while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--)
+ ;
+}
+
+static void bmi_tx_port_init(struct fm_bmi_tx_port *tx_port)
+{
+ /* set BMI to independent mode, Tx port disable */
+ out_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_IM);
+ /* Tx frame next engine -RISC */
+ out_be32(&tx_port->fmbm_tfne, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
+ out_be32(&tx_port->fmbm_tfene, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
+ /* Tx command attribute - no order, MR[3] = 1 */
+ clrbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_ORDER | FMBM_TFCA_MR_MASK);
+ setbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_MR(4));
+ /* enable Tx statistic counters */
+ out_be32(&tx_port->fmbm_tstc, FMBM_TSTC_EN);
+ /* disable Tx performance counters */
+ out_be32(&tx_port->fmbm_tpc, 0);
+}
+
+static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+ u32 pram_page_offset;
+ void *rx_bd_ring_base;
+ void *rx_buf_pool;
+ u32 bd_ring_base_lo, bd_ring_base_hi;
+ u32 buf_lo, buf_hi;
+ struct fm_port_bd *rxbd;
+ struct fm_port_qd *rxqd;
+ struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port;
+ int i;
+
+ /* alloc global parameter ram at MURAM */
+ pram = fm_muram_alloc(FM_PRAM_SIZE, FM_PRAM_ALIGN);
+ if (!pram) {
+ printf("%s: No muram for Rx global parameter\n", __func__);
+ return -ENOMEM;
+ }
+
+ fm_eth->rx_pram = pram;
+
+ /* parameter page offset to MURAM */
+ pram_page_offset = (void *)pram - fm_muram_base();
+
+ /* enable global mode- snooping data buffers and BDs */
+ out_be32(&pram->mode, PRAM_MODE_GLOBAL);
+
+ /* init the Rx queue descriptor pointer */
+ out_be32(&pram->rxqd_ptr, pram_page_offset + 0x20);
+
+ /* set the max receive buffer length, power of 2 */
+ muram_writew(&pram->mrblr, MAX_RXBUF_LOG2);
+
+ /* alloc Rx buffer descriptors from main memory */
+ rx_bd_ring_base = dma_alloc_coherent(sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE, DMA_ADDRESS_BROKEN);
+ if (!rx_bd_ring_base)
+ return -ENOMEM;
+
+ memset(rx_bd_ring_base, 0, sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+
+ /* alloc Rx buffer from main memory */
+ rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+ if (!rx_buf_pool)
+ return -ENOMEM;
+
+ memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+
+ /* save them to fm_eth */
+ fm_eth->rx_bd_ring = rx_bd_ring_base;
+ fm_eth->cur_rxbd = rx_bd_ring_base;
+ fm_eth->rx_buf = rx_buf_pool;
+
+ /* init Rx BDs ring */
+ rxbd = rx_bd_ring_base;
+ for (i = 0; i < RX_BD_RING_SIZE; i++) {
+ muram_writew(&rxbd->status, RxBD_EMPTY);
+ muram_writew(&rxbd->len, 0);
+ buf_hi = upper_32_bits(virt_to_phys(rx_buf_pool +
+ i * MAX_RXBUF_LEN));
+ buf_lo = lower_32_bits(virt_to_phys(rx_buf_pool +
+ i * MAX_RXBUF_LEN));
+ muram_writew(&rxbd->buf_ptr_hi, (u16)buf_hi);
+ out_be32(&rxbd->buf_ptr_lo, buf_lo);
+ rxbd++;
+ }
+
+ /* set the Rx queue descriptor */
+ rxqd = &pram->rxqd;
+ muram_writew(&rxqd->gen, 0);
+ bd_ring_base_hi = upper_32_bits(virt_to_phys(rx_bd_ring_base));
+ bd_ring_base_lo = lower_32_bits(virt_to_phys(rx_bd_ring_base));
+ muram_writew(&rxqd->bd_ring_base_hi, (u16)bd_ring_base_hi);
+ out_be32(&rxqd->bd_ring_base_lo, bd_ring_base_lo);
+ muram_writew(&rxqd->bd_ring_size, sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+ muram_writew(&rxqd->offset_in, 0);
+ muram_writew(&rxqd->offset_out, 0);
+
+ /* set IM parameter ram pointer to Rx Frame Queue ID */
+ out_be32(&bmi_rx_port->fmbm_rfqid, pram_page_offset);
+
+ return 0;
+}
+
+static int fm_eth_tx_port_parameter_init(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+ u32 pram_page_offset;
+ void *tx_bd_ring_base;
+ u32 bd_ring_base_lo, bd_ring_base_hi;
+ struct fm_port_bd *txbd;
+ struct fm_port_qd *txqd;
+ struct fm_bmi_tx_port *bmi_tx_port = fm_eth->tx_port;
+ int i;
+
+ /* alloc global parameter ram at MURAM */
+ pram = fm_muram_alloc(FM_PRAM_SIZE, FM_PRAM_ALIGN);
+ if (!pram)
+ return -ENOMEM;
+
+ fm_eth->tx_pram = pram;
+
+ /* parameter page offset to MURAM */
+ pram_page_offset = (void *)pram - fm_muram_base();
+
+ /* enable global mode- snooping data buffers and BDs */
+ out_be32(&pram->mode, PRAM_MODE_GLOBAL);
+
+ /* init the Tx queue descriptor pionter */
+ out_be32(&pram->txqd_ptr, pram_page_offset + 0x40);
+
+ /* alloc Tx buffer descriptors from main memory */
+ tx_bd_ring_base = dma_alloc_coherent(sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE, DMA_ADDRESS_BROKEN);
+ if (!tx_bd_ring_base)
+ return -ENOMEM;
+
+ memset(tx_bd_ring_base, 0, sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ /* save it to fm_eth */
+ fm_eth->tx_bd_ring = tx_bd_ring_base;
+ fm_eth->cur_txbd = tx_bd_ring_base;
+
+ /* init Tx BDs ring */
+ txbd = tx_bd_ring_base;
+ for (i = 0; i < TX_BD_RING_SIZE; i++) {
+ muram_writew(&txbd->status, TxBD_LAST);
+ muram_writew(&txbd->len, 0);
+ muram_writew(&txbd->buf_ptr_hi, 0);
+ out_be32(&txbd->buf_ptr_lo, 0);
+ txbd++;
+ }
+
+ /* set the Tx queue decriptor */
+ txqd = &pram->txqd;
+ bd_ring_base_hi = upper_32_bits(virt_to_phys(tx_bd_ring_base));
+ bd_ring_base_lo = lower_32_bits(virt_to_phys(tx_bd_ring_base));
+ muram_writew(&txqd->bd_ring_base_hi, (u16)bd_ring_base_hi);
+ out_be32(&txqd->bd_ring_base_lo, bd_ring_base_lo);
+ muram_writew(&txqd->bd_ring_size, sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ muram_writew(&txqd->offset_in, 0);
+ muram_writew(&txqd->offset_out, 0);
+
+ /* set IM parameter ram pointer to Tx Confirmation Frame Queue ID */
+ out_be32(&bmi_tx_port->fmbm_tcfqid, pram_page_offset);
+
+ return 0;
+}
+
+static void fmc_tx_port_graceful_stop_enable(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+
+ pram = fm_eth->tx_pram;
+ /* graceful stop transmission of frames */
+ setbits_be32(&pram->mode, PRAM_MODE_GRACEFUL_STOP);
+}
+
+static void fmc_tx_port_graceful_stop_disable(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+
+ pram = fm_eth->tx_pram;
+ /* re-enable transmission of frames */
+ clrbits_be32(&pram->mode, PRAM_MODE_GRACEFUL_STOP);
+}
+
+static void memac_adjust_link_speed(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ int speed = edev->phydev->speed;
+ u32 if_mode;
+ phy_interface_t type = fm_eth->enet_if;
+
+ if_mode = in_be32(&regs->if_mode);
+
+ if (type == PHY_INTERFACE_MODE_RGMII ||
+ type == PHY_INTERFACE_MODE_RGMII_ID ||
+ type == PHY_INTERFACE_MODE_RGMII_TXID) {
+ if_mode &= ~IF_MODE_EN_AUTO;
+ if_mode &= ~IF_MODE_SETSP_MASK;
+
+ switch (speed) {
+ case SPEED_1000:
+ if_mode |= IF_MODE_SETSP_1000M;
+ break;
+ case SPEED_100:
+ if_mode |= IF_MODE_SETSP_100M;
+ break;
+ case SPEED_10:
+ if_mode |= IF_MODE_SETSP_10M;
+ default:
+ break;
+ }
+ }
+
+ out_be32(&regs->if_mode, if_mode);
+
+ return;
+}
+
+static int fm_eth_open(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ int ret;
+
+ ret = phy_device_connect(edev, NULL, -1, memac_adjust_link_speed, 0,
+ fm_eth->enet_if);
+ if (ret)
+ return ret;
+
+ /* enable bmi Rx port */
+ setbits_be32(&fm_eth->rx_port->fmbm_rcfg, FMBM_RCFG_EN);
+ /* enable MAC rx/tx port */
+ setbits_be32(&regs->command_config,
+ MEMAC_CMD_CFG_RXTX_EN | MEMAC_CMD_CFG_NO_LEN_CHK);
+
+ /* enable bmi Tx port */
+ setbits_be32(&fm_eth->tx_port->fmbm_tcfg, FMBM_TCFG_EN);
+ /* re-enable transmission of frame */
+ fmc_tx_port_graceful_stop_disable(fm_eth);
+
+ return 0;
+}
+
+static void memac_disable_mac(struct fm_eth *fm_eth)
+{
+ struct memac *regs = fm_eth->regs;
+
+ clrbits_be32(&regs->command_config, MEMAC_CMD_CFG_RXTX_EN);
+}
+
+static void fm_eth_halt(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+
+ /* graceful stop the transmission of frames */
+ fmc_tx_port_graceful_stop_enable(fm_eth);
+ /* disable bmi Tx port */
+ bmi_tx_port_disable(fm_eth->tx_port);
+ /* disable MAC rx/tx port */
+ memac_disable_mac(fm_eth);
+ /* disable bmi Rx port */
+ bmi_rx_port_disable(fm_eth->rx_port);
+}
+
+static int fm_eth_send(struct eth_device *edev, void *buf, int len)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct fm_port_global_pram *pram;
+ struct fm_port_bd *txbd, *txbd_base;
+ u16 offset_in;
+ int i;
+ dma_addr_t dma;
+
+ pram = fm_eth->tx_pram;
+ txbd = fm_eth->cur_txbd;
+
+ /* find one empty TxBD */
+ for (i = 0; muram_readw(&txbd->status) & TxBD_READY; i++) {
+ udelay(100);
+ if (i > 0x1000) {
+ dev_err(&edev->dev, "Tx buffer not ready, txbd->status = 0x%x\n",
+ muram_readw(&txbd->status));
+ return -EIO;
+ }
+ }
+
+ dma = dma_map_single(fm_eth->dev, buf, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(fm_eth->dev, dma))
+ return -EFAULT;
+
+ /* setup TxBD */
+ muram_writew(&txbd->buf_ptr_hi, (u16)upper_32_bits(dma));
+ out_be32(&txbd->buf_ptr_lo, lower_32_bits(dma));
+ muram_writew(&txbd->len, len);
+ muram_writew(&txbd->status, TxBD_READY | TxBD_LAST);
+
+ /* update TxQD, let RISC to send the packet */
+ offset_in = muram_readw(&pram->txqd.offset_in);
+ offset_in += sizeof(struct fm_port_bd);
+ if (offset_in >= muram_readw(&pram->txqd.bd_ring_size))
+ offset_in = 0;
+ muram_writew(&pram->txqd.offset_in, offset_in);
+
+ /* wait for buffer to be transmitted */
+ for (i = 0; muram_readw(&txbd->status) & TxBD_READY; i++) {
+ udelay(10);
+ if (i > 0x10000) {
+ dev_err(&edev->dev, "Tx error, txbd->status = 0x%x\n",
+ muram_readw(&txbd->status));
+ return -EIO;
+ }
+ }
+
+ dma_unmap_single(fm_eth->dev, dma, len, DMA_TO_DEVICE);
+
+ /* advance the TxBD */
+ txbd++;
+ txbd_base = fm_eth->tx_bd_ring;
+ if (txbd >= (txbd_base + TX_BD_RING_SIZE))
+ txbd = txbd_base;
+ /* update current txbd */
+ fm_eth->cur_txbd = (void *)txbd;
+
+ return 0;
+}
+
+static int fm_eth_recv(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct fm_port_global_pram *pram;
+ struct fm_port_bd *rxbd, *rxbd_base;
+ u16 status, len;
+ u32 buf_lo, buf_hi;
+ u8 *data;
+ u16 offset_out;
+ int ret = 1;
+
+ pram = fm_eth->rx_pram;
+ rxbd = fm_eth->cur_rxbd;
+ status = muram_readw(&rxbd->status);
+
+ while (!(status & RxBD_EMPTY)) {
+ if (!(status & RxBD_ERROR)) {
+ buf_hi = muram_readw(&rxbd->buf_ptr_hi);
+ buf_lo = in_be32(&rxbd->buf_ptr_lo);
+ data = (u8 *)((unsigned long)(buf_hi << 16) << 16 | buf_lo);
+ len = muram_readw(&rxbd->len);
+
+ dma_sync_single_for_cpu((unsigned long)data,
+ len,
+ DMA_FROM_DEVICE);
+
+ net_receive(edev, data, len);
+
+ dma_sync_single_for_device((unsigned long)data,
+ len,
+ DMA_FROM_DEVICE);
+ } else {
+ dev_err(&edev->dev, "Rx error\n");
+ ret = 0;
+ }
+
+ /* clear the RxBDs */
+ muram_writew(&rxbd->status, RxBD_EMPTY);
+ muram_writew(&rxbd->len, 0);
+
+ /* advance RxBD */
+ rxbd++;
+ rxbd_base = fm_eth->rx_bd_ring;
+ if (rxbd >= (rxbd_base + RX_BD_RING_SIZE))
+ rxbd = rxbd_base;
+ /* read next status */
+ status = muram_readw(&rxbd->status);
+
+ /* update RxQD */
+ offset_out = muram_readw(&pram->rxqd.offset_out);
+ offset_out += sizeof(struct fm_port_bd);
+ if (offset_out >= muram_readw(&pram->rxqd.bd_ring_size))
+ offset_out = 0;
+ muram_writew(&pram->rxqd.offset_out, offset_out);
+ }
+ fm_eth->cur_rxbd = rxbd;
+
+ return ret;
+}
+
+static void memac_init_mac(struct fm_eth *fm_eth)
+{
+ struct memac *regs = fm_eth->regs;
+
+ /* mask all interrupt */
+ out_be32(&regs->imask, IMASK_MASK_ALL);
+
+ /* clear all events */
+ out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
+
+ /* set the max receive length */
+ out_be32(&regs->maxfrm, MAX_RXBUF_LEN);
+
+ /* multicast frame reception for the hash entry disable */
+ out_be32(&regs->hashtable_ctrl, 0);
+}
+
+static int memac_set_ethaddr(struct eth_device *edev, const unsigned char *adr)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ u32 mac_addr0, mac_addr1;
+
+ /*
+ * if a station address of 0x12345678ABCD, perform a write to
+ * MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB
+ */
+ mac_addr0 = (adr[3] << 24) | (adr[2] << 16) | \
+ (adr[1] << 8) | (adr[0]);
+ out_be32(&regs->mac_addr_0, mac_addr0);
+
+ mac_addr1 = ((adr[5] << 8) | adr[4]) & 0x0000ffff;
+ out_be32(&regs->mac_addr_1, mac_addr1);
+
+ return 0;
+}
+
+static int memac_get_ethaddr(struct eth_device *edev, unsigned char *adr)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ u32 mac_addr0, mac_addr1;
+
+ mac_addr0 = in_be32(&regs->mac_addr_0);
+ mac_addr1 = in_be32(&regs->mac_addr_1);
+
+ adr[0] = mac_addr0 & 0xff;
+ adr[1] = (mac_addr0 >> 8) & 0xff;
+ adr[2] = (mac_addr0 >> 16) & 0xff;
+ adr[3] = (mac_addr0 >> 24) & 0xff;
+ adr[4] = mac_addr1 & 0xff;
+ adr[5] = (mac_addr1 >> 8) & 0xff;
+
+ return 0;
+}
+
+static void memac_set_interface_mode(struct fm_eth *fm_eth,
+ phy_interface_t type)
+{
+ struct memac *regs = fm_eth->regs;
+ u32 if_mode;
+
+ /* clear all bits relative with interface mode */
+ if_mode = in_be32(&regs->if_mode) & ~IF_MODE_MASK;
+
+ /* set interface mode */
+ switch (type) {
+ case PHY_INTERFACE_MODE_GMII:
+ if_mode |= IF_MODE_GMII | IF_MODE_EN_AUTO;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ if_mode |= IF_MODE_GMII | IF_MODE_RG | IF_MODE_EN_AUTO;
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ if_mode |= IF_MODE_GMII | IF_MODE_EN_AUTO;
+ break;
+ case PHY_INTERFACE_MODE_XGMII:
+ if_mode |= IF_MODE_XGMII;
+ break;
+ default:
+ break;
+ }
+
+ out_be32(&regs->if_mode, if_mode);
+}
+
+static int fm_eth_startup(struct fm_eth *fm_eth)
+{
+ int ret;
+
+ ret = fm_eth_rx_port_parameter_init(fm_eth);
+ if (ret)
+ return ret;
+
+ ret = fm_eth_tx_port_parameter_init(fm_eth);
+ if (ret)
+ return ret;
+
+ /* setup the MAC controller */
+ memac_init_mac(fm_eth);
+
+ /* init bmi rx port, IM mode and disable */
+ bmi_rx_port_init(fm_eth->rx_port);
+
+ /* init bmi tx port, IM mode and disable */
+ bmi_tx_port_init(fm_eth->tx_port);
+
+ memac_set_interface_mode(fm_eth, fm_eth->enet_if);
+
+ return 0;
+}
+
+static int fsl_fman_mdio_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ int ret;
+ struct fsl_fman_mdio *priv;
+
+ dev_dbg(dev, "probe\n");
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->bus.read = memac_mdio_read;
+ priv->bus.write = memac_mdio_write;
+ priv->bus.parent = dev;
+ priv->regs = IOMEM(iores->start);
+
+ memac_setbits_32(&priv->regs->mdio_stat,
+ MDIO_STAT_CLKDIV(258) | MDIO_STAT_NEG);
+
+ ret = mdiobus_register(&priv->bus);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_fman_port_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ int ret;
+ struct fsl_fman_port *port;
+ unsigned long type;
+
+ dev_dbg(dev, "probe\n");
+
+ ret = dev_get_drvdata(dev, (const void **)&type);
+ if (ret)
+ return ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ port = xzalloc(sizeof(*port));
+
+ port->regs = IOMEM(iores->start);
+ port->type = type;
+
+ if (type == FMAN_PORT_TYPE_RX)
+ port->rxport = port->regs;
+ else
+ port->txport = port->regs;
+
+ dev->priv = port;
+
+ return 0;
+}
+
+static int fsl_fman_memac_port_bind(struct fm_eth *fm_eth, enum fman_port_type type)
+{
+ struct device_node *macnp = fm_eth->dev->device_node;
+ struct device_node *portnp;
+ struct device_d *portdev;
+ struct fsl_fman_port *port;
+
+ portnp = of_parse_phandle(macnp, "fsl,fman-ports", type);
+ if (!portnp) {
+ dev_err(fm_eth->dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n",
+ macnp->full_name);
+ return -EINVAL;
+ }
+
+ portdev = of_find_device_by_node(portnp);
+ if (!portdev)
+ return -ENOENT;
+
+ port = portdev->priv;
+ if (!port)
+ return -EINVAL;
+
+ if (type == FMAN_PORT_TYPE_TX)
+ fm_eth->tx_port = port->txport;
+ else
+ fm_eth->rx_port = port->rxport;
+
+ return 0;
+}
+
+static int fsl_fman_memac_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ struct fm_eth *fm_eth;
+ struct eth_device *edev;
+ int ret;
+ int phy_mode;
+
+ dev_dbg(dev, "probe\n");
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ /* alloc the FMan ethernet private struct */
+ fm_eth = xzalloc(sizeof(*fm_eth));
+
+ fm_eth->dev = dev;
+
+ ret = fsl_fman_memac_port_bind(fm_eth, FMAN_PORT_TYPE_TX);
+ if (ret)
+ return ret;
+
+ ret = fsl_fman_memac_port_bind(fm_eth, FMAN_PORT_TYPE_RX);
+ if (ret)
+ return ret;
+
+ phy_mode = of_get_phy_mode(dev->device_node);
+ if (phy_mode < 0)
+ return phy_mode;
+
+ fm_eth->enet_if = phy_mode;
+
+ fm_eth->regs = IOMEM(iores->start);
+
+ dev->priv = fm_eth;
+
+ edev = &fm_eth->edev;
+ edev->open = fm_eth_open;
+ edev->halt = fm_eth_halt;
+ edev->send = fm_eth_send;
+ edev->recv = fm_eth_recv;
+ edev->get_ethaddr = memac_get_ethaddr;
+ edev->set_ethaddr = memac_set_ethaddr;
+ edev->parent = dev;
+
+ /* startup the FM im */
+ ret = fm_eth_startup(fm_eth);
+ if (ret)
+ return ret;
+
+ ret = eth_register(edev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_fman_muram_probe(struct device_d *dev)
+{
+ struct resource *iores;
+
+ dev_dbg(dev, "probe\n");
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ muram.base = IOMEM(iores->start);
+ muram.size = resource_size(iores);
+ muram.alloc = muram.base + FM_MURAM_RES_SIZE;
+ muram.top = muram.base + muram.size;
+
+ return 0;
+}
+
+static struct of_device_id fsl_fman_mdio_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-memac-mdio",
+ }, {
+ }
+};
+
+static struct driver_d fman_mdio_driver = {
+ .name = "fsl-fman-mdio",
+ .probe = fsl_fman_mdio_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_mdio_dt_ids),
+};
+
+static struct of_device_id fsl_fman_port_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-v3-port-rx",
+ .data = (void *)FMAN_PORT_TYPE_RX,
+ }, {
+ .compatible = "fsl,fman-v3-port-tx",
+ .data = (void *)FMAN_PORT_TYPE_TX,
+ }, {
+ }
+};
+
+static struct driver_d fman_port_driver = {
+ .name = "fsl-fman-port",
+ .probe = fsl_fman_port_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_port_dt_ids),
+};
+
+static struct of_device_id fsl_fman_memac_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-memac",
+ }, {
+ }
+};
+
+static struct driver_d fman_memac_driver = {
+ .name = "fsl-fman-memac",
+ .probe = fsl_fman_memac_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_memac_dt_ids),
+};
+
+static struct of_device_id fsl_fman_muram_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-muram",
+ }, {
+ }
+};
+
+static struct driver_d fman_muram_driver = {
+ .name = "fsl-fman-muram",
+ .probe = fsl_fman_muram_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_muram_dt_ids),
+};
+
+static int fsl_fman_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ struct ccsr_fman *reg;
+ int ret;
+
+ dev_dbg(dev, "----------------> probe\n");
+
+ iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ reg = IOMEM(iores->start);
+
+ ret = of_platform_populate(dev->device_node, NULL, dev);
+ if (ret)
+ return ret;
+
+ platform_driver_register(&fman_muram_driver);
+ platform_driver_register(&fman_mdio_driver);
+ platform_driver_register(&fman_port_driver);
+ platform_driver_register(&fman_memac_driver);
+
+ ret = fm_init_common(dev, reg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct of_device_id fsl_fman_dt_ids[] = {
+ {
+ .compatible = "fsl,fman",
+ }, {
+ }
+};
+
+static struct driver_d fman_driver = {
+ .name = "fsl-fman",
+ .probe = fsl_fman_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_dt_ids),
+};
+device_platform_driver(fman_driver);
diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c
index 7aa54a2..359c70c 100644
--- a/drivers/net/mvneta.c
+++ b/drivers/net/mvneta.c
@@ -580,7 +580,7 @@ static void mvneta_init_rx_ring(struct mvneta_port *priv)
priv->curr_rxdesc = 0;
}
-void mvneta_setup_tx_rx(struct mvneta_port *priv)
+static void mvneta_setup_tx_rx(struct mvneta_port *priv)
{
u32 val;
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 0665aef..c08b825 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -48,6 +48,11 @@ config NATIONAL_PHY
---help---
Currently supports the DP83865 PHY.
+config REALTEK_PHY
+ bool "Driver for Realtek PHYs"
+ ---help---
+ Supports the Realtek 821x PHY.
+
config SMSC_PHY
bool "Drivers for SMSC PHYs"
---help---
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 5f11a85..7053b57 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -1,4 +1,4 @@
-obj-y += phy.o mdio_bus.o
+obj-y += phy.o mdio_bus.o phy-core.o
obj-$(CONFIG_AR8327N_PHY) += ar8327.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
obj-$(CONFIG_DAVICOM_PHY) += davicom.o
@@ -6,6 +6,7 @@ obj-$(CONFIG_LXT_PHY) += lxt.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MICREL_PHY) += micrel.o
obj-$(CONFIG_NATIONAL_PHY) += national.o
+obj-$(CONFIG_REALTEK_PHY) += realtek.o
obj-$(CONFIG_SMSC_PHY) += smsc.o
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx/
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
new file mode 100644
index 0000000..21daaa9
--- /dev/null
+++ b/drivers/net/phy/phy-core.c
@@ -0,0 +1,175 @@
+#include <common.h>
+#include <linux/phy.h>
+
+/**
+ * phy_modify - Convenience function for modifying a given PHY register
+ * @phydev: the phy_device struct
+ * @regnum: register number to write
+ * @mask: bit mask of bits to clear
+ * @set: new value of bits set in mask to write to @regnum
+ *
+ */
+int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
+{
+ int ret;
+
+ ret = phy_read(phydev, regnum);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, regnum, (ret & ~mask) | set);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int phy_read_page(struct phy_device *phydev)
+{
+ struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+
+ return phydrv->read_page(phydev);
+}
+
+static int phy_write_page(struct phy_device *phydev, int page)
+{
+ struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+
+ return phydrv->write_page(phydev, page);
+}
+
+/**
+ * phy_save_page() - take the bus lock and save the current page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Take the MDIO bus lock, and return the current page number. On error,
+ * returns a negative errno. phy_restore_page() must always be called
+ * after this, irrespective of success or failure of this call.
+ */
+int phy_save_page(struct phy_device *phydev)
+{
+ return phy_read_page(phydev);
+}
+
+/**
+ * phy_select_page() - take the bus lock, save the current page, and set a page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: desired page
+ *
+ * Take the MDIO bus lock to protect against concurrent access, save the
+ * current PHY page, and set the current page. On error, returns a
+ * negative errno, otherwise returns the previous page number.
+ * phy_restore_page() must always be called after this, irrespective
+ * of success or failure of this call.
+ */
+int phy_select_page(struct phy_device *phydev, int page)
+{
+ int ret, oldpage;
+
+ oldpage = ret = phy_save_page(phydev);
+ if (ret < 0)
+ return ret;
+
+ if (oldpage != page) {
+ ret = phy_write_page(phydev, page);
+ if (ret < 0)
+ return ret;
+ }
+
+ return oldpage;
+}
+
+/**
+ * phy_restore_page() - restore the page register and release the bus lock
+ * @phydev: a pointer to a &struct phy_device
+ * @oldpage: the old page, return value from phy_save_page() or phy_select_page()
+ * @ret: operation's return code
+ *
+ * Release the MDIO bus lock, restoring @oldpage if it is a valid page.
+ * This function propagates the earliest error code from the group of
+ * operations.
+ *
+ * Returns:
+ * @oldpage if it was a negative value, otherwise
+ * @ret if it was a negative errno value, otherwise
+ * phy_write_page()'s negative value if it were in error, otherwise
+ * @ret.
+ */
+int phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
+{
+ int r;
+
+ if (oldpage >= 0) {
+ r = phy_write_page(phydev, oldpage);
+
+ /* Propagate the operation return code if the page write
+ * was successful.
+ */
+ if (ret >= 0 && r < 0)
+ ret = r;
+ } else {
+ /* Propagate the phy page selection error code */
+ ret = oldpage;
+ }
+
+ return ret;
+}
+
+/**
+ * phy_read_paged() - Convenience function for reading a paged register
+ * @phydev: a pointer to a &struct phy_device
+ * @page: the page for the phy
+ * @regnum: register number
+ *
+ * Same rules as for phy_read().
+ */
+int phy_read_paged(struct phy_device *phydev, int page, u32 regnum)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_select_page(phydev, page);
+ if (oldpage >= 0)
+ ret = phy_read(phydev, regnum);
+
+ return phy_restore_page(phydev, oldpage, ret);
+}
+
+/**
+ * phy_write_paged() - Convenience function for writing a paged register
+ * @phydev: a pointer to a &struct phy_device
+ * @page: the page for the phy
+ * @regnum: register number
+ * @val: value to write
+ *
+ * Same rules as for phy_write().
+ */
+int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_select_page(phydev, page);
+ if (oldpage >= 0)
+ ret = phy_write(phydev, regnum, val);
+
+ return phy_restore_page(phydev, oldpage, ret);
+}
+
+/**
+ * phy_modify_paged() - Convenience function for modifying a paged register
+ * @phydev: a pointer to a &struct phy_device
+ * @page: the page for the phy
+ * @regnum: register number
+ * @mask: bit mask of bits to clear
+ * @set: bit mask of bits to set
+ *
+ * Same rules as for phy_read() and phy_write().
+ */
+int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
+ u16 mask, u16 set)
+{
+ int ret = 0, oldpage;
+
+ oldpage = phy_select_page(phydev, page);
+ if (oldpage >= 0)
+ ret = phy_modify(phydev, regnum, mask, set);
+
+ return phy_restore_page(phydev, oldpage, ret);
+}
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
new file mode 100644
index 0000000..4ae0501
--- /dev/null
+++ b/drivers/net/phy/realtek.c
@@ -0,0 +1,172 @@
+/*
+ * drivers/net/phy/realtek.c
+ *
+ * Driver for Realtek PHYs
+ *
+ * Author: Johnson Leung <r58129@freescale.com>
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <common.h>
+#include <init.h>
+#include <linux/bitops.h>
+#include <linux/phy.h>
+
+#define RTL821x_PHYSR 0x11
+#define RTL821x_PHYSR_DUPLEX BIT(13)
+#define RTL821x_PHYSR_SPEED GENMASK(15, 14)
+
+#define RTL821x_INER 0x12
+#define RTL8211B_INER_INIT 0x6400
+#define RTL8211E_INER_LINK_STATUS BIT(10)
+#define RTL8211F_INER_LINK_STATUS BIT(4)
+
+#define RTL821x_INSR 0x13
+
+#define RTL821x_PAGE_SELECT 0x1f
+
+#define RTL8211F_INSR 0x1d
+
+#define RTL8211F_TX_DELAY BIT(8)
+
+#define RTL8201F_ISR 0x1e
+#define RTL8201F_IER 0x13
+
+#define RTL8366RB_POWER_SAVE 0x15
+#define RTL8366RB_POWER_SAVE_ON BIT(12)
+
+static int rtl821x_read_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, RTL821x_PAGE_SELECT);
+}
+
+static int rtl821x_write_page(struct phy_device *phydev, int page)
+{
+ return phy_write(phydev, RTL821x_PAGE_SELECT, page);
+}
+
+static int rtl8211_config_aneg(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_config_aneg(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* Quirk was copied from vendor driver. Unfortunately it includes no
+ * description of the magic numbers.
+ */
+ if (phydev->speed == SPEED_100 && phydev->autoneg == AUTONEG_DISABLE) {
+ phy_write(phydev, 0x17, 0x2138);
+ phy_write(phydev, 0x0e, 0x0260);
+ } else {
+ phy_write(phydev, 0x17, 0x2108);
+ phy_write(phydev, 0x0e, 0x0000);
+ }
+
+ return 0;
+}
+
+static int rtl8211c_config_init(struct phy_device *phydev)
+{
+ /* RTL8211C has an issue when operating in Gigabit slave mode */
+ phy_set_bits(phydev, MII_CTRL1000,
+ CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
+
+ return genphy_config_init(phydev);
+}
+
+static int rtl8211f_config_init(struct phy_device *phydev)
+{
+ int ret;
+ u16 val = 0;
+
+ ret = genphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ val = RTL8211F_TX_DELAY;
+
+ return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val);
+}
+
+static int rtl8366rb_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = genphy_config_init(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_set_bits(phydev, RTL8366RB_POWER_SAVE,
+ RTL8366RB_POWER_SAVE_ON);
+ if (ret) {
+ dev_err(&phydev->dev, "error enabling power management\n");
+ }
+
+ return ret;
+}
+
+static struct phy_driver realtek_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(0x00008201),
+ .drv.name = "RTL8201CP Ethernet",
+ .features = PHY_BASIC_FEATURES,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc816),
+ .drv.name = "RTL8201F Fast Ethernet",
+ .features = PHY_BASIC_FEATURES,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc910),
+ .drv.name = "RTL8211 Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .config_aneg = rtl8211_config_aneg,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc912),
+ .drv.name = "RTL8211B Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc913),
+ .drv.name = "RTL8211C Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = rtl8211c_config_init,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc914),
+ .drv.name = "RTL8211DN Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc915),
+ .drv.name = "RTL8211E Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc916),
+ .drv.name = "RTL8211F Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = &rtl8211f_config_init,
+ .read_page = rtl821x_read_page,
+ .write_page = rtl821x_write_page,
+ }, {
+ PHY_ID_MATCH_EXACT(0x001cc961),
+ .drv.name = "RTL8366RB Gigabit Ethernet",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = &rtl8366rb_config_init,
+ },
+};
+
+static int __init realtek_phy_init(void)
+{
+ return phy_drivers_register(realtek_drvs,
+ ARRAY_SIZE(realtek_drvs));
+}
+fs_initcall(realtek_phy_init);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 85c7953..838f530 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2278,6 +2278,8 @@ char *of_get_reproducible_name(struct device_node *node)
return basprintf("[0x%llx]", addr);
}
+ na = of_n_addr_cells(node);
+
/*
* Special workaround for the of partition binding. In the old binding
* the partitions are directly under the hardware devicenode whereas in
@@ -2290,8 +2292,6 @@ char *of_get_reproducible_name(struct device_node *node)
node = node->parent;
}
- na = of_n_addr_cells(node);
-
offset = of_read_number(reg, na);
str = of_get_reproducible_name(node->parent);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 45c3b35..46badee 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -83,7 +83,7 @@ config PINCTRL_TEGRA_XUSB
The pinmux controller found on the Tegra 124 line of SoCs used for
the SerDes lanes.
-source drivers/pinctrl/mvebu/Kconfig
+source "drivers/pinctrl/mvebu/Kconfig"
config PINCTRL_VF610
bool
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index d462c11..f12ff93 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -37,6 +37,10 @@ config DRIVER_SERIAL_IMX
default y
bool "i.MX serial driver"
+config DRIVER_SERIAL_STM32
+ depends on ARCH_STM32MP
+ bool "stm32mp serial driver"
+
config DRIVER_SERIAL_STM378X
depends on ARCH_MXS
default y
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 3d9f735..4174cc1 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_DRIVER_SERIAL_CLPS711X) += serial_clps711x.o
obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o
obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o
obj-$(CONFIG_DRIVER_SERIAL_S3C) += serial_s3c.o
+obj-$(CONFIG_DRIVER_SERIAL_STM32) += serial_stm32.o
obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o
obj-$(CONFIG_DRIVER_SERIAL_ALTERA_JTAG) += serial_altera_jtag.o
obj-$(CONFIG_DRIVER_SERIAL_PXA) += serial_pxa.o
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
new file mode 100644
index 0000000..a84e64e
--- /dev/null
+++ b/drivers/serial/serial_stm32.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
+ */
+
+#include <common.h>
+#include <console.h>
+#include <io.h>
+#include <linux/clk.h>
+#include <init.h>
+#include <driver.h>
+#include "serial_stm32.h"
+
+struct stm32_uart_info {
+ u8 uart_enable_bit; /* UART_CR1_UE */
+ bool stm32f4; /* true for STM32F4, false otherwise */
+ bool has_fifo;
+};
+
+struct stm32_uart {
+ struct console_device cdev;
+ void __iomem *base;
+ struct clk *clk;
+ bool stm32f4;
+ bool has_fifo;
+ int uart_enable_bit;
+};
+
+static struct stm32_uart *to_stm32_uart(struct console_device *cdev)
+{
+ return container_of(cdev, struct stm32_uart, cdev);
+}
+
+static int stm32_serial_setbaudrate(struct console_device *cdev, int baudrate)
+{
+ struct stm32_uart *stm32 = to_stm32_uart(cdev);
+ void __iomem *base = stm32->base;
+ bool stm32f4 = stm32->stm32f4;
+ u32 int_div, mantissa, fraction, oversampling;
+ unsigned long clock_rate;
+
+ clock_rate = clk_get_rate(stm32->clk);
+
+ int_div = DIV_ROUND_CLOSEST(clock_rate, baudrate);
+
+ if (int_div < 16) {
+ oversampling = 8;
+ setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_OVER8);
+ } else {
+ oversampling = 16;
+ clrbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_OVER8);
+ }
+
+ mantissa = (int_div / oversampling) << USART_BRR_M_SHIFT;
+ fraction = int_div % oversampling;
+
+ writel(mantissa | fraction, base + BRR_OFFSET(stm32f4));
+
+ return 0;
+}
+
+static int stm32_serial_getc(struct console_device *cdev)
+{
+ struct stm32_uart *stm32 = to_stm32_uart(cdev);
+ void __iomem *base = stm32->base;
+ bool stm32f4 = stm32->stm32f4;
+ u32 isr = readl(base + ISR_OFFSET(stm32f4));
+
+ if ((isr & USART_ISR_RXNE) == 0)
+ return -EAGAIN;
+
+ if (isr & (USART_ISR_PE | USART_ISR_ORE)) {
+ if (!stm32f4)
+ setbits_le32(base + ICR_OFFSET,
+ USART_ICR_PCECF | USART_ICR_ORECF);
+ else
+ readl(base + RDR_OFFSET(stm32f4));
+ return -EIO;
+ }
+
+ return readl(base + RDR_OFFSET(stm32f4));
+}
+
+static void stm32_serial_putc(struct console_device *cdev, char c)
+{
+ struct stm32_uart *stm32 = to_stm32_uart(cdev);
+ void __iomem *base = stm32->base;
+ bool stm32f4 = stm32->stm32f4;
+
+ while ((readl(base + ISR_OFFSET(stm32f4)) & USART_ISR_TXE) == 0);
+
+ writel(c, base + TDR_OFFSET(stm32f4));
+}
+
+static int stm32_serial_tstc(struct console_device *cdev)
+{
+ struct stm32_uart *stm32 = to_stm32_uart(cdev);
+ void __iomem *base = stm32->base;
+ bool stm32f4 = stm32->stm32f4;
+
+ return readl(base + ISR_OFFSET(stm32f4)) & USART_ISR_RXNE ? 1 : 0;
+}
+
+static void stm32_serial_flush(struct console_device *cdev)
+{
+ struct stm32_uart *stm32 = to_stm32_uart(cdev);
+ void __iomem *base = stm32->base;
+ bool stm32f4 = stm32->stm32f4;
+
+ while (!(readl(base + ISR_OFFSET(stm32f4)) & USART_ISR_TXE));
+}
+
+static void stm32_serial_init(struct console_device *cdev)
+{
+ struct stm32_uart *stm32 = to_stm32_uart(cdev);
+ void __iomem *base = stm32->base;
+ bool stm32f4 = stm32->stm32f4;
+ u8 uart_enable_bit = stm32->uart_enable_bit;
+ u32 cr1;
+
+ cr1 = readl(base + CR1_OFFSET(stm32f4));
+
+ /* Disable uart-> enable fifo -> enable uart */
+ cr1 &= ~(USART_CR1_RE | USART_CR1_TE | BIT(uart_enable_bit));
+
+ writel(cr1, base + CR1_OFFSET(stm32f4));
+
+ if (stm32->has_fifo)
+ cr1 |= USART_CR1_FIFOEN;
+
+ cr1 &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M1 | USART_CR1_M0);
+
+ cr1 |= USART_CR1_RE | USART_CR1_TE | BIT(uart_enable_bit);
+
+ writel(cr1, base + CR1_OFFSET(stm32f4));
+}
+
+static int stm32_serial_probe(struct device_d *dev)
+{
+ int ret;
+ struct console_device *cdev;
+ struct stm32_uart *stm32;
+ const char *devname;
+ struct resource *res;
+ struct stm32_uart_info *info;
+
+ ret = dev_get_drvdata(dev, (const void **)&info);
+ if (ret)
+ return ret;
+
+ stm32 = xzalloc(sizeof(*stm32));
+ cdev = &stm32->cdev;
+
+ res = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ goto err_free;
+ }
+ stm32->base = IOMEM(res->start);
+
+ stm32->has_fifo = info->has_fifo;
+ stm32->stm32f4 = info->stm32f4;
+ stm32->uart_enable_bit = info->uart_enable_bit;
+
+ stm32->clk = clk_get(dev, NULL);
+ if (IS_ERR(stm32->clk)) {
+ ret = PTR_ERR(stm32->clk);
+ dev_err(dev, "Failed to get UART clock %d\n", ret);
+ goto io_release;
+ }
+
+ ret = clk_enable(stm32->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable UART clock %d\n", ret);
+ goto io_release;
+ }
+
+ cdev->dev = dev;
+ cdev->tstc = stm32_serial_tstc;
+ cdev->putc = stm32_serial_putc;
+ cdev->getc = stm32_serial_getc;
+ cdev->flush = stm32_serial_flush;
+ cdev->setbrg = stm32_serial_setbaudrate;
+
+ if (dev->device_node) {
+ devname = of_alias_get(dev->device_node);
+ if (devname) {
+ cdev->devname = xstrdup(devname);
+ cdev->devid = DEVICE_ID_SINGLE;
+ }
+ }
+
+ stm32_serial_init(cdev);
+
+ ret = console_register(cdev);
+ if (!ret)
+ return 0;
+
+ clk_put(stm32->clk);
+io_release:
+ release_region(res);
+err_free:
+ free(stm32);
+
+ return ret;
+}
+
+struct stm32_uart_info stm32f4_info = {
+ .stm32f4 = true,
+ .uart_enable_bit = 13,
+ .has_fifo = false,
+};
+
+struct stm32_uart_info stm32f7_info = {
+ .uart_enable_bit = 0,
+ .stm32f4 = false,
+ .has_fifo = true,
+};
+
+struct stm32_uart_info stm32h7_info = {
+ .uart_enable_bit = 0,
+ .stm32f4 = false,
+ .has_fifo = true,
+};
+
+static struct of_device_id stm32_serial_dt_ids[] = {
+ {
+ .compatible = "st,stm32-uart",
+ .data = &stm32f4_info
+ }, {
+ .compatible = "st,stm32f7-uart",
+ .data = &stm32f7_info
+ }, {
+ .compatible = "st,stm32h7-uart",
+ .data = &stm32h7_info
+ }, {
+ }
+};
+
+static struct driver_d stm32_serial_driver = {
+ .name = "stm32-serial",
+ .probe = stm32_serial_probe,
+ .of_compatible = DRV_OF_COMPAT(stm32_serial_dt_ids),
+};
+console_platform_driver(stm32_serial_driver);
diff --git a/drivers/serial/serial_stm32.h b/drivers/serial/serial_stm32.h
new file mode 100644
index 0000000..dd3e930
--- /dev/null
+++ b/drivers/serial/serial_stm32.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
+ */
+
+#ifndef _SERIAL_STM32_
+#define _SERIAL_STM32_
+
+#define CR1_OFFSET(x) (x ? 0x0c : 0x00)
+#define CR3_OFFSET(x) (x ? 0x14 : 0x08)
+#define BRR_OFFSET(x) (x ? 0x08 : 0x0c)
+#define ISR_OFFSET(x) (x ? 0x00 : 0x1c)
+
+#define ICR_OFFSET 0x20
+
+/*
+ * STM32F4 has one Data Register (DR) for received or transmitted
+ * data, so map Receive Data Register (RDR) and Transmit Data
+ * Register (TDR) at the same offset
+ */
+#define RDR_OFFSET(x) (x ? 0x04 : 0x24)
+#define TDR_OFFSET(x) (x ? 0x04 : 0x28)
+
+#define USART_CR1_FIFOEN BIT(29)
+#define USART_CR1_M1 BIT(28)
+#define USART_CR1_OVER8 BIT(15)
+#define USART_CR1_M0 BIT(12)
+#define USART_CR1_PCE BIT(10)
+#define USART_CR1_PS BIT(9)
+#define USART_CR1_TE BIT(3)
+#define USART_CR1_RE BIT(2)
+
+#define USART_CR3_OVRDIS BIT(12)
+
+#define USART_ISR_TXE BIT(7)
+#define USART_ISR_RXNE BIT(5)
+#define USART_ISR_ORE BIT(3)
+#define USART_ISR_PE BIT(0)
+
+#define USART_BRR_F_MASK GENMASK(7, 0)
+#define USART_BRR_M_SHIFT 4
+#define USART_BRR_M_MASK GENMASK(15, 4)
+
+#define USART_ICR_ORECF BIT(3)
+#define USART_ICR_PCECF BIT(0)
+
+#endif
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8ff3d18..99eff1c 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -7,21 +7,20 @@ menuconfig USB_HOST
if USB_HOST
-source drivers/usb/imx/Kconfig
+source "drivers/usb/imx/Kconfig"
source "drivers/usb/dwc3/Kconfig"
-source drivers/usb/host/Kconfig
+source "drivers/usb/host/Kconfig"
-source drivers/usb/otg/Kconfig
+source "drivers/usb/otg/Kconfig"
-source drivers/usb/storage/Kconfig
+source "drivers/usb/storage/Kconfig"
source "drivers/usb/misc/Kconfig"
endif
-source drivers/usb/gadget/Kconfig
-
-source drivers/usb/musb/Kconfig
+source "drivers/usb/gadget/Kconfig"
+source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 39e5fe6..d01a016 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -21,7 +21,6 @@
#include <init.h>
#include <malloc.h>
#include <errno.h>
-#include <scsi.h>
#include <usb/phy.h>
#include <usb/usb.h>
#include <usb/usb_defs.h>
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 7b00843..d29cd13 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -63,7 +63,6 @@
static int dev_count;
static int dev_index;
-static int asynch_allowed;
static LIST_HEAD(host_list);
LIST_HEAD(usb_device_list);
@@ -98,7 +97,6 @@ int usb_register_host(struct usb_host *host)
list_add_tail(&host->list, &host_list);
host->busnum = host_busnum++;
host->sem = 0;
- asynch_allowed = 1;
return 0;
}
@@ -559,16 +557,6 @@ void usb_rescan(void)
pr_info("%d USB Device(s) found\n", dev_count);
}
-/*
- * disables the asynch behaviour of the control message. This is used for data
- * transfers that uses the exclusiv access to the control and bulk messages.
- */
-void usb_disable_asynch(int disable)
-{
- asynch_allowed = !disable;
-}
-
-
/*-------------------------------------------------------------------
* Message wrappers.
*
@@ -597,10 +585,9 @@ int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe,
/*
* submits a control message and waits for completion (at least timeout * 1ms)
* If timeout is 0, we don't wait for completion (used as example to set and
- * clear keyboards LEDs). For data transfers, (storage transfers) we don't
- * allow control messages with 0 timeout, by previously resetting the flag
- * asynch_allowed (usb_disable_asynch(1)).
- * returns the transfered length if OK or -1 if error. The transfered length
+ * clear keyboards LEDs).
+ *
+ * Returns the transfered length if OK or -1 if error. The transfered length
* and the current status are stored in the dev->act_len and dev->status.
*/
int usb_control_msg(struct usb_device *dev, unsigned int pipe,
@@ -612,11 +599,6 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe,
int ret;
struct devrequest *setup_packet = dev->setup_packet;
- if ((timeout == 0) && (!asynch_allowed)) {
- /* request for a asynch control pipe is not allowed */
- return -1;
- }
-
ret = usb_host_acquire(host);
if (ret)
return ret;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2e7031a..60fd631 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -663,6 +663,25 @@ static void dwc3_check_params(struct dwc3 *dwc)
}
}
+static void dwc3_coresoft_reset(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg |= DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /*
+ * Similar reset sequence in U-Boot has a 100ms delay here. In
+ * practice reset sequence seem to work as expected even
+ * without a delay.
+ */
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
static int dwc3_probe(struct device_d *dev)
{
struct dwc3 *dwc;
@@ -695,6 +714,8 @@ static int dwc3_probe(struct device_d *dev)
if (ret)
return ret;
+ dwc3_coresoft_reset(dwc);
+
dwc3_cache_hwparams(dwc);
ret = dwc3_core_init(dwc);
diff --git a/drivers/usb/otg/twl4030.c b/drivers/usb/otg/twl4030.c
index fd5f593..3668870 100644
--- a/drivers/usb/otg/twl4030.c
+++ b/drivers/usb/otg/twl4030.c
@@ -64,7 +64,7 @@ static int twl4030_usb_read(u8 address)
return ret;
}
-void twl4030_power_init(void)
+static void twl4030_power_init(void)
{
unsigned char byte;
struct twl4030 *twl4030 = twl4030_get();
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 4140991..48ccee2 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -24,8 +24,6 @@
#include <errno.h>
#include <dma.h>
-#undef USB_STOR_DEBUG
-
#include "usb.h"
#include "transport.h"
@@ -62,6 +60,7 @@ static int usb_stor_Bulk_clear_endpt_stall(struct us_data *us, unsigned int pipe
/* Determine what the maximum LUN supported is */
int usb_stor_Bulk_max_lun(struct us_data *us)
{
+ struct device_d *dev = &us->pusb_dev->dev;
int len, ret = 0;
unsigned char *iobuf = dma_alloc(1);
@@ -73,8 +72,8 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, us->ifnum, iobuf, 1, USB_CNTL_TIMEOUT);
- US_DEBUGP("GetMaxLUN command result is %d, data is %d\n",
- len, (int)iobuf[0]);
+ dev_dbg(dev, "GetMaxLUN command result is %d, data is %d\n",
+ len, (int)iobuf[0]);
/* if we have a successful request, return the result */
if (len > 0)
@@ -92,8 +91,12 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
return ret;
}
-int usb_stor_Bulk_transport(ccb *srb, struct us_data *us)
+int usb_stor_Bulk_transport(struct us_blk_dev *usb_blkdev,
+ const u8 *cmd, u8 cmdlen,
+ void *data, u32 datalen)
{
+ struct us_data *us = usb_blkdev->us;
+ struct device_d *dev = &us->pusb_dev->dev;
struct bulk_cb_wrap cbw;
struct bulk_cs_wrap csw;
int actlen, data_actlen;
@@ -101,30 +104,28 @@ int usb_stor_Bulk_transport(ccb *srb, struct us_data *us)
unsigned int residue;
unsigned int pipein = usb_rcvbulkpipe(us->pusb_dev, us->recv_bulk_ep);
unsigned int pipeout = usb_sndbulkpipe(us->pusb_dev, us->send_bulk_ep);
- int dir_in = US_DIRECTION(srb->cmd[0]);
-
- srb->trans_bytes = 0;
+ int dir_in = US_DIRECTION(cmd[0]);
/* set up the command wrapper */
cbw.Signature = cpu_to_le32(US_BULK_CB_SIGN);
- cbw.DataTransferLength = cpu_to_le32(srb->datalen);
+ cbw.DataTransferLength = cpu_to_le32(datalen);
cbw.Flags = (dir_in ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT);
cbw.Tag = ++cbw_tag;
- cbw.Lun = srb->lun;
- cbw.Length = srb->cmdlen;
+ cbw.Lun = usb_blkdev->lun;
+ cbw.Length = cmdlen;
/* copy the command payload */
- memcpy(cbw.CDB, srb->cmd, cbw.Length);
+ memcpy(cbw.CDB, cmd, cbw.Length);
/* send it to out endpoint */
- US_DEBUGP("Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n",
- le32_to_cpu(cbw.Signature), cbw.Tag,
- le32_to_cpu(cbw.DataTransferLength), cbw.Flags,
- (cbw.Lun >> 4), (cbw.Lun & 0x0F),
- cbw.Length);
+ dev_dbg(dev, "Bulk Command S 0x%x T 0x%x L %d F %d Trg %d LUN %d CL %d\n",
+ le32_to_cpu(cbw.Signature), cbw.Tag,
+ le32_to_cpu(cbw.DataTransferLength), cbw.Flags,
+ (cbw.Lun >> 4), (cbw.Lun & 0x0F),
+ cbw.Length);
result = usb_bulk_msg(us->pusb_dev, pipeout, &cbw, US_BULK_CB_WRAP_LEN,
&actlen, USB_BULK_TO);
- US_DEBUGP("Bulk command transfer result=%d\n", result);
+ dev_dbg(dev, "Bulk command transfer result=%d\n", result);
if (result < 0) {
usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_FAILED;
@@ -136,36 +137,36 @@ int usb_stor_Bulk_transport(ccb *srb, struct us_data *us)
mdelay(1);
data_actlen = 0;
- if (srb->datalen) {
+ if (datalen) {
unsigned int pipe = dir_in ? pipein : pipeout;
- result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata,
- srb->datalen, &data_actlen, USB_BULK_TO);
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
+ result = usb_bulk_msg(us->pusb_dev, pipe, data,
+ datalen, &data_actlen, USB_BULK_TO);
+ dev_dbg(dev, "Bulk data transfer result 0x%x\n", result);
/* special handling of STALL in DATA phase */
if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) {
- US_DEBUGP("DATA: stall\n");
+ dev_dbg(dev, "DATA: stall\n");
/* clear the STALL on the endpoint */
result = usb_stor_Bulk_clear_endpt_stall(us, pipe);
}
if (result < 0) {
- US_DEBUGP("Device status: %lx\n", us->pusb_dev->status);
+ dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status);
usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_FAILED;
}
}
/* STATUS phase + error handling */
- US_DEBUGP("Attempting to get CSW...\n");
+ dev_dbg(dev, "Attempting to get CSW...\n");
result = usb_bulk_msg(us->pusb_dev, pipein, &csw, US_BULK_CS_WRAP_LEN,
&actlen, USB_BULK_TO);
/* did the endpoint stall? */
if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) {
- US_DEBUGP("STATUS: stall\n");
+ dev_dbg(dev, "STATUS: stall\n");
/* clear the STALL on the endpoint */
result = usb_stor_Bulk_clear_endpt_stall(us, pipein);
if (result >= 0) {
- US_DEBUGP("Attempting to get CSW...\n");
+ dev_dbg(dev, "Attempting to get CSW...\n");
result = usb_bulk_msg(us->pusb_dev, pipein,
&csw, US_BULK_CS_WRAP_LEN,
&actlen, USB_BULK_TO);
@@ -173,36 +174,35 @@ int usb_stor_Bulk_transport(ccb *srb, struct us_data *us)
}
if (result < 0) {
- US_DEBUGP("Device status: %lx\n", us->pusb_dev->status);
+ dev_dbg(dev, "Device status: %lx\n", us->pusb_dev->status);
usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_FAILED;
}
/* check bulk status */
residue = le32_to_cpu(csw.Residue);
- US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
- le32_to_cpu(csw.Signature), csw.Tag, residue, csw.Status);
+ dev_dbg(dev, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
+ le32_to_cpu(csw.Signature), csw.Tag, residue, csw.Status);
if (csw.Signature != cpu_to_le32(US_BULK_CS_SIGN)) {
- US_DEBUGP("Bad CSW signature\n");
+ dev_dbg(dev, "Bad CSW signature\n");
usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_FAILED;
} else if (csw.Tag != cbw_tag) {
- US_DEBUGP("Mismatching tag\n");
+ dev_dbg(dev, "Mismatching tag\n");
usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_FAILED;
} else if (csw.Status >= US_BULK_STAT_PHASE) {
- US_DEBUGP("Status >= phase\n");
+ dev_dbg(dev, "Status >= phase\n");
usb_stor_Bulk_reset(us);
return USB_STOR_TRANSPORT_ERROR;
- } else if (residue > srb->datalen) {
- US_DEBUGP("residue (%uB) > req data (%luB)\n",
- residue, srb->datalen);
+ } else if (residue > datalen) {
+ dev_dbg(dev, "residue (%uB) > req data (%uB)\n",
+ residue, datalen);
return USB_STOR_TRANSPORT_FAILED;
} else if (csw.Status == US_BULK_STAT_FAIL) {
- US_DEBUGP("FAILED\n");
+ dev_dbg(dev, "FAILED\n");
return USB_STOR_TRANSPORT_FAILED;
}
- srb->trans_bytes = min(srb->datalen - residue, (ulong)data_actlen);
return 0;
}
@@ -213,11 +213,12 @@ int usb_stor_Bulk_transport(ccb *srb, struct us_data *us)
*/
int usb_stor_Bulk_reset(struct us_data *us)
{
+ struct device_d *dev = &us->pusb_dev->dev;
int result;
int result2;
unsigned int pipe;
- US_DEBUGP("%s called\n", __func__);
+ dev_dbg(dev, "%s called\n", __func__);
/* issue the command */
result = usb_control_msg(us->pusb_dev,
@@ -226,24 +227,24 @@ int usb_stor_Bulk_reset(struct us_data *us)
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, us->ifnum, 0, 0, USB_CNTL_TIMEOUT);
if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) {
- US_DEBUGP("Soft reset stalled: %d\n", result);
+ dev_dbg(dev, "Soft reset stalled: %d\n", result);
return result;
}
mdelay(150);
/* clear the bulk endpoints halt */
- US_DEBUGP("Soft reset: clearing %s endpoint halt\n", "bulk-in");
+ dev_dbg(dev, "Soft reset: clearing %s endpoint halt\n", "bulk-in");
pipe = usb_rcvbulkpipe(us->pusb_dev, us->recv_bulk_ep);
result = usb_clear_halt(us->pusb_dev, pipe);
mdelay(150);
- US_DEBUGP("Soft reset: clearing %s endpoint halt\n", "bulk-out");
+ dev_dbg(dev, "Soft reset: clearing %s endpoint halt\n", "bulk-out");
pipe = usb_sndbulkpipe(us->pusb_dev, us->send_bulk_ep);
result2 = usb_clear_halt(us->pusb_dev, pipe);
mdelay(150);
if (result >= 0)
result = result2;
- US_DEBUGP("Soft reset %s\n", ((result < 0) ? "failed" : "done"));
+ dev_dbg(dev, "Soft reset %s\n", ((result < 0) ? "failed" : "done"));
return result;
}
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
index bc8a769..22d7dea 100644
--- a/drivers/usb/storage/transport.h
+++ b/drivers/usb/storage/transport.h
@@ -84,7 +84,7 @@ struct bulk_cs_wrap {
struct us_data;
-extern int usb_stor_Bulk_transport(ccb *, struct us_data *);
+extern trans_cmnd usb_stor_Bulk_transport;
extern int usb_stor_Bulk_max_lun(struct us_data *);
extern int usb_stor_Bulk_reset(struct us_data *);
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 8218bb2..fda24d6 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -26,164 +26,176 @@
#include <usb/usb.h>
#include <usb/usb_defs.h>
-#undef USB_STOR_DEBUG
+#include <asm/unaligned.h>
#include "usb.h"
#include "transport.h"
-static LIST_HEAD(us_blkdev_list);
-
-
/***********************************************************************
* USB Storage routines
***********************************************************************/
-static int usb_stor_inquiry(ccb *srb, struct us_data *us)
+#define USB_STOR_NO_REQUEST_SENSE -1
+
+static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev)
{
- int retries, result;
+ struct us_data *us = usb_blkdev->us;
+ struct device_d *dev = &us->pusb_dev->dev;
+ u8 cmd[6];
+ const u8 datalen = 18;
+ u8 *data = xzalloc(datalen);
- srb->datalen = min(128UL, srb->datalen);
- if (srb->datalen < 5) {
- US_DEBUGP("SCSI_INQUIRY: invalid data buffer size\n");
- return -EINVAL;
- }
+ dev_dbg(dev, "SCSI_REQ_SENSE\n");
- retries = 3;
- do {
- US_DEBUGP("SCSI_INQUIRY\n");
- memset(&srb->cmd[0], 0, 6);
- srb->cmdlen = 6;
- srb->cmd[0] = SCSI_INQUIRY;
- srb->cmd[3] = (u8)(srb->datalen >> 8);
- srb->cmd[4] = (u8)(srb->datalen >> 0);
- result = us->transport(srb, us);
- US_DEBUGP("SCSI_INQUIRY returns %d\n", result);
- } while ((result != USB_STOR_TRANSPORT_GOOD) && retries--);
-
- return (result != USB_STOR_TRANSPORT_GOOD) ? -EIO : 0;
-}
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = SCSI_REQ_SENSE;
+ cmd[4] = datalen;
+ us->transport(usb_blkdev, cmd, sizeof(cmd), data, datalen);
+ dev_dbg(dev, "Request Sense returned %02X %02X %02X\n",
+ data[2], data[12], data[13]);
-static int usb_stor_request_sense(ccb *srb, struct us_data *us)
-{
- unsigned char *pdata = srb->pdata;
- unsigned long datalen = srb->datalen;
-
- US_DEBUGP("SCSI_REQ_SENSE\n");
- srb->pdata = &srb->sense_buf[0];
- srb->datalen = 18;
- memset(&srb->cmd[0], 0, 6);
- srb->cmdlen = 6;
- srb->cmd[0] = SCSI_REQ_SENSE;
- srb->cmd[4] = (u8)(srb->datalen >> 0);
- us->transport(srb, us);
- US_DEBUGP("Request Sense returned %02X %02X %02X\n",
- srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]);
- srb->pdata = pdata;
- srb->datalen = datalen;
+ free(data);
return 0;
}
-static int usb_stor_test_unit_ready(ccb *srb, struct us_data *us)
+static const char *usb_stor_opcode_name(u8 opcode)
{
- int retries, result;
-
- retries = 10;
- do {
- US_DEBUGP("SCSI_TST_U_RDY\n");
- memset(&srb->cmd[0], 0, 12);
- srb->cmdlen = 12;
- srb->cmd[0] = SCSI_TST_U_RDY;
- srb->datalen = 0;
- result = us->transport(srb, us);
- US_DEBUGP("SCSI_TST_U_RDY returns %d\n", result);
- if (result == USB_STOR_TRANSPORT_GOOD)
- return 0;
- usb_stor_request_sense(srb, us);
- mdelay(100);
- } while (retries--);
-
- return -1;
+ switch (opcode) {
+ case SCSI_INQUIRY:
+ return "SCSI_INQUIRY";
+ case SCSI_TST_U_RDY:
+ return "SCSI_TST_U_RDY";
+ case SCSI_RD_CAPAC:
+ return "SCSI_RD_CAPAC";
+ case SCSI_READ10:
+ return "SCSI_READ10";
+ case SCSI_WRITE10:
+ return "SCSI_WRITE10";
+ };
+
+ return "UNKNOWN";
}
-static int usb_stor_read_capacity(ccb *srb, struct us_data *us)
+static int usb_stor_transport(struct us_blk_dev *usb_blkdev,
+ const u8 *cmd, u8 cmdlen,
+ void *data, u32 datalen,
+ int retries, int request_sense_delay_ms)
{
- int retries, result;
+ struct us_data *us = usb_blkdev->us;
+ struct device_d *dev = &us->pusb_dev->dev;
+ int i, ret;
+
- if (srb->datalen < 8) {
- US_DEBUGP("SCSI_RD_CAPAC: invalid data buffer size\n");
- return -EINVAL;
+ for (i = 0; i < retries; i++) {
+ dev_dbg(dev, "%s\n", usb_stor_opcode_name(cmd[0]));
+ ret = us->transport(usb_blkdev, cmd, cmdlen, data, datalen);
+ dev_dbg(dev, "%s returns %d\n", usb_stor_opcode_name(cmd[0]),
+ ret);
+ if (ret == USB_STOR_TRANSPORT_GOOD)
+ return 0;
+
+ if (request_sense_delay_ms == USB_STOR_NO_REQUEST_SENSE)
+ continue;
+
+ usb_stor_request_sense(usb_blkdev);
+
+ if (request_sense_delay_ms)
+ mdelay(request_sense_delay_ms);
}
- retries = 3;
- do {
- US_DEBUGP("SCSI_RD_CAPAC\n");
- memset(&srb->cmd[0], 0, 10);
- srb->cmdlen = 10;
- srb->cmd[0] = SCSI_RD_CAPAC;
- srb->datalen = 8;
- result = us->transport(srb, us);
- US_DEBUGP("SCSI_RD_CAPAC returns %d\n", result);
- } while ((result != USB_STOR_TRANSPORT_GOOD) && retries--);
-
- return (result != USB_STOR_TRANSPORT_GOOD) ? -EIO : 0;
+ return -EIO;
}
-static int usb_stor_read_10(ccb *srb, struct us_data *us,
- unsigned long start, unsigned short blocks)
+static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev)
{
- int retries, result;
-
- retries = 2;
- do {
- US_DEBUGP("SCSI_READ10: start %lx blocks %x\n", start, blocks);
- memset(&srb->cmd[0], 0, 10);
- srb->cmdlen = 10;
- srb->cmd[0] = SCSI_READ10;
- srb->cmd[2] = (u8)(start >> 24);
- srb->cmd[3] = (u8)(start >> 16);
- srb->cmd[4] = (u8)(start >> 8);
- srb->cmd[5] = (u8)(start >> 0);
- srb->cmd[7] = (u8)(blocks >> 8);
- srb->cmd[8] = (u8)(blocks >> 0);
- result = us->transport(srb, us);
- US_DEBUGP("SCSI_READ10 returns %d\n", result);
- if (result == USB_STOR_TRANSPORT_GOOD)
- return 0;
- usb_stor_request_sense(srb, us);
- } while (retries--);
+ struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
+ int ret;
+ u8 cmd[6];
+ const u16 datalen = 36;
+ u8 *data = xzalloc(datalen);
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = SCSI_INQUIRY;
+ put_unaligned_be16(datalen, &cmd[3]);
+
+ ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen,
+ 3, USB_STOR_NO_REQUEST_SENSE);
+ if (ret < 0) {
+ ret = -ENODEV;
+ goto exit;
+ }
- return -EIO;
+ dev_dbg(dev, "Peripheral type: %x, removable: %x\n",
+ data[0], (data[1] >> 7));
+ dev_dbg(dev, "ISO ver: %x, resp format: %x\n",
+ data[2], data[3]);
+ dev_dbg(dev, "Vendor/product/rev: %28s\n",
+ &data[8]);
+ // TODO: process and store device info
+
+exit:
+ free(data);
+ return ret;
}
-static int usb_stor_write_10(ccb *srb, struct us_data *us,
- unsigned long start, unsigned short blocks)
+static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev)
{
- int retries, result;
-
- retries = 2;
- do {
- US_DEBUGP("SCSI_WRITE10: start %lx blocks %x\n", start, blocks);
- memset(&srb->cmd[0], 0, 10);
- srb->cmdlen = 10;
- srb->cmd[0] = SCSI_WRITE10;
- srb->cmd[2] = (u8)(start >> 24);
- srb->cmd[3] = (u8)(start >> 16);
- srb->cmd[4] = (u8)(start >> 8);
- srb->cmd[5] = (u8)(start >> 0);
- srb->cmd[7] = (u8)(blocks >> 8);
- srb->cmd[8] = (u8)(blocks >> 0);
- result = us->transport(srb, us);
- US_DEBUGP("SCSI_WRITE10 returns %d\n", result);
- if (result == USB_STOR_TRANSPORT_GOOD)
- return 0;
- usb_stor_request_sense(srb, us);
- } while (retries--);
+ u8 cmd[12];
+ int ret;
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = SCSI_TST_U_RDY;
- return us->transport(srb, us);
+ ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0,
+ 10, 100);
+ if (ret < 0)
+ return -ENODEV;
+
+ return 0;
}
+static int usb_stor_read_capacity(struct us_blk_dev *usb_blkdev,
+ u32 *last_lba, u32 *block_length)
+{
+ struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
+ const u32 datalen = 8;
+ u32 *data = xzalloc(datalen);
+ u8 cmd[10];
+ int ret;
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = SCSI_RD_CAPAC;
+
+ ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen,
+ 3, USB_STOR_NO_REQUEST_SENSE);
+ if (ret < 0)
+ goto exit;
+
+ dev_dbg(dev, "Read Capacity returns: 0x%x, 0x%x\n",
+ data[0], data[1]);
+ *last_lba = be32_to_cpu(data[0]);
+ *block_length = be32_to_cpu(data[1]);
+
+exit:
+ free(data);
+ return ret;
+}
+
+static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode,
+ u32 start, u8 *data, u16 blocks)
+{
+ u8 cmd[10];
+
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = opcode;
+ put_unaligned_be32(start, &cmd[2]);
+ put_unaligned_be16(blocks, &cmd[7]);
+
+ return usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data,
+ blocks * SECTOR_SIZE, 2, 0);
+}
/***********************************************************************
* Disk driver interface
@@ -191,101 +203,59 @@ static int usb_stor_write_10(ccb *srb, struct us_data *us,
#define US_MAX_IO_BLK 32
-#define to_usb_mass_storage(x) container_of((x), struct us_blk_dev, blk)
-
-enum { io_rd, io_wr };
-
/* Read / write a chunk of sectors on media */
-static int usb_stor_blk_io(int io_op, struct block_device *disk_dev,
- int sector_start, int sector_count, void *buffer)
+static int usb_stor_blk_io(struct block_device *disk_dev,
+ int sector_start, int sector_count, void *buffer,
+ bool read)
{
- struct us_blk_dev *pblk_dev = to_usb_mass_storage(disk_dev);
+ struct us_blk_dev *pblk_dev = container_of(disk_dev,
+ struct us_blk_dev,
+ blk);
struct us_data *us = pblk_dev->us;
- ccb us_ccb;
- unsigned sectors_done;
-
- if (sector_count == 0)
- return 0;
-
- /* check for unsupported block size */
- if (pblk_dev->blk.blockbits != SECTOR_SHIFT) {
- US_DEBUGP("%s: unsupported block shift %d\n",
- __func__, pblk_dev->blk.blockbits);
- return -EINVAL;
- }
-
- /* check for invalid sector_start */
- if (sector_start >= pblk_dev->blk.num_blocks || sector_start > (ulong)-1) {
- US_DEBUGP("%s: start sector %d too large\n",
- __func__, sector_start);
- return -EINVAL;
- }
-
- us_ccb.lun = pblk_dev->lun;
- usb_disable_asynch(1);
+ struct device_d *dev = &us->pusb_dev->dev;
/* ensure unit ready */
- US_DEBUGP("Testing for unit ready\n");
- if (usb_stor_test_unit_ready(&us_ccb, us)) {
- US_DEBUGP("Device NOT ready\n");
- usb_disable_asynch(0);
+ dev_dbg(dev, "Testing for unit ready\n");
+ if (usb_stor_test_unit_ready(pblk_dev)) {
+ dev_dbg(dev, "Device NOT ready\n");
return -EIO;
}
- /* possibly limit the amount of I/O data */
- if (sector_count > INT_MAX) {
- sector_count = INT_MAX;
- US_DEBUGP("Restricting I/O to %u blocks\n", sector_count);
- }
- if (sector_start + sector_count > pblk_dev->blk.num_blocks) {
- sector_count = pblk_dev->blk.num_blocks - sector_start;
- US_DEBUGP("Restricting I/O to %u blocks\n", sector_count);
- }
-
/* read / write the requested data */
- US_DEBUGP("%s %u block(s), starting from %d\n",
- ((io_op == io_rd) ? "Read" : "Write"),
- sector_count, sector_start);
- sectors_done = 0;
+ dev_dbg(dev, "%s %u block(s), starting from %d\n",
+ read ? "Read" : "Write",
+ sector_count, sector_start);
+
while (sector_count > 0) {
- int result;
unsigned n = min(sector_count, US_MAX_IO_BLK);
- us_ccb.pdata = buffer + (sectors_done * SECTOR_SIZE);
- us_ccb.datalen = n * SECTOR_SIZE;
- if (io_op == io_rd)
- result = usb_stor_read_10(&us_ccb, us,
- (ulong)sector_start, n);
- else
- result = usb_stor_write_10(&us_ccb, us,
- (ulong)sector_start, n);
- if (result != 0) {
- US_DEBUGP("I/O error at sector %d\n", sector_start);
+
+ if (usb_stor_io_10(pblk_dev,
+ read ? SCSI_READ10 : SCSI_WRITE10,
+ sector_start,
+ buffer, n)) {
+ dev_dbg(dev, "I/O error at sector %d\n", sector_start);
break;
}
sector_start += n;
sector_count -= n;
- sectors_done += n;
+ buffer += n * SECTOR_SIZE;
}
- usb_disable_asynch(0);
-
- US_DEBUGP("Successful I/O of %d blocks\n", sectors_done);
-
- return (sector_count != 0) ? -EIO : 0;
+ return sector_count ? -EIO : 0;
}
/* Write a chunk of sectors to media */
static int __maybe_unused usb_stor_blk_write(struct block_device *blk,
const void *buffer, int block, int num_blocks)
{
- return usb_stor_blk_io(io_wr, blk, block, num_blocks, (void *)buffer);
+ return usb_stor_blk_io(blk, block, num_blocks, (void *)buffer, false);
}
/* Read a chunk of sectors from media */
static int usb_stor_blk_read(struct block_device *blk, void *buffer, int block,
int num_blocks)
{
- return usb_stor_blk_io(io_rd, blk, block, num_blocks, buffer);
+ return usb_stor_blk_io(blk, block, num_blocks, buffer, true);
}
static struct block_device_ops usb_mass_storage_ops = {
@@ -299,82 +269,61 @@ static struct block_device_ops usb_mass_storage_ops = {
* Block device routines
***********************************************************************/
-static unsigned char us_io_buf[512];
-
-static int usb_limit_blk_cnt(unsigned cnt)
-{
- if (cnt > 0x7fffffff) {
- pr_warn("Limiting device size due to 31 bit contraints\n");
- return 0x7fffffff;
- }
-
- return (int)cnt;
-}
-
/* Prepare a disk device */
static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
{
struct us_data *us = pblk_dev->us;
- ccb us_ccb;
- unsigned long *pcap;
- int result = 0;
-
- us_ccb.pdata = us_io_buf;
- us_ccb.lun = pblk_dev->lun;
-
- pblk_dev->blk.num_blocks = 0;
- usb_disable_asynch(1);
+ struct device_d *dev = &us->pusb_dev->dev;
+ u32 last_lba = 0, block_length = 0;
+ int result;
/* get device info */
- US_DEBUGP("Reading device info\n");
- us_ccb.datalen = 36;
- if (usb_stor_inquiry(&us_ccb, us)) {
- US_DEBUGP("Cannot read device info\n");
- result = -ENODEV;
- goto Exit;
+ dev_dbg(dev, "Reading device info\n");
+
+ result = usb_stor_inquiry(pblk_dev);
+ if (result) {
+ dev_dbg(dev, "Cannot read device info\n");
+ return result;
}
- US_DEBUGP("Peripheral type: %x, removable: %x\n",
- us_io_buf[0], (us_io_buf[1] >> 7));
- US_DEBUGP("ISO ver: %x, resp format: %x\n", us_io_buf[2], us_io_buf[3]);
- US_DEBUGP("Vendor/product/rev: %28s\n", &us_io_buf[8]);
- // TODO: process and store device info
/* ensure unit ready */
- US_DEBUGP("Testing for unit ready\n");
- us_ccb.datalen = 0;
- if (usb_stor_test_unit_ready(&us_ccb, us)) {
- US_DEBUGP("Device NOT ready\n");
- result = -ENODEV;
- goto Exit;
+ dev_dbg(dev, "Testing for unit ready\n");
+
+ result = usb_stor_test_unit_ready(pblk_dev);
+ if (result) {
+ dev_dbg(dev, "Device NOT ready\n");
+ return result;
}
/* read capacity */
- US_DEBUGP("Reading capacity\n");
- memset(us_ccb.pdata, 0, 8);
- us_ccb.datalen = sizeof(us_io_buf);
- if (usb_stor_read_capacity(&us_ccb, us) != 0) {
- US_DEBUGP("Cannot read device capacity\n");
- result = -EIO;
- goto Exit;
+ dev_dbg(dev, "Reading capacity\n");
+
+ result = usb_stor_read_capacity(pblk_dev, &last_lba, &block_length);
+ if (result < 0) {
+ dev_dbg(dev, "Cannot read device capacity\n");
+ return result;
}
- pcap = (unsigned long *)us_ccb.pdata;
- US_DEBUGP("Read Capacity returns: 0x%lx, 0x%lx\n", pcap[0], pcap[1]);
- pblk_dev->blk.num_blocks = usb_limit_blk_cnt(be32_to_cpu(pcap[0]) + 1);
- if (be32_to_cpu(pcap[1]) != SECTOR_SIZE)
+
+ if (last_lba > INT_MAX - 1) {
+ last_lba = INT_MAX - 1;
+ dev_warn(dev,
+ "Limiting device size due to 31 bit contraints\n");
+ }
+
+ pblk_dev->blk.num_blocks = last_lba + 1;
+ if (block_length != SECTOR_SIZE)
pr_warn("Support only %d bytes sectors\n", SECTOR_SIZE);
pblk_dev->blk.blockbits = SECTOR_SHIFT;
- US_DEBUGP("Capacity = 0x%x, blockshift = 0x%x\n",
- pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits);
+ dev_dbg(dev, "Capacity = 0x%x, blockshift = 0x%x\n",
+ pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits);
-Exit:
- usb_disable_asynch(0);
- return result;
+ return 0;
}
/* Create and register a disk device for the specified LUN */
-static int usb_stor_add_blkdev(struct us_data *us, struct device_d *dev,
- unsigned char lun)
+static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun)
{
+ struct device_d *dev = &us->pusb_dev->dev;
struct us_blk_dev *pblk_dev;
int result;
@@ -412,12 +361,12 @@ static int usb_stor_add_blkdev(struct us_data *us, struct device_d *dev,
dev_warn(dev, "No partition table found\n");
list_add_tail(&pblk_dev->list, &us->blk_dev_list);
- US_DEBUGP("USB disk device successfully added\n");
+ dev_dbg(dev, "USB disk device successfully added\n");
return 0;
BadDevice:
- US_DEBUGP("%s failed with %d\n", __func__, result);
+ dev_dbg(dev, "%s failed with %d\n", __func__, result);
free(pblk_dev);
return result;
}
@@ -429,6 +378,7 @@ BadDevice:
/* Get the transport settings */
static void get_transport(struct us_data *us)
{
+ struct device_d *dev = &us->pusb_dev->dev;
switch (us->protocol) {
case US_PR_BULK:
us->transport_name = "Bulk";
@@ -437,17 +387,17 @@ static void get_transport(struct us_data *us)
break;
}
- US_DEBUGP("Transport: %s\n", us->transport_name);
+ dev_dbg(dev, "Transport: %s\n", us->transport_name);
}
/* Get the endpoint settings */
static int get_pipes(struct us_data *us, struct usb_interface *intf)
{
+ struct device_d *dev = &us->pusb_dev->dev;
unsigned int i;
struct usb_endpoint_descriptor *ep;
struct usb_endpoint_descriptor *ep_in = NULL;
struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_endpoint_descriptor *ep_int = NULL;
/*
* Find the first endpoint of each type we need.
@@ -468,44 +418,38 @@ static int get_pipes(struct us_data *us, struct usb_interface *intf)
ep_out = ep;
}
}
- else if (USB_EP_IS_INT_IN(ep)) {
- if (!ep_int)
- ep_int = ep;
- }
}
- if (!ep_in || !ep_out || (us->protocol == US_PR_CBI && !ep_int)) {
- US_DEBUGP("Endpoint sanity check failed! Rejecting dev.\n");
+ if (!ep_in || !ep_out || us->protocol == US_PR_CBI) {
+ dev_dbg(dev, "Endpoint sanity check failed! Rejecting dev.\n");
return -EIO;
}
/* Store the pipe values */
us->send_bulk_ep = USB_EP_NUM(ep_out);
us->recv_bulk_ep = USB_EP_NUM(ep_in);
- if (ep_int) {
- us->recv_intr_ep = USB_EP_NUM(ep_int);
- us->ep_bInterval = ep_int->bInterval;
- }
+
return 0;
}
/* Scan device's LUNs, registering a disk device for each LUN */
static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us)
{
+ struct device_d *dev = &usbdev->dev;
unsigned char lun;
int num_devs = 0;
/* obtain the max LUN */
- us->max_lun = 0;
+ us->max_lun = 1;
if (us->protocol == US_PR_BULK)
us->max_lun = usb_stor_Bulk_max_lun(us);
/* register a disk device for each active LUN */
for (lun=0; lun<=us->max_lun; lun++) {
- if (usb_stor_add_blkdev(us, &usbdev->dev, lun) == 0)
+ if (usb_stor_add_blkdev(us, lun) == 0)
num_devs++;
}
- US_DEBUGP("Found %d block devices on %s\n", num_devs, usbdev->dev.name);
+ dev_dbg(dev, "Found %d block devices on %s\n", num_devs, usbdev->dev.name);
return num_devs ? 0 : -ENODEV;
}
@@ -514,12 +458,13 @@ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us)
static int usb_stor_probe(struct usb_device *usbdev,
const struct usb_device_id *id)
{
+ struct device_d *dev = &usbdev->dev;
struct us_data *us;
int result;
int ifno;
struct usb_interface *intf;
- US_DEBUGP("Supported USB Mass Storage device detected\n");
+ dev_dbg(dev, "Supported USB Mass Storage device detected\n");
/* scan usbdev interfaces again to find one that we can handle */
for (ifno=0; ifno<usbdev->config.no_of_if; ifno++) {
@@ -538,19 +483,14 @@ static int usb_stor_probe(struct usb_device *usbdev,
if (result)
return result;
- US_DEBUGP("Selected interface %d\n", (int)intf->desc.bInterfaceNumber);
+ dev_dbg(dev, "Selected interface %d\n", (int)intf->desc.bInterfaceNumber);
/* allocate us_data structure */
- us = (struct us_data *)malloc(sizeof(struct us_data));
- if (!us)
- return -ENOMEM;
- memset(us, 0, sizeof(struct us_data));
+ us = xzalloc(sizeof(*us));
/* initialize the us_data structure */
us->pusb_dev = usbdev;
- us->flags = 0;
us->ifnum = intf->desc.bInterfaceNumber;
- us->subclass = intf->desc.bInterfaceSubClass;
us->protocol = intf->desc.bInterfaceProtocol;
INIT_LIST_HEAD(&us->blk_dev_list);
@@ -571,7 +511,7 @@ static int usb_stor_probe(struct usb_device *usbdev,
return 0;
BadDevice:
- US_DEBUGP("%s failed with %d\n", __func__, result);
+ dev_dbg(dev, "%s failed with %d\n", __func__, result);
free(us);
return result;
}
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index e649d53..cd4904f 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -28,13 +28,6 @@
#include <linux/list.h>
-#ifdef USB_STOR_DEBUG
-#define US_DEBUGP(fmt, args...) printf(fmt , ##args)
-#else
-#define US_DEBUGP(fmt, args...)
-#endif
-
-
/* some defines, similar to ch9.h */
#define USB_EP_NUM(epd) \
((epd)->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)
@@ -51,32 +44,30 @@
struct us_data;
+struct us_blk_dev;
-typedef int (*trans_cmnd)(ccb *cb, struct us_data *data);
+typedef int (trans_cmnd)(struct us_blk_dev *usb_blkdev,
+ const u8 *cmd, u8 cmdlen,
+ void *data, u32 datalen);
typedef int (*trans_reset)(struct us_data *data);
/* one us_data object allocated per usb storage device */
struct us_data {
struct usb_device *pusb_dev; /* this usb_device */
- unsigned int flags; /* from filter */
unsigned char send_bulk_ep; /* used endpoints */
unsigned char recv_bulk_ep;
- unsigned char recv_intr_ep;
unsigned char ifnum; /* interface number */
- unsigned char subclass;
unsigned char protocol;
unsigned char max_lun;
- unsigned char ep_bInterval;
char *transport_name;
- trans_cmnd transport; /* transport function */
+ trans_cmnd *transport; /* transport function */
trans_reset transport_reset;/* transport device reset */
/* SCSI interfaces */
- ccb *srb; /* current srb */
struct list_head blk_dev_list;
};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 79de32c..a26bace 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -96,7 +96,7 @@ config DRIVER_VIDEO_BCM283X
help
Add support for the BCM283X/VideoCore frame buffer device.
-source drivers/video/imx-ipu-v3/Kconfig
+source "drivers/video/imx-ipu-v3/Kconfig"
config DRIVER_VIDEO_SIMPLEFB
bool "Simple framebuffer support"
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
index 20f9354..17d8823 100644
--- a/drivers/video/of_display_timing.c
+++ b/drivers/video/of_display_timing.c
@@ -14,7 +14,8 @@
void display_timings_release(struct display_timings *disp)
{
- free(disp->modes);
+ if (disp->modes)
+ free(disp->modes);
free(disp);
}
EXPORT_SYMBOL_GPL(display_timings_release);
@@ -43,7 +44,7 @@ static int parse_timing_property(const struct device_node *np, const char *name,
}
cells = length / sizeof(u32);
- if (cells == 1) {
+ if ((cells == 1) || (cells == 3)) {
ret = of_property_read_u32(np, name, res);
} else {
pr_err("%s: illegal timing specification in %s\n",
@@ -129,7 +130,7 @@ struct display_timings *of_get_display_timings(struct device_node *np)
if (!entry) {
pr_err("%s: no timing specifications given\n",
np->full_name);
- goto entryfail;
+ goto fail;
}
pr_debug("%s: using %s as default timing\n",
@@ -141,7 +142,7 @@ struct display_timings *of_get_display_timings(struct device_node *np)
if (disp->num_modes == 0) {
/* should never happen, as entry was already found above */
pr_err("%s: no timings specified\n", np->full_name);
- goto entryfail;
+ goto fail;
}
disp->modes = xzalloc(sizeof(struct fb_videomode) * disp->num_modes);
@@ -163,7 +164,7 @@ struct display_timings *of_get_display_timings(struct device_node *np)
*/
pr_err("%s: error in timing %d\n",
np->full_name, disp->num_modes + 1);
- goto timingfail;
+ goto fail;
}
mode->name = xstrdup(entry->name);
@@ -180,10 +181,8 @@ struct display_timings *of_get_display_timings(struct device_node *np)
return disp;
-timingfail:
+fail:
display_timings_release(disp);
-entryfail:
- free(disp);
return NULL;
}
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig
index dbc1e3c..4a16197 100644
--- a/drivers/w1/Kconfig
+++ b/drivers/w1/Kconfig
@@ -8,8 +8,8 @@ menuconfig W1
if W1
-source drivers/w1/masters/Kconfig
-source drivers/w1/slaves/Kconfig
+source "drivers/w1/masters/Kconfig"
+source "drivers/w1/slaves/Kconfig"
config W1_DUAL_SEARCH
bool "dual search"
diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c
index 6446f4b..ab2ceff 100644
--- a/drivers/w1/slaves/w1_ds2431.c
+++ b/drivers/w1/slaves/w1_ds2431.c
@@ -60,7 +60,7 @@ static inline size_t ds2431_fix_count(loff_t off, size_t count, size_t size)
*
* count must not exceed W1_F2D_READ_MAXLEN.
*/
-int ds2431_readblock(struct w1_device *dev, int off, int count, char *buf)
+static int ds2431_readblock(struct w1_device *dev, int off, int count, char *buf)
{
struct w1_bus *bus = dev->bus;
u8 wrbuf[3];
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2793ee9..04efb1a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -48,7 +48,7 @@ config WATCHDOG_MXS28
config WATCHDOG_IMX
bool "i.MX watchdog"
- depends on ARCH_IMX
+ depends on ARCH_IMX || ARCH_LAYERSCAPE
help
Add support for watchdog found on Freescale i.MX SoCs.
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 69189ba..6c8d36c 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o
obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o
obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
+obj-$(CONFIG_WATCHDOG_IMX) += imxwd.o
obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
diff --git a/drivers/watchdog/imxwd.c b/drivers/watchdog/imxwd.c
index 8dba662..77a3bd7 100644
--- a/drivers/watchdog/imxwd.c
+++ b/drivers/watchdog/imxwd.c
@@ -38,6 +38,7 @@ struct imx_wd {
const struct imx_wd_ops *ops;
struct restart_handler restart;
bool ext_reset;
+ bool bigendian;
};
#define to_imx_wd(h) container_of(h, struct imx_wd, wd)
@@ -66,6 +67,22 @@ struct imx_wd {
/* valid for i.MX27, i.MX31, always '0' on i.MX25, i.MX35, i.MX51 */
#define WSTR_COLDSTART (1 << 4)
+static void imxwd_write(struct imx_wd *priv, int reg, uint16_t val)
+{
+ if (priv->bigendian)
+ out_be16(priv->base + reg, val);
+ else
+ writew(val, priv->base + reg);
+}
+
+static uint16_t imxwd_read(struct imx_wd *priv, int reg)
+{
+ if (priv->bigendian)
+ return in_be16(priv->base + reg);
+ else
+ return readw(priv->base + reg);
+}
+
static int imx1_watchdog_set_timeout(struct imx_wd *priv, unsigned timeout)
{
u16 val;
@@ -73,18 +90,18 @@ static int imx1_watchdog_set_timeout(struct imx_wd *priv, unsigned timeout)
dev_dbg(priv->dev, "%s: %d\n", __func__, timeout);
if (!timeout) {
- writew(IMX1_WDOG_WCR_WHALT, priv->base + IMX1_WDOG_WCR);
+ imxwd_write(priv, IMX1_WDOG_WCR, IMX1_WDOG_WCR_WHALT);
return 0;
}
val = (timeout * 2 - 1) << 8;
- writew(val, priv->base + IMX1_WDOG_WCR);
- writew(IMX1_WDOG_WCR_WDE | val, priv->base + IMX1_WDOG_WCR);
+ imxwd_write(priv, IMX1_WDOG_WCR, val);
+ imxwd_write(priv, IMX1_WDOG_WCR, IMX1_WDOG_WCR_WDE | val);
/* Write Service Sequence */
- writew(0x5555, priv->base + IMX1_WDOG_WSR);
- writew(0xaaaa, priv->base + IMX1_WDOG_WSR);
+ imxwd_write(priv, IMX1_WDOG_WSR, 0x5555);
+ imxwd_write(priv, IMX1_WDOG_WSR, 0xaaaa);
return 0;
}
@@ -113,13 +130,13 @@ static int imx21_watchdog_set_timeout(struct imx_wd *priv, unsigned timeout)
* set time and some write once bits first prior enabling the
* watchdog according to the datasheet
*/
- writew(val, priv->base + IMX21_WDOG_WCR);
+ imxwd_write(priv, IMX21_WDOG_WCR, val);
- writew(IMX21_WDOG_WCR_WDE | val, priv->base + IMX21_WDOG_WCR);
+ imxwd_write(priv, IMX21_WDOG_WCR, IMX21_WDOG_WCR_WDE | val);
/* Write Service Sequence */
- writew(0x5555, priv->base + IMX21_WDOG_WSR);
- writew(0xaaaa, priv->base + IMX21_WDOG_WSR);
+ imxwd_write(priv, IMX21_WDOG_WSR, 0x5555);
+ imxwd_write(priv, IMX21_WDOG_WSR, 0xaaaa);
return 0;
}
@@ -134,11 +151,11 @@ static void imx21_soc_reset(struct imx_wd *priv)
else
val |= IMX21_WDOG_WCR_WDA; /* do not assert ext-reset */
- writew(val, priv->base + IMX21_WDOG_WCR);
+ imxwd_write(priv, IMX21_WDOG_WCR, val);
/* Two additional writes due to errata ERR004346 */
- writew(val, priv->base + IMX21_WDOG_WCR);
- writew(val, priv->base + IMX21_WDOG_WCR);
+ imxwd_write(priv, IMX21_WDOG_WCR, val);
+ imxwd_write(priv, IMX21_WDOG_WCR, val);
}
static int imx_watchdog_set_timeout(struct watchdog *wd, unsigned timeout)
@@ -161,7 +178,7 @@ static void __noreturn imxwd_force_soc_reset(struct restart_handler *rst)
static void imx_watchdog_detect_reset_source(struct imx_wd *priv)
{
- u16 val = readw(priv->base + IMX21_WDOG_WSTR);
+ u16 val = imxwd_read(priv, IMX21_WDOG_WSTR);
int priority = RESET_SOURCE_DEFAULT_PRIORITY;
if (reset_source_get() == RESET_WDG)
@@ -192,7 +209,7 @@ static int imx21_wd_init(struct imx_wd *priv)
/*
* Disable watchdog powerdown counter
*/
- writew(0x0, priv->base + IMX21_WDOG_WMCR);
+ imxwd_write(priv, IMX21_WDOG_WMCR, 0x0);
return 0;
}
@@ -220,6 +237,7 @@ static int imx_wd_probe(struct device_d *dev)
priv->wd.timeout_max = priv->ops->timeout_max;
priv->wd.hwdev = dev;
priv->dev = dev;
+ priv->bigendian = of_device_is_big_endian(dev->device_node);
priv->ext_reset = of_property_read_bool(dev->device_node,
"fsl,ext-reset-output");