diff options
Diffstat (limited to 'drivers')
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), +}; |