summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig6
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/mpc83xx_clk.c410
-rw-r--r--drivers/clk/mpc83xx_clk.h379
-rw-r--r--drivers/core/fdtaddr.c2
-rw-r--r--drivers/core/of_access.c27
-rw-r--r--drivers/core/ofnode.c14
-rw-r--r--drivers/core/root.c35
-rw-r--r--drivers/cpu/Kconfig7
-rw-r--r--drivers/cpu/Makefile2
-rw-r--r--drivers/cpu/cpu-uclass.c23
-rw-r--r--drivers/cpu/cpu_sandbox.c61
-rw-r--r--drivers/cpu/mpc83xx_cpu.c349
-rw-r--r--drivers/cpu/mpc83xx_cpu.h126
-rw-r--r--drivers/misc/Kconfig7
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/mpc83xx_serdes.c185
-rw-r--r--drivers/misc/mpc83xx_serdes.h232
-rw-r--r--drivers/ram/Kconfig9
-rw-r--r--drivers/ram/Makefile1
-rw-r--r--drivers/ram/mpc83xx_sdram.c1096
-rw-r--r--drivers/sysreset/Kconfig5
-rw-r--r--drivers/sysreset/Makefile9
-rw-r--r--drivers/sysreset/sysreset-uclass.c10
-rw-r--r--drivers/sysreset/sysreset_mpc83xx.c212
-rw-r--r--drivers/sysreset/sysreset_mpc83xx.h103
-rw-r--r--drivers/sysreset/sysreset_sandbox.c16
-rw-r--r--drivers/timer/Kconfig7
-rw-r--r--drivers/timer/Makefile17
-rw-r--r--drivers/timer/mpc83xx_timer.c249
30 files changed, 3575 insertions, 26 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 809eb3dacf..c996d6574b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -107,4 +107,10 @@ config ICS8N3QV01
Crystal Oscillator). The output frequency can be programmed via an
I2C interface.
+config CLK_MPC83XX
+ bool "Enable MPC83xx clock driver"
+ depends on CLK
+ help
+ Support for the clock driver of the MPC83xx series of SoCs.
+
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 82c36b7478..11468f2ee6 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
obj-$(CONFIG_CLK_EXYNOS) += exynos/
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
+obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
obj-$(CONFIG_CLK_OWL) += owl/
obj-$(CONFIG_CLK_RENESAS) += renesas/
obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o
diff --git a/drivers/clk/mpc83xx_clk.c b/drivers/clk/mpc83xx_clk.c
new file mode 100644
index 0000000000..489004190e
--- /dev/null
+++ b/drivers/clk/mpc83xx_clk.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2017
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dt-bindings/clk/mpc83xx-clk.h>
+#include <asm/arch/soc.h>
+
+#include "mpc83xx_clk.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct mpc83xx_clk_priv - Private data structure for the MPC83xx clock
+ * driver
+ * @speed: Array containing the speed values of all system clocks (initialized
+ * once, then only read back)
+ */
+struct mpc83xx_clk_priv {
+ u32 speed[MPC83XX_CLK_COUNT];
+};
+
+/**
+ * is_clk_valid() - Check if clock ID is valid for given clock device
+ * @clk: The clock device for which to check a clock ID
+ * @id: The clock ID to check
+ *
+ * Return: true if clock ID is valid for clock device, false if not
+ */
+static inline bool is_clk_valid(struct udevice *clk, int id)
+{
+ ulong type = dev_get_driver_data(clk);
+
+ switch (id) {
+ case MPC83XX_CLK_MEM:
+ return true;
+ case MPC83XX_CLK_MEM_SEC:
+ return type == SOC_MPC8360;
+ case MPC83XX_CLK_ENC:
+ return (type == SOC_MPC8308) || (type == SOC_MPC8309);
+ case MPC83XX_CLK_I2C1:
+ return true;
+ case MPC83XX_CLK_TDM:
+ return type == SOC_MPC8315;
+ case MPC83XX_CLK_SDHC:
+ return mpc83xx_has_sdhc(type);
+ case MPC83XX_CLK_TSEC1:
+ case MPC83XX_CLK_TSEC2:
+ return mpc83xx_has_tsec(type);
+ case MPC83XX_CLK_USBDR:
+ return type == SOC_MPC8360;
+ case MPC83XX_CLK_USBMPH:
+ return type == SOC_MPC8349;
+ case MPC83XX_CLK_PCIEXP1:
+ return mpc83xx_has_pcie1(type);
+ case MPC83XX_CLK_PCIEXP2:
+ return mpc83xx_has_pcie2(type);
+ case MPC83XX_CLK_SATA:
+ return mpc83xx_has_sata(type);
+ case MPC83XX_CLK_DMAC:
+ return (type == SOC_MPC8308) || (type == SOC_MPC8309);
+ case MPC83XX_CLK_PCI:
+ return mpc83xx_has_pci(type);
+ case MPC83XX_CLK_CSB:
+ return true;
+ case MPC83XX_CLK_I2C2:
+ return mpc83xx_has_second_i2c(type);
+ case MPC83XX_CLK_QE:
+ case MPC83XX_CLK_BRG:
+ return mpc83xx_has_quicc_engine(type) && (type != SOC_MPC8309);
+ case MPC83XX_CLK_LCLK:
+ case MPC83XX_CLK_LBIU:
+ case MPC83XX_CLK_CORE:
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * init_single_clk() - Initialize a clock with a given ID
+ * @dev: The clock device for which to initialize the clock
+ * @clk: The clock ID
+ *
+ * The clock speed is read from the hardware's registers, and stored in the
+ * private data structure of the driver. From there it is only retrieved, and
+ * not set.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int init_single_clk(struct udevice *dev, int clk)
+{
+ struct mpc83xx_clk_priv *priv = dev_get_priv(dev);
+ immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ ulong type = dev_get_driver_data(dev);
+ struct clk_mode mode;
+ ulong mask;
+ u32 csb_clk = get_csb_clk(im);
+ int ret;
+
+ ret = retrieve_mode(clk, type, &mode);
+ if (ret) {
+ debug("%s: Could not retrieve mode for clk %d (ret = %d)\n",
+ dev->name, clk, ret);
+ return ret;
+ }
+
+ if (mode.type == TYPE_INVALID) {
+ debug("%s: clock %d invalid\n", dev->name, clk);
+ return -EINVAL;
+ }
+
+ if (mode.type == TYPE_SCCR_STANDARD) {
+ mask = GENMASK(31 - mode.low, 31 - mode.high);
+
+ switch (sccr_field(im, mask)) {
+ case 0:
+ priv->speed[clk] = 0;
+ break;
+ case 1:
+ priv->speed[clk] = csb_clk;
+ break;
+ case 2:
+ priv->speed[clk] = csb_clk / 2;
+ break;
+ case 3:
+ priv->speed[clk] = csb_clk / 3;
+ break;
+ default:
+ priv->speed[clk] = 0;
+ }
+
+ return 0;
+ }
+
+ if (mode.type == TYPE_SPMR_DIRECT_MULTIPLY) {
+ mask = GENMASK(31 - mode.low, 31 - mode.high);
+
+ priv->speed[clk] = csb_clk * (1 + sccr_field(im, mask));
+ return 0;
+ }
+
+ if (clk == MPC83XX_CLK_CSB || clk == MPC83XX_CLK_I2C2) {
+ priv->speed[clk] = csb_clk; /* i2c-2 clk is equal to csb clk */
+ return 0;
+ }
+
+ if (clk == MPC83XX_CLK_QE || clk == MPC83XX_CLK_BRG) {
+ u32 pci_sync_in = get_pci_sync_in(im);
+ u32 qepmf = spmr_field(im, SPMR_CEPMF);
+ u32 qepdf = spmr_field(im, SPMR_CEPDF);
+ u32 qe_clk = (pci_sync_in * qepmf) / (1 + qepdf);
+
+ if (clk == MPC83XX_CLK_QE)
+ priv->speed[clk] = qe_clk;
+ else
+ priv->speed[clk] = qe_clk / 2;
+
+ return 0;
+ }
+
+ if (clk == MPC83XX_CLK_LCLK || clk == MPC83XX_CLK_LBIU) {
+ u32 lbiu_clk = csb_clk *
+ (1 + spmr_field(im, SPMR_LBIUCM));
+ u32 clkdiv = lcrr_field(im, LCRR_CLKDIV);
+
+ if (clk == MPC83XX_CLK_LBIU)
+ priv->speed[clk] = lbiu_clk;
+
+ switch (clkdiv) {
+ case 2:
+ case 4:
+ case 8:
+ priv->speed[clk] = lbiu_clk / clkdiv;
+ break;
+ default:
+ /* unknown lcrr */
+ priv->speed[clk] = 0;
+ }
+
+ return 0;
+ }
+
+ if (clk == MPC83XX_CLK_CORE) {
+ u8 corepll = spmr_field(im, SPMR_COREPLL);
+ u32 corecnf_tab_index = ((corepll & 0x1F) << 2) |
+ ((corepll & 0x60) >> 5);
+
+ if (corecnf_tab_index > (ARRAY_SIZE(corecnf_tab))) {
+ debug("%s: Core configuration index %02x too high; possible wrong value",
+ dev->name, corecnf_tab_index);
+ return -EINVAL;
+ }
+
+ switch (corecnf_tab[corecnf_tab_index].core_csb_ratio) {
+ case RAT_BYP:
+ case RAT_1_TO_1:
+ priv->speed[clk] = csb_clk;
+ break;
+ case RAT_1_5_TO_1:
+ priv->speed[clk] = (3 * csb_clk) / 2;
+ break;
+ case RAT_2_TO_1:
+ priv->speed[clk] = 2 * csb_clk;
+ break;
+ case RAT_2_5_TO_1:
+ priv->speed[clk] = (5 * csb_clk) / 2;
+ break;
+ case RAT_3_TO_1:
+ priv->speed[clk] = 3 * csb_clk;
+ break;
+ default:
+ /* unknown core to csb ratio */
+ priv->speed[clk] = 0;
+ }
+
+ return 0;
+ }
+
+ /* Unknown clk value -> error */
+ debug("%s: clock %d invalid\n", dev->name, clk);
+ return -EINVAL;
+}
+
+/**
+ * init_all_clks() - Initialize all clocks of a clock device
+ * @dev: The clock device whose clocks should be initialized
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static inline int init_all_clks(struct udevice *dev)
+{
+ int i;
+
+ for (i = 0; i < MPC83XX_CLK_COUNT; i++) {
+ int ret;
+
+ if (!is_clk_valid(dev, i))
+ continue;
+
+ ret = init_single_clk(dev, i);
+ if (ret) {
+ debug("%s: Failed to initialize %s clock\n",
+ dev->name, names[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mpc83xx_clk_request(struct clk *clock)
+{
+ /* Reject requests of clocks that are not available */
+ if (is_clk_valid(clock->dev, clock->id))
+ return 0;
+ else
+ return -ENODEV;
+}
+
+static ulong mpc83xx_clk_get_rate(struct clk *clk)
+{
+ struct mpc83xx_clk_priv *priv = dev_get_priv(clk->dev);
+
+ if (clk->id >= MPC83XX_CLK_COUNT) {
+ debug("%s: clock index %lu invalid\n", __func__, clk->id);
+ return 0;
+ }
+
+ return priv->speed[clk->id];
+}
+
+int get_clocks(void)
+{
+ /* Empty implementation to keep the prototype in common.h happy */
+ return 0;
+}
+
+int get_serial_clock(void)
+{
+ struct mpc83xx_clk_priv *priv;
+ struct udevice *clk;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_CLK, &clk);
+ if (ret) {
+ debug("%s: Could not get clock device\n", __func__);
+ return ret;
+ }
+
+ priv = dev_get_priv(clk);
+
+ return priv->speed[MPC83XX_CLK_CSB];
+}
+
+const struct clk_ops mpc83xx_clk_ops = {
+ .request = mpc83xx_clk_request,
+ .get_rate = mpc83xx_clk_get_rate,
+};
+
+static const struct udevice_id mpc83xx_clk_match[] = {
+ { .compatible = "fsl,mpc8308-clk", .data = SOC_MPC8308 },
+ { .compatible = "fsl,mpc8309-clk", .data = SOC_MPC8309 },
+ { .compatible = "fsl,mpc8313-clk", .data = SOC_MPC8313 },
+ { .compatible = "fsl,mpc8315-clk", .data = SOC_MPC8315 },
+ { .compatible = "fsl,mpc832x-clk", .data = SOC_MPC832X },
+ { .compatible = "fsl,mpc8349-clk", .data = SOC_MPC8349 },
+ { .compatible = "fsl,mpc8360-clk", .data = SOC_MPC8360 },
+ { .compatible = "fsl,mpc8379-clk", .data = SOC_MPC8379 },
+ { /* sentinel */ }
+};
+
+static int mpc83xx_clk_probe(struct udevice *dev)
+{
+ struct mpc83xx_clk_priv *priv = dev_get_priv(dev);
+ ulong type;
+ int ret;
+
+ ret = init_all_clks(dev);
+ if (ret) {
+ debug("%s: Could not initialize all clocks (ret = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ type = dev_get_driver_data(dev);
+
+ if (mpc83xx_has_sdhc(type))
+ gd->arch.sdhc_clk = priv->speed[MPC83XX_CLK_SDHC];
+
+ gd->arch.core_clk = priv->speed[MPC83XX_CLK_CORE];
+ gd->arch.i2c1_clk = priv->speed[MPC83XX_CLK_I2C1];
+ if (mpc83xx_has_second_i2c(type))
+ gd->arch.i2c2_clk = priv->speed[MPC83XX_CLK_I2C2];
+
+ gd->mem_clk = priv->speed[MPC83XX_CLK_MEM];
+
+ if (mpc83xx_has_pci(type))
+ gd->pci_clk = priv->speed[MPC83XX_CLK_PCI];
+
+ gd->cpu_clk = priv->speed[MPC83XX_CLK_CORE];
+ gd->bus_clk = priv->speed[MPC83XX_CLK_CSB];
+
+ return 0;
+}
+
+static int mpc83xx_clk_bind(struct udevice *dev)
+{
+ int ret;
+ struct udevice *sys_child;
+
+ /*
+ * Since there is no corresponding device tree entry, and since the
+ * clock driver has to be present in either case, bind the sysreset
+ * driver here.
+ */
+ ret = device_bind_driver(dev, "mpc83xx_sysreset", "sysreset",
+ &sys_child);
+ if (ret)
+ debug("%s: No sysreset driver: ret=%d\n",
+ dev->name, ret);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(mpc83xx_clk) = {
+ .name = "mpc83xx_clk",
+ .id = UCLASS_CLK,
+ .of_match = mpc83xx_clk_match,
+ .ops = &mpc83xx_clk_ops,
+ .probe = mpc83xx_clk_probe,
+ .priv_auto_alloc_size = sizeof(struct mpc83xx_clk_priv),
+ .bind = mpc83xx_clk_bind,
+};
+
+static int do_clocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ int i;
+ char buf[32];
+ struct udevice *clk;
+ int ret;
+ struct mpc83xx_clk_priv *priv;
+
+ ret = uclass_first_device_err(UCLASS_CLK, &clk);
+ if (ret) {
+ debug("%s: Could not get clock device\n", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < MPC83XX_CLK_COUNT; i++) {
+ if (!is_clk_valid(clk, i))
+ continue;
+
+ priv = dev_get_priv(clk);
+
+ printf("%s = %s MHz\n", names[i], strmhz(buf, priv->speed[i]));
+ }
+
+ return 0;
+}
+
+U_BOOT_CMD(clocks, 1, 1, do_clocks,
+ "display values of SoC's clocks",
+ ""
+);
diff --git a/drivers/clk/mpc83xx_clk.h b/drivers/clk/mpc83xx_clk.h
new file mode 100644
index 0000000000..7fb8802920
--- /dev/null
+++ b/drivers/clk/mpc83xx_clk.h
@@ -0,0 +1,379 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+/**
+ * enum ratio - Description of a core clock ratio
+ * @RAT_UNK: Unknown ratio
+ * @RAT_BYP: Bypass
+ * @RAT_1_TO_8: Ratio 1:8
+ * @RAT_1_TO_4: Ratio 1:4
+ * @RAT_1_TO_2: Ratio 1:2
+ * @RAT_1_TO_1: Ratio 1:1
+ * @RAT_1_5_TO_1: Ratio 1.5:1
+ * @RAT_2_TO_1: Ratio 2:1
+ * @RAT_2_5_TO_1: Ratio 2.5:1
+ * @RAT_3_TO_1: Ratio 3:1
+ */
+enum ratio {
+ RAT_UNK,
+ RAT_BYP,
+ RAT_1_TO_8,
+ RAT_1_TO_4,
+ RAT_1_TO_2,
+ RAT_1_TO_1,
+ RAT_1_5_TO_1,
+ RAT_2_TO_1,
+ RAT_2_5_TO_1,
+ RAT_3_TO_1
+};
+
+/**
+ * struct corecnf - Description for a core clock configuration
+ * @core_csb_ratio: Core clock frequency to CSB clock frequency ratio
+ * @vco_divider: VCO divider (Core VCO frequency = Core frequency * VCO divider)
+ */
+struct corecnf {
+ int core_csb_ratio;
+ int vco_divider;
+};
+
+/*
+ * Table with all valid Core CSB frequency ratio / VCO divider combinations as
+ * indexed by the COREPLL field of the SPMR
+ */
+static const struct corecnf corecnf_tab[] = {
+ {RAT_BYP, RAT_BYP}, /* 0x00 */
+ {RAT_BYP, RAT_BYP}, /* 0x01 */
+ {RAT_BYP, RAT_BYP}, /* 0x02 */
+ {RAT_BYP, RAT_BYP}, /* 0x03 */
+ {RAT_BYP, RAT_BYP}, /* 0x04 */
+ {RAT_BYP, RAT_BYP}, /* 0x05 */
+ {RAT_BYP, RAT_BYP}, /* 0x06 */
+ {RAT_BYP, RAT_BYP}, /* 0x07 */
+ {RAT_1_TO_1, RAT_1_TO_2}, /* 0x08 */
+ {RAT_1_TO_1, RAT_1_TO_4}, /* 0x09 */
+ {RAT_1_TO_1, RAT_1_TO_8}, /* 0x0A */
+ {RAT_1_TO_1, RAT_1_TO_8}, /* 0x0B */
+ {RAT_1_5_TO_1, RAT_1_TO_2}, /* 0x0C */
+ {RAT_1_5_TO_1, RAT_1_TO_4}, /* 0x0D */
+ {RAT_1_5_TO_1, RAT_1_TO_8}, /* 0x0E */
+ {RAT_1_5_TO_1, RAT_1_TO_8}, /* 0x0F */
+ {RAT_2_TO_1, RAT_1_TO_2}, /* 0x10 */
+ {RAT_2_TO_1, RAT_1_TO_4}, /* 0x11 */
+ {RAT_2_TO_1, RAT_1_TO_8}, /* 0x12 */
+ {RAT_2_TO_1, RAT_1_TO_8}, /* 0x13 */
+ {RAT_2_5_TO_1, RAT_1_TO_2}, /* 0x14 */
+ {RAT_2_5_TO_1, RAT_1_TO_4}, /* 0x15 */
+ {RAT_2_5_TO_1, RAT_1_TO_8}, /* 0x16 */
+ {RAT_2_5_TO_1, RAT_1_TO_8}, /* 0x17 */
+ {RAT_3_TO_1, RAT_1_TO_2}, /* 0x18 */
+ {RAT_3_TO_1, RAT_1_TO_4}, /* 0x19 */
+ {RAT_3_TO_1, RAT_1_TO_8}, /* 0x1A */
+ {RAT_3_TO_1, RAT_1_TO_8}, /* 0x1B */
+};
+
+/**
+ * enum reg_type - Register to read a field from
+ * @REG_SCCR: Use the SCCR register
+ * @REG_SPMR: Use the SPMR register
+ */
+enum reg_type {
+ REG_SCCR,
+ REG_SPMR,
+};
+
+/**
+ * enum mode_type - Description of how to read a specific frequency value
+ * @TYPE_INVALID: Unknown type, will provoke error
+ * @TYPE_SCCR_STANDARD: Read a field from the SCCR register, and use it
+ * as a divider for the CSB clock to compute the
+ * frequency
+ * @TYPE_SCCR_ONOFF: The field describes a bit flag that can turn the
+ * clock on or off
+ * @TYPE_SPMR_DIRECT_MULTIPLY: Read a field from the SPMR register, and use it
+ * as a multiplier for the CSB clock to compute the
+ * frequency
+ * @TYPE_SPECIAL: The frequency is calculated in a non-standard way
+ */
+enum mode_type {
+ TYPE_INVALID = 0,
+ TYPE_SCCR_STANDARD,
+ TYPE_SCCR_ONOFF,
+ TYPE_SPMR_DIRECT_MULTIPLY,
+ TYPE_SPECIAL,
+};
+
+/* Map of each clock index to its human-readable name */
+static const char * const names[] = {
+ [MPC83XX_CLK_CORE] = "Core",
+ [MPC83XX_CLK_CSB] = "Coherent System Bus",
+ [MPC83XX_CLK_QE] = "QE",
+ [MPC83XX_CLK_BRG] = "BRG",
+ [MPC83XX_CLK_LBIU] = "Local Bus Controller",
+ [MPC83XX_CLK_LCLK] = "Local Bus",
+ [MPC83XX_CLK_MEM] = "DDR",
+ [MPC83XX_CLK_MEM_SEC] = "DDR Secondary",
+ [MPC83XX_CLK_ENC] = "SEC",
+ [MPC83XX_CLK_I2C1] = "I2C1",
+ [MPC83XX_CLK_I2C2] = "I2C2",
+ [MPC83XX_CLK_TDM] = "TDM",
+ [MPC83XX_CLK_SDHC] = "SDHC",
+ [MPC83XX_CLK_TSEC1] = "TSEC1",
+ [MPC83XX_CLK_TSEC2] = "TSEC2",
+ [MPC83XX_CLK_USBDR] = "USB DR",
+ [MPC83XX_CLK_USBMPH] = "USB MPH",
+ [MPC83XX_CLK_PCIEXP1] = "PCIEXP1",
+ [MPC83XX_CLK_PCIEXP2] = "PCIEXP2",
+ [MPC83XX_CLK_SATA] = "SATA",
+ [MPC83XX_CLK_DMAC] = "DMAC",
+ [MPC83XX_CLK_PCI] = "PCI",
+};
+
+/**
+ * struct clk_mode - Structure for clock mode descriiptions
+ * @low: The low bit of the data field to read for this mode (may not apply to
+ * some modes)
+ * @high: The high bit of the data field to read for this mode (may not apply to
+ * some modes)
+ * @type: The type of the mode description (one of enum mode_type)
+ */
+struct clk_mode {
+ u8 low;
+ u8 high;
+ int type;
+};
+
+/**
+ * set_mode() - Build a clock mode description from data
+ * @mode: The clock mode description to be filled out
+ * @low: The low bit of the data field to read for this mode (may not apply to
+ * some modes)
+ * @high: The high bit of the data field to read for this mode (may not apply to
+ * some modes)
+ * @type: The type of the mode description (one of enum mode_type)
+ *
+ * Clock mode descriptions are a succinct description of how to read a specific
+ * clock's rate from the hardware; usually by reading a specific field of a
+ * register, such a s the SCCR register, but some types use different methods
+ * for obtaining the clock rate.
+ */
+static void set_mode(struct clk_mode *mode, u8 low, u8 high, int type)
+{
+ mode->low = low;
+ mode->high = high;
+ mode->type = type;
+}
+
+/**
+ * retrieve_mode() - Get the clock mode description for a specific clock
+ * @clk: The identifier of the clock for which the clock description should
+ * be retrieved
+ * @soc_type: The type of MPC83xx SoC for which the clock description should be
+ * retrieved
+ * @mode: Pointer to a clk_mode structure to be filled with data for the
+ * clock
+ *
+ * Since some clock rate are stored in different places on different MPC83xx
+ * SoCs, the SoC type has to be supplied along with the clock's identifier.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int retrieve_mode(int clk, int soc_type, struct clk_mode *mode)
+{
+ switch (clk) {
+ case MPC83XX_CLK_CORE:
+ case MPC83XX_CLK_CSB:
+ case MPC83XX_CLK_QE:
+ case MPC83XX_CLK_BRG:
+ case MPC83XX_CLK_LCLK:
+ case MPC83XX_CLK_I2C2:
+ set_mode(mode, 0, 0, TYPE_SPECIAL);
+ break;
+ case MPC83XX_CLK_MEM:
+ set_mode(mode, 1, 1, TYPE_SPMR_DIRECT_MULTIPLY);
+ break;
+ case MPC83XX_CLK_LBIU:
+ case MPC83XX_CLK_MEM_SEC:
+ set_mode(mode, 0, 0, TYPE_SPMR_DIRECT_MULTIPLY);
+ break;
+ case MPC83XX_CLK_TSEC1:
+ set_mode(mode, 0, 1, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_TSEC2:
+ if (soc_type == SOC_MPC8313) /* I2C and TSEC2 are the same register */
+ set_mode(mode, 2, 3, TYPE_SCCR_STANDARD);
+ else /* FIXME(mario.six@gdsys.cc): This has separate enable/disable bit! */
+ set_mode(mode, 0, 1, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_SDHC:
+ set_mode(mode, 4, 5, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_ENC:
+ set_mode(mode, 6, 7, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_I2C1:
+ if (soc_type == SOC_MPC8349)
+ set_mode(mode, 2, 3, TYPE_SCCR_STANDARD);
+ else /* I2C and ENC are the same register */
+ set_mode(mode, 6, 7, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_PCIEXP1:
+ set_mode(mode, 10, 11, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_PCIEXP2:
+ set_mode(mode, 12, 13, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_USBDR:
+ if (soc_type == SOC_MPC8313 || soc_type == SOC_MPC8349)
+ set_mode(mode, 10, 11, TYPE_SCCR_STANDARD);
+ else
+ set_mode(mode, 8, 9, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_USBMPH:
+ set_mode(mode, 8, 9, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_PCI:
+ set_mode(mode, 15, 15, TYPE_SCCR_ONOFF);
+ break;
+ case MPC83XX_CLK_DMAC:
+ set_mode(mode, 26, 27, TYPE_SCCR_STANDARD);
+ break;
+ case MPC83XX_CLK_SATA:
+ /* FIXME(mario.six@gdsys.cc): All SATA controllers must have the same clock ratio */
+ if (soc_type == SOC_MPC8379) {
+ set_mode(mode, 24, 25, TYPE_SCCR_STANDARD);
+ set_mode(mode, 26, 27, TYPE_SCCR_STANDARD);
+ set_mode(mode, 28, 29, TYPE_SCCR_STANDARD);
+ set_mode(mode, 30, 31, TYPE_SCCR_STANDARD);
+ } else {
+ set_mode(mode, 18, 19, TYPE_SCCR_STANDARD);
+ set_mode(mode, 20, 21, TYPE_SCCR_STANDARD);
+ }
+ break;
+ case MPC83XX_CLK_TDM:
+ set_mode(mode, 26, 27, TYPE_SCCR_STANDARD);
+ break;
+ default:
+ debug("%s: Unknown clock type %d on soc type %d\n",
+ __func__, clk, soc_type);
+ set_mode(mode, 0, 0, TYPE_INVALID);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * get_spmr() - Read the SPMR (System PLL Mode Register)
+ * @im: Pointer to the MPC83xx main register map in question
+ *
+ * Return: The SPMR value as a 32-bit number.
+ */
+static inline u32 get_spmr(immap_t *im)
+{
+ u32 res = in_be32(&im->clk.spmr);
+
+ return res;
+}
+
+/**
+ * get_sccr() - Read the SCCR (System Clock Control Register)
+ * @im: Pointer to the MPC83xx main register map in question
+ *
+ * Return: The SCCR value as a 32-bit number.
+ */
+static inline u32 get_sccr(immap_t *im)
+{
+ u32 res = in_be32(&im->clk.sccr);
+
+ return res;
+}
+
+/**
+ * get_lcrr() - Read the LCRR (Clock Ratio Register)
+ * @im: Pointer to the MPC83xx main register map in question
+ *
+ * Return: The LCRR value as a 32-bit number.
+ */
+static inline u32 get_lcrr(immap_t *im)
+{
+ u32 res = in_be32(&im->im_lbc.lcrr);
+
+ return res;
+}
+
+/**
+ * get_pci_sync_in() - Read the PCI synchronization clock speed
+ * @im: Pointer to the MPC83xx main register map in question
+ *
+ * Return: The PCI synchronization clock speed value as a 32-bit number.
+ */
+static inline u32 get_pci_sync_in(immap_t *im)
+{
+ u8 clkin_div;
+
+ clkin_div = (get_spmr(im) & SPMR_CKID) >> SPMR_CKID_SHIFT;
+ return CONFIG_SYS_CLK_FREQ / (1 + clkin_div);
+}
+
+/**
+ * get_csb_clk() - Read the CSB (Coheren System Bus) clock speed
+ * @im: Pointer to the MPC83xx main register map in question
+ *
+ * Return: The CSB clock speed value as a 32-bit number.
+ */
+static inline u32 get_csb_clk(immap_t *im)
+{
+ u8 spmf;
+
+ spmf = (get_spmr(im) & SPMR_SPMF) >> SPMR_SPMF_SHIFT;
+ return CONFIG_SYS_CLK_FREQ * spmf;
+}
+
+/**
+ * spmr_field() - Read a specific SPMR field
+ * @im: Pointer to the MPC83xx main register map in question
+ * @mask: A bitmask that describes the bitfield to be read
+ *
+ * Return: The value of the bit field as a 32-bit number.
+ */
+static inline uint spmr_field(immap_t *im, u32 mask)
+{
+ /* Extract shift from bitmask */
+ uint shift = mask ? ffs(mask) - 1 : 0;
+
+ return (get_spmr(im) & mask) >> shift;
+}
+
+/**
+ * sccr_field() - Read a specific SCCR field
+ * @im: Pointer to the MPC83xx main register map in question
+ * @mask: A bitmask that describes the bitfield to be read
+ *
+ * Return: The value of the bit field as a 32-bit number.
+ */
+static inline uint sccr_field(immap_t *im, u32 mask)
+{
+ /* Extract shift from bitmask */
+ uint shift = mask ? ffs(mask) - 1 : 0;
+
+ return (get_sccr(im) & mask) >> shift;
+}
+
+/**
+ * lcrr_field() - Read a specific LCRR field
+ * @im: Pointer to the MPC83xx main register map in question
+ * @mask: A bitmask that describes the bitfield to be read
+ *
+ * Return: The value of the bit field as a 32-bit number.
+ */
+static inline uint lcrr_field(immap_t *im, u32 mask)
+{
+ /* Extract shift from bitmask */
+ uint shift = mask ? ffs(mask) - 1 : 0;
+
+ return (get_lcrr(im) & mask) >> shift;
+}
diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c
index f8cdbd6688..bfd9580050 100644
--- a/drivers/core/fdtaddr.c
+++ b/drivers/core/fdtaddr.c
@@ -138,7 +138,7 @@ void *devfdt_get_addr_ptr(struct udevice *dev)
void *devfdt_remap_addr_index(struct udevice *dev, int index)
{
- fdt_addr_t addr = devfdt_get_addr(dev);
+ fdt_addr_t addr = devfdt_get_addr_index(dev, index);
if (addr == FDT_ADDR_T_NONE)
return NULL;
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index 0729dfcdb3..14c020a687 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -376,6 +376,33 @@ struct device_node *of_find_compatible_node(struct device_node *from,
return np;
}
+static int of_device_has_prop_value(const struct device_node *device,
+ const char *propname, const void *propval,
+ int proplen)
+{
+ struct property *prop = of_find_property(device, propname, NULL);
+
+ if (!prop || !prop->value || prop->length != proplen)
+ return 0;
+ return !memcmp(prop->value, propval, proplen);
+}
+
+struct device_node *of_find_node_by_prop_value(struct device_node *from,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ struct device_node *np;
+
+ for_each_of_allnodes_from(from, np) {
+ if (of_device_has_prop_value(np, propname, propval, proplen) &&
+ of_node_get(np))
+ break;
+ }
+ of_node_put(from);
+
+ return np;
+}
+
struct device_node *of_find_node_by_phandle(phandle handle)
{
struct device_node *np;
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 0cfb0fbabb..a7e1927723 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -777,3 +777,17 @@ ofnode ofnode_by_compatible(ofnode from, const char *compat)
gd->fdt_blob, ofnode_to_offset(from), compat));
}
}
+
+ofnode ofnode_by_prop_value(ofnode from, const char *propname,
+ const void *propval, int proplen)
+{
+ if (of_live_active()) {
+ return np_to_ofnode(of_find_node_by_prop_value(
+ (struct device_node *)ofnode_to_np(from), propname,
+ propval, proplen));
+ } else {
+ return offset_to_ofnode(fdt_node_offset_by_prop_value(
+ gd->fdt_blob, ofnode_to_offset(from),
+ propname, propval, proplen));
+ }
+}
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 72bcc7d7f2..47d10b888f 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -330,10 +330,25 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob,
}
#endif
+static int dm_scan_fdt_ofnode_path(const char *path, bool pre_reloc_only)
+{
+ ofnode node;
+
+ node = ofnode_path(path);
+ if (!ofnode_valid(node))
+ return 0;
+
+#if CONFIG_IS_ENABLED(OF_LIVE)
+ if (of_live_active())
+ return dm_scan_fdt_live(gd->dm_root, node.np, pre_reloc_only);
+#endif
+ return dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, node.of_offset,
+ pre_reloc_only);
+}
+
int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only)
{
int ret;
- ofnode node;
ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only);
if (ret) {
@@ -341,21 +356,15 @@ int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only)
return ret;
}
- /* bind fixed-clock */
- node = ofnode_path("/clocks");
- /* if no DT "clocks" node, no need to go further */
- if (!ofnode_valid(node))
+ ret = dm_scan_fdt_ofnode_path("/clocks", pre_reloc_only);
+ if (ret) {
+ debug("scan for /clocks failed: %d\n", ret);
return ret;
+ }
-#if CONFIG_IS_ENABLED(OF_LIVE)
- if (of_live_active())
- ret = dm_scan_fdt_live(gd->dm_root, node.np, pre_reloc_only);
- else
-#endif
- ret = dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, node.of_offset,
- pre_reloc_only);
+ ret = dm_scan_fdt_ofnode_path("/firmware", pre_reloc_only);
if (ret)
- debug("dm_scan_fdt_node() failed: %d\n", ret);
+ debug("scan for /firmware failed: %d\n", ret);
return ret;
}
diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig
index 0d1424d38e..d4052005e2 100644
--- a/drivers/cpu/Kconfig
+++ b/drivers/cpu/Kconfig
@@ -6,3 +6,10 @@ config CPU
multiple CPUs, then normally have to be set up in U-Boot so that
they can work correctly in the OS. This provides a framework for
finding out information about available CPUs and making changes.
+
+config CPU_MPC83XX
+ bool "Enable MPC83xx CPU driver"
+ depends on CPU
+ select CLK_MPC83XX
+ help
+ Support CPU cores for SoCs of the MPC83xx series.
diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile
index db515f6f17..f452ee404e 100644
--- a/drivers/cpu/Makefile
+++ b/drivers/cpu/Makefile
@@ -7,3 +7,5 @@
obj-$(CONFIG_CPU) += cpu-uclass.o
obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o
+obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o
+obj-$(CONFIG_SANDBOX) += cpu_sandbox.o
diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c
index f362eb11e7..457f77b7c8 100644
--- a/drivers/cpu/cpu-uclass.c
+++ b/drivers/cpu/cpu-uclass.c
@@ -11,6 +11,29 @@
#include <dm/lists.h>
#include <dm/root.h>
+int cpu_probe_all(void)
+{
+ struct udevice *cpu;
+ int ret;
+
+ ret = uclass_first_device(UCLASS_CPU, &cpu);
+ if (ret) {
+ debug("%s: No CPU found (err = %d)\n", __func__, ret);
+ return ret;
+ }
+
+ while (cpu) {
+ ret = uclass_next_device(&cpu);
+ if (ret) {
+ debug("%s: Error while probing CPU (err = %d)\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int cpu_get_desc(struct udevice *dev, char *buf, int size)
{
struct cpu_ops *ops = cpu_get_ops(dev);
diff --git a/drivers/cpu/cpu_sandbox.c b/drivers/cpu/cpu_sandbox.c
new file mode 100644
index 0000000000..ff87e8adca
--- /dev/null
+++ b/drivers/cpu/cpu_sandbox.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <cpu.h>
+
+int cpu_sandbox_get_desc(struct udevice *dev, char *buf, int size)
+{
+ snprintf(buf, size, "LEG Inc. SuperMegaUltraTurbo CPU No. 1");
+
+ return 0;
+}
+
+int cpu_sandbox_get_info(struct udevice *dev, struct cpu_info *info)
+{
+ info->cpu_freq = 42 * 42 * 42 * 42 * 42;
+ info->features = 0x42424242;
+
+ return 0;
+}
+
+int cpu_sandbox_get_count(struct udevice *dev)
+{
+ return 42;
+}
+
+int cpu_sandbox_get_vendor(struct udevice *dev, char *buf, int size)
+{
+ snprintf(buf, size, "Languid Example Garbage Inc.");
+
+ return 0;
+}
+
+static const struct cpu_ops cpu_sandbox_ops = {
+ .get_desc = cpu_sandbox_get_desc,
+ .get_info = cpu_sandbox_get_info,
+ .get_count = cpu_sandbox_get_count,
+ .get_vendor = cpu_sandbox_get_vendor,
+};
+
+int cpu_sandbox_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static const struct udevice_id cpu_sandbox_ids[] = {
+ { .compatible = "sandbox,cpu_sandbox" },
+ { }
+};
+
+U_BOOT_DRIVER(cpu_sandbox) = {
+ .name = "cpu_sandbox",
+ .id = UCLASS_CPU,
+ .ops = &cpu_sandbox_ops,
+ .of_match = cpu_sandbox_ids,
+ .probe = cpu_sandbox_probe,
+};
diff --git a/drivers/cpu/mpc83xx_cpu.c b/drivers/cpu/mpc83xx_cpu.c
new file mode 100644
index 0000000000..31717afaec
--- /dev/null
+++ b/drivers/cpu/mpc83xx_cpu.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <bitfield.h>
+#include <clk.h>
+#include <cpu.h>
+#include <dm.h>
+
+#include "mpc83xx_cpu.h"
+
+/**
+ * struct mpc83xx_cpu_priv - Private data for MPC83xx CPUs
+ * @e300_type: The e300 core type of the MPC83xx CPU
+ * @family: The MPC83xx family the CPU belongs to
+ * @type: The MPC83xx type of the CPU
+ * @is_e_processor: Flag indicating whether the CPU is a E processor or not
+ * @is_a_variant: Flag indicating whtther the CPU is a A variant or not
+ * @revid: The revision ID of the CPU
+ * @revid.major: The major part of the CPU's revision ID
+ * @revid.minor: The minor part of the CPU's revision ID
+ */
+struct mpc83xx_cpu_priv {
+ enum e300_type e300_type;
+ enum mpc83xx_cpu_family family;
+ enum mpc83xx_cpu_type type;
+ bool is_e_processor;
+ bool is_a_variant;
+ struct {
+ uint major;
+ uint minor;
+ } revid;
+};
+
+int checkcpu(void)
+{
+ /* Activate all CPUs from board_f.c */
+ return cpu_probe_all();
+}
+
+/**
+ * get_spridr() - Read SPRIDR (System Part and Revision ID Register) of CPU
+ *
+ * Return: The SPRIDR value
+ */
+static inline u32 get_spridr(void)
+{
+ immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
+
+ return in_be32(&immr->sysconf.spridr);
+}
+
+/**
+ * determine_type() - Determine CPU family of MPC83xx device
+ * @dev: CPU device from which to read CPU family from
+ */
+static inline void determine_family(struct udevice *dev)
+{
+ struct mpc83xx_cpu_priv *priv = dev_get_priv(dev);
+ /* Upper 12 bits of PARTID field (bits 0-23 in SPRIDR) */
+ const u32 PARTID_FAMILY_MASK = 0xFFF00000;
+
+ switch (bitfield_extract_by_mask(get_spridr(), PARTID_FAMILY_MASK)) {
+ case 0x810:
+ case 0x811:
+ priv->family = FAMILY_830X;
+ break;
+ case 0x80B:
+ priv->family = FAMILY_831X;
+ break;
+ case 0x806:
+ priv->family = FAMILY_832X;
+ break;
+ case 0x803:
+ priv->family = FAMILY_834X;
+ break;
+ case 0x804:
+ priv->family = FAMILY_836X;
+ break;
+ case 0x80C:
+ priv->family = FAMILY_837X;
+ break;
+ default:
+ priv->family = FAMILY_UNKNOWN;
+ }
+}
+
+/**
+ * determine_type() - Determine CPU type of MPC83xx device
+ * @dev: CPU device from which to read CPU type from
+ */
+static inline void determine_type(struct udevice *dev)
+{
+ struct mpc83xx_cpu_priv *priv = dev_get_priv(dev);
+ /* Upper 16 bits of PVR (Processor Version Register) */
+ const u32 PCR_UPPER_MASK = 0xFFFF0000;
+ u32 val;
+
+ val = bitfield_extract_by_mask(get_spridr(), PCR_UPPER_MASK);
+
+ /* Mask out E-variant bit */
+ switch (val & 0xFFFE) {
+ case 0x8100:
+ priv->type = TYPE_8308;
+ break;
+ case 0x8110:
+ priv->type = TYPE_8309;
+ break;
+ case 0x80B2:
+ priv->type = TYPE_8311;
+ break;
+ case 0x80B0:
+ priv->type = TYPE_8313;
+ break;
+ case 0x80B6:
+ priv->type = TYPE_8314;
+ break;
+ case 0x80B4:
+ priv->type = TYPE_8315;
+ break;
+ case 0x8066:
+ priv->type = TYPE_8321;
+ break;
+ case 0x8062:
+ priv->type = TYPE_8323;
+ break;
+ case 0x8036:
+ priv->type = TYPE_8343;
+ break;
+ case 0x8032:
+ priv->type = TYPE_8347_TBGA;
+ break;
+ case 0x8034:
+ priv->type = TYPE_8347_PBGA;
+ break;
+ case 0x8030:
+ priv->type = TYPE_8349;
+ break;
+ case 0x804A:
+ priv->type = TYPE_8358_TBGA;
+ break;
+ case 0x804E:
+ priv->type = TYPE_8358_PBGA;
+ break;
+ case 0x8048:
+ priv->type = TYPE_8360;
+ break;
+ case 0x80C6:
+ priv->type = TYPE_8377;
+ break;
+ case 0x80C4:
+ priv->type = TYPE_8378;
+ break;
+ case 0x80C2:
+ priv->type = TYPE_8379;
+ break;
+ default:
+ priv->type = TYPE_UNKNOWN;
+ }
+}
+
+/**
+ * determine_e300_type() - Determine e300 core type of MPC83xx device
+ * @dev: CPU device from which to read e300 core type from
+ */
+static inline void determine_e300_type(struct udevice *dev)
+{
+ struct mpc83xx_cpu_priv *priv = dev_get_priv(dev);
+ /* Upper 16 bits of PVR (Processor Version Register) */
+ const u32 PCR_UPPER_MASK = 0xFFFF0000;
+ u32 pvr = get_pvr();
+
+ switch ((pvr & PCR_UPPER_MASK) >> 16) {
+ case 0x8083:
+ priv->e300_type = E300C1;
+ break;
+ case 0x8084:
+ priv->e300_type = E300C2;
+ break;
+ case 0x8085:
+ priv->e300_type = E300C3;
+ break;
+ case 0x8086:
+ priv->e300_type = E300C4;
+ break;
+ default:
+ priv->e300_type = E300_UNKNOWN;
+ }
+}
+
+/**
+ * determine_revid() - Determine revision ID of CPU device
+ * @dev: CPU device from which to read revision ID
+ */
+static inline void determine_revid(struct udevice *dev)
+{
+ struct mpc83xx_cpu_priv *priv = dev_get_priv(dev);
+ u32 REVID_MAJOR_MASK;
+ u32 REVID_MINOR_MASK;
+ u32 spridr = get_spridr();
+
+ if (priv->family == FAMILY_834X) {
+ REVID_MAJOR_MASK = 0x0000FF00;
+ REVID_MINOR_MASK = 0x000000FF;
+ } else {
+ REVID_MAJOR_MASK = 0x000000F0;
+ REVID_MINOR_MASK = 0x0000000F;
+ }
+
+ priv->revid.major = bitfield_extract_by_mask(spridr, REVID_MAJOR_MASK);
+ priv->revid.minor = bitfield_extract_by_mask(spridr, REVID_MINOR_MASK);
+}
+
+/**
+ * determine_cpu_data() - Determine CPU information from hardware
+ * @dev: CPU device from which to read information
+ */
+static void determine_cpu_data(struct udevice *dev)
+{
+ struct mpc83xx_cpu_priv *priv = dev_get_priv(dev);
+ const u32 E_FLAG_MASK = 0x00010000;
+ u32 spridr = get_spridr();
+
+ determine_family(dev);
+ determine_type(dev);
+ determine_e300_type(dev);
+ determine_revid(dev);
+
+ if ((priv->family == FAMILY_834X ||
+ priv->family == FAMILY_836X) && priv->revid.major >= 2)
+ priv->is_a_variant = true;
+
+ priv->is_e_processor = !bitfield_extract_by_mask(spridr, E_FLAG_MASK);
+}
+
+static int mpc83xx_cpu_get_desc(struct udevice *dev, char *buf, int size)
+{
+ struct mpc83xx_cpu_priv *priv = dev_get_priv(dev);
+ struct clk core_clk;
+ struct clk csb_clk;
+ char core_freq[32];
+ char csb_freq[32];
+ int ret;
+
+ ret = clk_get_by_index(dev, 0, &core_clk);
+ if (ret) {
+ debug("%s: Failed to get core clock (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(dev, 1, &csb_clk);
+ if (ret) {
+ debug("%s: Failed to get CSB clock (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ determine_cpu_data(dev);
+
+ snprintf(buf, size,
+ "CPU: %s, MPC%s%s%s, Rev: %d.%d at %s MHz, CSB: %s MHz\n",
+ e300_names[priv->e300_type],
+ cpu_type_names[priv->type],
+ priv->is_e_processor ? "E" : "",
+ priv->is_a_variant ? "A" : "",
+ priv->revid.major,
+ priv->revid.minor,
+ strmhz(core_freq, clk_get_rate(&core_clk)),
+ strmhz(csb_freq, clk_get_rate(&csb_clk)));
+
+ return 0;
+}
+
+static int mpc83xx_cpu_get_info(struct udevice *dev, struct cpu_info *info)
+{
+ struct clk clock;
+ int ret;
+ ulong freq;
+
+ ret = clk_get_by_index(dev, 0, &clock);
+ if (ret) {
+ debug("%s: Failed to get core clock (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ freq = clk_get_rate(&clock);
+ if (!freq) {
+ debug("%s: Core clock speed is zero\n", dev->name);
+ return -EINVAL;
+ }
+
+ info->cpu_freq = freq;
+ info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
+
+ return 0;
+}
+
+static int mpc83xx_cpu_get_count(struct udevice *dev)
+{
+ /* We have one e300cX core */
+ return 1;
+}
+
+static int mpc83xx_cpu_get_vendor(struct udevice *dev, char *buf, int size)
+{
+ snprintf(buf, size, "NXP");
+
+ return 0;
+}
+
+static const struct cpu_ops mpc83xx_cpu_ops = {
+ .get_desc = mpc83xx_cpu_get_desc,
+ .get_info = mpc83xx_cpu_get_info,
+ .get_count = mpc83xx_cpu_get_count,
+ .get_vendor = mpc83xx_cpu_get_vendor,
+};
+
+static int mpc83xx_cpu_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static const struct udevice_id mpc83xx_cpu_ids[] = {
+ { .compatible = "fsl,mpc83xx", },
+ { .compatible = "fsl,mpc8308", },
+ { .compatible = "fsl,mpc8309", },
+ { .compatible = "fsl,mpc8313", },
+ { .compatible = "fsl,mpc8315", },
+ { .compatible = "fsl,mpc832x", },
+ { .compatible = "fsl,mpc8349", },
+ { .compatible = "fsl,mpc8360", },
+ { .compatible = "fsl,mpc8379", },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(mpc83xx_cpu) = {
+ .name = "mpc83xx_cpu",
+ .id = UCLASS_CPU,
+ .of_match = mpc83xx_cpu_ids,
+ .probe = mpc83xx_cpu_probe,
+ .priv_auto_alloc_size = sizeof(struct mpc83xx_cpu_priv),
+ .ops = &mpc83xx_cpu_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/cpu/mpc83xx_cpu.h b/drivers/cpu/mpc83xx_cpu.h
new file mode 100644
index 0000000000..2aaa4e1884
--- /dev/null
+++ b/drivers/cpu/mpc83xx_cpu.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#ifndef _MPC83XX_CPU_H_
+#define _MPC83XX_CPU_H_
+
+/**
+ * enum e300_type - Identifiers for e300 cores
+ * @E300C1: Identifier for e300c1 cores
+ * @E300C2: Identifier for e300c2 cores
+ * @E300C3: Identifier for e300c3 cores
+ * @E300C4: Identifier for e300c4 cores
+ * @E300_UNKNOWN: Identifier for unknown e300 cores
+ */
+enum e300_type {
+ E300C1,
+ E300C2,
+ E300C3,
+ E300C4,
+ E300_UNKNOWN,
+};
+
+/* Array mapping the e300 core types to their human-readable names */
+static const char * const e300_names[] = {
+ [E300C1] = "e300c1",
+ [E300C2] = "e300c2",
+ [E300C3] = "e300c3",
+ [E300C4] = "e300c4",
+ [E300_UNKNOWN] = "Unknown e300",
+};
+
+/**
+ * enum mpc83xx_cpu_family - Identifiers for MPC83xx CPU families
+ * @FAMILY_830X: Identifier for the MPC830x CPU family
+ * @FAMILY_831X: Identifier for the MPC831x CPU family
+ * @FAMILY_832X: Identifier for the MPC832x CPU family
+ * @FAMILY_834X: Identifier for the MPC834x CPU family
+ * @FAMILY_836X: Identifier for the MPC836x CPU family
+ * @FAMILY_837X: Identifier for the MPC837x CPU family
+ * @FAMILY_UNKNOWN: Identifier for an unknown MPC83xx CPU family
+ */
+enum mpc83xx_cpu_family {
+ FAMILY_830X,
+ FAMILY_831X,
+ FAMILY_832X,
+ FAMILY_834X,
+ FAMILY_836X,
+ FAMILY_837X,
+ FAMILY_UNKNOWN,
+};
+
+/**
+ * enum mpc83xx_cpu_type - Identifiers for MPC83xx CPU types
+ * @TYPE_8308: Identifier for the MPC8308 CPU type
+ * @TYPE_8309: Identifier for the MPC8309 CPU type
+ * @TYPE_8311: Identifier for the MPC8311 CPU type
+ * @TYPE_8313: Identifier for the MPC8313 CPU type
+ * @TYPE_8314: Identifier for the MPC8314 CPU type
+ * @TYPE_8315: Identifier for the MPC8315 CPU type
+ * @TYPE_8321: Identifier for the MPC8321 CPU type
+ * @TYPE_8323: Identifier for the MPC8323 CPU type
+ * @TYPE_8343: Identifier for the MPC8343 CPU type
+ * @TYPE_8347_TBGA: Identifier for the MPC8347 CPU type (Tape Ball Grid Array
+ * version)
+ * @TYPE_8347_PBGA: Identifier for the MPC8347 CPU type (Plastic Ball Grid Array
+ * version)
+ * @TYPE_8349: Identifier for the MPC8349 CPU type
+ * @TYPE_8358_TBGA: Identifier for the MPC8358 CPU type (Tape Ball Grid Array
+ * version)
+ * @TYPE_8358_PBGA: Identifier for the MPC8358 CPU type (Plastic Ball Grid Array
+ * version)
+ * @TYPE_8360: Identifier for the MPC8360 CPU type
+ * @TYPE_8377: Identifier for the MPC8377 CPU type
+ * @TYPE_8378: Identifier for the MPC8378 CPU type
+ * @TYPE_8379: Identifier for the MPC8379 CPU type
+ * @TYPE_UNKNOWN: Identifier for an unknown MPC83xx CPU type
+ */
+enum mpc83xx_cpu_type {
+ TYPE_8308,
+ TYPE_8309,
+ TYPE_8311,
+ TYPE_8313,
+ TYPE_8314,
+ TYPE_8315,
+ TYPE_8321,
+ TYPE_8323,
+ TYPE_8343,
+ TYPE_8347_TBGA,
+ TYPE_8347_PBGA,
+ TYPE_8349,
+ TYPE_8358_TBGA,
+ TYPE_8358_PBGA,
+ TYPE_8360,
+ TYPE_8377,
+ TYPE_8378,
+ TYPE_8379,
+ TYPE_UNKNOWN,
+};
+
+/* Array mapping the MCP83xx CPUs to their human-readable names */
+static const char * const cpu_type_names[] = {
+ [TYPE_8308] = "8308",
+ [TYPE_8309] = "8309",
+ [TYPE_8311] = "8311",
+ [TYPE_8313] = "8313",
+ [TYPE_8314] = "8314",
+ [TYPE_8315] = "8315",
+ [TYPE_8321] = "8321",
+ [TYPE_8323] = "8323",
+ [TYPE_8343] = "8343",
+ [TYPE_8347_TBGA] = "8347_TBGA",
+ [TYPE_8347_PBGA] = "8347_PBGA",
+ [TYPE_8349] = "8349",
+ [TYPE_8358_TBGA] = "8358_TBGA",
+ [TYPE_8358_PBGA] = "8358_PBGA",
+ [TYPE_8360] = "8360",
+ [TYPE_8377] = "8377",
+ [TYPE_8378] = "8378",
+ [TYPE_8379] = "8379",
+ [TYPE_UNKNOWN] = "Unknown CPU",
+};
+
+#endif /* !_MPC83XX_CPU_H_ */
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c2b7cc15db..bfa5c91687 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -288,4 +288,11 @@ config GDSYS_IOEP
depends on MISC
help
Support gdsys FPGA's IO endpoint driver.
+
+config MPC83XX_SERDES
+ bool "Enable MPC83xx serdes driver"
+ depends on MISC
+ help
+ Support for serdes found on MPC83xx SoCs.
+
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 32ef4a53c7..da4666fdfc 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o
obj-$(CONFIG_SYS_DPAA_QBMAN) += fsl_portals.o
obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
+obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o
diff --git a/drivers/misc/mpc83xx_serdes.c b/drivers/misc/mpc83xx_serdes.c
new file mode 100644
index 0000000000..d572dda3c1
--- /dev/null
+++ b/drivers/misc/mpc83xx_serdes.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * base on the MPC83xx serdes initialization, which is
+ *
+ * Copyright 2007,2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 MontaVista Software, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <misc.h>
+
+#include "mpc83xx_serdes.h"
+
+/**
+ * struct mpc83xx_serdes_priv - Private structure for MPC83xx serdes
+ * @regs: The device's register map
+ * @rfcks: Variable to keep the serdes reference clock selection set during
+ * initialization in (is or'd to every value written to SRDSCR4)
+ */
+struct mpc83xx_serdes_priv {
+ struct mpc83xx_serdes_regs *regs;
+ u32 rfcks;
+};
+
+/**
+ * setup_sata() - Configure the SerDes device to SATA mode
+ * @dev: The device to configure
+ */
+static void setup_sata(struct udevice *dev)
+{
+ struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
+
+ /* Set and clear reset bits */
+ setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET);
+ udelay(1000);
+ clrbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET);
+
+ /* Configure SRDSCR0 */
+ clrsetbits_be32(&priv->regs->srdscr0,
+ SRDSCR0_TXEQA_MASK | SRDSCR0_TXEQE_MASK,
+ SRDSCR0_TXEQA_SATA | SRDSCR0_TXEQE_SATA);
+
+ /* Configure SRDSCR1 */
+ clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW);
+
+ /* Configure SRDSCR2 */
+ clrsetbits_be32(&priv->regs->srdscr2,
+ SRDSCR2_SEIC_MASK,
+ SRDSCR2_SEIC_SATA);
+
+ /* Configure SRDSCR3 */
+ out_be32(&priv->regs->srdscr3,
+ SRDSCR3_KFR_SATA | SRDSCR3_KPH_SATA |
+ SRDSCR3_SDFM_SATA_PEX | SRDSCR3_SDTXL_SATA);
+
+ /* Configure SRDSCR4 */
+ out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SATA);
+}
+
+/**
+ * setup_pex() - Configure the SerDes device to PCI Express mode
+ * @dev: The device to configure
+ * @type: The PCI Express type to configure for (x1 or x2)
+ */
+static void setup_pex(struct udevice *dev, enum pex_type type)
+{
+ struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
+
+ /* Configure SRDSCR1 */
+ setbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW);
+
+ /* Configure SRDSCR2 */
+ clrsetbits_be32(&priv->regs->srdscr2,
+ SRDSCR2_SEIC_MASK,
+ SRDSCR2_SEIC_PEX);
+
+ /* Configure SRDSCR3 */
+ out_be32(&priv->regs->srdscr3, SRDSCR3_SDFM_SATA_PEX);
+
+ /* Configure SRDSCR4 */
+ if (type == PEX_X2)
+ out_be32(&priv->regs->srdscr4,
+ priv->rfcks | SRDSCR4_PROT_PEX | SRDSCR4_PLANE_X2);
+ else
+ out_be32(&priv->regs->srdscr4,
+ priv->rfcks | SRDSCR4_PROT_PEX);
+}
+
+/**
+ * setup_sgmii() - Configure the SerDes device to SGMII mode
+ * @dev: The device to configure
+ */
+static void setup_sgmii(struct udevice *dev)
+{
+ struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
+
+ /* Configure SRDSCR1 */
+ clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW);
+
+ /* Configure SRDSCR2 */
+ clrsetbits_be32(&priv->regs->srdscr2,
+ SRDSCR2_SEIC_MASK,
+ SRDSCR2_SEIC_SGMII);
+
+ /* Configure SRDSCR3 */
+ out_be32(&priv->regs->srdscr3, 0);
+
+ /* Configure SRDSCR4 */
+ out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SGMII);
+}
+
+static int mpc83xx_serdes_probe(struct udevice *dev)
+{
+ struct mpc83xx_serdes_priv *priv = dev_get_priv(dev);
+ bool vdd;
+ const char *proto;
+
+ priv->regs = map_sysmem(dev_read_addr(dev),
+ sizeof(struct mpc83xx_serdes_regs));
+
+ switch (dev_read_u32_default(dev, "serdes-clk", -1)) {
+ case 100:
+ priv->rfcks = SRDSCR4_RFCKS_100;
+ break;
+ case 125:
+ priv->rfcks = SRDSCR4_RFCKS_125;
+ break;
+ case 150:
+ priv->rfcks = SRDSCR4_RFCKS_150;
+ break;
+ default:
+ debug("%s: Could not read serdes clock value\n", dev->name);
+ return -EINVAL;
+ }
+
+ vdd = dev_read_bool(dev, "vdd");
+
+ /* 1.0V corevdd */
+ if (vdd) {
+ /* DPPE/DPPA = 0 */
+ clrbits_be32(&priv->regs->srdscr0, SRDSCR0_DPP_1V2);
+
+ /* VDD = 0 */
+ clrbits_be32(&priv->regs->srdscr0, SRDSCR2_VDD_1V2);
+ }
+
+ proto = dev_read_string(dev, "proto");
+
+ /* protocol specific configuration */
+ if (!strcmp(proto, "sata")) {
+ setup_sata(dev);
+ } else if (!strcmp(proto, "pex")) {
+ setup_pex(dev, PEX_X1);
+ } else if (!strcmp(proto, "pex-x2")) {
+ setup_pex(dev, PEX_X2);
+ } else if (!strcmp(proto, "sgmii")) {
+ setup_sgmii(dev);
+ } else {
+ debug("%s: Invalid protocol value %s\n", dev->name, proto);
+ return -EINVAL;
+ }
+
+ /* Do a software reset */
+ setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_RST);
+
+ return 0;
+}
+
+static const struct udevice_id mpc83xx_serdes_ids[] = {
+ { .compatible = "fsl,mpc83xx-serdes" },
+ { }
+};
+
+U_BOOT_DRIVER(mpc83xx_serdes) = {
+ .name = "mpc83xx_serdes",
+ .id = UCLASS_MISC,
+ .of_match = mpc83xx_serdes_ids,
+ .probe = mpc83xx_serdes_probe,
+ .priv_auto_alloc_size = sizeof(struct mpc83xx_serdes_priv),
+};
diff --git a/drivers/misc/mpc83xx_serdes.h b/drivers/misc/mpc83xx_serdes.h
new file mode 100644
index 0000000000..89ea1dbab7
--- /dev/null
+++ b/drivers/misc/mpc83xx_serdes.h
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+/**
+ * enum srdscr0_mask - Bit masks for SRDSCR0 (SerDes Control Register 0)
+ * @SRDSCR0_DPPA: Bitmask for the DPPA (diff pk-pk swing for lane A)
+ * field of the SRCSCR0
+ * @SRDSCR0_DPPE: Bitmask for the DPPE (diff pk-pk swing for lane E)
+ * field of the SRCSCR0
+ * @SRDSCR0_DPP_1V2: Combined bitmask to set diff pk-pk swing for both lanes
+ * @SRDSCR0_TXEQA_MASK: Bitmask for the TXEQA (transmit equalization for
+ * lane A) field of the SRCSCR0
+ * @SRDSCR0_TXEQA_SATA: Bitmask to set the TXEQA to the value used for SATA
+ * @SRDSCR0_TXEQE_MASK: Bitmask for the TXEQE (transmit equalization for
+ * lane E) field of the SRCSCR0
+ * @SRDSCR0_TXEQE_SATA: Bitmask to set the TXEQE to the value used for SATA
+ */
+enum srdscr0_mask {
+ SRDSCR0_DPPA = BIT(31 - 16),
+ SRDSCR0_DPPE = BIT(31 - 20),
+ SRDSCR0_DPP_1V2 = SRDSCR0_DPPE | SRDSCR0_DPPA,
+
+ SRDSCR0_TXEQA_MASK = 0x00007000,
+ SRDSCR0_TXEQA_SATA = 0x00001000,
+ SRDSCR0_TXEQE_MASK = 0x00000700,
+ SRDSCR0_TXEQE_SATA = 0x00000100,
+};
+
+/**
+ * enum srdscr1_mask - Bit masks for SRDSCR1 (SerDes Control Register 1)
+ * @SRDSCR1_PLLBW: Bitmask for the PLLBW (PLL bandwidth) field of SRDSCR1
+ */
+enum srdscr1_mask {
+ SRDSCR1_PLLBW = BIT(31 - 25),
+};
+
+/**
+ * enum srdscr2_mask - Bit masks for SRDSCR2 (SerDes Control Register 2)
+ * @SRDSCR2_VDD_1V2: Bit mask to to set the VDD field of the SCRSCR2
+ * @SRDSCR2_SEICA_MASK: Bitmask for the SEICA (Receiver electrical idle
+ * detection control for lane A) field of the SRCSCR2
+ * @SRDSCR2_SEICE_MASK: Bitmask for the SEICE (Receiver electrical idle
+ * detection control for lane E) field of the SRCSCR2
+ * @SRDSCR2_SEIC_MASK: Combined bitmask to set the receiver electrical idle
+ * detection control for both lanes
+ * @SRDSCR2_SEICA_SATA: Bitmask to set the SEICA field to the value used for
+ * SATA
+ * @SRDSCR2_SEICE_SATA: Bitmask to set the SEICE field to the value used for
+ * SATA
+ * @SRDSCR2_SEIC_SATA: Combined bitmask to set the value of both SEIC fields
+ * to the value used for SATA
+ * @SRDSCR2_SEICA_PEX: Bitmask to set the SEICA field to the value used for
+ * PCI Express
+ * @SRDSCR2_SEICE_PEX: Bitmask to set the SEICE field to the value used for
+ * PCI Express
+ * @SRDSCR2_SEIC_PEX: Combined bitmask to set the value of both SEIC fields
+ * to the value used for PCI Express
+ * @SRDSCR2_SEICA_SGMII: Bitmask to set the SEICA field to the value used for
+ * SGMII
+ * @SRDSCR2_SEICE_SGMII: Bitmask to set the SEICE field to the value used for
+ * SGMII
+ * @SRDSCR2_SEIC_SGMII: Combined bitmask to set the value of both SEIC fields
+ * to the value used for SGMII
+ */
+enum srdscr2_mask {
+ SRDSCR2_VDD_1V2 = 0x00800000,
+
+ SRDSCR2_SEICA_MASK = 0x00001c00,
+ SRDSCR2_SEICE_MASK = 0x0000001c,
+ SRDSCR2_SEIC_MASK = SRDSCR2_SEICA_MASK | SRDSCR2_SEICE_MASK,
+
+ SRDSCR2_SEICA_SATA = 0x00001400,
+ SRDSCR2_SEICE_SATA = 0x00000014,
+ SRDSCR2_SEIC_SATA = SRDSCR2_SEICA_SATA | SRDSCR2_SEICE_SATA,
+
+ SRDSCR2_SEICA_PEX = 0x00001000,
+ SRDSCR2_SEICE_PEX = 0x00000010,
+ SRDSCR2_SEIC_PEX = SRDSCR2_SEICA_PEX | SRDSCR2_SEICE_PEX,
+
+ SRDSCR2_SEICA_SGMII = 0x00000100,
+ SRDSCR2_SEICE_SGMII = 0x00000001,
+ SRDSCR2_SEIC_SGMII = SRDSCR2_SEICA_SGMII | SRDSCR2_SEICE_SGMII,
+};
+
+/**
+ * enum srdscr3_mask - Bit masks for SRDSCR3 (SerDes Control Register 3)
+ * @SRDSCR3_KFRA_SATA: Bitmask to set the KFRA field of SRDSCR3 to the
+ * value used by SATA
+ * @SRDSCR3_KFRE_SATA: Bitmask to set the KFRE field of SRDSCR3 to the
+ * value used by SATA
+ * @SRDSCR3_KFR_SATA: Combined bitmask to set both KFR fields to the
+ * value used by SATA
+ * @SRDSCR3_KPHA_SATA: Bitmask to set the KPHA field of SRDSCR3 to the
+ * value used by SATA
+ * @SRDSCR3_KPHE_SATA: Bitmask to set the KPHE field of SRDSCR3 to the
+ * value used by SATA
+ * @SRDSCR3_KPH_SATA: Combined bitmask to set both KPH fields to the
+ * value used by SATA
+ * @SRDSCR3_SDFMA_SATA_PEX: Bitmask to set the SDFMA field of SRDSCR3 to the
+ * value used by SATA and PCI Express
+ * @SRDSCR3_SDFME_SATA_PEX: Bitmask to set the SDFME field of SRDSCR3 to the
+ * value used by SATA and PCI Express
+ * @SRDSCR3_SDFM_SATA_PEX: Combined bitmask to set both SDFM fields to the
+ * value used by SATA and PCI Express
+ * @SRDSCR3_SDTXLA_SATA: Bitmask to set the SDTXLA field of SRDSCR3 to the
+ * value used by SATA
+ * @SRDSCR3_SDTXLE_SATA: Bitmask to set the SDTXLE field of SRDSCR3 to the
+ * value used by SATA
+ * @SRDSCR3_SDTXL_SATA: Combined bitmask to set both SDTXL fields to the
+ * value used by SATA
+ *
+ * KFRA = 'Kfr' gain selection in the CDR for lane A
+ * KFRE = 'Kfr' gain selection in the CDR for lane E
+ * SDFMA = Bandwidth of digital filter for lane A
+ * SDFME = Bandwidth of digital filter for lane E
+ * SDTXLA = Lane A transmitter amplitude levels
+ * SDTXLE = Lane E transmitter amplitude levels
+ */
+enum srdscr3_mask {
+ SRDSCR3_KFRA_SATA = 0x10000000,
+ SRDSCR3_KFRE_SATA = 0x00100000,
+ SRDSCR3_KFR_SATA = SRDSCR3_KFRA_SATA | SRDSCR3_KFRE_SATA,
+
+ SRDSCR3_KPHA_SATA = 0x04000000,
+ SRDSCR3_KPHE_SATA = 0x00040000,
+ SRDSCR3_KPH_SATA = SRDSCR3_KPHA_SATA | SRDSCR3_KPHE_SATA,
+
+ SRDSCR3_SDFMA_SATA_PEX = 0x01000000,
+ SRDSCR3_SDFME_SATA_PEX = 0x00010000,
+ SRDSCR3_SDFM_SATA_PEX = SRDSCR3_SDFMA_SATA_PEX | SRDSCR3_SDFME_SATA_PEX,
+
+ SRDSCR3_SDTXLA_SATA = 0x00000500,
+ SRDSCR3_SDTXLE_SATA = 0x00000005,
+ SRDSCR3_SDTXL_SATA = SRDSCR3_SDTXLA_SATA | SRDSCR3_SDTXLE_SATA,
+};
+
+/**
+ * enum srdscr4_mask - Bit masks for SRDSCR4 (SerDes Control Register 4)
+ * @SRDSCR4_PROTA_SATA: Bitmask to set the PROTA field of SRDSCR4 to the
+ * value used by SATA
+ * @SRDSCR4_PROTE_SATA: Bitmask to set the PROTE field of SRDSCR4 to the
+ * value used by SATA
+ * @SRDSCR4_PROT_SATA: Combined bitmask to set both PROT fields to the
+ * value used by SATA
+ * @SRDSCR4_PROTA_PEX: Bitmask to set the PROTA field of SRDSCR4 to the
+ * value used by PCI Express
+ * @SRDSCR4_PROTE_PEX: Bitmask to set the PROTE field of SRDSCR4 to the
+ * value used by PCI Express
+ * @SRDSCR4_PROT_PEX: Combined bitmask to set both PROT fields to the
+ * value used by PCI Express
+ * @SRDSCR4_PROTA_SGMII: Bitmask to set the PROTA field of SRDSCR4 to the
+ * value used by SGMII
+ * @SRDSCR4_PROTE_SGMII: Bitmask to set the PROTE field of SRDSCR4 to the
+ * value used by SGMII
+ * @SRDSCR4_PROT_SGMII: Combined bitmask to set both PROT fields to the
+ * value used by SGMII
+ * @SRDSCR4_PLANE_X2: Bitmask to set the PLANE field of SRDSCR4
+ * @SRDSCR4_RFCKS_100: Bitmask to set the RFCKS field of SRDSCR4 to the
+ * value 100Mhz
+ * @SRDSCR4_RFCKS_125: Bitmask to set the RFCKS field of SRDSCR4 to the
+ * value 125Mhz
+ * @SRDSCR4_RFCKS_150: Bitmask to set the RFCKS field of SRDSCR4 to the
+ * value 150Mhz
+ *
+ * PROTA = Lane A protocol select
+ * PROTE = Lane E protocol select
+ * PLAME = Number of PCI Express lanes
+ */
+enum srdscr4_mask {
+ SRDSCR4_PROTA_SATA = 0x00000800,
+ SRDSCR4_PROTE_SATA = 0x00000008,
+ SRDSCR4_PROT_SATA = SRDSCR4_PROTA_SATA | SRDSCR4_PROTE_SATA,
+
+ SRDSCR4_PROTA_PEX = 0x00000100,
+ SRDSCR4_PROTE_PEX = 0x00000001,
+ SRDSCR4_PROT_PEX = SRDSCR4_PROTA_PEX | SRDSCR4_PROTE_PEX,
+
+ SRDSCR4_PROTA_SGMII = 0x00000500,
+ SRDSCR4_PROTE_SGMII = 0x00000005,
+ SRDSCR4_PROT_SGMII = SRDSCR4_PROTA_SGMII | SRDSCR4_PROTE_SGMII,
+
+ SRDSCR4_PLANE_X2 = 0x01000000,
+
+ SRDSCR4_RFCKS_100 = (0 << 28),
+ SRDSCR4_RFCKS_125 = (1 << 28),
+ SRDSCR4_RFCKS_150 = (3 << 28),
+};
+
+/**
+ * enum srdsrstctl_mask - Bit masks for SRDSRSTCTL (SerDes Reset Control Register)
+ * @SRDSRSTCTL_RST: Bitmask for the RST (Software reset) field of the
+ * SRDSRSTCTL
+ * @SRDSRSTCTL_SATA_RESET: Bitmask for the SATA_RESET (SATA reset) field of the
+ * SRDSRSTCTL
+ */
+enum srdsrstctl_mask {
+ SRDSRSTCTL_RST = 0x80000000,
+ SRDSRSTCTL_SATA_RESET = 0xf,
+};
+
+/**
+ * struct mpc83xx_serdes_regs - Register map of the SerDes controller
+ * @srdscr0: SerDes Control Register 0
+ * @srdscr1: SerDes Control Register 1
+ * @srdscr2: SerDes Control Register 2
+ * @srdscr3: SerDes Control Register 3
+ * @srdscr4: SerDes Control Register 4
+ * @fill0: Reserved space in the register map
+ * @srdsrstctl: SerDes Reset Control Register
+ */
+struct mpc83xx_serdes_regs {
+ u32 srdscr0;
+ u32 srdscr1;
+ u32 srdscr2;
+ u32 srdscr3;
+ u32 srdscr4;
+ u8 fill0[12];
+ u32 srdsrstctl;
+};
+
+/**
+ * enum pex_type - Types of PCI Express
+ * @PEX_X1: PCI Express in x1 mode
+ * @PEX_X2: PCI Express in x2 mode
+ */
+enum pex_type {
+ PEX_X1,
+ PEX_X2,
+};
diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig
index 496e2b793b..54bb4b419f 100644
--- a/drivers/ram/Kconfig
+++ b/drivers/ram/Kconfig
@@ -34,4 +34,13 @@ config STM32_SDRAM
support external memories like sdram, psram & nand.
This driver is for the sdram memory interface with the FMC.
+config MPC83XX_SDRAM
+ bool "Enable MPC83XX SDRAM support"
+ depends on RAM
+ help
+ Enable support for the internal DDR Memory Controller of the MPC83xx
+ family of SoCs. Both static configurations, as well as configuring
+ the RAM through the use of SPD (Serial Presence Detect) is supported
+ via device tree settings.
+
source "drivers/ram/stm32mp1/Kconfig"
diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile
index 3820d03aa4..4ad3604d16 100644
--- a/drivers/ram/Makefile
+++ b/drivers/ram/Makefile
@@ -5,6 +5,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_RAM) += ram-uclass.o
+obj-$(CONFIG_MPC83XX_SDRAM) += mpc83xx_sdram.o
obj-$(CONFIG_SANDBOX) += sandbox_ram.o
obj-$(CONFIG_STM32MP1_DDR) += stm32mp1/
obj-$(CONFIG_STM32_SDRAM) += stm32_sdram.o
diff --git a/drivers/ram/mpc83xx_sdram.c b/drivers/ram/mpc83xx_sdram.c
new file mode 100644
index 0000000000..441baeb6f1
--- /dev/null
+++ b/drivers/ram/mpc83xx_sdram.c
@@ -0,0 +1,1096 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <ram.h>
+#include <dt-bindings/memory/mpc83xx-sdram.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Masks for the CS config register */
+static const u32 CSCONFIG_ENABLE = 0x80000000;
+
+static const u32 BANK_BITS_2;
+static const u32 BANK_BITS_3 = 0x00004000;
+
+static const u32 ROW_BITS_12;
+static const u32 ROW_BITS_13 = 0x00000100;
+static const u32 ROW_BITS_14 = 0x00000200;
+
+static const u32 COL_BITS_8;
+static const u32 COL_BITS_9 = 0x00000001;
+static const u32 COL_BITS_10 = 0x00000002;
+static const u32 COL_BITS_11 = 0x00000003;
+
+/* Shifts for the DDR SDRAM Timing Configuration 3 register */
+static const uint TIMING_CFG3_EXT_REFREC_SHIFT = (31 - 15);
+
+/* Shifts for the DDR SDRAM Timing Configuration 0 register */
+static const uint TIMING_CFG0_RWT_SHIFT = (31 - 1);
+static const uint TIMING_CFG0_WRT_SHIFT = (31 - 3);
+static const uint TIMING_CFG0_RRT_SHIFT = (31 - 5);
+static const uint TIMING_CFG0_WWT_SHIFT = (31 - 7);
+static const uint TIMING_CFG0_ACT_PD_EXIT_SHIFT = (31 - 11);
+static const uint TIMING_CFG0_PRE_PD_EXIT_SHIFT = (31 - 15);
+static const uint TIMING_CFG0_ODT_PD_EXIT_SHIFT = (31 - 23);
+static const uint TIMING_CFG0_MRS_CYC_SHIFT = (31 - 31);
+
+/* Shifts for the DDR SDRAM Timing Configuration 1 register */
+static const uint TIMING_CFG1_PRETOACT_SHIFT = (31 - 3);
+static const uint TIMING_CFG1_ACTTOPRE_SHIFT = (31 - 7);
+static const uint TIMING_CFG1_ACTTORW_SHIFT = (31 - 11);
+static const uint TIMING_CFG1_CASLAT_SHIFT = (31 - 15);
+static const uint TIMING_CFG1_REFREC_SHIFT = (31 - 19);
+static const uint TIMING_CFG1_WRREC_SHIFT = (31 - 23);
+static const uint TIMING_CFG1_ACTTOACT_SHIFT = (31 - 27);
+static const uint TIMING_CFG1_WRTORD_SHIFT = (31 - 31);
+
+/* Shifts for the DDR SDRAM Timing Configuration 2 register */
+static const uint TIMING_CFG2_CPO_SHIFT = (31 - 8);
+static const uint TIMING_CFG2_WR_DATA_DELAY_SHIFT = (31 - 21);
+static const uint TIMING_CFG2_ADD_LAT_SHIFT = (31 - 3);
+static const uint TIMING_CFG2_WR_LAT_DELAY_SHIFT = (31 - 12);
+static const uint TIMING_CFG2_RD_TO_PRE_SHIFT = (31 - 18);
+static const uint TIMING_CFG2_CKE_PLS_SHIFT = (31 - 25);
+static const uint TIMING_CFG2_FOUR_ACT_SHIFT;
+
+/* Shifts for the DDR SDRAM Control Configuration register */
+static const uint SDRAM_CFG_SREN_SHIFT = (31 - 1);
+static const uint SDRAM_CFG_ECC_EN_SHIFT = (31 - 2);
+static const uint SDRAM_CFG_RD_EN_SHIFT = (31 - 3);
+static const uint SDRAM_CFG_SDRAM_TYPE_SHIFT = (31 - 7);
+static const uint SDRAM_CFG_DYN_PWR_SHIFT = (31 - 10);
+static const uint SDRAM_CFG_DBW_SHIFT = (31 - 12);
+static const uint SDRAM_CFG_NCAP_SHIFT = (31 - 14);
+static const uint SDRAM_CFG_2T_EN_SHIFT = (31 - 16);
+static const uint SDRAM_CFG_BA_INTLV_CTL_SHIFT = (31 - 23);
+static const uint SDRAM_CFG_PCHB8_SHIFT = (31 - 27);
+static const uint SDRAM_CFG_HSE_SHIFT = (31 - 28);
+static const uint SDRAM_CFG_BI_SHIFT = (31 - 31);
+
+/* Shifts for the DDR SDRAM Control Configuration 2 register */
+static const uint SDRAM_CFG2_FRC_SR_SHIFT = (31 - 0);
+static const uint SDRAM_CFG2_DLL_RST_DIS = (31 - 2);
+static const uint SDRAM_CFG2_DQS_CFG = (31 - 5);
+static const uint SDRAM_CFG2_ODT_CFG = (31 - 10);
+static const uint SDRAM_CFG2_NUM_PR = (31 - 19);
+
+/* Shifts for the DDR SDRAM Mode register */
+static const uint SDRAM_MODE_ESD_SHIFT = (31 - 15);
+static const uint SDRAM_MODE_SD_SHIFT = (31 - 31);
+
+/* Shifts for the DDR SDRAM Mode 2 register */
+static const uint SDRAM_MODE2_ESD2_SHIFT = (31 - 15);
+static const uint SDRAM_MODE2_ESD3_SHIFT = (31 - 31);
+
+/* Shifts for the DDR SDRAM Interval Configuration register */
+static const uint SDRAM_INTERVAL_REFINT_SHIFT = (31 - 15);
+static const uint SDRAM_INTERVAL_BSTOPRE_SHIFT = (31 - 31);
+
+/* Mask for the DDR SDRAM Mode Control register */
+static const u32 SDRAM_CFG_MEM_EN = 0x80000000;
+
+int dram_init(void)
+{
+ struct udevice *ram_ctrl;
+ int ret;
+
+ /* Current assumption: There is only one RAM controller */
+ ret = uclass_first_device_err(UCLASS_RAM, &ram_ctrl);
+ if (ret) {
+ debug("%s: uclass_first_device_err failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* FIXME(mario.six@gdsys.cc): Set gd->ram_size? */
+
+ return 0;
+}
+
+phys_size_t get_effective_memsize(void)
+{
+ if (!IS_ENABLED(CONFIG_VERY_BIG_RAM))
+ return gd->ram_size;
+
+ /* Limit stack to what we can reasonable map */
+ return ((gd->ram_size > CONFIG_MAX_MEM_MAPPED) ?
+ CONFIG_MAX_MEM_MAPPED : gd->ram_size);
+}
+
+/**
+ * struct mpc83xx_sdram_priv - Private data for MPC83xx RAM controllers
+ * @total_size: The total size of all RAM modules associated with this RAM
+ * controller in bytes
+ */
+struct mpc83xx_sdram_priv {
+ ulong total_size;
+};
+
+/**
+ * mpc83xx_sdram_static_init() - Statically initialize a RAM module.
+ * @node: Device tree node associated with ths module in question
+ * @cs: The chip select to use for this RAM module
+ * @mapaddr: The address where the RAM module should be mapped
+ * @size: The size of the RAM module to be mapped in bytes
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int mpc83xx_sdram_static_init(ofnode node, u32 cs, u32 mapaddr, u32 size)
+{
+ immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ u32 msize = size;
+ u32 msize_log2 = __ilog2(msize);
+ u32 auto_precharge, odt_rd_cfg, odt_wr_cfg, bank_bits, row_bits,
+ col_bits;
+ u32 bank_bits_mask, row_bits_mask, col_bits_mask;
+
+ /* Configure the DDR local access window */
+ out_be32(&im->sysconf.ddrlaw[cs].bar, mapaddr & 0xfffff000);
+ out_be32(&im->sysconf.ddrlaw[cs].ar, LBLAWAR_EN | (msize_log2 - 1));
+
+ out_be32(&im->ddr.csbnds[cs].csbnds, (msize - 1) >> 24);
+
+ auto_precharge = ofnode_read_u32_default(node, "auto_precharge", 0);
+ switch (auto_precharge) {
+ case AUTO_PRECHARGE_ENABLE:
+ case AUTO_PRECHARGE_DISABLE:
+ break;
+ default:
+ debug("%s: auto_precharge value %d invalid.\n",
+ ofnode_get_name(node), auto_precharge);
+ return -EINVAL;
+ }
+
+ odt_rd_cfg = ofnode_read_u32_default(node, "odt_rd_cfg", 0);
+ switch (odt_rd_cfg) {
+ case ODT_RD_ONLY_OTHER_DIMM:
+ if (!IS_ENABLED(CONFIG_MPC8360) &&
+ !IS_ENABLED(CONFIG_MPC837x)) {
+ debug("%s: odt_rd_cfg value %d invalid.\n",
+ ofnode_get_name(node), odt_rd_cfg);
+ return -EINVAL;
+ }
+ /* fall through */
+ case ODT_RD_NEVER:
+ case ODT_RD_ONLY_CURRENT:
+ case ODT_RD_ONLY_OTHER_CS:
+ if (!IS_ENABLED(CONFIG_MPC830x) &&
+ !IS_ENABLED(CONFIG_MPC831x) &&
+ !IS_ENABLED(CONFIG_MPC8360) &&
+ !IS_ENABLED(CONFIG_MPC837x)) {
+ debug("%s: odt_rd_cfg value %d invalid.\n",
+ ofnode_get_name(node), odt_rd_cfg);
+ return -EINVAL;
+ }
+ /* fall through */
+ /* Only MPC832x knows this value */
+ case ODT_RD_ALL:
+ break;
+ default:
+ debug("%s: odt_rd_cfg value %d invalid.\n",
+ ofnode_get_name(node), odt_rd_cfg);
+ return -EINVAL;
+ }
+
+ odt_wr_cfg = ofnode_read_u32_default(node, "odt_wr_cfg", 0);
+ switch (odt_wr_cfg) {
+ case ODT_WR_ONLY_OTHER_DIMM:
+ if (!IS_ENABLED(CONFIG_MPC8360) &&
+ !IS_ENABLED(CONFIG_MPC837x)) {
+ debug("%s: odt_wr_cfg value %d invalid.\n",
+ ofnode_get_name(node), odt_wr_cfg);
+ return -EINVAL;
+ }
+ /* fall through */
+ case ODT_WR_NEVER:
+ case ODT_WR_ONLY_CURRENT:
+ case ODT_WR_ONLY_OTHER_CS:
+ if (!IS_ENABLED(CONFIG_MPC830x) &&
+ !IS_ENABLED(CONFIG_MPC831x) &&
+ !IS_ENABLED(CONFIG_MPC8360) &&
+ !IS_ENABLED(CONFIG_MPC837x)) {
+ debug("%s: odt_wr_cfg value %d invalid.\n",
+ ofnode_get_name(node), odt_wr_cfg);
+ return -EINVAL;
+ }
+ /* fall through */
+ /* MPC832x only knows this value */
+ case ODT_WR_ALL:
+ break;
+ default:
+ debug("%s: odt_wr_cfg value %d invalid.\n",
+ ofnode_get_name(node), odt_wr_cfg);
+ return -EINVAL;
+ }
+
+ bank_bits = ofnode_read_u32_default(node, "bank_bits", 0);
+ switch (bank_bits) {
+ case 2:
+ bank_bits_mask = BANK_BITS_2;
+ break;
+ case 3:
+ bank_bits_mask = BANK_BITS_3;
+ break;
+ default:
+ debug("%s: bank_bits value %d invalid.\n",
+ ofnode_get_name(node), bank_bits);
+ return -EINVAL;
+ }
+
+ row_bits = ofnode_read_u32_default(node, "row_bits", 0);
+ switch (row_bits) {
+ case 12:
+ row_bits_mask = ROW_BITS_12;
+ break;
+ case 13:
+ row_bits_mask = ROW_BITS_13;
+ break;
+ case 14:
+ row_bits_mask = ROW_BITS_14;
+ break;
+ default:
+ debug("%s: row_bits value %d invalid.\n",
+ ofnode_get_name(node), row_bits);
+ return -EINVAL;
+ }
+
+ col_bits = ofnode_read_u32_default(node, "col_bits", 0);
+ switch (col_bits) {
+ case 8:
+ col_bits_mask = COL_BITS_8;
+ break;
+ case 9:
+ col_bits_mask = COL_BITS_9;
+ break;
+ case 10:
+ col_bits_mask = COL_BITS_10;
+ break;
+ case 11:
+ col_bits_mask = COL_BITS_11;
+ break;
+ default:
+ debug("%s: col_bits value %d invalid.\n",
+ ofnode_get_name(node), col_bits);
+ return -EINVAL;
+ }
+
+ /* Write CS config value */
+ out_be32(&im->ddr.cs_config[cs], CSCONFIG_ENABLE | auto_precharge |
+ odt_rd_cfg | odt_wr_cfg |
+ bank_bits_mask | row_bits_mask |
+ col_bits_mask);
+ return 0;
+}
+
+/**
+ * mpc83xx_sdram_spd_init() - Initialize a RAM module using a SPD flash.
+ * @node: Device tree node associated with ths module in question
+ * @cs: The chip select to use for this RAM module
+ * @mapaddr: The address where the RAM module should be mapped
+ * @size: The size of the RAM module to be mapped in bytes
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int mpc83xx_sdram_spd_init(ofnode node, u32 cs, u32 mapaddr, u32 size)
+{
+ /* TODO(mario.six@gdsys.cc): Implement */
+ return 0;
+}
+
+static int mpc83xx_sdram_ofdata_to_platdata(struct udevice *dev)
+{
+ return 0;
+}
+
+static int mpc83xx_sdram_probe(struct udevice *dev)
+{
+ struct mpc83xx_sdram_priv *priv = dev_get_priv(dev);
+ immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+ int ret = 0;
+ ofnode subnode;
+ /* DDR control driver register values */
+ u32 dso, pz_override, nz_override, odt_term, ddr_type, mvref_sel, m_odr;
+ u32 ddrcdr;
+ /* DDR SDRAM Clock Control register values */
+ u32 clock_adjust;
+ /* DDR SDRAM Timing Configuration 3 register values */
+ u32 ext_refresh_rec, ext_refresh_rec_mask;
+ /* DDR SDRAM Timing Configuration 0 register values */
+ u32 read_to_write, write_to_read, read_to_read, write_to_write,
+ active_powerdown_exit, precharge_powerdown_exit,
+ odt_powerdown_exit, mode_reg_set_cycle;
+ u32 timing_cfg_0;
+ /* DDR SDRAM Timing Configuration 1 register values */
+ u32 precharge_to_activate, activate_to_precharge,
+ activate_to_readwrite, mcas_latency, refresh_recovery,
+ last_data_to_precharge, activate_to_activate,
+ last_write_data_to_read;
+ u32 timing_cfg_1;
+ /* DDR SDRAM Timing Configuration 2 register values */
+ u32 additive_latency, mcas_to_preamble_override, write_latency,
+ read_to_precharge, write_cmd_to_write_data,
+ minimum_cke_pulse_width, four_activates_window;
+ u32 timing_cfg_2;
+ /* DDR SDRAM Control Configuration register values */
+ u32 self_refresh, ecc, registered_dram, sdram_type,
+ dynamic_power_management, databus_width, nc_auto_precharge,
+ timing_2t, bank_interleaving_ctrl, precharge_bit_8, half_strength,
+ bypass_initialization;
+ u32 sdram_cfg;
+ /* DDR SDRAM Control Configuration 2 register values */
+ u32 force_self_refresh, dll_reset, dqs_config, odt_config,
+ posted_refreshes;
+ u32 sdram_cfg2;
+ /* DDR SDRAM Mode Configuration register values */
+ u32 sdmode, esdmode;
+ u32 sdram_mode;
+ /* DDR SDRAM Mode Configuration 2 register values */
+ u32 esdmode2, esdmode3;
+ u32 sdram_mode2;
+ /* DDR SDRAM Interval Configuration register values */
+ u32 refresh_interval, precharge_interval;
+ u32 sdram_interval;
+
+ priv->total_size = 0;
+
+ /* Disable both banks initially (might be re-enabled in loop below) */
+ out_be32(&im->ddr.cs_config[0], 0);
+ out_be32(&im->ddr.cs_config[1], 0);
+
+ dso = dev_read_u32_default(dev, "driver_software_override", 0);
+ if (dso > 1) {
+ debug("%s: driver_software_override value %d invalid.\n",
+ dev->name, dso);
+ return -EINVAL;
+ }
+
+ pz_override = dev_read_u32_default(dev, "p_impedance_override", 0);
+
+ switch (pz_override) {
+ case DSO_P_IMPEDANCE_HIGHEST_Z:
+ case DSO_P_IMPEDANCE_MUCH_HIGHER_Z:
+ case DSO_P_IMPEDANCE_HIGHER_Z:
+ case DSO_P_IMPEDANCE_NOMINAL:
+ case DSO_P_IMPEDANCE_LOWER_Z:
+ break;
+ default:
+ debug("%s: p_impedance_override value %d invalid.\n",
+ dev->name, pz_override);
+ return -EINVAL;
+ }
+
+ nz_override = dev_read_u32_default(dev, "n_impedance_override", 0);
+
+ switch (nz_override) {
+ case DSO_N_IMPEDANCE_HIGHEST_Z:
+ case DSO_N_IMPEDANCE_MUCH_HIGHER_Z:
+ case DSO_N_IMPEDANCE_HIGHER_Z:
+ case DSO_N_IMPEDANCE_NOMINAL:
+ case DSO_N_IMPEDANCE_LOWER_Z:
+ break;
+ default:
+ debug("%s: n_impedance_override value %d invalid.\n",
+ dev->name, nz_override);
+ return -EINVAL;
+ }
+
+ odt_term = dev_read_u32_default(dev, "odt_termination_value", 0);
+ if (odt_term > 1) {
+ debug("%s: odt_termination_value value %d invalid.\n",
+ dev->name, odt_term);
+ return -EINVAL;
+ }
+
+ ddr_type = dev_read_u32_default(dev, "ddr_type", 0);
+ if (ddr_type > 1) {
+ debug("%s: ddr_type value %d invalid.\n",
+ dev->name, ddr_type);
+ return -EINVAL;
+ }
+
+ mvref_sel = dev_read_u32_default(dev, "mvref_sel", 0);
+ if (mvref_sel > 1) {
+ debug("%s: mvref_sel value %d invalid.\n",
+ dev->name, mvref_sel);
+ return -EINVAL;
+ }
+
+ m_odr = dev_read_u32_default(dev, "m_odr", 0);
+ if (mvref_sel > 1) {
+ debug("%s: m_odr value %d invalid.\n",
+ dev->name, m_odr);
+ return -EINVAL;
+ }
+
+ ddrcdr = dso << (31 - 1) |
+ pz_override << (31 - 5) |
+ nz_override << (31 - 9) |
+ odt_term << (31 - 12) |
+ ddr_type << (31 - 13) |
+ mvref_sel << (31 - 29) |
+ m_odr << (31 - 30) | 1;
+
+ /* Configure the DDR control driver register */
+ out_be32(&im->sysconf.ddrcdr, ddrcdr);
+
+ dev_for_each_subnode(subnode, dev) {
+ u32 val[3];
+ u32 cs, addr, size;
+
+ /* CS, map address, size -> three values */
+ ofnode_read_u32_array(subnode, "reg", val, 3);
+
+ cs = val[0];
+ addr = val[1];
+ size = val[2];
+
+ if (cs > 1) {
+ debug("%s: chip select value %d invalid.\n",
+ dev->name, cs);
+ return -EINVAL;
+ }
+
+ /* TODO(mario.six@gdsys.cc): Sanity check for size. */
+
+ if (ofnode_read_bool(subnode, "read-spd"))
+ ret = mpc83xx_sdram_spd_init(subnode, cs, addr, size);
+ else
+ ret = mpc83xx_sdram_static_init(subnode, cs, addr,
+ size);
+ if (ret) {
+ debug("%s: RAM init failed.\n", dev->name);
+ return ret;
+ }
+ };
+
+ /*
+ * TODO(mario.six@gdsys.cc): This should only occur for static
+ * configuration
+ */
+
+ clock_adjust = dev_read_u32_default(dev, "clock_adjust", 0);
+ switch (clock_adjust) {
+ case CLOCK_ADJUST_025:
+ case CLOCK_ADJUST_05:
+ case CLOCK_ADJUST_075:
+ case CLOCK_ADJUST_1:
+ break;
+ default:
+ debug("%s: clock_adjust value %d invalid.\n",
+ dev->name, clock_adjust);
+ return -EINVAL;
+ }
+
+ /* Configure the DDR SDRAM Clock Control register */
+ out_be32(&im->ddr.sdram_clk_cntl, clock_adjust);
+
+ ext_refresh_rec = dev_read_u32_default(dev, "ext_refresh_rec", 0);
+ switch (ext_refresh_rec) {
+ case 0:
+ ext_refresh_rec_mask = 0 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 16:
+ ext_refresh_rec_mask = 1 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 32:
+ ext_refresh_rec_mask = 2 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 48:
+ ext_refresh_rec_mask = 3 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 64:
+ ext_refresh_rec_mask = 4 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 80:
+ ext_refresh_rec_mask = 5 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 96:
+ ext_refresh_rec_mask = 6 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ case 112:
+ ext_refresh_rec_mask = 7 << TIMING_CFG3_EXT_REFREC_SHIFT;
+ break;
+ default:
+ debug("%s: ext_refresh_rec value %d invalid.\n",
+ dev->name, ext_refresh_rec);
+ return -EINVAL;
+ }
+
+ /* Configure the DDR SDRAM Timing Configuration 3 register */
+ out_be32(&im->ddr.timing_cfg_3, ext_refresh_rec_mask);
+
+ read_to_write = dev_read_u32_default(dev, "read_to_write", 0);
+ if (read_to_write > 3) {
+ debug("%s: read_to_write value %d invalid.\n",
+ dev->name, read_to_write);
+ return -EINVAL;
+ }
+
+ write_to_read = dev_read_u32_default(dev, "write_to_read", 0);
+ if (write_to_read > 3) {
+ debug("%s: write_to_read value %d invalid.\n",
+ dev->name, write_to_read);
+ return -EINVAL;
+ }
+
+ read_to_read = dev_read_u32_default(dev, "read_to_read", 0);
+ if (read_to_read > 3) {
+ debug("%s: read_to_read value %d invalid.\n",
+ dev->name, read_to_read);
+ return -EINVAL;
+ }
+
+ write_to_write = dev_read_u32_default(dev, "write_to_write", 0);
+ if (write_to_write > 3) {
+ debug("%s: write_to_write value %d invalid.\n",
+ dev->name, write_to_write);
+ return -EINVAL;
+ }
+
+ active_powerdown_exit =
+ dev_read_u32_default(dev, "active_powerdown_exit", 0);
+ if (active_powerdown_exit > 7) {
+ debug("%s: active_powerdown_exit value %d invalid.\n",
+ dev->name, active_powerdown_exit);
+ return -EINVAL;
+ }
+
+ precharge_powerdown_exit =
+ dev_read_u32_default(dev, "precharge_powerdown_exit", 0);
+ if (precharge_powerdown_exit > 7) {
+ debug("%s: precharge_powerdown_exit value %d invalid.\n",
+ dev->name, precharge_powerdown_exit);
+ return -EINVAL;
+ }
+
+ odt_powerdown_exit = dev_read_u32_default(dev, "odt_powerdown_exit", 0);
+ if (odt_powerdown_exit > 15) {
+ debug("%s: odt_powerdown_exit value %d invalid.\n",
+ dev->name, odt_powerdown_exit);
+ return -EINVAL;
+ }
+
+ mode_reg_set_cycle = dev_read_u32_default(dev, "mode_reg_set_cycle", 0);
+ if (mode_reg_set_cycle > 15) {
+ debug("%s: mode_reg_set_cycle value %d invalid.\n",
+ dev->name, mode_reg_set_cycle);
+ return -EINVAL;
+ }
+
+ timing_cfg_0 = read_to_write << TIMING_CFG0_RWT_SHIFT |
+ write_to_read << TIMING_CFG0_WRT_SHIFT |
+ read_to_read << TIMING_CFG0_RRT_SHIFT |
+ write_to_write << TIMING_CFG0_WWT_SHIFT |
+ active_powerdown_exit << TIMING_CFG0_ACT_PD_EXIT_SHIFT |
+ precharge_powerdown_exit << TIMING_CFG0_PRE_PD_EXIT_SHIFT |
+ odt_powerdown_exit << TIMING_CFG0_ODT_PD_EXIT_SHIFT |
+ mode_reg_set_cycle << TIMING_CFG0_MRS_CYC_SHIFT;
+
+ out_be32(&im->ddr.timing_cfg_0, timing_cfg_0);
+
+ precharge_to_activate =
+ dev_read_u32_default(dev, "precharge_to_activate", 0);
+ if (precharge_to_activate > 7 || precharge_to_activate == 0) {
+ debug("%s: precharge_to_activate value %d invalid.\n",
+ dev->name, precharge_to_activate);
+ return -EINVAL;
+ }
+
+ activate_to_precharge =
+ dev_read_u32_default(dev, "activate_to_precharge", 0);
+ if (activate_to_precharge > 19) {
+ debug("%s: activate_to_precharge value %d invalid.\n",
+ dev->name, activate_to_precharge);
+ return -EINVAL;
+ }
+
+ activate_to_readwrite =
+ dev_read_u32_default(dev, "activate_to_readwrite", 0);
+ if (activate_to_readwrite > 7 || activate_to_readwrite == 0) {
+ debug("%s: activate_to_readwrite value %d invalid.\n",
+ dev->name, activate_to_readwrite);
+ return -EINVAL;
+ }
+
+ mcas_latency = dev_read_u32_default(dev, "mcas_latency", 0);
+ switch (mcas_latency) {
+ case CASLAT_20:
+ case CASLAT_25:
+ if (!IS_ENABLED(CONFIG_ARCH_MPC8308)) {
+ debug("%s: MCAS latency < 3.0 unsupported on MPC8308\n",
+ dev->name);
+ return -EINVAL;
+ }
+ /* fall through */
+ case CASLAT_30:
+ case CASLAT_35:
+ case CASLAT_40:
+ case CASLAT_45:
+ case CASLAT_50:
+ case CASLAT_55:
+ case CASLAT_60:
+ case CASLAT_65:
+ case CASLAT_70:
+ case CASLAT_75:
+ case CASLAT_80:
+ break;
+ default:
+ debug("%s: mcas_latency value %d invalid.\n",
+ dev->name, mcas_latency);
+ return -EINVAL;
+ }
+
+ refresh_recovery = dev_read_u32_default(dev, "refresh_recovery", 0);
+ if (refresh_recovery > 23 || refresh_recovery < 8) {
+ debug("%s: refresh_recovery value %d invalid.\n",
+ dev->name, refresh_recovery);
+ return -EINVAL;
+ }
+
+ last_data_to_precharge =
+ dev_read_u32_default(dev, "last_data_to_precharge", 0);
+ if (last_data_to_precharge > 7 || last_data_to_precharge == 0) {
+ debug("%s: last_data_to_precharge value %d invalid.\n",
+ dev->name, last_data_to_precharge);
+ return -EINVAL;
+ }
+
+ activate_to_activate =
+ dev_read_u32_default(dev, "activate_to_activate", 0);
+ if (activate_to_activate > 7 || activate_to_activate == 0) {
+ debug("%s: activate_to_activate value %d invalid.\n",
+ dev->name, activate_to_activate);
+ return -EINVAL;
+ }
+
+ last_write_data_to_read =
+ dev_read_u32_default(dev, "last_write_data_to_read", 0);
+ if (last_write_data_to_read > 7 || last_write_data_to_read == 0) {
+ debug("%s: last_write_data_to_read value %d invalid.\n",
+ dev->name, last_write_data_to_read);
+ return -EINVAL;
+ }
+
+ timing_cfg_1 = precharge_to_activate << TIMING_CFG1_PRETOACT_SHIFT |
+ (activate_to_precharge > 15 ?
+ activate_to_precharge - 16 :
+ activate_to_precharge) << TIMING_CFG1_ACTTOPRE_SHIFT |
+ activate_to_readwrite << TIMING_CFG1_ACTTORW_SHIFT |
+ mcas_latency << TIMING_CFG1_CASLAT_SHIFT |
+ (refresh_recovery - 8) << TIMING_CFG1_REFREC_SHIFT |
+ last_data_to_precharge << TIMING_CFG1_WRREC_SHIFT |
+ activate_to_activate << TIMING_CFG1_ACTTOACT_SHIFT |
+ last_write_data_to_read << TIMING_CFG1_WRTORD_SHIFT;
+
+ /* Configure the DDR SDRAM Timing Configuration 1 register */
+ out_be32(&im->ddr.timing_cfg_1, timing_cfg_1);
+
+ additive_latency = dev_read_u32_default(dev, "additive_latency", 0);
+ if (additive_latency > 5) {
+ debug("%s: additive_latency value %d invalid.\n",
+ dev->name, additive_latency);
+ return -EINVAL;
+ }
+
+ mcas_to_preamble_override =
+ dev_read_u32_default(dev, "mcas_to_preamble_override", 0);
+ switch (mcas_to_preamble_override) {
+ case READ_LAT_PLUS_1:
+ case READ_LAT:
+ case READ_LAT_PLUS_1_4:
+ case READ_LAT_PLUS_1_2:
+ case READ_LAT_PLUS_3_4:
+ case READ_LAT_PLUS_5_4:
+ case READ_LAT_PLUS_3_2:
+ case READ_LAT_PLUS_7_4:
+ case READ_LAT_PLUS_2:
+ case READ_LAT_PLUS_9_4:
+ case READ_LAT_PLUS_5_2:
+ case READ_LAT_PLUS_11_4:
+ case READ_LAT_PLUS_3:
+ case READ_LAT_PLUS_13_4:
+ case READ_LAT_PLUS_7_2:
+ case READ_LAT_PLUS_15_4:
+ case READ_LAT_PLUS_4:
+ case READ_LAT_PLUS_17_4:
+ case READ_LAT_PLUS_9_2:
+ case READ_LAT_PLUS_19_4:
+ break;
+ default:
+ debug("%s: mcas_to_preamble_override value %d invalid.\n",
+ dev->name, mcas_to_preamble_override);
+ return -EINVAL;
+ }
+
+ write_latency = dev_read_u32_default(dev, "write_latency", 0);
+ if (write_latency > 7 || write_latency == 0) {
+ debug("%s: write_latency value %d invalid.\n",
+ dev->name, write_latency);
+ return -EINVAL;
+ }
+
+ read_to_precharge = dev_read_u32_default(dev, "read_to_precharge", 0);
+ if (read_to_precharge > 4 || read_to_precharge == 0) {
+ debug("%s: read_to_precharge value %d invalid.\n",
+ dev->name, read_to_precharge);
+ return -EINVAL;
+ }
+
+ write_cmd_to_write_data =
+ dev_read_u32_default(dev, "write_cmd_to_write_data", 0);
+ switch (write_cmd_to_write_data) {
+ case CLOCK_DELAY_0:
+ case CLOCK_DELAY_1_4:
+ case CLOCK_DELAY_1_2:
+ case CLOCK_DELAY_3_4:
+ case CLOCK_DELAY_1:
+ case CLOCK_DELAY_5_4:
+ case CLOCK_DELAY_3_2:
+ break;
+ default:
+ debug("%s: write_cmd_to_write_data value %d invalid.\n",
+ dev->name, write_cmd_to_write_data);
+ return -EINVAL;
+ }
+
+ minimum_cke_pulse_width =
+ dev_read_u32_default(dev, "minimum_cke_pulse_width", 0);
+ if (minimum_cke_pulse_width > 4 || minimum_cke_pulse_width == 0) {
+ debug("%s: minimum_cke_pulse_width value %d invalid.\n",
+ dev->name, minimum_cke_pulse_width);
+ return -EINVAL;
+ }
+
+ four_activates_window =
+ dev_read_u32_default(dev, "four_activates_window", 0);
+ if (four_activates_window > 20 || four_activates_window == 0) {
+ debug("%s: four_activates_window value %d invalid.\n",
+ dev->name, four_activates_window);
+ return -EINVAL;
+ }
+
+ timing_cfg_2 = additive_latency << TIMING_CFG2_ADD_LAT_SHIFT |
+ mcas_to_preamble_override << TIMING_CFG2_CPO_SHIFT |
+ write_latency << TIMING_CFG2_WR_LAT_DELAY_SHIFT |
+ read_to_precharge << TIMING_CFG2_RD_TO_PRE_SHIFT |
+ write_cmd_to_write_data << TIMING_CFG2_WR_DATA_DELAY_SHIFT |
+ minimum_cke_pulse_width << TIMING_CFG2_CKE_PLS_SHIFT |
+ four_activates_window << TIMING_CFG2_FOUR_ACT_SHIFT;
+
+ out_be32(&im->ddr.timing_cfg_2, timing_cfg_2);
+
+ self_refresh = dev_read_u32_default(dev, "self_refresh", 0);
+ switch (self_refresh) {
+ case SREN_DISABLE:
+ case SREN_ENABLE:
+ break;
+ default:
+ debug("%s: self_refresh value %d invalid.\n",
+ dev->name, self_refresh);
+ return -EINVAL;
+ }
+
+ ecc = dev_read_u32_default(dev, "ecc", 0);
+ switch (ecc) {
+ case ECC_DISABLE:
+ case ECC_ENABLE:
+ break;
+ default:
+ debug("%s: ecc value %d invalid.\n", dev->name, ecc);
+ return -EINVAL;
+ }
+
+ registered_dram = dev_read_u32_default(dev, "registered_dram", 0);
+ switch (registered_dram) {
+ case RD_DISABLE:
+ case RD_ENABLE:
+ break;
+ default:
+ debug("%s: registered_dram value %d invalid.\n",
+ dev->name, registered_dram);
+ return -EINVAL;
+ }
+
+ sdram_type = dev_read_u32_default(dev, "sdram_type", 0);
+ switch (sdram_type) {
+ case TYPE_DDR1:
+ case TYPE_DDR2:
+ break;
+ default:
+ debug("%s: sdram_type value %d invalid.\n",
+ dev->name, sdram_type);
+ return -EINVAL;
+ }
+
+ dynamic_power_management =
+ dev_read_u32_default(dev, "dynamic_power_management", 0);
+ switch (dynamic_power_management) {
+ case DYN_PWR_DISABLE:
+ case DYN_PWR_ENABLE:
+ break;
+ default:
+ debug("%s: dynamic_power_management value %d invalid.\n",
+ dev->name, dynamic_power_management);
+ return -EINVAL;
+ }
+
+ databus_width = dev_read_u32_default(dev, "databus_width", 0);
+ switch (databus_width) {
+ case DATA_BUS_WIDTH_16:
+ case DATA_BUS_WIDTH_32:
+ break;
+ default:
+ debug("%s: databus_width value %d invalid.\n",
+ dev->name, databus_width);
+ return -EINVAL;
+ }
+
+ nc_auto_precharge = dev_read_u32_default(dev, "nc_auto_precharge", 0);
+ switch (nc_auto_precharge) {
+ case NCAP_DISABLE:
+ case NCAP_ENABLE:
+ break;
+ default:
+ debug("%s: nc_auto_precharge value %d invalid.\n",
+ dev->name, nc_auto_precharge);
+ return -EINVAL;
+ }
+
+ timing_2t = dev_read_u32_default(dev, "timing_2t", 0);
+ switch (timing_2t) {
+ case TIMING_1T:
+ case TIMING_2T:
+ break;
+ default:
+ debug("%s: timing_2t value %d invalid.\n",
+ dev->name, timing_2t);
+ return -EINVAL;
+ }
+
+ bank_interleaving_ctrl =
+ dev_read_u32_default(dev, "bank_interleaving_ctrl", 0);
+ switch (bank_interleaving_ctrl) {
+ case INTERLEAVE_NONE:
+ case INTERLEAVE_1_AND_2:
+ break;
+ default:
+ debug("%s: bank_interleaving_ctrl value %d invalid.\n",
+ dev->name, bank_interleaving_ctrl);
+ return -EINVAL;
+ }
+
+ precharge_bit_8 = dev_read_u32_default(dev, "precharge_bit_8", 0);
+ switch (precharge_bit_8) {
+ case PRECHARGE_MA_10:
+ case PRECHARGE_MA_8:
+ break;
+ default:
+ debug("%s: precharge_bit_8 value %d invalid.\n",
+ dev->name, precharge_bit_8);
+ return -EINVAL;
+ }
+
+ half_strength = dev_read_u32_default(dev, "half_strength", 0);
+ switch (half_strength) {
+ case STRENGTH_FULL:
+ case STRENGTH_HALF:
+ break;
+ default:
+ debug("%s: half_strength value %d invalid.\n",
+ dev->name, half_strength);
+ return -EINVAL;
+ }
+
+ bypass_initialization =
+ dev_read_u32_default(dev, "bypass_initialization", 0);
+ switch (bypass_initialization) {
+ case INITIALIZATION_DONT_BYPASS:
+ case INITIALIZATION_BYPASS:
+ break;
+ default:
+ debug("%s: bypass_initialization value %d invalid.\n",
+ dev->name, bypass_initialization);
+ return -EINVAL;
+ }
+
+ sdram_cfg = self_refresh << SDRAM_CFG_SREN_SHIFT |
+ ecc << SDRAM_CFG_ECC_EN_SHIFT |
+ registered_dram << SDRAM_CFG_RD_EN_SHIFT |
+ sdram_type << SDRAM_CFG_SDRAM_TYPE_SHIFT |
+ dynamic_power_management << SDRAM_CFG_DYN_PWR_SHIFT |
+ databus_width << SDRAM_CFG_DBW_SHIFT |
+ nc_auto_precharge << SDRAM_CFG_NCAP_SHIFT |
+ timing_2t << SDRAM_CFG_2T_EN_SHIFT |
+ bank_interleaving_ctrl << SDRAM_CFG_BA_INTLV_CTL_SHIFT |
+ precharge_bit_8 << SDRAM_CFG_PCHB8_SHIFT |
+ half_strength << SDRAM_CFG_HSE_SHIFT |
+ bypass_initialization << SDRAM_CFG_BI_SHIFT;
+
+ out_be32(&im->ddr.sdram_cfg, sdram_cfg);
+
+ force_self_refresh = dev_read_u32_default(dev, "force_self_refresh", 0);
+ switch (force_self_refresh) {
+ case MODE_NORMAL:
+ case MODE_REFRESH:
+ break;
+ default:
+ debug("%s: force_self_refresh value %d invalid.\n",
+ dev->name, force_self_refresh);
+ return -EINVAL;
+ }
+
+ dll_reset = dev_read_u32_default(dev, "dll_reset", 0);
+ switch (dll_reset) {
+ case DLL_RESET_ENABLE:
+ case DLL_RESET_DISABLE:
+ break;
+ default:
+ debug("%s: dll_reset value %d invalid.\n",
+ dev->name, dll_reset);
+ return -EINVAL;
+ }
+
+ dqs_config = dev_read_u32_default(dev, "dqs_config", 0);
+ switch (dqs_config) {
+ case DQS_TRUE:
+ break;
+ default:
+ debug("%s: dqs_config value %d invalid.\n",
+ dev->name, dqs_config);
+ return -EINVAL;
+ }
+
+ odt_config = dev_read_u32_default(dev, "odt_config", 0);
+ switch (odt_config) {
+ case ODT_ASSERT_NEVER:
+ case ODT_ASSERT_WRITES:
+ case ODT_ASSERT_READS:
+ case ODT_ASSERT_ALWAYS:
+ break;
+ default:
+ debug("%s: odt_config value %d invalid.\n",
+ dev->name, odt_config);
+ return -EINVAL;
+ }
+
+ posted_refreshes = dev_read_u32_default(dev, "posted_refreshes", 0);
+ if (posted_refreshes > 8 || posted_refreshes == 0) {
+ debug("%s: posted_refreshes value %d invalid.\n",
+ dev->name, posted_refreshes);
+ return -EINVAL;
+ }
+
+ sdram_cfg2 = force_self_refresh << SDRAM_CFG2_FRC_SR_SHIFT |
+ dll_reset << SDRAM_CFG2_DLL_RST_DIS |
+ dqs_config << SDRAM_CFG2_DQS_CFG |
+ odt_config << SDRAM_CFG2_ODT_CFG |
+ posted_refreshes << SDRAM_CFG2_NUM_PR;
+
+ out_be32(&im->ddr.sdram_cfg2, sdram_cfg2);
+
+ sdmode = dev_read_u32_default(dev, "sdmode", 0);
+ if (sdmode > 0xFFFF) {
+ debug("%s: sdmode value %d invalid.\n",
+ dev->name, sdmode);
+ return -EINVAL;
+ }
+
+ esdmode = dev_read_u32_default(dev, "esdmode", 0);
+ if (esdmode > 0xFFFF) {
+ debug("%s: esdmode value %d invalid.\n", dev->name, esdmode);
+ return -EINVAL;
+ }
+
+ sdram_mode = sdmode << SDRAM_MODE_SD_SHIFT |
+ esdmode << SDRAM_MODE_ESD_SHIFT;
+
+ out_be32(&im->ddr.sdram_mode, sdram_mode);
+
+ esdmode2 = dev_read_u32_default(dev, "esdmode2", 0);
+ if (esdmode2 > 0xFFFF) {
+ debug("%s: esdmode2 value %d invalid.\n", dev->name, esdmode2);
+ return -EINVAL;
+ }
+
+ esdmode3 = dev_read_u32_default(dev, "esdmode3", 0);
+ if (esdmode3 > 0xFFFF) {
+ debug("%s: esdmode3 value %d invalid.\n", dev->name, esdmode3);
+ return -EINVAL;
+ }
+
+ sdram_mode2 = esdmode2 << SDRAM_MODE2_ESD2_SHIFT |
+ esdmode3 << SDRAM_MODE2_ESD3_SHIFT;
+
+ out_be32(&im->ddr.sdram_mode2, sdram_mode2);
+
+ refresh_interval = dev_read_u32_default(dev, "refresh_interval", 0);
+ if (refresh_interval > 0xFFFF) {
+ debug("%s: refresh_interval value %d invalid.\n",
+ dev->name, refresh_interval);
+ return -EINVAL;
+ }
+
+ precharge_interval = dev_read_u32_default(dev, "precharge_interval", 0);
+ if (precharge_interval > 0x3FFF) {
+ debug("%s: precharge_interval value %d invalid.\n",
+ dev->name, precharge_interval);
+ return -EINVAL;
+ }
+
+ sdram_interval = refresh_interval << SDRAM_INTERVAL_REFINT_SHIFT |
+ precharge_interval << SDRAM_INTERVAL_BSTOPRE_SHIFT;
+
+ out_be32(&im->ddr.sdram_interval, sdram_interval);
+ sync();
+
+ /* Enable DDR controller */
+ setbits_be32(&im->ddr.sdram_cfg, SDRAM_CFG_MEM_EN);
+ sync();
+
+ dev_for_each_subnode(subnode, dev) {
+ u32 val[3];
+ u32 addr, size;
+
+ /* CS, map address, size -> three values */
+ ofnode_read_u32_array(subnode, "reg", val, 3);
+
+ addr = val[1];
+ size = val[2];
+
+ priv->total_size += get_ram_size((long int *)addr, size);
+ };
+
+ gd->ram_size = priv->total_size;
+
+ return 0;
+}
+
+static int mpc83xx_sdram_get_info(struct udevice *dev, struct ram_info *info)
+{
+ /* TODO(mario.six@gdsys.cc): Implement */
+ return 0;
+}
+
+static struct ram_ops mpc83xx_sdram_ops = {
+ .get_info = mpc83xx_sdram_get_info,
+};
+
+static const struct udevice_id mpc83xx_sdram_ids[] = {
+ { .compatible = "fsl,mpc83xx-mem-controller" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(mpc83xx_sdram) = {
+ .name = "mpc83xx_sdram",
+ .id = UCLASS_RAM,
+ .of_match = mpc83xx_sdram_ids,
+ .ops = &mpc83xx_sdram_ops,
+ .ofdata_to_platdata = mpc83xx_sdram_ofdata_to_platdata,
+ .probe = mpc83xx_sdram_probe,
+ .priv_auto_alloc_size = sizeof(struct mpc83xx_sdram_priv),
+};
diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig
index ed1d437fb0..8ce3e2e207 100644
--- a/drivers/sysreset/Kconfig
+++ b/drivers/sysreset/Kconfig
@@ -64,4 +64,9 @@ config SYSRESET_X86
help
Reboot support for generic x86 processor reset.
+config SYSRESET_MCP83XX
+ bool "Enable support MPC83xx SoC family reboot driver"
+ help
+ Reboot support for NXP MPC83xx SoCs.
+
endmenu
diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile
index 02ee1df6b0..b3728ac17f 100644
--- a/drivers/sysreset/Makefile
+++ b/drivers/sysreset/Makefile
@@ -3,15 +3,16 @@
# (C) Copyright 2016 Cadence Design Systems Inc.
obj-$(CONFIG_SYSRESET) += sysreset-uclass.o
+obj-$(CONFIG_ARCH_ASPEED) += sysreset_ast.o
+obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o
+obj-$(CONFIG_ARCH_STI) += sysreset_sti.o
+obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
obj-$(CONFIG_SYSRESET_GPIO) += sysreset_gpio.o
+obj-$(CONFIG_SYSRESET_MCP83XX) += sysreset_mpc83xx.o
obj-$(CONFIG_SYSRESET_MICROBLAZE) += sysreset_microblaze.o
obj-$(CONFIG_SYSRESET_PSCI) += sysreset_psci.o
obj-$(CONFIG_SYSRESET_TI_SCI) += sysreset-ti-sci.o
obj-$(CONFIG_SYSRESET_SYSCON) += sysreset_syscon.o
obj-$(CONFIG_SYSRESET_WATCHDOG) += sysreset_watchdog.o
obj-$(CONFIG_SYSRESET_X86) += sysreset_x86.o
-obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o
-obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o
-obj-$(CONFIG_ARCH_STI) += sysreset_sti.o
obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o
-obj-$(CONFIG_ARCH_ASPEED) += sysreset_ast.o
diff --git a/drivers/sysreset/sysreset-uclass.c b/drivers/sysreset/sysreset-uclass.c
index b918365e73..06ef0ed96c 100644
--- a/drivers/sysreset/sysreset-uclass.c
+++ b/drivers/sysreset/sysreset-uclass.c
@@ -24,6 +24,16 @@ int sysreset_request(struct udevice *dev, enum sysreset_t type)
return ops->request(dev, type);
}
+int sysreset_get_status(struct udevice *dev, char *buf, int size)
+{
+ struct sysreset_ops *ops = sysreset_get_ops(dev);
+
+ if (!ops->get_status)
+ return -ENOSYS;
+
+ return ops->get_status(dev, buf, size);
+}
+
int sysreset_walk(enum sysreset_t type)
{
struct udevice *dev;
diff --git a/drivers/sysreset/sysreset_mpc83xx.c b/drivers/sysreset/sysreset_mpc83xx.c
new file mode 100644
index 0000000000..9092764e0b
--- /dev/null
+++ b/drivers/sysreset/sysreset_mpc83xx.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <sysreset.h>
+#include <wait_bit.h>
+
+#include "sysreset_mpc83xx.h"
+
+/* Magic 4-byte word to enable reset ('RSTE' in ASCII) */
+static const u32 RPR_MAGIC = 0x52535445;
+/* Wait at most 2000ms for reset control enable bit */
+static const uint RESET_WAIT_TIMEOUT = 2000;
+
+/**
+ * __do_reset() - Execute the system reset
+ *
+ * Return: The functions resets the system, and never returns.
+ */
+static int __do_reset(void)
+{
+ ulong msr;
+ int res;
+
+ immap_t *immap = (immap_t *)CONFIG_SYS_IMMR;
+
+ puts("Resetting the board.\n");
+
+ /* Interrupts and MMU off */
+ msr = mfmsr();
+ msr &= ~(MSR_EE | MSR_IR | MSR_DR);
+ mtmsr(msr);
+
+ /* Enable Reset Control Reg */
+ out_be32(&immap->reset.rpr, RPR_MAGIC);
+ sync();
+ isync();
+
+ /* Confirm Reset Control Reg is enabled */
+ res = wait_for_bit_be32(&immap->reset.rcer, RCER_CRE, true,
+ RESET_WAIT_TIMEOUT, false);
+ if (res) {
+ debug("%s: Timed out waiting for reset control to be set\n",
+ __func__);
+ return res;
+ }
+
+ udelay(200);
+
+ /* Perform reset, only one bit */
+ out_be32(&immap->reset.rcr, RCR_SWHR);
+
+ /* Never executes */
+ return 0;
+}
+
+static int mpc83xx_sysreset_request(struct udevice *dev, enum sysreset_t type)
+{
+ switch (type) {
+ case SYSRESET_WARM:
+ case SYSRESET_COLD:
+ return __do_reset();
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ return -EINPROGRESS;
+}
+
+/**
+ * print_83xx_arb_event() - Print arbiter events to buffer
+ * @force: Print arbiter events, even if none are indicated by the system
+ * @buf: The buffer to receive the printed arbiter event information
+ * @size: The size of the buffer to receive the printed arbiter event
+ * information in bytes
+ *
+ * Return: Number of bytes printed to buffer, -ve on error
+ */
+static int print_83xx_arb_event(bool force, char *buf, int size)
+{
+ int etype = (gd->arch.arbiter_event_attributes & AEATR_EVENT)
+ >> AEATR_EVENT_SHIFT;
+ int mstr_id = (gd->arch.arbiter_event_attributes & AEATR_MSTR_ID)
+ >> AEATR_MSTR_ID_SHIFT;
+ int tbst = (gd->arch.arbiter_event_attributes & AEATR_TBST)
+ >> AEATR_TBST_SHIFT;
+ int tsize = (gd->arch.arbiter_event_attributes & AEATR_TSIZE)
+ >> AEATR_TSIZE_SHIFT;
+ int ttype = (gd->arch.arbiter_event_attributes & AEATR_TTYPE)
+ >> AEATR_TTYPE_SHIFT;
+ int tsize_val = (tbst << 3) | tsize;
+ int tsize_bytes = tbst ? (tsize ? tsize : 8) : 16 + 8 * tsize;
+ int res = 0;
+
+ /*
+ * If we don't force output, and there is no event (event address ==
+ * 0), then don't print anything
+ */
+ if (!force && !gd->arch.arbiter_event_address)
+ return 0;
+
+ if (CONFIG_IS_ENABLED(CONFIG_DISPLAY_AER_FULL)) {
+ res = snprintf(buf, size,
+ "Arbiter Event Status:\n"
+ " %s: 0x%08lX\n"
+ " %s: 0x%1x = %s\n"
+ " %s: 0x%02x = %s\n"
+ " %s: 0x%1x = %d bytes\n"
+ " %s: 0x%02x = %s\n",
+ "Event Address", gd->arch.arbiter_event_address,
+ "Event Type", etype, event[etype],
+ "Master ID", mstr_id, master[mstr_id],
+ "Transfer Size", tsize_val, tsize_bytes,
+ "Transfer Type", ttype, transfer[ttype]);
+ } else if (CONFIG_IS_ENABLED(CONFIG_DISPLAY_AER_BRIEF)) {
+ res = snprintf(buf, size,
+ "Arbiter Event Status: AEATR=0x%08lX, AEADR=0x%08lX\n",
+ gd->arch.arbiter_event_attributes,
+ gd->arch.arbiter_event_address);
+ }
+
+ return res;
+}
+
+static int mpc83xx_sysreset_get_status(struct udevice *dev, char *buf, int size)
+{
+ /* Ad-hoc data structure to map RSR bit values to their descriptions */
+ static const struct {
+ /* Bit mask for the bit in question */
+ ulong mask;
+ /* Description of the bitmask in question */
+ char *desc;
+ } bits[] = {
+ {
+ RSR_SWSR, "Software Soft"}, {
+ RSR_SWHR, "Software Hard"}, {
+ RSR_JSRS, "JTAG Soft"}, {
+ RSR_CSHR, "Check Stop"}, {
+ RSR_SWRS, "Software Watchdog"}, {
+ RSR_BMRS, "Bus Monitor"}, {
+ RSR_SRS, "External/Internal Soft"}, {
+ RSR_HRS, "External/Internal Hard"}
+ };
+ int res;
+ ulong rsr = gd->arch.reset_status;
+ int i;
+ char *sep;
+
+ res = snprintf(buf, size, "Reset Status:");
+ if (res < 0) {
+ debug("%s: Could not write reset status message (err = %d)\n",
+ dev->name, res);
+ return -EIO;
+ }
+
+ buf += res;
+ size -= res;
+
+ sep = " ";
+ for (i = 0; i < ARRAY_SIZE(bits); i++)
+ /* Print description of set bits */
+ if (rsr & bits[i].mask) {
+ res = snprintf(buf, size, "%s%s%s", sep, bits[i].desc,
+ (i == ARRAY_SIZE(bits) - 1) ? "\n" : "");
+ if (res < 0) {
+ debug("%s: Could not write reset status message (err = %d)\n",
+ dev->name, res);
+ return -EIO;
+ }
+ buf += res;
+ size -= res;
+ sep = ", ";
+ }
+
+ /*
+ * TODO(mario.six@gdsys.cc): Move this into a dedicated
+ * arbiter driver
+ */
+ if (CONFIG_IS_ENABLED(CONFIG_DISPLAY_AER_FULL) ||
+ CONFIG_IS_ENABLED(CONFIG_DISPLAY_AER_BRIEF)) {
+ /*
+ * If there was a bus monitor reset event, we force the arbiter
+ * event to be printed
+ */
+ res = print_83xx_arb_event(rsr & RSR_BMRS, buf, size);
+ if (res < 0) {
+ debug("%s: Could not write arbiter event message (err = %d)\n",
+ dev->name, res);
+ return -EIO;
+ }
+ buf += res;
+ size -= res;
+ }
+ snprintf(buf, size, "\n");
+
+ return 0;
+}
+
+static struct sysreset_ops mpc83xx_sysreset = {
+ .request = mpc83xx_sysreset_request,
+ .get_status = mpc83xx_sysreset_get_status,
+};
+
+U_BOOT_DRIVER(sysreset_mpc83xx) = {
+ .name = "mpc83xx_sysreset",
+ .id = UCLASS_SYSRESET,
+ .ops = &mpc83xx_sysreset,
+};
diff --git a/drivers/sysreset/sysreset_mpc83xx.h b/drivers/sysreset/sysreset_mpc83xx.h
new file mode 100644
index 0000000000..dc3c05921f
--- /dev/null
+++ b/drivers/sysreset/sysreset_mpc83xx.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#ifndef _SYSRESET_MPC83XX_H_
+#define _SYSRESET_MPC83XX_H_
+
+/*
+ * String array for all possible event types; indexed by the EVENT field of the
+ * AEATR register.
+ */
+static const char * const event[] = {
+ "Address Time Out",
+ "Data Time Out",
+ "Address Only Transfer Type",
+ "External Control Word Transfer Type",
+ "Reserved Transfer Type",
+ "Transfer Error",
+ "reserved",
+ "reserved"
+};
+
+/*
+ * String array for all possible master IDs, which reflects the source of the
+ * transaction that caused the error; indexed by the MSTR_ID field of the AEATR
+ * register.
+ */
+static const char * const master[] = {
+ "e300 Core Data Transaction",
+ "reserved",
+ "e300 Core Instruction Fetch",
+ "reserved",
+ "TSEC1",
+ "TSEC2",
+ "USB MPH",
+ "USB DR",
+ "Encryption Core",
+ "I2C Boot Sequencer",
+ "JTAG",
+ "reserved",
+ "eSDHC",
+ "PCI1",
+ "PCI2",
+ "DMA",
+ "QUICC Engine 00",
+ "QUICC Engine 01",
+ "QUICC Engine 10",
+ "QUICC Engine 11",
+ "reserved",
+ "reserved",
+ "reserved",
+ "reserved",
+ "SATA1",
+ "SATA2",
+ "SATA3",
+ "SATA4",
+ "reserved",
+ "PCI Express 1",
+ "PCI Express 2",
+ "TDM-DMAC"
+};
+
+/*
+ * String array for all possible transfer types; indexed by the TTYPE field of
+ * the AEATR register.
+ */
+static const char * const transfer[] = {
+ "Address-only, Clean Block",
+ "Address-only, lwarx reservation set",
+ "Single-beat or Burst write",
+ "reserved",
+ "Address-only, Flush Block",
+ "reserved",
+ "Burst write",
+ "reserved",
+ "Address-only, sync",
+ "Address-only, tlbsync",
+ "Single-beat or Burst read",
+ "Single-beat or Burst read",
+ "Address-only, Kill Block",
+ "Address-only, icbi",
+ "Burst read",
+ "reserved",
+ "Address-only, eieio",
+ "reserved",
+ "Single-beat write",
+ "reserved",
+ "ecowx - Illegal single-beat write",
+ "reserved",
+ "reserved",
+ "reserved",
+ "Address-only, TLB Invalidate",
+ "reserved",
+ "Single-beat or Burst read",
+ "reserved",
+ "eciwx - Illegal single-beat read",
+ "reserved",
+ "Burst read",
+ "reserved"
+};
+#endif /* _SYSRESET_MPC83XX_H_ */
diff --git a/drivers/sysreset/sysreset_sandbox.c b/drivers/sysreset/sysreset_sandbox.c
index f12c4e8419..75004d9f77 100644
--- a/drivers/sysreset/sysreset_sandbox.c
+++ b/drivers/sysreset/sysreset_sandbox.c
@@ -29,6 +29,13 @@ static int sandbox_warm_sysreset_request(struct udevice *dev,
return -EINPROGRESS;
}
+int sandbox_warm_sysreset_get_status(struct udevice *dev, char *buf, int size)
+{
+ strlcpy(buf, "Reset Status: WARM", size);
+
+ return 0;
+}
+
static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type)
{
struct sandbox_state *state = state_get_current();
@@ -60,8 +67,16 @@ static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type)
return -EINPROGRESS;
}
+int sandbox_sysreset_get_status(struct udevice *dev, char *buf, int size)
+{
+ strlcpy(buf, "Reset Status: COLD", size);
+
+ return 0;
+}
+
static struct sysreset_ops sandbox_sysreset_ops = {
.request = sandbox_sysreset_request,
+ .get_status = sandbox_sysreset_get_status,
};
static const struct udevice_id sandbox_sysreset_ids[] = {
@@ -78,6 +93,7 @@ U_BOOT_DRIVER(sysreset_sandbox) = {
static struct sysreset_ops sandbox_warm_sysreset_ops = {
.request = sandbox_warm_sysreset_request,
+ .get_status = sandbox_warm_sysreset_get_status,
};
static const struct udevice_id sandbox_warm_sysreset_ids[] = {
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index 5ab6749193..a7d600b6e4 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -140,4 +140,11 @@ config STM32_TIMER
Select this to enable support for the timer found on
STM32 devices.
+config MPC83XX_TIMER
+ bool "MPC83xx timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the timer found on
+ devices based on the MPC83xx family of SoCs.
+
endmenu
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
index 0c8a62767a..7f19c4970a 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -3,17 +3,18 @@
# Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
obj-y += timer-uclass.o
+obj-$(CONFIG_AG101P_TIMER) += ag101p_timer.o
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
+obj-$(CONFIG_ARC_TIMER) += arc_timer.o
+obj-$(CONFIG_AST_TIMER) += ast_timer.o
+obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o
+obj-$(CONFIG_ATMEL_PIT_TIMER) += atmel_pit_timer.o
obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o
obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o
-obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o
-obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
+obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o
obj-$(CONFIG_OMAP_TIMER) += omap-timer.o
-obj-$(CONFIG_AST_TIMER) += ast_timer.o
-obj-$(CONFIG_STI_TIMER) += sti-timer.o
-obj-$(CONFIG_ARC_TIMER) += arc_timer.o
-obj-$(CONFIG_AG101P_TIMER) += ag101p_timer.o
-obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o
obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o
-obj-$(CONFIG_ATMEL_PIT_TIMER) += atmel_pit_timer.o
+obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o
+obj-$(CONFIG_STI_TIMER) += sti-timer.o
obj-$(CONFIG_STM32_TIMER) += stm32_timer.o
+obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
diff --git a/drivers/timer/mpc83xx_timer.c b/drivers/timer/mpc83xx_timer.c
new file mode 100644
index 0000000000..84a9ab072a
--- /dev/null
+++ b/drivers/timer/mpc83xx_timer.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <common.h>
+#include <board.h>
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <watchdog.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct mpc83xx_timer_priv - Private data structure for MPC83xx timer driver
+ * @decrementer_count: Value to which the decrementer register should be re-set
+ * to when a timer interrupt occurs, thus determines the
+ * interrupt frequency (value for 1e6/HZ microseconds)
+ * @timestamp: Counter for the number of timer interrupts that have
+ * occurred (i.e. can be used to trigger events
+ * periodically in the timer interrupt)
+ */
+struct mpc83xx_timer_priv {
+ uint decrementer_count;
+ ulong timestamp;
+};
+
+/*
+ * Bitmask for enabling the time base in the SPCR (System Priority
+ * Configuration Register)
+ */
+static const u32 SPCR_TBEN_MASK = BIT(31 - 9);
+
+/**
+ * get_dec() - Get the value of the decrementer register
+ *
+ * Return: The value of the decrementer register
+ */
+static inline unsigned long get_dec(void)
+{
+ unsigned long val;
+
+ asm volatile ("mfdec %0" : "=r" (val) : );
+
+ return val;
+}
+
+/**
+ * set_dec() - Set the value of the decrementer register
+ * @val: The value of the decrementer register to be set
+ */
+static inline void set_dec(unsigned long val)
+{
+ if (val)
+ asm volatile ("mtdec %0"::"r" (val));
+}
+
+/**
+ * mftbu() - Get value of TBU (upper time base) register
+ *
+ * Return: Value of the TBU register
+ */
+static inline u32 mftbu(void)
+{
+ u32 rval;
+
+ asm volatile("mftbu %0" : "=r" (rval));
+ return rval;
+}
+
+/**
+ * mftb() - Get value of TBL (lower time base) register
+ *
+ * Return: Value of the TBL register
+ */
+static inline u32 mftb(void)
+{
+ u32 rval;
+
+ asm volatile("mftb %0" : "=r" (rval));
+ return rval;
+}
+
+/*
+ * TODO(mario.six@gdsys.cc): This should really be done by timer_init, and the
+ * interrupt init should go into a interrupt driver.
+ */
+int interrupt_init(void)
+{
+ immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
+ struct udevice *csb;
+ struct udevice *board;
+ struct udevice *timer;
+ struct mpc83xx_timer_priv *timer_priv;
+ struct clk clock;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_TIMER, &timer);
+ if (ret) {
+ debug("%s: Could not find timer device (error: %d)",
+ __func__, ret);
+ return ret;
+ }
+
+ timer_priv = dev_get_priv(timer);
+
+ if (board_get(&board)) {
+ debug("%s: board device could not be fetched.\n", __func__);
+ return -ENOENT;
+ }
+
+ ret = uclass_get_device_by_phandle(UCLASS_SIMPLE_BUS, board,
+ "csb", &csb);
+ if (ret) {
+ debug("%s: Could not retrieve CSB device (error: %d)",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(csb, 0, &clock);
+ if (ret) {
+ debug("%s: Could not retrieve clock (error: %d)",
+ __func__, ret);
+ return ret;
+ }
+
+ timer_priv->decrementer_count = (clk_get_rate(&clock) / 4)
+ / CONFIG_SYS_HZ;
+ /* Enable e300 time base */
+ setbits_be32(&immr->sysconf.spcr, SPCR_TBEN_MASK);
+
+ set_dec(timer_priv->decrementer_count);
+
+ /* Switch on interrupts */
+ set_msr(get_msr() | MSR_EE);
+
+ return 0;
+}
+
+/**
+ * timer_interrupt() - Handler for the timer interrupt
+ * @regs: Array of register values
+ */
+void timer_interrupt(struct pt_regs *regs)
+{
+ struct udevice *timer = gd->timer;
+ struct mpc83xx_timer_priv *priv;
+
+ /*
+ * During initialization, gd->timer might not be set yet, but the timer
+ * interrupt may already be enabled. In this case, wait for the
+ * initialization to complete
+ */
+ if (!timer)
+ return;
+
+ priv = dev_get_priv(timer);
+
+ /* Restore Decrementer Count */
+ set_dec(priv->decrementer_count);
+
+ priv->timestamp++;
+
+#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG)
+ if ((timestamp % (CONFIG_SYS_WATCHDOG_FREQ)) == 0)
+ WATCHDOG_RESET();
+#endif /* CONFIG_WATCHDOG || CONFIG_HW_WATCHDOG */
+
+#ifdef CONFIG_LED_STATUS
+ status_led_tick(priv->timestamp);
+#endif /* CONFIG_LED_STATUS */
+
+#ifdef CONFIG_SHOW_ACTIVITY
+ board_show_activity(priv->timestamp);
+#endif /* CONFIG_SHOW_ACTIVITY */
+}
+
+void wait_ticks(ulong ticks)
+{
+ ulong end = get_ticks() + ticks;
+
+ while (end > get_ticks())
+ WATCHDOG_RESET();
+}
+
+static int mpc83xx_timer_get_count(struct udevice *dev, u64 *count)
+{
+ u32 tbu, tbl;
+
+ /*
+ * To make sure that no tbl overflow occurred between reading tbl and
+ * tbu, read tbu again, and compare it with the previously read tbu
+ * value: If they're different, a tbl overflow has occurred.
+ */
+ do {
+ tbu = mftbu();
+ tbl = mftb();
+ } while (tbu != mftbu());
+
+ *count = (tbu * 0x10000ULL) + tbl;
+
+ return 0;
+}
+
+static int mpc83xx_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev->uclass_priv;
+ struct clk clock;
+ int ret;
+
+ ret = interrupt_init();
+ if (ret) {
+ debug("%s: interrupt_init failed (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ ret = clk_get_by_index(dev, 0, &clock);
+ if (ret) {
+ debug("%s: Could not retrieve clock (err = %d)\n",
+ dev->name, ret);
+ return ret;
+ }
+
+ uc_priv->clock_rate = (clk_get_rate(&clock) + 3L) / 4L;
+
+ return 0;
+}
+
+static const struct timer_ops mpc83xx_timer_ops = {
+ .get_count = mpc83xx_timer_get_count,
+};
+
+static const struct udevice_id mpc83xx_timer_ids[] = {
+ { .compatible = "fsl,mpc83xx-timer" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(mpc83xx_timer) = {
+ .name = "mpc83xx_timer",
+ .id = UCLASS_TIMER,
+ .of_match = mpc83xx_timer_ids,
+ .probe = mpc83xx_timer_probe,
+ .ops = &mpc83xx_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+ .priv_auto_alloc_size = sizeof(struct mpc83xx_timer_priv),
+};