From 9c2cb97e22a7a12bf87d0af7e90156afe7a78d21 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 2 Aug 2018 23:58:02 -0700 Subject: lib: bitrev: Sync with Linux kernel v4.17 Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- include/linux/bitrev.h | 102 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/bitrev.h b/include/linux/bitrev.h index cc5abd73969..8ec9411aad2 100644 --- a/include/linux/bitrev.h +++ b/include/linux/bitrev.h @@ -1,21 +1,105 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Based on bitrev from the Linux kernel, by Akinobu Mita - */ - +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_BITREV_H #define _LINUX_BITREV_H #include -extern u8 const byte_rev_table[256]; +#ifdef CONFIG_HAVE_ARCH_BITREVERSE +#include + +#define __bitrev32 __arch_bitrev32 +#define __bitrev16 __arch_bitrev16 +#define __bitrev8 __arch_bitrev8 -static inline u8 bitrev8(u8 byte) +#else +extern u8 const byte_rev_table[256]; +static inline u8 __bitrev8(u8 byte) { return byte_rev_table[byte]; } -u16 bitrev16(u16 in); -u32 bitrev32(u32 in); +static inline u16 __bitrev16(u16 x) +{ + return (__bitrev8(x & 0xff) << 8) | __bitrev8(x >> 8); +} + +static inline u32 __bitrev32(u32 x) +{ + return (__bitrev16(x & 0xffff) << 16) | __bitrev16(x >> 16); +} + +#endif /* CONFIG_HAVE_ARCH_BITREVERSE */ + +#define __bitrev8x4(x) (__bitrev32(swab32(x))) + +#define __constant_bitrev32(x) \ +({ \ + u32 __x = x; \ + __x = (__x >> 16) | (__x << 16);\ + __x = ((__x & (u32)0xFF00FF00UL) >> 8) | ((__x & (u32)0x00FF00FFUL) << 8); \ + __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \ + __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \ + __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \ + __x; \ +}) + +#define __constant_bitrev16(x) \ +({ \ + u16 __x = x; \ + __x = (__x >> 8) | (__x << 8); \ + __x = ((__x & (u16)0xF0F0U) >> 4) | ((__x & (u16)0x0F0FU) << 4); \ + __x = ((__x & (u16)0xCCCCU) >> 2) | ((__x & (u16)0x3333U) << 2); \ + __x = ((__x & (u16)0xAAAAU) >> 1) | ((__x & (u16)0x5555U) << 1); \ + __x; \ +}) + +#define __constant_bitrev8x4(x) \ +({ \ + u32 __x = x; \ + __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \ + __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \ + __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \ + __x; \ +}) + +#define __constant_bitrev8(x) \ +({ \ + u8 __x = x; \ + __x = (__x >> 4) | (__x << 4); \ + __x = ((__x & (u8)0xCCU) >> 2) | ((__x & (u8)0x33U) << 2); \ + __x = ((__x & (u8)0xAAU) >> 1) | ((__x & (u8)0x55U) << 1); \ + __x; \ +}) + +#define bitrev32(x) \ +({ \ + u32 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev32(__x) : \ + __bitrev32(__x); \ +}) + +#define bitrev16(x) \ +({ \ + u16 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev16(__x) : \ + __bitrev16(__x); \ +}) + +#define bitrev8x4(x) \ +({ \ + u32 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev8x4(__x) : \ + __bitrev8x4(__x); \ +}) +#define bitrev8(x) \ +({ \ + u8 __x = x; \ + __builtin_constant_p(__x) ? \ + __constant_bitrev8(__x) : \ + __bitrev8(__x) ; \ +}) #endif /* _LINUX_BITREV_H */ -- cgit v1.3.1 From e40615565d68465284b3c6a5fc4147f662824a88 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:30 +0200 Subject: ram: Add driver for MPC83xx Add a RAM driver for the MPC83xx architecture. Reviewed-by: Simon Glass Signed-off-by: Mario Six --- .../bindings/ram/fsl,mpc83xx-mem-controller.txt | 314 ++++++ MAINTAINERS | 2 + arch/powerpc/cpu/mpc83xx/spd_sdram.c | 4 + drivers/ram/Kconfig | 9 + drivers/ram/Makefile | 1 + drivers/ram/mpc83xx_sdram.c | 1096 ++++++++++++++++++++ include/dt-bindings/memory/mpc83xx-sdram.h | 161 +++ include/mpc83xx.h | 6 + 8 files changed, 1593 insertions(+) create mode 100644 Documentation/devicetree/bindings/ram/fsl,mpc83xx-mem-controller.txt create mode 100644 drivers/ram/mpc83xx_sdram.c create mode 100644 include/dt-bindings/memory/mpc83xx-sdram.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/ram/fsl,mpc83xx-mem-controller.txt b/Documentation/devicetree/bindings/ram/fsl,mpc83xx-mem-controller.txt new file mode 100644 index 00000000000..da01fe908de --- /dev/null +++ b/Documentation/devicetree/bindings/ram/fsl,mpc83xx-mem-controller.txt @@ -0,0 +1,314 @@ +MPC83xx RAM controller + +This driver supplies support for the embedded RAM controller on MCP83xx-series +SoCs. + +For static configuration mode, each controller node should have child nodes +describing the actual RAM modules installed. + +Controller node +=============== + +Required properties: +- compatible: Must be "fsl,mpc83xx-mem-controller" +- reg: The address of the RAM controller's register space +- #address-cells: Must be 2 +- #size-cells: Must be 1 +- driver_software_override: DDR driver software override is enabled (1) or + disabled (0) +- p_impedance_override: DDR driver software p-impedance override; possible + values: + * DSO_P_IMPEDANCE_HIGHEST_Z + * DSO_P_IMPEDANCE_MUCH_HIGHER_Z + * DSO_P_IMPEDANCE_HIGHER_Z + * DSO_P_IMPEDANCE_NOMINAL + * DSO_P_IMPEDANCE_LOWER_Z +- n_impedance_override: DDR driver software n-impedance override; possible + values: + * DSO_N_IMPEDANCE_HIGHEST_Z + * DSO_N_IMPEDANCE_MUCH_HIGHER_Z + * DSO_N_IMPEDANCE_HIGHER_Z + * DSO_N_IMPEDANCE_NOMINAL + * DSO_N_IMPEDANCE_LOWER_Z +- odt_termination_value: ODT termination value for I/Os; possible values: + * ODT_TERMINATION_75_OHM + * ODT_TERMINATION_150_OHM +- ddr_type: Selects voltage level for DDR pads; possible + values: + * DDR_TYPE_DDR2_1_8_VOLT + * DDR_TYPE_DDR1_2_5_VOLT +- mvref_sel: Determine where MVREF_SEL signal is generated; + possible values: + * MVREF_SEL_EXTERNAL + * MVREF_SEL_INTERNAL_GVDD +- m_odr: Disable memory transaction reordering; possible + values: + * M_ODR_ENABLE + * M_ODR_DISABLE +- clock_adjust: Clock adjust; possible values: + * CLOCK_ADJUST_025 + * CLOCK_ADJUST_05 + * CLOCK_ADJUST_075 + * CLOCK_ADJUST_1 +- ext_refresh_rec: Extended refresh recovery time; possible values: + 0, 16, 32, 48, 64, 80, 96, 112 +- read_to_write: Read-to-write turnaround; possible values: + 0, 1, 2, 3 +- write_to_read: Write-to-read turnaround; possible values: + 0, 1, 2, 3 +- read_to_read: Read-to-read turnaround; possible values: + 0, 1, 2, 3 +- write_to_write: Write-to-write turnaround; possible values: + 0, 1, 2, 3 +- active_powerdown_exit: Active powerdown exit timing; possible values: + 1, 2, 3, 4, 5, 6, 7 +- precharge_powerdown_exit: Precharge powerdown exit timing; possible values: + 1, 2, 3, 4, 5, 6, 7 +- odt_powerdown_exit: ODT powerdown exit timing; possible values: + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15 +- mode_reg_set_cycle: Mode register set cycle time; possible values: + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +- precharge_to_activate: Precharge-to-acitvate interval; possible values: + 1, 2, 3, 4, 5, 6, 7 +- activate_to_precharge: Activate to precharge interval; possible values: + 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19 +- activate_to_readwrite: Activate to read/write interval for SDRAM; + possible values: + 1, 2, 3, 4, 5, 6, 7 +- mcas_latency: MCAS latency from READ command; possible values: + * CASLAT_20 + * CASLAT_25 + * CASLAT_30 + * CASLAT_35 + * CASLAT_40 + * CASLAT_45 + * CASLAT_50 + * CASLAT_55 + * CASLAT_60 + * CASLAT_65 + * CASLAT_70 + * CASLAT_75 + * CASLAT_80 +- refresh_recovery: Refresh recovery time; possible values: + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23 +- last_data_to_precharge: Last data to precharge minimum interval; possible + values: + 1, 2, 3, 4, 5, 6, 7 +- activate_to_activate: Activate-to-activate interval; possible values: + 1, 2, 3, 4, 5, 6, 7 +- last_write_data_to_read: Last write data pair to read command issue + interval; possible values: + 1, 2, 3, 4, 5, 6, 7 +- additive_latency: Additive latency; possible values: + 0, 1, 2, 3, 4, 5 +- mcas_to_preamble_override: MCAS-to-preamble-override; possible values: + * READ_LAT + * READ_LAT_PLUS_1_4 + * READ_LAT_PLUS_1_2 + * READ_LAT_PLUS_3_4 + * READ_LAT_PLUS_1 + * READ_LAT_PLUS_5_4 + * READ_LAT_PLUS_3_2 + * READ_LAT_PLUS_7_4 + * READ_LAT_PLUS_2 + * READ_LAT_PLUS_9_4 + * READ_LAT_PLUS_5_2 + * READ_LAT_PLUS_11_4 + * READ_LAT_PLUS_3 + * READ_LAT_PLUS_13_4 + * READ_LAT_PLUS_7_2 + * READ_LAT_PLUS_15_4 + * READ_LAT_PLUS_4 + * READ_LAT_PLUS_17_4 + * READ_LAT_PLUS_9_2 + * READ_LAT_PLUS_19_4 +- write_latency: Write latency; possible values: + 1, 2, 3, 4, 5, 6, 7 +- read_to_precharge: Read to precharge; possible values: + 1, 2, 3, 4 +- write_cmd_to_write_data: Write command to write data strobe timing + adjustment; possible values: + * CLOCK_DELAY_0 + * CLOCK_DELAY_1_4 + * CLOCK_DELAY_1_2 + * CLOCK_DELAY_3_4 + * CLOCK_DELAY_1 + * CLOCK_DELAY_5_4 + * CLOCK_DELAY_3_2 +- minimum_cke_pulse_width: Minimum CKE pulse width; possible values: + 1, 2, 3, 4 +- four_activates_window: Window for four activates; possible values: + 1, 2, 3, 4 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19 +- self_refresh: Self refresh (during sleep); possible values: + * SREN_DISABLE + * SREN_ENABLE +- ecc: Support for ECC; possible values: + * ECC_DISABLE + * ECC_ENABLE +- registered_dram: Support for registered DRAM; possible values: + * RD_DISABLE + * RD_ENABLE +- sdram_type: Type of SDRAM device to be used; possible values: + * TYPE_DDR1 + * TYPE_DDR2 +- dynamic_power_management: Dynamic power management mode; possible values: + * DYN_PWR_DISABLE + * DYN_PWR_ENABLE +- databus_width: DRAM data bus width; possible values + * DATA_BUS_WIDTH_16 + * DATA_BUS_WIDTH_32 +- nc_auto_precharge: Non-concurrent auto-precharge; possible values: + * NCAP_DISABLE + * NCAP_ENABLE +- timing_2t: 2T timing; possible values: + * TIMING_1T + * TIMING_2T +- bank_interleaving_ctrl: Bank (chip select) interleaving control; possible + values: + * INTERLEAVE_NONE + * INTERLEAVE_1_AND_2 +- precharge_bit_8: Precharge bin 8; possible values + * PRECHARGE_MA_10 + * PRECHARGE_MA_8 +- half_strength: Global half-strength override; possible values: + * STRENGTH_FULL + * STRENGTH_HALF +- bypass_initialization: Bypass initialization; possible values: + * INITIALIZATION_DONT_BYPASS + * INITIALIZATION_BYPASS +- force_self_refresh: Force self refresh; possible values: + * MODE_NORMAL + * MODE_REFRESH +- dll_reset: DLL reset; possible values: + * DLL_RESET_ENABLE + * DLL_RESET_DISABLE +- dqs_config: DQS configuration; possible values: + * DQS_TRUE +- odt_config: ODT configuration; possible values: + * ODT_ASSERT_NEVER + * ODT_ASSERT_WRITES + * ODT_ASSERT_READS + * ODT_ASSERT_ALWAYS +- posted_refreshes: Number of posted refreshes + 1, 2, 3, 4, 5, 6, 7, 8 +- sdmode: Initial value loaded into the DDR SDRAM mode + register +- esdmode: Initial value loaded into the DDR SDRAM extended + mode register +- esdmode2: Initial value loaded into the DDR SDRAM extended + mode 2 register +- esdmode3: Initial value loaded into the DDR SDRAM extended + mode 3 register +- refresh_interval: Refresh interval; possible values: + 0 - 65535 +- precharge_interval: Precharge interval; possible values: + 0 - 16383 + +RAM module node: +================ + +Required properties: +- reg: A triple , which consists of: + * cs - the chipselect used to drive this RAM module + * addr - the address where this RAM module's memory is map + to in the global memory space + * size - the size of the RAM module's memory in bytes +- auto_precharge: Chip select auto-precharge; possible values: + * AUTO_PRECHARGE_ENABLE + * AUTO_PRECHARGE_DISABLE +- odt_rd_cfg: ODT for reads configuration; possible values: + * ODT_RD_NEVER + * ODT_RD_ONLY_CURRENT + * ODT_RD_ONLY_OTHER_CS + * ODT_RD_ONLY_OTHER_DIMM + * ODT_RD_ALL +- odt_wr_cfg: ODT for writes configuration; possible values: + * ODT_WR_NEVER + * ODT_WR_ONLY_CURRENT + * ODT_WR_ONLY_OTHER_CS + * ODT_WR_ONLY_OTHER_DIMM + * ODT_WR_ALL +- bank_bits: Number of bank bits for SDRAM on chip select; possible + values: + 2, 3 +- row_bits: Number of row bits for SDRAM on chip select; possible values: + 12, 13, 14 +- col_bits: Number of column bits for SDRAM on chip select; possible + values: + 8, 9, 10, 11 + +Example: + +memory@2000 { + #address-cells = <2>; + #size-cells = <1>; + compatible = "fsl,mpc83xx-mem-controller"; + reg = <0x2000 0x1000>; + device_type = "memory"; + u-boot,dm-pre-reloc; + + driver_software_override = ; + p_impedance_override = ; + n_impedance_override = ; + odt_termination_value = ; + ddr_type = ; + + clock_adjust = ; + + read_to_write = <0>; + write_to_read = <0>; + read_to_read = <0>; + write_to_write = <0>; + active_powerdown_exit = <2>; + precharge_powerdown_exit = <6>; + odt_powerdown_exit = <8>; + mode_reg_set_cycle = <2>; + + precharge_to_activate = <2>; + activate_to_precharge = <6>; + activate_to_readwrite = <2>; + mcas_latency = ; + refresh_recovery = <17>; + last_data_to_precharge = <2>; + activate_to_activate = <2>; + last_write_data_to_read = <2>; + + additive_latency = <0>; + mcas_to_preamble_override = ; + write_latency = <3>; + read_to_precharge = <2>; + write_cmd_to_write_data = ; + minimum_cke_pulse_width = <3>; + four_activates_window = <5>; + + self_refresh = ; + sdram_type = ; + databus_width = ; + + force_self_refresh = ; + dll_reset = ; + dqs_config = ; + odt_config = ; + posted_refreshes = <1>; + + refresh_interval = <2084>; + precharge_interval = <256>; + + sdmode = <0x0242>; + esdmode = <0x0440>; + + ram@0 { + reg = <0x0 0x0 0x8000000>; + compatible = "nanya,nt5tu64m16hg"; + + odt_rd_cfg = ; + odt_wr_cfg = ; + bank_bits = <3>; + row_bits = <13>; + col_bits = <10>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 39d28e5d45f..76b2505ade1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -519,6 +519,8 @@ POWERPC MPC83XX M: Mario Six S: Maintained T: git git://git.denx.de/u-boot-mpc83xx.git +F: drivers/ram/mpc83xx_sdram.c +F: include/dt-bindings/memory/mpc83xx-sdram.h F: arch/powerpc/cpu/mpc83xx/ F: arch/powerpc/include/asm/arch-mpc83xx/ diff --git a/arch/powerpc/cpu/mpc83xx/spd_sdram.c b/arch/powerpc/cpu/mpc83xx/spd_sdram.c index bbc8ef03c7b..328a018eb6f 100644 --- a/arch/powerpc/cpu/mpc83xx/spd_sdram.c +++ b/arch/powerpc/cpu/mpc83xx/spd_sdram.c @@ -10,6 +10,8 @@ * Xianghua Xiao (X.Xiao@motorola.com) */ +#ifndef CONFIG_MPC83XX_SDRAM + #include #include #include @@ -924,3 +926,5 @@ void ddr_enable_ecc(unsigned int dram_size) __asm__ __volatile__ ("isync"); } #endif /* CONFIG_DDR_ECC */ + +#endif /* !CONFIG_MPC83XX_SDRAM */ diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index 496e2b793bd..54bb4b419f0 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 3820d03aa41..4ad3604d163 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 00000000000..441baeb6f1a --- /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 +#include +#include +#include + +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/include/dt-bindings/memory/mpc83xx-sdram.h b/include/dt-bindings/memory/mpc83xx-sdram.h new file mode 100644 index 00000000000..7d4ce01cc48 --- /dev/null +++ b/include/dt-bindings/memory/mpc83xx-sdram.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#ifndef DT_BINDINGS_MPC83XX_SDRAM_H +#define DT_BINDINGS_MPC83XX_SDRAM_H + +/* DDR Control Driver register */ + +#define DSO_DISABLE 0 +#define DSO_ENABLE 1 + +#define DSO_P_IMPEDANCE_HIGHEST_Z 0x0 +#define DSO_P_IMPEDANCE_MUCH_HIGHER_Z 0x8 +#define DSO_P_IMPEDANCE_HIGHER_Z 0xC +#define DSO_P_IMPEDANCE_NOMINAL 0xE +#define DSO_P_IMPEDANCE_LOWER_Z 0xF + +#define DSO_N_IMPEDANCE_HIGHEST_Z 0x0 +#define DSO_N_IMPEDANCE_MUCH_HIGHER_Z 0x8 +#define DSO_N_IMPEDANCE_HIGHER_Z 0xC +#define DSO_N_IMPEDANCE_NOMINAL 0xE +#define DSO_N_IMPEDANCE_LOWER_Z 0xF + +#define ODT_TERMINATION_75_OHM 0 +#define ODT_TERMINATION_150_OHM 1 + +#define DDR_TYPE_DDR2_1_8_VOLT 0 +#define DDR_TYPE_DDR1_2_5_VOLT 1 + +#define MVREF_SEL_EXTERNAL 0 +#define MVREF_SEL_INTERNAL_GVDD 1 + +#define M_ODR_ENABLE 0 +#define M_ODR_DISABLE 1 + +/* CS config register */ + +#define AUTO_PRECHARGE_ENABLE 0x00800000 +#define AUTO_PRECHARGE_DISABLE 0x00000000 + +#define ODT_RD_NEVER 0x00000000 +#define ODT_RD_ONLY_CURRENT 0x00100000 +#define ODT_RD_ONLY_OTHER_CS 0x00200000 +#define ODT_RD_ONLY_OTHER_DIMM 0x00300000 +#define ODT_RD_ALL 0x00400000 + +#define ODT_WR_NEVER 0x00000000 +#define ODT_WR_ONLY_CURRENT 0x00010000 +#define ODT_WR_ONLY_OTHER_CS 0x00020000 +#define ODT_WR_ONLY_OTHER_DIMM 0x00030000 +#define ODT_WR_ALL 0x00040000 + +/* DDR SDRAM Clock Control register */ + +#define CLOCK_ADJUST_025 0x01000000 +#define CLOCK_ADJUST_05 0x02000000 +#define CLOCK_ADJUST_075 0x03000000 +#define CLOCK_ADJUST_1 0x04000000 + +#define CASLAT_20 0x3 /* CAS latency = 2.0 */ +#define CASLAT_25 0x4 /* CAS latency = 2.5 */ +#define CASLAT_30 0x5 /* CAS latency = 3.0 */ +#define CASLAT_35 0x6 /* CAS latency = 3.5 */ +#define CASLAT_40 0x7 /* CAS latency = 4.0 */ +#define CASLAT_45 0x8 /* CAS latency = 4.5 */ +#define CASLAT_50 0x9 /* CAS latency = 5.0 */ +#define CASLAT_55 0xa /* CAS latency = 5.5 */ +#define CASLAT_60 0xb /* CAS latency = 6.0 */ +#define CASLAT_65 0xc /* CAS latency = 6.5 */ +#define CASLAT_70 0xd /* CAS latency = 7.0 */ +#define CASLAT_75 0xe /* CAS latency = 7.5 */ +#define CASLAT_80 0xf /* CAS latency = 8.0 */ + +/* DDR SDRAM Timing Configuration 2 register */ + +#define READ_LAT_PLUS_1 0x0 +#define READ_LAT 0x2 +#define READ_LAT_PLUS_1_4 0x3 +#define READ_LAT_PLUS_1_2 0x4 +#define READ_LAT_PLUS_3_4 0x5 +#define READ_LAT_PLUS_5_4 0x7 +#define READ_LAT_PLUS_3_2 0x8 +#define READ_LAT_PLUS_7_4 0x9 +#define READ_LAT_PLUS_2 0xA +#define READ_LAT_PLUS_9_4 0xB +#define READ_LAT_PLUS_5_2 0xC +#define READ_LAT_PLUS_11_4 0xD +#define READ_LAT_PLUS_3 0xE +#define READ_LAT_PLUS_13_4 0xF +#define READ_LAT_PLUS_7_2 0x10 +#define READ_LAT_PLUS_15_4 0x11 +#define READ_LAT_PLUS_4 0x12 +#define READ_LAT_PLUS_17_4 0x13 +#define READ_LAT_PLUS_9_2 0x14 +#define READ_LAT_PLUS_19_4 0x15 + +#define CLOCK_DELAY_0 0x0 +#define CLOCK_DELAY_1_4 0x1 +#define CLOCK_DELAY_1_2 0x2 +#define CLOCK_DELAY_3_4 0x3 +#define CLOCK_DELAY_1 0x4 +#define CLOCK_DELAY_5_4 0x5 +#define CLOCK_DELAY_3_2 0x6 + +/* DDR SDRAM Control Configuration */ + +#define SREN_DISABLE 0x0 +#define SREN_ENABLE 0x1 + +#define ECC_DISABLE 0x0 +#define ECC_ENABLE 0x1 + +#define RD_DISABLE 0x0 +#define RD_ENABLE 0x1 + +#define TYPE_DDR1 0x2 +#define TYPE_DDR2 0x3 + +#define DYN_PWR_DISABLE 0x0 +#define DYN_PWR_ENABLE 0x1 + +#define DATA_BUS_WIDTH_16 0x1 +#define DATA_BUS_WIDTH_32 0x2 + +#define NCAP_DISABLE 0x0 +#define NCAP_ENABLE 0x1 + +#define TIMING_1T 0x0 +#define TIMING_2T 0x1 + +#define INTERLEAVE_NONE 0x0 +#define INTERLEAVE_1_AND_2 0x1 + +#define PRECHARGE_MA_10 0x0 +#define PRECHARGE_MA_8 0x1 + +#define STRENGTH_FULL 0x0 +#define STRENGTH_HALF 0x1 + +#define INITIALIZATION_DONT_BYPASS 0x0 +#define INITIALIZATION_BYPASS 0x1 + +/* DDR SDRAM Control Configuration 2 register */ + +#define MODE_NORMAL 0x0 +#define MODE_REFRESH 0x1 + +#define DLL_RESET_ENABLE 0x0 +#define DLL_RESET_DISABLE 0x1 + +#define DQS_TRUE 0x0 + +#define ODT_ASSERT_NEVER 0x0 +#define ODT_ASSERT_WRITES 0x1 +#define ODT_ASSERT_READS 0x2 +#define ODT_ASSERT_ALWAYS 0x3 + +#endif diff --git a/include/mpc83xx.h b/include/mpc83xx.h index e1e50ab6b50..a4c5bd38376 100644 --- a/include/mpc83xx.h +++ b/include/mpc83xx.h @@ -1110,6 +1110,8 @@ #define CSBNDS_EA 0x000000FF #define CSBNDS_EA_SHIFT 24 +#ifndef CONFIG_MPC83XX_SDRAM + /* * CSn_CONFIG - Chip Select Configuration Register */ @@ -1407,6 +1409,8 @@ #define ECC_ERROR_MAN_SBEC (0xff000000 >> 24) #define ECC_ERROR_MAN_SBEC_SHIFT 0 +#endif /* !CONFIG_MPC83XX_SDRAM */ + /* * CONFIG_ADDRESS - PCI Config Address Register */ @@ -1510,6 +1514,7 @@ */ #define PMCCR1_POWER_OFF 0x00000020 +#ifndef CONFIG_RAM /* * DDRCDR - DDR Control Driver Register */ @@ -1531,6 +1536,7 @@ #define DDRCDR_DDR_CFG 0x00040000 #define DDRCDR_M_ODR 0x00000002 #define DDRCDR_Q_DRN 0x00000001 +#endif /* !CONFIG_RAM */ /* * PCIE Bridge Register -- cgit v1.3.1 From 245f5cda69a1332585c2a22a48420f4ca1ab67ad Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:32 +0200 Subject: sysreset: Add get_status method It's useful to have the reset status of the SoC printed out during reset (e.g. to learn whether the reset was caused by software or a watchdog). As a first step to implement this, add a get_status method to the sysreset class, which enables the caller to get printable information about the reset status (akin to get_desc in the CPU uclass). Reviewed-by: Simon Glass Signed-off-by: Mario Six --- drivers/sysreset/sysreset-uclass.c | 10 ++++++++++ include/sysreset.h | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/sysreset/sysreset-uclass.c b/drivers/sysreset/sysreset-uclass.c index b918365e73a..06ef0ed96c7 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/include/sysreset.h b/include/sysreset.h index 81318bdbf5d..a5c0b74a473 100644 --- a/include/sysreset.h +++ b/include/sysreset.h @@ -28,6 +28,14 @@ struct sysreset_ops { * (in which case this method will not actually return) */ int (*request)(struct udevice *dev, enum sysreset_t type); + /** + * get_status() - get printable reset status information + * + * @buf: Buffer to receive the textual reset information + * @size: Size of the passed buffer + * @return 0 if OK, -ve on error + */ + int (*get_status)(struct udevice *dev, char *buf, int size); }; #define sysreset_get_ops(dev) ((struct sysreset_ops *)(dev)->driver->ops) @@ -40,6 +48,15 @@ struct sysreset_ops { */ int sysreset_request(struct udevice *dev, enum sysreset_t type); +/** + * get_status() - get printable reset status information + * + * @buf: Buffer to receive the textual reset information + * @size: Size of the passed buffer + * @return 0 if OK, -ve on error + */ +int sysreset_get_status(struct udevice *dev, char *buf, int size); + /** * sysreset_walk() - cause a system reset * -- cgit v1.3.1 From 07d538d2814fa03be243c71879372f4263030b78 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:36 +0200 Subject: clk: Add MPC83xx clock driver Add a clock driver for the MPC83xx architecture. Signed-off-by: Mario Six --- .../devicetree/bindings/clk/fsl,mpc83xx-clk.txt | 23 ++ MAINTAINERS | 3 + arch/powerpc/cpu/mpc83xx/speed.c | 4 + arch/powerpc/include/asm/arch-mpc83xx/soc.h | 74 ++++ arch/powerpc/include/asm/config.h | 2 +- arch/powerpc/include/asm/global_data.h | 4 + drivers/clk/Kconfig | 6 + drivers/clk/Makefile | 1 + drivers/clk/mpc83xx_clk.c | 410 +++++++++++++++++++++ drivers/clk/mpc83xx_clk.h | 379 +++++++++++++++++++ include/dt-bindings/clk/mpc83xx-clk.h | 33 ++ 11 files changed, 938 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/clk/fsl,mpc83xx-clk.txt create mode 100644 arch/powerpc/include/asm/arch-mpc83xx/soc.h create mode 100644 drivers/clk/mpc83xx_clk.c create mode 100644 drivers/clk/mpc83xx_clk.h create mode 100644 include/dt-bindings/clk/mpc83xx-clk.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clk/fsl,mpc83xx-clk.txt b/Documentation/devicetree/bindings/clk/fsl,mpc83xx-clk.txt new file mode 100644 index 00000000000..8313da85076 --- /dev/null +++ b/Documentation/devicetree/bindings/clk/fsl,mpc83xx-clk.txt @@ -0,0 +1,23 @@ +MPC83xx system clock devices + +MPC83xx SoCs supply a variety of clocks to drive various components of a +system. + +Required properties: +- compatible: must be one of "fsl,mpc8308-clk", + "fsl,mpc8309-clk", + "fsl,mpc8313-clk", + "fsl,mpc8315-clk", + "fsl,mpc832x-clk", + "fsl,mpc8349-clk", + "fsl,mpc8360-clk", + "fsl,mpc8379-clk" + depending on which SoC is employed +- #clock-cells: Must be 1 + +Example: + +socclocks: clocks { + compatible = "fsl,mpc832x-clk"; + #clock-cells = <1>; +}; diff --git a/MAINTAINERS b/MAINTAINERS index e2ab71391d8..1aab4b35066 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -523,6 +523,9 @@ F: drivers/ram/mpc83xx_sdram.c F: include/dt-bindings/memory/mpc83xx-sdram.h F: drivers/sysreset/sysreset_mpc83xx.c F: drivers/sysreset/sysreset_mpc83xx.h +F: drivers/clk/mpc83xx_clk.c +F: drivers/clk/mpc83xx_clk.h +F: include/dt-bindings/clk/mpc83xx-clk.h F: arch/powerpc/cpu/mpc83xx/ F: arch/powerpc/include/asm/arch-mpc83xx/ diff --git a/arch/powerpc/cpu/mpc83xx/speed.c b/arch/powerpc/cpu/mpc83xx/speed.c index f0945281cd2..39bc1c53406 100644 --- a/arch/powerpc/cpu/mpc83xx/speed.c +++ b/arch/powerpc/cpu/mpc83xx/speed.c @@ -6,6 +6,8 @@ * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. */ +#ifndef CONFIG_CLK_MPC83XX + #include #include #include @@ -590,3 +592,5 @@ U_BOOT_CMD(clocks, 1, 0, do_clocks, "print clock configuration", " clocks" ); + +#endif diff --git a/arch/powerpc/include/asm/arch-mpc83xx/soc.h b/arch/powerpc/include/asm/arch-mpc83xx/soc.h new file mode 100644 index 00000000000..39bf7d5a7f9 --- /dev/null +++ b/arch/powerpc/include/asm/arch-mpc83xx/soc.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#ifndef _MPC83XX_SOC_H_ +#define _MPC83XX_SOC_H_ + +enum soc_type { + SOC_MPC8308, + SOC_MPC8309, + SOC_MPC8313, + SOC_MPC8315, + SOC_MPC832X, + SOC_MPC8349, + SOC_MPC8360, + SOC_MPC8379, +}; + +bool mpc83xx_has_sdhc(int type) +{ + return (type == SOC_MPC8308) || + (type == SOC_MPC8309) || + (type == SOC_MPC8379); +} + +bool mpc83xx_has_tsec(int type) +{ + return (type == SOC_MPC8308) || + (type == SOC_MPC8313) || + (type == SOC_MPC8315) || + (type == SOC_MPC8349) || + (type == SOC_MPC8379); +} + +bool mpc83xx_has_pcie1(int type) +{ + return (type == SOC_MPC8308) || + (type == SOC_MPC8315) || + (type == SOC_MPC8379); +} + +bool mpc83xx_has_pcie2(int type) +{ + return (type == SOC_MPC8315) || + (type == SOC_MPC8379); +} + +bool mpc83xx_has_sata(int type) +{ + return (type == SOC_MPC8315) || + (type == SOC_MPC8379); +} + +bool mpc83xx_has_pci(int type) +{ + return type != SOC_MPC8308; +} + +bool mpc83xx_has_second_i2c(int type) +{ + return (type != SOC_MPC8315) && + (type != SOC_MPC832X); +} + +bool mpc83xx_has_quicc_engine(int type) +{ + return (type == SOC_MPC8309) || + (type == SOC_MPC832X) || + (type == SOC_MPC8360); +} + +#endif /* _MPC83XX_SOC_H_ */ diff --git a/arch/powerpc/include/asm/config.h b/arch/powerpc/include/asm/config.h index bf11f40e231..849a69acedc 100644 --- a/arch/powerpc/include/asm/config.h +++ b/arch/powerpc/include/asm/config.h @@ -75,7 +75,7 @@ /* All PPC boards must swap IDE bytes */ #define CONFIG_IDE_SWAP_IO -#if defined(CONFIG_DM_SERIAL) +#if defined(CONFIG_DM_SERIAL) && !defined(CONFIG_CLK_MPC83XX) /* * TODO: Convert this to a clock driver exists that can give us the UART * clock here. diff --git a/arch/powerpc/include/asm/global_data.h b/arch/powerpc/include/asm/global_data.h index 07d5beaf0e9..d00cee95fbb 100644 --- a/arch/powerpc/include/asm/global_data.h +++ b/arch/powerpc/include/asm/global_data.h @@ -30,6 +30,9 @@ struct arch_global_data { #endif /* TODO: sjg@chromium.org: Should these be unslgned long? */ #if defined(CONFIG_MPC83xx) +#ifdef CONFIG_CLK_MPC83XX + u32 core_clk; +#else /* There are other clocks in the MPC83XX */ u32 csb_clk; # if defined(CONFIG_MPC8308) || defined(CONFIG_MPC831x) || \ @@ -62,6 +65,7 @@ struct arch_global_data { u32 mem_sec_clk; # endif /* CONFIG_MPC8360 */ #endif +#endif #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) u32 lbc_clk; void *cpu; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 809eb3dacf9..c996d6574ba 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 82c36b7478e..11468f2ee62 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 00000000000..489004190eb --- /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 +#include +#include +#include +#include +#include + +#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 00000000000..7fb88029204 --- /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/include/dt-bindings/clk/mpc83xx-clk.h b/include/dt-bindings/clk/mpc83xx-clk.h new file mode 100644 index 00000000000..db4ea15cf42 --- /dev/null +++ b/include/dt-bindings/clk/mpc83xx-clk.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#ifndef DT_BINDINGS_MPC83XX_CLK_H +#define DT_BINDINGS_MPC83XX_CLK_H +#define MPC83XX_CLK_CORE 0 +#define MPC83XX_CLK_CSB 1 +#define MPC83XX_CLK_QE 2 +#define MPC83XX_CLK_BRG 3 +#define MPC83XX_CLK_LBIU 4 +#define MPC83XX_CLK_LCLK 5 +#define MPC83XX_CLK_MEM 6 +#define MPC83XX_CLK_MEM_SEC 7 +#define MPC83XX_CLK_ENC 8 +#define MPC83XX_CLK_I2C1 9 +#define MPC83XX_CLK_I2C2 10 +#define MPC83XX_CLK_TDM 11 +#define MPC83XX_CLK_SDHC 12 +#define MPC83XX_CLK_TSEC1 13 +#define MPC83XX_CLK_TSEC2 14 +#define MPC83XX_CLK_USBDR 15 +#define MPC83XX_CLK_USBMPH 16 +#define MPC83XX_CLK_PCIEXP1 17 +#define MPC83XX_CLK_PCIEXP2 18 +#define MPC83XX_CLK_SATA 19 +#define MPC83XX_CLK_DMAC 20 +#define MPC83XX_CLK_PCI 21 +/* Count */ +#define MPC83XX_CLK_COUNT 22 +#endif /* DT_BINDINGS_MPC83XX_CLK_H */ -- cgit v1.3.1 From dc145a7be3fc6ae2181488e48aee51a34d43ffa0 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:40 +0200 Subject: init: Fix documentation The documentation in init.h is not in kernel-doc format. Fix this. Signed-off-by: Mario Six --- include/init.h | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/init.h b/include/init.h index f114a889631..a58d7a6917f 100644 --- a/include/init.h +++ b/include/init.h @@ -27,7 +27,7 @@ void board_init_f(ulong dummy); * board_f.c for where it is called. If this is not provided, a default * version (which does nothing) will be used. * - * @return: 0 on success, otherwise error + * Return: 0 on success, otherwise error */ int arch_cpu_init(void); @@ -38,7 +38,7 @@ int arch_cpu_init(void); * relocation. This is similar to arch_cpu_init() but is able to reference * devices * - * @return 0 if OK, -ve on error + * Return: 0 if OK, -ve on error */ int arch_cpu_init_dm(void); @@ -50,7 +50,7 @@ int arch_cpu_init_dm(void); * board_f.c for where it is called. If this is not provided, a default * version (which does nothing) will be used. * - * @return: 0 on success, otherwise error + * Return: 0 on success, otherwise error */ int mach_cpu_init(void); @@ -60,6 +60,8 @@ int mach_cpu_init(void); * Where U-Boot relies on binary blobs to handle part of the system init, this * function can be used to set up the blobs. This is used on some Intel * platforms. + * + * Return: 0 */ int arch_fsp_init(void); @@ -78,12 +80,12 @@ int dram_init(void); * CONFIG_SYS_SDRAM_BASE and the size will be determined by a call to * get_effective_memsize(). * - * @return 0 if OK, -ve on error + * Return: 0 if OK, -ve on error */ int dram_init_banksize(void); /** - * Reserve all necessary stacks + * arch_reserve_stacks() - Reserve all necessary stacks * * This is used in generic board init sequence in common/board_f.c. Each * architecture could provide this function to tailor the required stacks. @@ -96,14 +98,14 @@ int dram_init_banksize(void); * positions of the stack. The stack pointer(s) will be set to this later. * gd->irq_sp is only required, if the architecture needs it. * - * @return 0 if no error + * Return: 0 if no error */ int arch_reserve_stacks(void); /** * init_cache_f_r() - Turn on the cache in preparation for relocation * - * @return 0 if OK, -ve on error + * Return: 0 if OK, -ve on error */ int init_cache_f_r(void); @@ -111,6 +113,7 @@ int print_cpuinfo(void); int timer_init(void); int reserve_mmu(void); int misc_init_f(void); + #if defined(CONFIG_DTB_RESELECT) int embedded_dtb_select(void); #endif @@ -120,28 +123,28 @@ extern ulong monitor_flash_len; /** * ulong board_init_f_alloc_reserve - allocate reserved area + * @top: top of the reserve area, growing down. * * This function is called by each architecture very early in the start-up * code to allow the C runtime to reserve space on the stack for writable * 'globals' such as GD and the malloc arena. * - * @top: top of the reserve area, growing down. - * @return: bottom of reserved area + * Return: bottom of reserved area */ ulong board_init_f_alloc_reserve(ulong top); /** * board_init_f_init_reserve - initialize the reserved area(s) + * @base: top from which reservation was done * * This function is called once the C runtime has allocated the reserved * area on the stack. It must initialize the GD at the base of that area. - * - * @base: top from which reservation was done */ void board_init_f_init_reserve(ulong base); /** * arch_setup_gd() - Set up the global_data pointer + * @gd_ptr: Pointer to global data * * This pointer is special in some architectures and cannot easily be assigned * to. For example on x86 it is implemented by adding a specific record to its @@ -149,8 +152,6 @@ void board_init_f_init_reserve(ulong base); * For most architectures this can simply be: * * gd = gd_ptr; - * - * @gd_ptr: Pointer to global data */ void arch_setup_gd(gd_t *gd_ptr); -- cgit v1.3.1 From c0434407b595f785fc7401237896c48c791b45fd Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:41 +0200 Subject: board_f: Use static print_cpuinfo if CONFIG_CPU is active When the DM CPU drivers are active, printing information about a CPU should be delegated to a matching driver. Hence, add a static print_cpuinfo that implements this delegation when DM CPU drivers are active. Reviewed-by: Simon Glass Signed-off-by: Mario Six Changed condition to CONFIG_IS_ENABLED(CPU): Signed-off-by: Simon Glass --- common/board_f.c | 28 ++++++++++++++++++++++++++++ include/init.h | 7 +++++++ 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/common/board_f.c b/common/board_f.c index afafec5e4d0..213d0440667 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -165,6 +166,33 @@ static int print_resetinfo(void) } #endif +#if defined(CONFIG_DISPLAY_CPUINFO) && CONFIG_IS_ENABLED(CPU) +static int print_cpuinfo(void) +{ + struct udevice *dev; + char desc[512]; + int ret; + + ret = uclass_first_device_err(UCLASS_CPU, &dev); + if (ret) { + debug("%s: Could not get CPU device (err = %d)\n", + __func__, ret); + return ret; + } + + ret = cpu_get_desc(dev, desc, sizeof(desc)); + if (ret) { + debug("%s: Could not get CPU description (err = %d)\n", + dev->name, ret); + return ret; + } + + printf("%s", desc); + + return 0; +} +#endif + static int announce_dram_init(void) { puts("DRAM: "); diff --git a/include/init.h b/include/init.h index a58d7a6917f..afc953d51e2 100644 --- a/include/init.h +++ b/include/init.h @@ -109,7 +109,14 @@ int arch_reserve_stacks(void); */ int init_cache_f_r(void); +#if !CONFIG_IS_ENABLED(CPU) +/** + * print_cpuinfo() - Display information about the CPU + * + * Return: 0 if OK, -ve on error + */ int print_cpuinfo(void); +#endif int timer_init(void); int reserve_mmu(void); int misc_init_f(void); -- cgit v1.3.1 From 50d188b9557f394b77f87561c71173c060953628 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:42 +0200 Subject: cpu: Fix cpu.h documentation Documentation in cpu.h is not in kernel-doc format. Change it to comply with it. Signed-off-by: Mario Six --- include/cpu.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/cpu.h b/include/cpu.h index 5cc7c5abd72..22467cb9b3a 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -9,19 +9,21 @@ /** * struct cpu_platdata - platform data for a CPU + * @cpu_id: Platform-specific way of identifying the CPU. + * @ucode_version: Microcode version, if CPU_FEAT_UCODE is set + * @device_id: Driver-defined device identifier + * @family: DMTF CPU Family identifier + * @id: DMTF CPU Processor identifier * * This can be accessed with dev_get_parent_platdata() for any UCLASS_CPU * device. - * - * @cpu_id: Platform-specific way of identifying the CPU. - * @ucode_version: Microcode version, if CPU_FEAT_UCODE is set */ struct cpu_platdata { int cpu_id; int ucode_version; ulong device_id; - u16 family; /* DMTF CPU Family */ - u32 id[2]; /* DMTF CPU Processor IDs */ + u16 family; + u32 id[2]; }; /* CPU features - mostly just a placeholder for now */ @@ -88,38 +90,38 @@ struct cpu_ops { /** * cpu_get_desc() - Get a description string for a CPU - * * @dev: Device to check (UCLASS_CPU) * @buf: Buffer to place string * @size: Size of string space - * @return 0 if OK, -ENOSPC if buffer is too small, other -ve on error + * + * Return: 0 if OK, -ENOSPC if buffer is too small, other -ve on error */ int cpu_get_desc(struct udevice *dev, char *buf, int size); /** * cpu_get_info() - Get information about a CPU - * * @dev: Device to check (UCLASS_CPU) * @info: Returns CPU info - * @return 0 if OK, -ve on error + * + * Return: 0 if OK, -ve on error */ int cpu_get_info(struct udevice *dev, struct cpu_info *info); /** * cpu_get_count() - Get number of CPUs - * * @dev: Device to check (UCLASS_CPU) - * @return CPU count if OK, -ve on error + * + * Return: CPU count if OK, -ve on error */ int cpu_get_count(struct udevice *dev); /** * cpu_get_vendor() - Get vendor name of a CPU - * * @dev: Device to check (UCLASS_CPU) * @buf: Buffer to place string * @size: Size of string space - * @return 0 if OK, -ENOSPC if buffer is too small, other -ve on error + * + * Return: 0 if OK, -ENOSPC if buffer is too small, other -ve on error */ int cpu_get_vendor(struct udevice *dev, char *buf, int size); -- cgit v1.3.1 From 57370de377340b0fcb81d90a187aa8b8f3494447 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:43 +0200 Subject: cpu: Add cpu_probe_all method Add a method to probe all CPUs of the board. Signed-off-by: Mario Six --- drivers/cpu/cpu-uclass.c | 23 +++++++++++++++++++++++ include/cpu.h | 7 +++++++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c index f362eb11e72..457f77b7c89 100644 --- a/drivers/cpu/cpu-uclass.c +++ b/drivers/cpu/cpu-uclass.c @@ -11,6 +11,29 @@ #include #include +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/include/cpu.h b/include/cpu.h index 22467cb9b3a..367c5f46a00 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -125,4 +125,11 @@ int cpu_get_count(struct udevice *dev); */ int cpu_get_vendor(struct udevice *dev, char *buf, int size); +/** + * cpu_probe_all() - Probe all available CPUs + * + * Return: 0 if OK, -ve on error + */ +int cpu_probe_all(void); + #endif -- cgit v1.3.1 From 61fba0faba432ae1cefc3984f863c28880d30329 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 20 Aug 2018 11:09:58 +0200 Subject: ofnode: add ofnode_by_prop_value() Adds ofnode_by_prop_value() to search for nodes with a given property and value, an ofnode version of fdt_node_offset_by_prop_value(). Signed-off-by: Jens Wiklander Reviewed-by: Simon Glass --- drivers/core/of_access.c | 27 +++++++++++++++++++++++++++ drivers/core/ofnode.c | 14 ++++++++++++++ include/dm/of_access.h | 16 ++++++++++++++++ include/dm/ofnode.h | 14 ++++++++++++++ 4 files changed, 71 insertions(+) (limited to 'include') diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 0729dfcdb3b..14c020a687b 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 0cfb0fbabb0..a7e19277232 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/include/dm/of_access.h b/include/dm/of_access.h index dd1abb8e97b..5ed1a0cdb42 100644 --- a/include/dm/of_access.h +++ b/include/dm/of_access.h @@ -193,6 +193,22 @@ static inline struct device_node *of_find_node_by_path(const char *path) struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible); +/** + * of_find_node_by_prop_value() - find a node with a given property value + * + * Find a node based on a property value. + * @from: Node to start searching from or NULL. the node you pass will not be + * searched, only the next one will; typically, you pass what the previous + * call returned. + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * @return node pointer or NULL if not found + */ +struct device_node *of_find_node_by_prop_value(struct device_node *from, + const char *propname, + const void *propval, + int proplen); /** * of_find_node_by_phandle() - Find a node given a phandle * diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index ab36b74c4ca..c06d77849c7 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -702,6 +702,20 @@ int ofnode_read_resource_byname(ofnode node, const char *name, */ ofnode ofnode_by_compatible(ofnode from, const char *compat); +/** + * ofnode_by_prop_value() - Find the next node with given property value + * + * Find the next node after @from that has a @propname with a value + * @propval and a length @proplen. + * + * @from: ofnode to start from (use ofnode_null() to start at the + * beginning) @propname: property name to check @propval: property value to + * search for @proplen: length of the value in propval @return ofnode + * found, or ofnode_null() if none + */ +ofnode ofnode_by_prop_value(ofnode from, const char *propname, + const void *propval, int proplen); + /** * ofnode_for_each_subnode() - iterate over all subnodes of a parent * -- cgit v1.3.1 From 75629a25087cd9897305375421abe2248bc40e72 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 7 Sep 2018 07:51:52 -0700 Subject: dm: uclass: Adding missing child_pre_probe description The comment of child_pre_probe, one of the 'struct uclass_driver' members, is currently missing. Signed-off-by: Bin Meng Reviewed-by: Simon Glass --- include/dm/uclass.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dm/uclass.h b/include/dm/uclass.h index 0e882cec59c..6e7c1cd3e8b 100644 --- a/include/dm/uclass.h +++ b/include/dm/uclass.h @@ -58,6 +58,7 @@ struct udevice; * @post_probe: Called after a new device is probed * @pre_remove: Called before a device is removed * @child_post_bind: Called after a child is bound to a device in this uclass + * @child_pre_probe: Called before a child is probed in this uclass * @init: Called to set up the uclass * @destroy: Called to destroy the uclass * @priv_auto_alloc_size: If non-zero this is the size of the private data -- cgit v1.3.1