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 +++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 Documentation/devicetree/bindings/ram/fsl,mpc83xx-mem-controller.txt (limited to 'Documentation') 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>; + }; +}; -- 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 'Documentation') 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 2c21749d7118b66b98cbab3f6301576726e06525 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:38 +0200 Subject: timer: Add MPC83xx timer driver Add a timer driver for the MPC83xx architecture. Signed-off-by: Mario Six --- .../bindings/timer/fsl,mpc83xx-timer.txt | 21 ++ MAINTAINERS | 1 + arch/powerpc/cpu/mpc83xx/cpu.c | 4 +- arch/powerpc/lib/Makefile | 4 + arch/powerpc/lib/interrupts.c | 5 +- drivers/timer/Kconfig | 7 + drivers/timer/Makefile | 1 + drivers/timer/mpc83xx_timer.c | 249 +++++++++++++++++++++ 8 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/timer/fsl,mpc83xx-timer.txt create mode 100644 drivers/timer/mpc83xx_timer.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/timer/fsl,mpc83xx-timer.txt b/Documentation/devicetree/bindings/timer/fsl,mpc83xx-timer.txt new file mode 100644 index 00000000000..608d24110ba --- /dev/null +++ b/Documentation/devicetree/bindings/timer/fsl,mpc83xx-timer.txt @@ -0,0 +1,21 @@ +MPC83xx timer devices + +MPC83xx SoCs offer a decrementer interrupt that can be used to implement delay +functionality, and periodically triggered actions. + +Required properties: +- compatible: must be "fsl,mpc83xx-timer" +- clocks: must be a reference to the system's CSB (coherent system bus) clock, + provided by one of the "fsl,mpc83xx-clk" devices + +Example: + +socclocks: clocks { + compatible = "fsl,mpc832x-clk"; + #clock-cells = <1>; +}; + +timer { + compatible = "fsl,mpc83xx-timer"; + clocks = <&socclocks MPC83XX_CLK_CSB>; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 1aab4b35066..e23abaffe71 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -526,6 +526,7 @@ 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: drivers/timer/mpc83xx_timer.c F: arch/powerpc/cpu/mpc83xx/ F: arch/powerpc/include/asm/arch-mpc83xx/ diff --git a/arch/powerpc/cpu/mpc83xx/cpu.c b/arch/powerpc/cpu/mpc83xx/cpu.c index e1d2f2f07cb..ffb42415feb 100644 --- a/arch/powerpc/cpu/mpc83xx/cpu.c +++ b/arch/powerpc/cpu/mpc83xx/cpu.c @@ -175,12 +175,12 @@ do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) /* * Get timebase clock frequency (like cpu_clk in Hz) */ - +#ifndef CONFIG_TIMER unsigned long get_tbclk(void) { return (gd->bus_clk + 3L) / 4L; } - +#endif #if defined(CONFIG_WATCHDOG) void watchdog_reset (void) diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index c3acefaea7e..8ac49bdd060 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -17,13 +17,17 @@ endif ifdef MINIMAL obj-y += cache.o time.o +ifndef CONFIG_TIMER obj-y += ticks.o +endif else obj-y += ppcstring.o obj-y += ppccache.o +ifndef CONFIG_TIMER obj-y += ticks.o +endif obj-y += reloc.o obj-$(CONFIG_BAT_RW) += bat_rw.o diff --git a/arch/powerpc/lib/interrupts.c b/arch/powerpc/lib/interrupts.c index f63e5cf799b..19682cfcfad 100644 --- a/arch/powerpc/lib/interrupts.c +++ b/arch/powerpc/lib/interrupts.c @@ -14,6 +14,7 @@ #include #endif +#ifndef CONFIG_MPC83XX_TIMER #ifdef CONFIG_SHOW_ACTIVITY void board_show_activity (ulong) __attribute__((weak, alias("__board_show_activity"))); @@ -44,7 +45,7 @@ static __inline__ void set_dec (unsigned long val) if (val) asm volatile ("mtdec %0"::"r" (val)); } - +#endif /* !CONFIG_MPC83XX_TIMER */ void enable_interrupts (void) { @@ -60,6 +61,7 @@ int disable_interrupts (void) return ((msr & MSR_EE) != 0); } +#ifndef CONFIG_MPC83XX_TIMER int interrupt_init (void) { /* call cpu specific function from $(CPU)/interrupts.c */ @@ -102,3 +104,4 @@ ulong get_timer (ulong base) { return (timestamp - base); } +#endif /* !CONFIG_MPC83XX_TIMER */ diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 5ab6749193c..a7d600b6e4e 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -140,4 +140,11 @@ config STM32_TIMER Select this to enable support for the timer found on STM32 devices. +config MPC83XX_TIMER + bool "MPC83xx timer support" + depends on TIMER + help + Select this to enable support for the timer found on + devices based on the MPC83xx family of SoCs. + endmenu diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index ed482df3e4c..7f19c4970a4 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_ATCPIT100_TIMER) += atcpit100_timer.o obj-$(CONFIG_ATMEL_PIT_TIMER) += atmel_pit_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence-ttc.o obj-$(CONFIG_DESIGNWARE_APB_TIMER) += dw-apb-timer.o +obj-$(CONFIG_MPC83XX_TIMER) += mpc83xx_timer.o obj-$(CONFIG_OMAP_TIMER) += omap-timer.o obj-$(CONFIG_ROCKCHIP_TIMER) += rockchip_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o diff --git a/drivers/timer/mpc83xx_timer.c b/drivers/timer/mpc83xx_timer.c new file mode 100644 index 00000000000..84a9ab072a9 --- /dev/null +++ b/drivers/timer/mpc83xx_timer.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct mpc83xx_timer_priv - Private data structure for MPC83xx timer driver + * @decrementer_count: Value to which the decrementer register should be re-set + * to when a timer interrupt occurs, thus determines the + * interrupt frequency (value for 1e6/HZ microseconds) + * @timestamp: Counter for the number of timer interrupts that have + * occurred (i.e. can be used to trigger events + * periodically in the timer interrupt) + */ +struct mpc83xx_timer_priv { + uint decrementer_count; + ulong timestamp; +}; + +/* + * Bitmask for enabling the time base in the SPCR (System Priority + * Configuration Register) + */ +static const u32 SPCR_TBEN_MASK = BIT(31 - 9); + +/** + * get_dec() - Get the value of the decrementer register + * + * Return: The value of the decrementer register + */ +static inline unsigned long get_dec(void) +{ + unsigned long val; + + asm volatile ("mfdec %0" : "=r" (val) : ); + + return val; +} + +/** + * set_dec() - Set the value of the decrementer register + * @val: The value of the decrementer register to be set + */ +static inline void set_dec(unsigned long val) +{ + if (val) + asm volatile ("mtdec %0"::"r" (val)); +} + +/** + * mftbu() - Get value of TBU (upper time base) register + * + * Return: Value of the TBU register + */ +static inline u32 mftbu(void) +{ + u32 rval; + + asm volatile("mftbu %0" : "=r" (rval)); + return rval; +} + +/** + * mftb() - Get value of TBL (lower time base) register + * + * Return: Value of the TBL register + */ +static inline u32 mftb(void) +{ + u32 rval; + + asm volatile("mftb %0" : "=r" (rval)); + return rval; +} + +/* + * TODO(mario.six@gdsys.cc): This should really be done by timer_init, and the + * interrupt init should go into a interrupt driver. + */ +int interrupt_init(void) +{ + immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + struct udevice *csb; + struct udevice *board; + struct udevice *timer; + struct mpc83xx_timer_priv *timer_priv; + struct clk clock; + int ret; + + ret = uclass_first_device_err(UCLASS_TIMER, &timer); + if (ret) { + debug("%s: Could not find timer device (error: %d)", + __func__, ret); + return ret; + } + + timer_priv = dev_get_priv(timer); + + if (board_get(&board)) { + debug("%s: board device could not be fetched.\n", __func__); + return -ENOENT; + } + + ret = uclass_get_device_by_phandle(UCLASS_SIMPLE_BUS, board, + "csb", &csb); + if (ret) { + debug("%s: Could not retrieve CSB device (error: %d)", + __func__, ret); + return ret; + } + + ret = clk_get_by_index(csb, 0, &clock); + if (ret) { + debug("%s: Could not retrieve clock (error: %d)", + __func__, ret); + return ret; + } + + timer_priv->decrementer_count = (clk_get_rate(&clock) / 4) + / CONFIG_SYS_HZ; + /* Enable e300 time base */ + setbits_be32(&immr->sysconf.spcr, SPCR_TBEN_MASK); + + set_dec(timer_priv->decrementer_count); + + /* Switch on interrupts */ + set_msr(get_msr() | MSR_EE); + + return 0; +} + +/** + * timer_interrupt() - Handler for the timer interrupt + * @regs: Array of register values + */ +void timer_interrupt(struct pt_regs *regs) +{ + struct udevice *timer = gd->timer; + struct mpc83xx_timer_priv *priv; + + /* + * During initialization, gd->timer might not be set yet, but the timer + * interrupt may already be enabled. In this case, wait for the + * initialization to complete + */ + if (!timer) + return; + + priv = dev_get_priv(timer); + + /* Restore Decrementer Count */ + set_dec(priv->decrementer_count); + + priv->timestamp++; + +#if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG) + if ((timestamp % (CONFIG_SYS_WATCHDOG_FREQ)) == 0) + WATCHDOG_RESET(); +#endif /* CONFIG_WATCHDOG || CONFIG_HW_WATCHDOG */ + +#ifdef CONFIG_LED_STATUS + status_led_tick(priv->timestamp); +#endif /* CONFIG_LED_STATUS */ + +#ifdef CONFIG_SHOW_ACTIVITY + board_show_activity(priv->timestamp); +#endif /* CONFIG_SHOW_ACTIVITY */ +} + +void wait_ticks(ulong ticks) +{ + ulong end = get_ticks() + ticks; + + while (end > get_ticks()) + WATCHDOG_RESET(); +} + +static int mpc83xx_timer_get_count(struct udevice *dev, u64 *count) +{ + u32 tbu, tbl; + + /* + * To make sure that no tbl overflow occurred between reading tbl and + * tbu, read tbu again, and compare it with the previously read tbu + * value: If they're different, a tbl overflow has occurred. + */ + do { + tbu = mftbu(); + tbl = mftb(); + } while (tbu != mftbu()); + + *count = (tbu * 0x10000ULL) + tbl; + + return 0; +} + +static int mpc83xx_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev->uclass_priv; + struct clk clock; + int ret; + + ret = interrupt_init(); + if (ret) { + debug("%s: interrupt_init failed (err = %d)\n", + dev->name, ret); + return ret; + } + + ret = clk_get_by_index(dev, 0, &clock); + if (ret) { + debug("%s: Could not retrieve clock (err = %d)\n", + dev->name, ret); + return ret; + } + + uc_priv->clock_rate = (clk_get_rate(&clock) + 3L) / 4L; + + return 0; +} + +static const struct timer_ops mpc83xx_timer_ops = { + .get_count = mpc83xx_timer_get_count, +}; + +static const struct udevice_id mpc83xx_timer_ids[] = { + { .compatible = "fsl,mpc83xx-timer" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(mpc83xx_timer) = { + .name = "mpc83xx_timer", + .id = UCLASS_TIMER, + .of_match = mpc83xx_timer_ids, + .probe = mpc83xx_timer_probe, + .ops = &mpc83xx_timer_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct mpc83xx_timer_priv), +}; -- cgit v1.3.1 From 19fbdca47b3d847824ada3ab2ed575019c88516e Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:45 +0200 Subject: cpu: Add MPC83xx CPU driver Add a CPU driver for the MPC83xx architecture. Signed-off-by: Mario Six --- .../devicetree/bindings/cpu/fsl,mpc83xx.txt | 34 ++ MAINTAINERS | 2 + arch/powerpc/cpu/mpc83xx/cpu.c | 2 + arch/powerpc/cpu/mpc83xx/cpu_init.c | 2 + arch/powerpc/include/asm/processor.h | 2 + drivers/cpu/Kconfig | 7 + drivers/cpu/Makefile | 1 + drivers/cpu/mpc83xx_cpu.c | 349 +++++++++++++++++++++ drivers/cpu/mpc83xx_cpu.h | 126 ++++++++ 9 files changed, 525 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpu/fsl,mpc83xx.txt create mode 100644 drivers/cpu/mpc83xx_cpu.c create mode 100644 drivers/cpu/mpc83xx_cpu.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/cpu/fsl,mpc83xx.txt b/Documentation/devicetree/bindings/cpu/fsl,mpc83xx.txt new file mode 100644 index 00000000000..ac563d906ac --- /dev/null +++ b/Documentation/devicetree/bindings/cpu/fsl,mpc83xx.txt @@ -0,0 +1,34 @@ +MPC83xx CPU devices + +MPC83xx SoCs contain a e300 core as their main processor. + +Required properties: +- compatible: must be one of "fsl,mpc83xx", + "fsl,mpc8308", + "fsl,mpc8309", + "fsl,mpc8313", + "fsl,mpc8315", + "fsl,mpc832x", + "fsl,mpc8349", + "fsl,mpc8360", + "fsl,mpc8379" +- clocks: has to have two entries, which must be the core clock at index 0 and + the CSB (Coherent System Bus) clock at index 1. Both are given by a suitable + "fsl,mpc83xx-clk" device + +Example: + +socclocks: clocks { + compatible = "fsl,mpc8315-clk"; + #clock-cells = <1>; +}; + +cpus { + compatible = "cpu_bus"; + + PowerPC,8315@0 { + compatible = "fsl,mpc8315"; + clocks = <&socclocks MPC83XX_CLK_CORE + &socclocks MPC83XX_CLK_CSB>; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index e23abaffe71..9ff3bbef025 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -527,6 +527,8 @@ F: drivers/clk/mpc83xx_clk.c F: drivers/clk/mpc83xx_clk.h F: include/dt-bindings/clk/mpc83xx-clk.h F: drivers/timer/mpc83xx_timer.c +F: drivers/cpu/mpc83xx_cpu.c +F: drivers/cpu/mpc83xx_cpu.h F: arch/powerpc/cpu/mpc83xx/ F: arch/powerpc/include/asm/arch-mpc83xx/ diff --git a/arch/powerpc/cpu/mpc83xx/cpu.c b/arch/powerpc/cpu/mpc83xx/cpu.c index ffb42415feb..b29f271e9bc 100644 --- a/arch/powerpc/cpu/mpc83xx/cpu.c +++ b/arch/powerpc/cpu/mpc83xx/cpu.c @@ -25,6 +25,7 @@ DECLARE_GLOBAL_DATA_PTR; +#ifndef CONFIG_CPU_MPC83XX int checkcpu(void) { volatile immap_t *immr; @@ -114,6 +115,7 @@ int checkcpu(void) return 0; } +#endif #ifndef CONFIG_SYSRESET int diff --git a/arch/powerpc/cpu/mpc83xx/cpu_init.c b/arch/powerpc/cpu/mpc83xx/cpu_init.c index fcac9f63a81..1555205e069 100644 --- a/arch/powerpc/cpu/mpc83xx/cpu_init.c +++ b/arch/powerpc/cpu/mpc83xx/cpu_init.c @@ -464,6 +464,7 @@ static int print_83xx_arb_event(int force) } #endif /* CONFIG_DISPLAY_AER_xxxx */ +#ifndef CONFIG_CPU_MPC83XX /* * Figure out the cause of the reset */ @@ -505,3 +506,4 @@ int prt_83xx_rsr(void) return 0; } +#endif diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 6fbe8c46b31..f97ce48cc27 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -1325,7 +1325,9 @@ void ll_puts(const char *); /* In misc.c */ void _nmask_and_or_msr(unsigned long nmask, unsigned long or_val); +#ifndef CONFIG_CPU_MPC83XX int prt_83xx_rsr(void); +#endif #endif /* ndef ASSEMBLY*/ diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig index 0d1424d38e9..d4052005e24 100644 --- a/drivers/cpu/Kconfig +++ b/drivers/cpu/Kconfig @@ -6,3 +6,10 @@ config CPU multiple CPUs, then normally have to be set up in U-Boot so that they can work correctly in the OS. This provides a framework for finding out information about available CPUs and making changes. + +config CPU_MPC83XX + bool "Enable MPC83xx CPU driver" + depends on CPU + select CLK_MPC83XX + help + Support CPU cores for SoCs of the MPC83xx series. diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile index 980c68f44d0..f452ee404e5 100644 --- a/drivers/cpu/Makefile +++ b/drivers/cpu/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_CPU) += cpu-uclass.o obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o +obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o obj-$(CONFIG_SANDBOX) += cpu_sandbox.o diff --git a/drivers/cpu/mpc83xx_cpu.c b/drivers/cpu/mpc83xx_cpu.c new file mode 100644 index 00000000000..31717afaec4 --- /dev/null +++ b/drivers/cpu/mpc83xx_cpu.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include +#include +#include +#include +#include + +#include "mpc83xx_cpu.h" + +/** + * struct mpc83xx_cpu_priv - Private data for MPC83xx CPUs + * @e300_type: The e300 core type of the MPC83xx CPU + * @family: The MPC83xx family the CPU belongs to + * @type: The MPC83xx type of the CPU + * @is_e_processor: Flag indicating whether the CPU is a E processor or not + * @is_a_variant: Flag indicating whtther the CPU is a A variant or not + * @revid: The revision ID of the CPU + * @revid.major: The major part of the CPU's revision ID + * @revid.minor: The minor part of the CPU's revision ID + */ +struct mpc83xx_cpu_priv { + enum e300_type e300_type; + enum mpc83xx_cpu_family family; + enum mpc83xx_cpu_type type; + bool is_e_processor; + bool is_a_variant; + struct { + uint major; + uint minor; + } revid; +}; + +int checkcpu(void) +{ + /* Activate all CPUs from board_f.c */ + return cpu_probe_all(); +} + +/** + * get_spridr() - Read SPRIDR (System Part and Revision ID Register) of CPU + * + * Return: The SPRIDR value + */ +static inline u32 get_spridr(void) +{ + immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + + return in_be32(&immr->sysconf.spridr); +} + +/** + * determine_type() - Determine CPU family of MPC83xx device + * @dev: CPU device from which to read CPU family from + */ +static inline void determine_family(struct udevice *dev) +{ + struct mpc83xx_cpu_priv *priv = dev_get_priv(dev); + /* Upper 12 bits of PARTID field (bits 0-23 in SPRIDR) */ + const u32 PARTID_FAMILY_MASK = 0xFFF00000; + + switch (bitfield_extract_by_mask(get_spridr(), PARTID_FAMILY_MASK)) { + case 0x810: + case 0x811: + priv->family = FAMILY_830X; + break; + case 0x80B: + priv->family = FAMILY_831X; + break; + case 0x806: + priv->family = FAMILY_832X; + break; + case 0x803: + priv->family = FAMILY_834X; + break; + case 0x804: + priv->family = FAMILY_836X; + break; + case 0x80C: + priv->family = FAMILY_837X; + break; + default: + priv->family = FAMILY_UNKNOWN; + } +} + +/** + * determine_type() - Determine CPU type of MPC83xx device + * @dev: CPU device from which to read CPU type from + */ +static inline void determine_type(struct udevice *dev) +{ + struct mpc83xx_cpu_priv *priv = dev_get_priv(dev); + /* Upper 16 bits of PVR (Processor Version Register) */ + const u32 PCR_UPPER_MASK = 0xFFFF0000; + u32 val; + + val = bitfield_extract_by_mask(get_spridr(), PCR_UPPER_MASK); + + /* Mask out E-variant bit */ + switch (val & 0xFFFE) { + case 0x8100: + priv->type = TYPE_8308; + break; + case 0x8110: + priv->type = TYPE_8309; + break; + case 0x80B2: + priv->type = TYPE_8311; + break; + case 0x80B0: + priv->type = TYPE_8313; + break; + case 0x80B6: + priv->type = TYPE_8314; + break; + case 0x80B4: + priv->type = TYPE_8315; + break; + case 0x8066: + priv->type = TYPE_8321; + break; + case 0x8062: + priv->type = TYPE_8323; + break; + case 0x8036: + priv->type = TYPE_8343; + break; + case 0x8032: + priv->type = TYPE_8347_TBGA; + break; + case 0x8034: + priv->type = TYPE_8347_PBGA; + break; + case 0x8030: + priv->type = TYPE_8349; + break; + case 0x804A: + priv->type = TYPE_8358_TBGA; + break; + case 0x804E: + priv->type = TYPE_8358_PBGA; + break; + case 0x8048: + priv->type = TYPE_8360; + break; + case 0x80C6: + priv->type = TYPE_8377; + break; + case 0x80C4: + priv->type = TYPE_8378; + break; + case 0x80C2: + priv->type = TYPE_8379; + break; + default: + priv->type = TYPE_UNKNOWN; + } +} + +/** + * determine_e300_type() - Determine e300 core type of MPC83xx device + * @dev: CPU device from which to read e300 core type from + */ +static inline void determine_e300_type(struct udevice *dev) +{ + struct mpc83xx_cpu_priv *priv = dev_get_priv(dev); + /* Upper 16 bits of PVR (Processor Version Register) */ + const u32 PCR_UPPER_MASK = 0xFFFF0000; + u32 pvr = get_pvr(); + + switch ((pvr & PCR_UPPER_MASK) >> 16) { + case 0x8083: + priv->e300_type = E300C1; + break; + case 0x8084: + priv->e300_type = E300C2; + break; + case 0x8085: + priv->e300_type = E300C3; + break; + case 0x8086: + priv->e300_type = E300C4; + break; + default: + priv->e300_type = E300_UNKNOWN; + } +} + +/** + * determine_revid() - Determine revision ID of CPU device + * @dev: CPU device from which to read revision ID + */ +static inline void determine_revid(struct udevice *dev) +{ + struct mpc83xx_cpu_priv *priv = dev_get_priv(dev); + u32 REVID_MAJOR_MASK; + u32 REVID_MINOR_MASK; + u32 spridr = get_spridr(); + + if (priv->family == FAMILY_834X) { + REVID_MAJOR_MASK = 0x0000FF00; + REVID_MINOR_MASK = 0x000000FF; + } else { + REVID_MAJOR_MASK = 0x000000F0; + REVID_MINOR_MASK = 0x0000000F; + } + + priv->revid.major = bitfield_extract_by_mask(spridr, REVID_MAJOR_MASK); + priv->revid.minor = bitfield_extract_by_mask(spridr, REVID_MINOR_MASK); +} + +/** + * determine_cpu_data() - Determine CPU information from hardware + * @dev: CPU device from which to read information + */ +static void determine_cpu_data(struct udevice *dev) +{ + struct mpc83xx_cpu_priv *priv = dev_get_priv(dev); + const u32 E_FLAG_MASK = 0x00010000; + u32 spridr = get_spridr(); + + determine_family(dev); + determine_type(dev); + determine_e300_type(dev); + determine_revid(dev); + + if ((priv->family == FAMILY_834X || + priv->family == FAMILY_836X) && priv->revid.major >= 2) + priv->is_a_variant = true; + + priv->is_e_processor = !bitfield_extract_by_mask(spridr, E_FLAG_MASK); +} + +static int mpc83xx_cpu_get_desc(struct udevice *dev, char *buf, int size) +{ + struct mpc83xx_cpu_priv *priv = dev_get_priv(dev); + struct clk core_clk; + struct clk csb_clk; + char core_freq[32]; + char csb_freq[32]; + int ret; + + ret = clk_get_by_index(dev, 0, &core_clk); + if (ret) { + debug("%s: Failed to get core clock (err = %d)\n", + dev->name, ret); + return ret; + } + + ret = clk_get_by_index(dev, 1, &csb_clk); + if (ret) { + debug("%s: Failed to get CSB clock (err = %d)\n", + dev->name, ret); + return ret; + } + + determine_cpu_data(dev); + + snprintf(buf, size, + "CPU: %s, MPC%s%s%s, Rev: %d.%d at %s MHz, CSB: %s MHz\n", + e300_names[priv->e300_type], + cpu_type_names[priv->type], + priv->is_e_processor ? "E" : "", + priv->is_a_variant ? "A" : "", + priv->revid.major, + priv->revid.minor, + strmhz(core_freq, clk_get_rate(&core_clk)), + strmhz(csb_freq, clk_get_rate(&csb_clk))); + + return 0; +} + +static int mpc83xx_cpu_get_info(struct udevice *dev, struct cpu_info *info) +{ + struct clk clock; + int ret; + ulong freq; + + ret = clk_get_by_index(dev, 0, &clock); + if (ret) { + debug("%s: Failed to get core clock (err = %d)\n", + dev->name, ret); + return ret; + } + + freq = clk_get_rate(&clock); + if (!freq) { + debug("%s: Core clock speed is zero\n", dev->name); + return -EINVAL; + } + + info->cpu_freq = freq; + info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU); + + return 0; +} + +static int mpc83xx_cpu_get_count(struct udevice *dev) +{ + /* We have one e300cX core */ + return 1; +} + +static int mpc83xx_cpu_get_vendor(struct udevice *dev, char *buf, int size) +{ + snprintf(buf, size, "NXP"); + + return 0; +} + +static const struct cpu_ops mpc83xx_cpu_ops = { + .get_desc = mpc83xx_cpu_get_desc, + .get_info = mpc83xx_cpu_get_info, + .get_count = mpc83xx_cpu_get_count, + .get_vendor = mpc83xx_cpu_get_vendor, +}; + +static int mpc83xx_cpu_probe(struct udevice *dev) +{ + return 0; +} + +static const struct udevice_id mpc83xx_cpu_ids[] = { + { .compatible = "fsl,mpc83xx", }, + { .compatible = "fsl,mpc8308", }, + { .compatible = "fsl,mpc8309", }, + { .compatible = "fsl,mpc8313", }, + { .compatible = "fsl,mpc8315", }, + { .compatible = "fsl,mpc832x", }, + { .compatible = "fsl,mpc8349", }, + { .compatible = "fsl,mpc8360", }, + { .compatible = "fsl,mpc8379", }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(mpc83xx_cpu) = { + .name = "mpc83xx_cpu", + .id = UCLASS_CPU, + .of_match = mpc83xx_cpu_ids, + .probe = mpc83xx_cpu_probe, + .priv_auto_alloc_size = sizeof(struct mpc83xx_cpu_priv), + .ops = &mpc83xx_cpu_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/cpu/mpc83xx_cpu.h b/drivers/cpu/mpc83xx_cpu.h new file mode 100644 index 00000000000..2aaa4e18844 --- /dev/null +++ b/drivers/cpu/mpc83xx_cpu.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#ifndef _MPC83XX_CPU_H_ +#define _MPC83XX_CPU_H_ + +/** + * enum e300_type - Identifiers for e300 cores + * @E300C1: Identifier for e300c1 cores + * @E300C2: Identifier for e300c2 cores + * @E300C3: Identifier for e300c3 cores + * @E300C4: Identifier for e300c4 cores + * @E300_UNKNOWN: Identifier for unknown e300 cores + */ +enum e300_type { + E300C1, + E300C2, + E300C3, + E300C4, + E300_UNKNOWN, +}; + +/* Array mapping the e300 core types to their human-readable names */ +static const char * const e300_names[] = { + [E300C1] = "e300c1", + [E300C2] = "e300c2", + [E300C3] = "e300c3", + [E300C4] = "e300c4", + [E300_UNKNOWN] = "Unknown e300", +}; + +/** + * enum mpc83xx_cpu_family - Identifiers for MPC83xx CPU families + * @FAMILY_830X: Identifier for the MPC830x CPU family + * @FAMILY_831X: Identifier for the MPC831x CPU family + * @FAMILY_832X: Identifier for the MPC832x CPU family + * @FAMILY_834X: Identifier for the MPC834x CPU family + * @FAMILY_836X: Identifier for the MPC836x CPU family + * @FAMILY_837X: Identifier for the MPC837x CPU family + * @FAMILY_UNKNOWN: Identifier for an unknown MPC83xx CPU family + */ +enum mpc83xx_cpu_family { + FAMILY_830X, + FAMILY_831X, + FAMILY_832X, + FAMILY_834X, + FAMILY_836X, + FAMILY_837X, + FAMILY_UNKNOWN, +}; + +/** + * enum mpc83xx_cpu_type - Identifiers for MPC83xx CPU types + * @TYPE_8308: Identifier for the MPC8308 CPU type + * @TYPE_8309: Identifier for the MPC8309 CPU type + * @TYPE_8311: Identifier for the MPC8311 CPU type + * @TYPE_8313: Identifier for the MPC8313 CPU type + * @TYPE_8314: Identifier for the MPC8314 CPU type + * @TYPE_8315: Identifier for the MPC8315 CPU type + * @TYPE_8321: Identifier for the MPC8321 CPU type + * @TYPE_8323: Identifier for the MPC8323 CPU type + * @TYPE_8343: Identifier for the MPC8343 CPU type + * @TYPE_8347_TBGA: Identifier for the MPC8347 CPU type (Tape Ball Grid Array + * version) + * @TYPE_8347_PBGA: Identifier for the MPC8347 CPU type (Plastic Ball Grid Array + * version) + * @TYPE_8349: Identifier for the MPC8349 CPU type + * @TYPE_8358_TBGA: Identifier for the MPC8358 CPU type (Tape Ball Grid Array + * version) + * @TYPE_8358_PBGA: Identifier for the MPC8358 CPU type (Plastic Ball Grid Array + * version) + * @TYPE_8360: Identifier for the MPC8360 CPU type + * @TYPE_8377: Identifier for the MPC8377 CPU type + * @TYPE_8378: Identifier for the MPC8378 CPU type + * @TYPE_8379: Identifier for the MPC8379 CPU type + * @TYPE_UNKNOWN: Identifier for an unknown MPC83xx CPU type + */ +enum mpc83xx_cpu_type { + TYPE_8308, + TYPE_8309, + TYPE_8311, + TYPE_8313, + TYPE_8314, + TYPE_8315, + TYPE_8321, + TYPE_8323, + TYPE_8343, + TYPE_8347_TBGA, + TYPE_8347_PBGA, + TYPE_8349, + TYPE_8358_TBGA, + TYPE_8358_PBGA, + TYPE_8360, + TYPE_8377, + TYPE_8378, + TYPE_8379, + TYPE_UNKNOWN, +}; + +/* Array mapping the MCP83xx CPUs to their human-readable names */ +static const char * const cpu_type_names[] = { + [TYPE_8308] = "8308", + [TYPE_8309] = "8309", + [TYPE_8311] = "8311", + [TYPE_8313] = "8313", + [TYPE_8314] = "8314", + [TYPE_8315] = "8315", + [TYPE_8321] = "8321", + [TYPE_8323] = "8323", + [TYPE_8343] = "8343", + [TYPE_8347_TBGA] = "8347_TBGA", + [TYPE_8347_PBGA] = "8347_PBGA", + [TYPE_8349] = "8349", + [TYPE_8358_TBGA] = "8358_TBGA", + [TYPE_8358_PBGA] = "8358_PBGA", + [TYPE_8360] = "8360", + [TYPE_8377] = "8377", + [TYPE_8378] = "8378", + [TYPE_8379] = "8379", + [TYPE_UNKNOWN] = "Unknown CPU", +}; + +#endif /* !_MPC83XX_CPU_H_ */ -- cgit v1.3.1 From d2166319df17286b2a3e2a1a7ce89f01e4f8bac9 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Mon, 6 Aug 2018 10:23:46 +0200 Subject: misc: Add MPC83xx serdes driver Add a driver to configure the SerDes (Serializer/Deserializer) lanes on the MPC83xx architecture. Reviewed-by: Simon Glass Signed-off-by: Mario Six --- .../bindings/misc/fsl,mpc83xx-serdes.txt | 24 +++ MAINTAINERS | 1 + arch/powerpc/cpu/mpc83xx/serdes.c | 4 + arch/powerpc/include/asm/fsl_mpc83xx_serdes.h | 4 + drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 + drivers/misc/mpc83xx_serdes.c | 185 ++++++++++++++++ drivers/misc/mpc83xx_serdes.h | 232 +++++++++++++++++++++ 8 files changed, 458 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt create mode 100644 drivers/misc/mpc83xx_serdes.c create mode 100644 drivers/misc/mpc83xx_serdes.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt b/Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt new file mode 100644 index 00000000000..64a9b5b154b --- /dev/null +++ b/Documentation/devicetree/bindings/misc/fsl,mpc83xx-serdes.txt @@ -0,0 +1,24 @@ +MPC83xx SerDes controller devices + +MPC83xx SoCs contain a built-in SerDes controller that determines which +protocols (SATA, PCI Express, SGMII, ...) are used on the system's serdes lines +and how the lines are configured. + +Required properties: +- compatible: must be "fsl,mpc83xx-serdes" +- reg: must point to the serdes controller's register map +- proto: selects for which protocol the serdes lines are configured. One of + "sata", "pex", "pex-x2", "sgmii" +- serdes-clk: determines the frequency the serdes lines are configured for. One + of 100, 125, 150. +- vdd: determines whether 1.0V core VDD is used or not + +Example: + +SERDES: serdes@e3000 { + reg = <0xe3000 0x200>; + compatible = "fsl,mpc83xx-serdes"; + proto = "pex"; + serdes-clk = <100>; + vdd; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 9ff3bbef025..237a022f078 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -529,6 +529,7 @@ F: include/dt-bindings/clk/mpc83xx-clk.h F: drivers/timer/mpc83xx_timer.c F: drivers/cpu/mpc83xx_cpu.c F: drivers/cpu/mpc83xx_cpu.h +F: drivers/misc/mpc83xx_serdes.c F: arch/powerpc/cpu/mpc83xx/ F: arch/powerpc/include/asm/arch-mpc83xx/ diff --git a/arch/powerpc/cpu/mpc83xx/serdes.c b/arch/powerpc/cpu/mpc83xx/serdes.c index 982a4475f00..8242f952653 100644 --- a/arch/powerpc/cpu/mpc83xx/serdes.c +++ b/arch/powerpc/cpu/mpc83xx/serdes.c @@ -8,6 +8,8 @@ * Author: Li Yang */ +#ifndef CONFIG_MPC83XX_SERDES + #include #include #include @@ -148,3 +150,5 @@ void fsl_setup_serdes(u32 offset, char proto, u32 rfcks, char vdd) tmp |= FSL_SRDSRSTCTL_RST; out_be32(regs + FSL_SRDSRSTCTL_OFFS, tmp); } + +#endif /* !CONFIG_MPC83XX_SERDES */ diff --git a/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h b/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h index e51d060d6a7..a02b5992654 100644 --- a/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h +++ b/arch/powerpc/include/asm/fsl_mpc83xx_serdes.h @@ -6,6 +6,8 @@ #ifndef __FSL_MPC83XX_SERDES_H #define __FSL_MPC83XX_SERDES_H +#ifndef CONFIG_MPC83XX_SERDES + #include #define FSL_SERDES_CLK_100 (0 << 28) @@ -19,4 +21,6 @@ extern void fsl_setup_serdes(u32 offset, char proto, u32 rfcks, char vdd); +#endif /* !CONFIG_MPC83XX_SERDES */ + #endif /* __FSL_MPC83XX_SERDES_H */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c2b7cc15db1..bfa5c916874 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -288,4 +288,11 @@ config GDSYS_IOEP depends on MISC help Support gdsys FPGA's IO endpoint driver. + +config MPC83XX_SERDES + bool "Enable MPC83xx serdes driver" + depends on MISC + help + Support for serdes found on MPC83xx SoCs. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 32ef4a53c79..da4666fdfcb 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_STM32MP_FUSE) += stm32mp_fuse.o obj-$(CONFIG_SYS_DPAA_QBMAN) += fsl_portals.o obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o +obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o diff --git a/drivers/misc/mpc83xx_serdes.c b/drivers/misc/mpc83xx_serdes.c new file mode 100644 index 00000000000..d572dda3c19 --- /dev/null +++ b/drivers/misc/mpc83xx_serdes.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * base on the MPC83xx serdes initialization, which is + * + * Copyright 2007,2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 MontaVista Software, Inc. + */ + +#include +#include +#include +#include + +#include "mpc83xx_serdes.h" + +/** + * struct mpc83xx_serdes_priv - Private structure for MPC83xx serdes + * @regs: The device's register map + * @rfcks: Variable to keep the serdes reference clock selection set during + * initialization in (is or'd to every value written to SRDSCR4) + */ +struct mpc83xx_serdes_priv { + struct mpc83xx_serdes_regs *regs; + u32 rfcks; +}; + +/** + * setup_sata() - Configure the SerDes device to SATA mode + * @dev: The device to configure + */ +static void setup_sata(struct udevice *dev) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + + /* Set and clear reset bits */ + setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET); + udelay(1000); + clrbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_SATA_RESET); + + /* Configure SRDSCR0 */ + clrsetbits_be32(&priv->regs->srdscr0, + SRDSCR0_TXEQA_MASK | SRDSCR0_TXEQE_MASK, + SRDSCR0_TXEQA_SATA | SRDSCR0_TXEQE_SATA); + + /* Configure SRDSCR1 */ + clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW); + + /* Configure SRDSCR2 */ + clrsetbits_be32(&priv->regs->srdscr2, + SRDSCR2_SEIC_MASK, + SRDSCR2_SEIC_SATA); + + /* Configure SRDSCR3 */ + out_be32(&priv->regs->srdscr3, + SRDSCR3_KFR_SATA | SRDSCR3_KPH_SATA | + SRDSCR3_SDFM_SATA_PEX | SRDSCR3_SDTXL_SATA); + + /* Configure SRDSCR4 */ + out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SATA); +} + +/** + * setup_pex() - Configure the SerDes device to PCI Express mode + * @dev: The device to configure + * @type: The PCI Express type to configure for (x1 or x2) + */ +static void setup_pex(struct udevice *dev, enum pex_type type) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + + /* Configure SRDSCR1 */ + setbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW); + + /* Configure SRDSCR2 */ + clrsetbits_be32(&priv->regs->srdscr2, + SRDSCR2_SEIC_MASK, + SRDSCR2_SEIC_PEX); + + /* Configure SRDSCR3 */ + out_be32(&priv->regs->srdscr3, SRDSCR3_SDFM_SATA_PEX); + + /* Configure SRDSCR4 */ + if (type == PEX_X2) + out_be32(&priv->regs->srdscr4, + priv->rfcks | SRDSCR4_PROT_PEX | SRDSCR4_PLANE_X2); + else + out_be32(&priv->regs->srdscr4, + priv->rfcks | SRDSCR4_PROT_PEX); +} + +/** + * setup_sgmii() - Configure the SerDes device to SGMII mode + * @dev: The device to configure + */ +static void setup_sgmii(struct udevice *dev) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + + /* Configure SRDSCR1 */ + clrbits_be32(&priv->regs->srdscr1, SRDSCR1_PLLBW); + + /* Configure SRDSCR2 */ + clrsetbits_be32(&priv->regs->srdscr2, + SRDSCR2_SEIC_MASK, + SRDSCR2_SEIC_SGMII); + + /* Configure SRDSCR3 */ + out_be32(&priv->regs->srdscr3, 0); + + /* Configure SRDSCR4 */ + out_be32(&priv->regs->srdscr4, priv->rfcks | SRDSCR4_PROT_SGMII); +} + +static int mpc83xx_serdes_probe(struct udevice *dev) +{ + struct mpc83xx_serdes_priv *priv = dev_get_priv(dev); + bool vdd; + const char *proto; + + priv->regs = map_sysmem(dev_read_addr(dev), + sizeof(struct mpc83xx_serdes_regs)); + + switch (dev_read_u32_default(dev, "serdes-clk", -1)) { + case 100: + priv->rfcks = SRDSCR4_RFCKS_100; + break; + case 125: + priv->rfcks = SRDSCR4_RFCKS_125; + break; + case 150: + priv->rfcks = SRDSCR4_RFCKS_150; + break; + default: + debug("%s: Could not read serdes clock value\n", dev->name); + return -EINVAL; + } + + vdd = dev_read_bool(dev, "vdd"); + + /* 1.0V corevdd */ + if (vdd) { + /* DPPE/DPPA = 0 */ + clrbits_be32(&priv->regs->srdscr0, SRDSCR0_DPP_1V2); + + /* VDD = 0 */ + clrbits_be32(&priv->regs->srdscr0, SRDSCR2_VDD_1V2); + } + + proto = dev_read_string(dev, "proto"); + + /* protocol specific configuration */ + if (!strcmp(proto, "sata")) { + setup_sata(dev); + } else if (!strcmp(proto, "pex")) { + setup_pex(dev, PEX_X1); + } else if (!strcmp(proto, "pex-x2")) { + setup_pex(dev, PEX_X2); + } else if (!strcmp(proto, "sgmii")) { + setup_sgmii(dev); + } else { + debug("%s: Invalid protocol value %s\n", dev->name, proto); + return -EINVAL; + } + + /* Do a software reset */ + setbits_be32(&priv->regs->srdsrstctl, SRDSRSTCTL_RST); + + return 0; +} + +static const struct udevice_id mpc83xx_serdes_ids[] = { + { .compatible = "fsl,mpc83xx-serdes" }, + { } +}; + +U_BOOT_DRIVER(mpc83xx_serdes) = { + .name = "mpc83xx_serdes", + .id = UCLASS_MISC, + .of_match = mpc83xx_serdes_ids, + .probe = mpc83xx_serdes_probe, + .priv_auto_alloc_size = sizeof(struct mpc83xx_serdes_priv), +}; diff --git a/drivers/misc/mpc83xx_serdes.h b/drivers/misc/mpc83xx_serdes.h new file mode 100644 index 00000000000..89ea1dbab73 --- /dev/null +++ b/drivers/misc/mpc83xx_serdes.h @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +/** + * enum srdscr0_mask - Bit masks for SRDSCR0 (SerDes Control Register 0) + * @SRDSCR0_DPPA: Bitmask for the DPPA (diff pk-pk swing for lane A) + * field of the SRCSCR0 + * @SRDSCR0_DPPE: Bitmask for the DPPE (diff pk-pk swing for lane E) + * field of the SRCSCR0 + * @SRDSCR0_DPP_1V2: Combined bitmask to set diff pk-pk swing for both lanes + * @SRDSCR0_TXEQA_MASK: Bitmask for the TXEQA (transmit equalization for + * lane A) field of the SRCSCR0 + * @SRDSCR0_TXEQA_SATA: Bitmask to set the TXEQA to the value used for SATA + * @SRDSCR0_TXEQE_MASK: Bitmask for the TXEQE (transmit equalization for + * lane E) field of the SRCSCR0 + * @SRDSCR0_TXEQE_SATA: Bitmask to set the TXEQE to the value used for SATA + */ +enum srdscr0_mask { + SRDSCR0_DPPA = BIT(31 - 16), + SRDSCR0_DPPE = BIT(31 - 20), + SRDSCR0_DPP_1V2 = SRDSCR0_DPPE | SRDSCR0_DPPA, + + SRDSCR0_TXEQA_MASK = 0x00007000, + SRDSCR0_TXEQA_SATA = 0x00001000, + SRDSCR0_TXEQE_MASK = 0x00000700, + SRDSCR0_TXEQE_SATA = 0x00000100, +}; + +/** + * enum srdscr1_mask - Bit masks for SRDSCR1 (SerDes Control Register 1) + * @SRDSCR1_PLLBW: Bitmask for the PLLBW (PLL bandwidth) field of SRDSCR1 + */ +enum srdscr1_mask { + SRDSCR1_PLLBW = BIT(31 - 25), +}; + +/** + * enum srdscr2_mask - Bit masks for SRDSCR2 (SerDes Control Register 2) + * @SRDSCR2_VDD_1V2: Bit mask to to set the VDD field of the SCRSCR2 + * @SRDSCR2_SEICA_MASK: Bitmask for the SEICA (Receiver electrical idle + * detection control for lane A) field of the SRCSCR2 + * @SRDSCR2_SEICE_MASK: Bitmask for the SEICE (Receiver electrical idle + * detection control for lane E) field of the SRCSCR2 + * @SRDSCR2_SEIC_MASK: Combined bitmask to set the receiver electrical idle + * detection control for both lanes + * @SRDSCR2_SEICA_SATA: Bitmask to set the SEICA field to the value used for + * SATA + * @SRDSCR2_SEICE_SATA: Bitmask to set the SEICE field to the value used for + * SATA + * @SRDSCR2_SEIC_SATA: Combined bitmask to set the value of both SEIC fields + * to the value used for SATA + * @SRDSCR2_SEICA_PEX: Bitmask to set the SEICA field to the value used for + * PCI Express + * @SRDSCR2_SEICE_PEX: Bitmask to set the SEICE field to the value used for + * PCI Express + * @SRDSCR2_SEIC_PEX: Combined bitmask to set the value of both SEIC fields + * to the value used for PCI Express + * @SRDSCR2_SEICA_SGMII: Bitmask to set the SEICA field to the value used for + * SGMII + * @SRDSCR2_SEICE_SGMII: Bitmask to set the SEICE field to the value used for + * SGMII + * @SRDSCR2_SEIC_SGMII: Combined bitmask to set the value of both SEIC fields + * to the value used for SGMII + */ +enum srdscr2_mask { + SRDSCR2_VDD_1V2 = 0x00800000, + + SRDSCR2_SEICA_MASK = 0x00001c00, + SRDSCR2_SEICE_MASK = 0x0000001c, + SRDSCR2_SEIC_MASK = SRDSCR2_SEICA_MASK | SRDSCR2_SEICE_MASK, + + SRDSCR2_SEICA_SATA = 0x00001400, + SRDSCR2_SEICE_SATA = 0x00000014, + SRDSCR2_SEIC_SATA = SRDSCR2_SEICA_SATA | SRDSCR2_SEICE_SATA, + + SRDSCR2_SEICA_PEX = 0x00001000, + SRDSCR2_SEICE_PEX = 0x00000010, + SRDSCR2_SEIC_PEX = SRDSCR2_SEICA_PEX | SRDSCR2_SEICE_PEX, + + SRDSCR2_SEICA_SGMII = 0x00000100, + SRDSCR2_SEICE_SGMII = 0x00000001, + SRDSCR2_SEIC_SGMII = SRDSCR2_SEICA_SGMII | SRDSCR2_SEICE_SGMII, +}; + +/** + * enum srdscr3_mask - Bit masks for SRDSCR3 (SerDes Control Register 3) + * @SRDSCR3_KFRA_SATA: Bitmask to set the KFRA field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KFRE_SATA: Bitmask to set the KFRE field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KFR_SATA: Combined bitmask to set both KFR fields to the + * value used by SATA + * @SRDSCR3_KPHA_SATA: Bitmask to set the KPHA field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KPHE_SATA: Bitmask to set the KPHE field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_KPH_SATA: Combined bitmask to set both KPH fields to the + * value used by SATA + * @SRDSCR3_SDFMA_SATA_PEX: Bitmask to set the SDFMA field of SRDSCR3 to the + * value used by SATA and PCI Express + * @SRDSCR3_SDFME_SATA_PEX: Bitmask to set the SDFME field of SRDSCR3 to the + * value used by SATA and PCI Express + * @SRDSCR3_SDFM_SATA_PEX: Combined bitmask to set both SDFM fields to the + * value used by SATA and PCI Express + * @SRDSCR3_SDTXLA_SATA: Bitmask to set the SDTXLA field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_SDTXLE_SATA: Bitmask to set the SDTXLE field of SRDSCR3 to the + * value used by SATA + * @SRDSCR3_SDTXL_SATA: Combined bitmask to set both SDTXL fields to the + * value used by SATA + * + * KFRA = 'Kfr' gain selection in the CDR for lane A + * KFRE = 'Kfr' gain selection in the CDR for lane E + * SDFMA = Bandwidth of digital filter for lane A + * SDFME = Bandwidth of digital filter for lane E + * SDTXLA = Lane A transmitter amplitude levels + * SDTXLE = Lane E transmitter amplitude levels + */ +enum srdscr3_mask { + SRDSCR3_KFRA_SATA = 0x10000000, + SRDSCR3_KFRE_SATA = 0x00100000, + SRDSCR3_KFR_SATA = SRDSCR3_KFRA_SATA | SRDSCR3_KFRE_SATA, + + SRDSCR3_KPHA_SATA = 0x04000000, + SRDSCR3_KPHE_SATA = 0x00040000, + SRDSCR3_KPH_SATA = SRDSCR3_KPHA_SATA | SRDSCR3_KPHE_SATA, + + SRDSCR3_SDFMA_SATA_PEX = 0x01000000, + SRDSCR3_SDFME_SATA_PEX = 0x00010000, + SRDSCR3_SDFM_SATA_PEX = SRDSCR3_SDFMA_SATA_PEX | SRDSCR3_SDFME_SATA_PEX, + + SRDSCR3_SDTXLA_SATA = 0x00000500, + SRDSCR3_SDTXLE_SATA = 0x00000005, + SRDSCR3_SDTXL_SATA = SRDSCR3_SDTXLA_SATA | SRDSCR3_SDTXLE_SATA, +}; + +/** + * enum srdscr4_mask - Bit masks for SRDSCR4 (SerDes Control Register 4) + * @SRDSCR4_PROTA_SATA: Bitmask to set the PROTA field of SRDSCR4 to the + * value used by SATA + * @SRDSCR4_PROTE_SATA: Bitmask to set the PROTE field of SRDSCR4 to the + * value used by SATA + * @SRDSCR4_PROT_SATA: Combined bitmask to set both PROT fields to the + * value used by SATA + * @SRDSCR4_PROTA_PEX: Bitmask to set the PROTA field of SRDSCR4 to the + * value used by PCI Express + * @SRDSCR4_PROTE_PEX: Bitmask to set the PROTE field of SRDSCR4 to the + * value used by PCI Express + * @SRDSCR4_PROT_PEX: Combined bitmask to set both PROT fields to the + * value used by PCI Express + * @SRDSCR4_PROTA_SGMII: Bitmask to set the PROTA field of SRDSCR4 to the + * value used by SGMII + * @SRDSCR4_PROTE_SGMII: Bitmask to set the PROTE field of SRDSCR4 to the + * value used by SGMII + * @SRDSCR4_PROT_SGMII: Combined bitmask to set both PROT fields to the + * value used by SGMII + * @SRDSCR4_PLANE_X2: Bitmask to set the PLANE field of SRDSCR4 + * @SRDSCR4_RFCKS_100: Bitmask to set the RFCKS field of SRDSCR4 to the + * value 100Mhz + * @SRDSCR4_RFCKS_125: Bitmask to set the RFCKS field of SRDSCR4 to the + * value 125Mhz + * @SRDSCR4_RFCKS_150: Bitmask to set the RFCKS field of SRDSCR4 to the + * value 150Mhz + * + * PROTA = Lane A protocol select + * PROTE = Lane E protocol select + * PLAME = Number of PCI Express lanes + */ +enum srdscr4_mask { + SRDSCR4_PROTA_SATA = 0x00000800, + SRDSCR4_PROTE_SATA = 0x00000008, + SRDSCR4_PROT_SATA = SRDSCR4_PROTA_SATA | SRDSCR4_PROTE_SATA, + + SRDSCR4_PROTA_PEX = 0x00000100, + SRDSCR4_PROTE_PEX = 0x00000001, + SRDSCR4_PROT_PEX = SRDSCR4_PROTA_PEX | SRDSCR4_PROTE_PEX, + + SRDSCR4_PROTA_SGMII = 0x00000500, + SRDSCR4_PROTE_SGMII = 0x00000005, + SRDSCR4_PROT_SGMII = SRDSCR4_PROTA_SGMII | SRDSCR4_PROTE_SGMII, + + SRDSCR4_PLANE_X2 = 0x01000000, + + SRDSCR4_RFCKS_100 = (0 << 28), + SRDSCR4_RFCKS_125 = (1 << 28), + SRDSCR4_RFCKS_150 = (3 << 28), +}; + +/** + * enum srdsrstctl_mask - Bit masks for SRDSRSTCTL (SerDes Reset Control Register) + * @SRDSRSTCTL_RST: Bitmask for the RST (Software reset) field of the + * SRDSRSTCTL + * @SRDSRSTCTL_SATA_RESET: Bitmask for the SATA_RESET (SATA reset) field of the + * SRDSRSTCTL + */ +enum srdsrstctl_mask { + SRDSRSTCTL_RST = 0x80000000, + SRDSRSTCTL_SATA_RESET = 0xf, +}; + +/** + * struct mpc83xx_serdes_regs - Register map of the SerDes controller + * @srdscr0: SerDes Control Register 0 + * @srdscr1: SerDes Control Register 1 + * @srdscr2: SerDes Control Register 2 + * @srdscr3: SerDes Control Register 3 + * @srdscr4: SerDes Control Register 4 + * @fill0: Reserved space in the register map + * @srdsrstctl: SerDes Reset Control Register + */ +struct mpc83xx_serdes_regs { + u32 srdscr0; + u32 srdscr1; + u32 srdscr2; + u32 srdscr3; + u32 srdscr4; + u8 fill0[12]; + u32 srdsrstctl; +}; + +/** + * enum pex_type - Types of PCI Express + * @PEX_X1: PCI Express in x1 mode + * @PEX_X2: PCI Express in x2 mode + */ +enum pex_type { + PEX_X1, + PEX_X2, +}; -- cgit v1.3.1