From e7c97c57e2d5040e90662459239bc28c8ea89be5 Mon Sep 17 00:00:00 2001 From: Paolo Sabatino Date: Wed, 7 Jul 2021 19:27:03 +0000 Subject: [PATCH] rk3228/rk3328: add dmc driver --- arch/arm/boot/dts/rk322x.dtsi | 69 +- drivers/devfreq/Kconfig | 24 + drivers/devfreq/Makefile | 2 + drivers/devfreq/rk3228_dmc.c | 623 ++++++++++++++ drivers/devfreq/rk3328_dmc.c | 846 +++++++++++++++++++ include/dt-bindings/clock/rockchip-ddr.h | 63 ++ include/dt-bindings/memory/rockchip,rk322x.h | 90 ++ 7 files changed, 1714 insertions(+), 3 deletions(-) create mode 100644 drivers/devfreq/rk3228_dmc.c create mode 100644 drivers/devfreq/rk3328_dmc.c create mode 100644 include/dt-bindings/clock/rockchip-ddr.h create mode 100644 include/dt-bindings/memory/rockchip,rk322x.h diff --git a/arch/arm/boot/dts/rk322x.dtsi b/arch/arm/boot/dts/rk322x.dtsi index 88e33eb11..22da2e3cd 100644 --- a/arch/arm/boot/dts/rk322x.dtsi +++ b/arch/arm/boot/dts/rk322x.dtsi @@ -7,6 +7,8 @@ #include #include #include +#include +#include / { #address-cells = <1>; @@ -106,6 +106,68 @@ dfi: dfi@11210000 { status = "okay"; }; + dmc: dmc@11200000 { + compatible = "rockchip,rk3228-dmc", "rockchip,rk322x-dram"; + reg = <0x11200000 0x400>; + clocks = <&cru SCLK_DDRCLK>; + clock-names = "ddr_sclk"; + operating-points-v2 = <&dmc_opp_table>; + rockchip,dram_timing = <&dram_timing>; + rockchip,grf = <&grf>; + devfreq-events = <&dfi>; + upthreshold = <15>; + downdifferential = <10>; + #cooling-cells = <2>; + status = "disabled"; + }; + + dmc_opp_table: dmc-opp-table { + compatible = "operating-points-v2"; + + opp-330000000 { + opp-hz = /bits/ 64 <330000000>; + opp-microvolt = <1050000 1000000 1200000>; + }; + opp-534000000 { + opp-hz = /bits/ 64 <534000000>; + opp-microvolt = <1050000 1000000 1200000>; + }; + opp-660000000 { + opp-hz = /bits/ 64 <660000000>; + opp-microvolt = <1100000 1000000 1200000>; + }; + opp-786000000 { + opp-hz = /bits/ 64 <786000000>; + opp-microvolt = <1150000 1000000 1200000>; + status = "disabled"; + }; + }; + + dram_timing: dram-timing { + compatible = "rockchip,dram-timing"; + dram_spd_bin = ; + sr_idle = <0x18>; + pd_idle = <0x20>; + dram_dll_disb_freq = <300>; + phy_dll_disb_freq = <400>; + dram_odt_disb_freq = <333>; + phy_odt_disb_freq = <333>; + ddr3_drv = ; + ddr3_odt = ; + lpddr3_drv = ; + lpddr3_odt = ; + lpddr2_drv = ; + /* lpddr2 not supported odt */ + phy_ddr3_clk_drv = ; + phy_ddr3_cmd_drv = ; + phy_ddr3_dqs_drv = ; + phy_ddr3_odt = ; + phy_lp23_clk_drv = ; + phy_lp23_cmd_drv = ; + phy_lp23_dqs_drv = ; + phy_lp3_odt = ; + }; + arm-pmu { compatible = "arm,cortex-a7-pmu"; interrupts = , @@ -673,17 +736,17 @@ gpu_opp_table: opp-table2 { opp-200000000 { opp-hz = /bits/ 64 <200000000>; - opp-microvolt = <1050000>; + opp-microvolt = <1050000 1000000 1200000>; }; opp-300000000 { opp-hz = /bits/ 64 <300000000>; - opp-microvolt = <1050000>; + opp-microvolt = <1050000 1000000 1200000>; }; opp-500000000 { opp-hz = /bits/ 64 <500000000>; - opp-microvolt = <1150000>; + opp-microvolt = <1150000 1000000 1200000>; }; }; diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 37dc40d1f..5f864a855 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -131,6 +131,30 @@ config ARM_TEGRA20_DEVFREQ It reads Memory Controller counters and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3328_DMC_DEVFREQ + tristate "ARM RK3328 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_DEVFREQ_EVENT + select PM_OPP + help + This adds the DEVFREQ driver for the RK3328 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + +config ARM_RK3228_DMC_DEVFREQ + tristate "ARM RK3228 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_DEVFREQ_EVENT + select PM_OPP + help + This adds the DEVFREQ driver for the RK3228 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + config ARM_RK3399_DMC_DEVFREQ tristate "ARM RK3399 DMC DEVFREQ Driver" depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 0b6be92a25d..ce80de1b19b 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -12,6 +12,8 @@ obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o +obj-$(CONFIG_ARM_RK3328_DMC_DEVFREQ) += rk3328_dmc.o +obj-$(CONFIG_ARM_RK3228_DMC_DEVFREQ) += rk3228_dmc.o obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o diff --git a/drivers/devfreq/rk3328_dmc.c b/drivers/devfreq/rk3328_dmc.c new file mode 100644 index 000000000..5dcea91cf --- /dev/null +++ b/drivers/devfreq/rk3328_dmc.c @@ -0,0 +1,846 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DTS_PAR_OFFSET (4096) + +struct share_params { + u32 hz; + u32 lcdc_type; + u32 vop; + u32 vop_dclk_mode; + u32 sr_idle_en; + u32 addr_mcu_el3; + /* + * 1: need to wait flag1 + * 0: never wait flag1 + */ + u32 wait_flag1; + /* + * 1: need to wait flag1 + * 0: never wait flag1 + */ + u32 wait_flag0; + u32 complt_hwirq; + /* if need, add parameter after */ +}; + +static struct share_params *ddr_psci_param; + +/* hope this define can adapt all future platform */ +static const char * const rk3328_dts_timing[] = { + "ddr3_speed_bin", + "ddr4_speed_bin", + "pd_idle", + "sr_idle", + "sr_mc_gate_idle", + "srpd_lite_idle", + "standby_idle", + + "auto_pd_dis_freq", + "auto_sr_dis_freq", + "ddr3_dll_dis_freq", + "ddr4_dll_dis_freq", + "phy_dll_dis_freq", + + "ddr3_odt_dis_freq", + "phy_ddr3_odt_dis_freq", + "ddr3_drv", + "ddr3_odt", + "phy_ddr3_ca_drv", + "phy_ddr3_ck_drv", + "phy_ddr3_dq_drv", + "phy_ddr3_odt", + + "lpddr3_odt_dis_freq", + "phy_lpddr3_odt_dis_freq", + "lpddr3_drv", + "lpddr3_odt", + "phy_lpddr3_ca_drv", + "phy_lpddr3_ck_drv", + "phy_lpddr3_dq_drv", + "phy_lpddr3_odt", + + "lpddr4_odt_dis_freq", + "phy_lpddr4_odt_dis_freq", + "lpddr4_drv", + "lpddr4_dq_odt", + "lpddr4_ca_odt", + "phy_lpddr4_ca_drv", + "phy_lpddr4_ck_cs_drv", + "phy_lpddr4_dq_drv", + "phy_lpddr4_odt", + + "ddr4_odt_dis_freq", + "phy_ddr4_odt_dis_freq", + "ddr4_drv", + "ddr4_odt", + "phy_ddr4_ca_drv", + "phy_ddr4_ck_drv", + "phy_ddr4_dq_drv", + "phy_ddr4_odt", +}; + +static const char * const rk3328_dts_ca_timing[] = { + "ddr3a1_ddr4a9_de-skew", + "ddr3a0_ddr4a10_de-skew", + "ddr3a3_ddr4a6_de-skew", + "ddr3a2_ddr4a4_de-skew", + "ddr3a5_ddr4a8_de-skew", + "ddr3a4_ddr4a5_de-skew", + "ddr3a7_ddr4a11_de-skew", + "ddr3a6_ddr4a7_de-skew", + "ddr3a9_ddr4a0_de-skew", + "ddr3a8_ddr4a13_de-skew", + "ddr3a11_ddr4a3_de-skew", + "ddr3a10_ddr4cs0_de-skew", + "ddr3a13_ddr4a2_de-skew", + "ddr3a12_ddr4ba1_de-skew", + "ddr3a15_ddr4odt0_de-skew", + "ddr3a14_ddr4a1_de-skew", + "ddr3ba1_ddr4a15_de-skew", + "ddr3ba0_ddr4bg0_de-skew", + "ddr3ras_ddr4cke_de-skew", + "ddr3ba2_ddr4ba0_de-skew", + "ddr3we_ddr4bg1_de-skew", + "ddr3cas_ddr4a12_de-skew", + "ddr3ckn_ddr4ckn_de-skew", + "ddr3ckp_ddr4ckp_de-skew", + "ddr3cke_ddr4a16_de-skew", + "ddr3odt0_ddr4a14_de-skew", + "ddr3cs0_ddr4act_de-skew", + "ddr3reset_ddr4reset_de-skew", + "ddr3cs1_ddr4cs1_de-skew", + "ddr3odt1_ddr4odt1_de-skew", +}; + +static const char * const rk3328_dts_cs0_timing[] = { + "cs0_dm0_rx_de-skew", + "cs0_dm0_tx_de-skew", + "cs0_dq0_rx_de-skew", + "cs0_dq0_tx_de-skew", + "cs0_dq1_rx_de-skew", + "cs0_dq1_tx_de-skew", + "cs0_dq2_rx_de-skew", + "cs0_dq2_tx_de-skew", + "cs0_dq3_rx_de-skew", + "cs0_dq3_tx_de-skew", + "cs0_dq4_rx_de-skew", + "cs0_dq4_tx_de-skew", + "cs0_dq5_rx_de-skew", + "cs0_dq5_tx_de-skew", + "cs0_dq6_rx_de-skew", + "cs0_dq6_tx_de-skew", + "cs0_dq7_rx_de-skew", + "cs0_dq7_tx_de-skew", + "cs0_dqs0_rx_de-skew", + "cs0_dqs0p_tx_de-skew", + "cs0_dqs0n_tx_de-skew", + + "cs0_dm1_rx_de-skew", + "cs0_dm1_tx_de-skew", + "cs0_dq8_rx_de-skew", + "cs0_dq8_tx_de-skew", + "cs0_dq9_rx_de-skew", + "cs0_dq9_tx_de-skew", + "cs0_dq10_rx_de-skew", + "cs0_dq10_tx_de-skew", + "cs0_dq11_rx_de-skew", + "cs0_dq11_tx_de-skew", + "cs0_dq12_rx_de-skew", + "cs0_dq12_tx_de-skew", + "cs0_dq13_rx_de-skew", + "cs0_dq13_tx_de-skew", + "cs0_dq14_rx_de-skew", + "cs0_dq14_tx_de-skew", + "cs0_dq15_rx_de-skew", + "cs0_dq15_tx_de-skew", + "cs0_dqs1_rx_de-skew", + "cs0_dqs1p_tx_de-skew", + "cs0_dqs1n_tx_de-skew", + + "cs0_dm2_rx_de-skew", + "cs0_dm2_tx_de-skew", + "cs0_dq16_rx_de-skew", + "cs0_dq16_tx_de-skew", + "cs0_dq17_rx_de-skew", + "cs0_dq17_tx_de-skew", + "cs0_dq18_rx_de-skew", + "cs0_dq18_tx_de-skew", + "cs0_dq19_rx_de-skew", + "cs0_dq19_tx_de-skew", + "cs0_dq20_rx_de-skew", + "cs0_dq20_tx_de-skew", + "cs0_dq21_rx_de-skew", + "cs0_dq21_tx_de-skew", + "cs0_dq22_rx_de-skew", + "cs0_dq22_tx_de-skew", + "cs0_dq23_rx_de-skew", + "cs0_dq23_tx_de-skew", + "cs0_dqs2_rx_de-skew", + "cs0_dqs2p_tx_de-skew", + "cs0_dqs2n_tx_de-skew", + + "cs0_dm3_rx_de-skew", + "cs0_dm3_tx_de-skew", + "cs0_dq24_rx_de-skew", + "cs0_dq24_tx_de-skew", + "cs0_dq25_rx_de-skew", + "cs0_dq25_tx_de-skew", + "cs0_dq26_rx_de-skew", + "cs0_dq26_tx_de-skew", + "cs0_dq27_rx_de-skew", + "cs0_dq27_tx_de-skew", + "cs0_dq28_rx_de-skew", + "cs0_dq28_tx_de-skew", + "cs0_dq29_rx_de-skew", + "cs0_dq29_tx_de-skew", + "cs0_dq30_rx_de-skew", + "cs0_dq30_tx_de-skew", + "cs0_dq31_rx_de-skew", + "cs0_dq31_tx_de-skew", + "cs0_dqs3_rx_de-skew", + "cs0_dqs3p_tx_de-skew", + "cs0_dqs3n_tx_de-skew", +}; + +static const char * const rk3328_dts_cs1_timing[] = { + "cs1_dm0_rx_de-skew", + "cs1_dm0_tx_de-skew", + "cs1_dq0_rx_de-skew", + "cs1_dq0_tx_de-skew", + "cs1_dq1_rx_de-skew", + "cs1_dq1_tx_de-skew", + "cs1_dq2_rx_de-skew", + "cs1_dq2_tx_de-skew", + "cs1_dq3_rx_de-skew", + "cs1_dq3_tx_de-skew", + "cs1_dq4_rx_de-skew", + "cs1_dq4_tx_de-skew", + "cs1_dq5_rx_de-skew", + "cs1_dq5_tx_de-skew", + "cs1_dq6_rx_de-skew", + "cs1_dq6_tx_de-skew", + "cs1_dq7_rx_de-skew", + "cs1_dq7_tx_de-skew", + "cs1_dqs0_rx_de-skew", + "cs1_dqs0p_tx_de-skew", + "cs1_dqs0n_tx_de-skew", + + "cs1_dm1_rx_de-skew", + "cs1_dm1_tx_de-skew", + "cs1_dq8_rx_de-skew", + "cs1_dq8_tx_de-skew", + "cs1_dq9_rx_de-skew", + "cs1_dq9_tx_de-skew", + "cs1_dq10_rx_de-skew", + "cs1_dq10_tx_de-skew", + "cs1_dq11_rx_de-skew", + "cs1_dq11_tx_de-skew", + "cs1_dq12_rx_de-skew", + "cs1_dq12_tx_de-skew", + "cs1_dq13_rx_de-skew", + "cs1_dq13_tx_de-skew", + "cs1_dq14_rx_de-skew", + "cs1_dq14_tx_de-skew", + "cs1_dq15_rx_de-skew", + "cs1_dq15_tx_de-skew", + "cs1_dqs1_rx_de-skew", + "cs1_dqs1p_tx_de-skew", + "cs1_dqs1n_tx_de-skew", + + "cs1_dm2_rx_de-skew", + "cs1_dm2_tx_de-skew", + "cs1_dq16_rx_de-skew", + "cs1_dq16_tx_de-skew", + "cs1_dq17_rx_de-skew", + "cs1_dq17_tx_de-skew", + "cs1_dq18_rx_de-skew", + "cs1_dq18_tx_de-skew", + "cs1_dq19_rx_de-skew", + "cs1_dq19_tx_de-skew", + "cs1_dq20_rx_de-skew", + "cs1_dq20_tx_de-skew", + "cs1_dq21_rx_de-skew", + "cs1_dq21_tx_de-skew", + "cs1_dq22_rx_de-skew", + "cs1_dq22_tx_de-skew", + "cs1_dq23_rx_de-skew", + "cs1_dq23_tx_de-skew", + "cs1_dqs2_rx_de-skew", + "cs1_dqs2p_tx_de-skew", + "cs1_dqs2n_tx_de-skew", + + "cs1_dm3_rx_de-skew", + "cs1_dm3_tx_de-skew", + "cs1_dq24_rx_de-skew", + "cs1_dq24_tx_de-skew", + "cs1_dq25_rx_de-skew", + "cs1_dq25_tx_de-skew", + "cs1_dq26_rx_de-skew", + "cs1_dq26_tx_de-skew", + "cs1_dq27_rx_de-skew", + "cs1_dq27_tx_de-skew", + "cs1_dq28_rx_de-skew", + "cs1_dq28_tx_de-skew", + "cs1_dq29_rx_de-skew", + "cs1_dq29_tx_de-skew", + "cs1_dq30_rx_de-skew", + "cs1_dq30_tx_de-skew", + "cs1_dq31_rx_de-skew", + "cs1_dq31_tx_de-skew", + "cs1_dqs3_rx_de-skew", + "cs1_dqs3p_tx_de-skew", + "cs1_dqs3n_tx_de-skew", +}; + +struct rk3328_ddr_dts_config_timing { + unsigned int ddr3_speed_bin; + unsigned int ddr4_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + + unsigned int auto_pd_dis_freq; + unsigned int auto_sr_dis_freq; + /* for ddr3 only */ + unsigned int ddr3_dll_dis_freq; + /* for ddr4 only */ + unsigned int ddr4_dll_dis_freq; + unsigned int phy_dll_dis_freq; + + unsigned int ddr3_odt_dis_freq; + unsigned int phy_ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_ck_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + + unsigned int lpddr3_odt_dis_freq; + unsigned int phy_lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_ck_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + + unsigned int lpddr4_odt_dis_freq; + unsigned int phy_lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; + + unsigned int ddr4_odt_dis_freq; + unsigned int phy_ddr4_odt_dis_freq; + unsigned int ddr4_drv; + unsigned int ddr4_odt; + unsigned int phy_ddr4_ca_drv; + unsigned int phy_ddr4_ck_drv; + unsigned int phy_ddr4_dq_drv; + unsigned int phy_ddr4_odt; + + unsigned int ca_skew[15]; + unsigned int cs0_skew[44]; + unsigned int cs1_skew[44]; + + unsigned int available; +}; + +struct rk3328_ddr_de_skew_setting { + unsigned int ca_de_skew[30]; + unsigned int cs0_de_skew[84]; + unsigned int cs1_de_skew[84]; +}; + +struct rk3328_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct regulator *vdd_center; + unsigned long rate, target_rate; + unsigned long volt, target_volt; + + int (*set_auto_self_refresh)(u32 en); +}; + +static void +rk3328_de_skew_setting_2_register(struct rk3328_ddr_de_skew_setting *de_skew, + struct rk3328_ddr_dts_config_timing *tim) +{ + u32 n; + u32 offset; + u32 shift; + + memset_io(tim->ca_skew, 0, sizeof(tim->ca_skew)); + memset_io(tim->cs0_skew, 0, sizeof(tim->cs0_skew)); + memset_io(tim->cs1_skew, 0, sizeof(tim->cs1_skew)); + + /* CA de-skew */ + for (n = 0; n < ARRAY_SIZE(de_skew->ca_de_skew); n++) { + offset = n / 2; + shift = n % 2; + /* 0 => 4; 1 => 0 */ + shift = (shift == 0) ? 4 : 0; + tim->ca_skew[offset] &= ~(0xf << shift); + tim->ca_skew[offset] |= (de_skew->ca_de_skew[n] << shift); + } + + /* CS0 data de-skew */ + for (n = 0; n < ARRAY_SIZE(de_skew->cs0_de_skew); n++) { + offset = ((n / 21) * 11) + ((n % 21) / 2); + shift = ((n % 21) % 2); + if ((n % 21) == 20) + shift = 0; + else + /* 0 => 4; 1 => 0 */ + shift = (shift == 0) ? 4 : 0; + tim->cs0_skew[offset] &= ~(0xf << shift); + tim->cs0_skew[offset] |= (de_skew->cs0_de_skew[n] << shift); + } + + /* CS1 data de-skew */ + for (n = 0; n < ARRAY_SIZE(de_skew->cs1_de_skew); n++) { + offset = ((n / 21) * 11) + ((n % 21) / 2); + shift = ((n % 21) % 2); + if ((n % 21) == 20) + shift = 0; + else + /* 0 => 4; 1 => 0 */ + shift = (shift == 0) ? 4 : 0; + tim->cs1_skew[offset] &= ~(0xf << shift); + tim->cs1_skew[offset] |= (de_skew->cs1_de_skew[n] << shift); + } +} + +static void of_get_rk3328_timings(struct device *dev, + struct device_node *np, uint32_t *timing) +{ + struct device_node *np_tim; + u32 *p; + struct rk3328_ddr_dts_config_timing *dts_timing; + struct rk3328_ddr_de_skew_setting *de_skew; + int ret = 0; + u32 i; + + dts_timing = + (struct rk3328_ddr_dts_config_timing *)(timing + + DTS_PAR_OFFSET / 4); + + np_tim = of_parse_phandle(np, "ddr_timing", 0); + if (!np_tim) { + ret = -EINVAL; + goto end; + } + de_skew = kmalloc(sizeof(*de_skew), GFP_KERNEL); + if (!de_skew) { + ret = -ENOMEM; + goto end; + } + + p = (u32 *)dts_timing; + for (i = 0; i < ARRAY_SIZE(rk3328_dts_timing); i++) { + ret |= of_property_read_u32(np_tim, rk3328_dts_timing[i], + p + i); + } + p = (u32 *)de_skew->ca_de_skew; + for (i = 0; i < ARRAY_SIZE(rk3328_dts_ca_timing); i++) { + ret |= of_property_read_u32(np_tim, rk3328_dts_ca_timing[i], + p + i); + } + p = (u32 *)de_skew->cs0_de_skew; + for (i = 0; i < ARRAY_SIZE(rk3328_dts_cs0_timing); i++) { + ret |= of_property_read_u32(np_tim, rk3328_dts_cs0_timing[i], + p + i); + } + p = (u32 *)de_skew->cs1_de_skew; + for (i = 0; i < ARRAY_SIZE(rk3328_dts_cs1_timing); i++) { + ret |= of_property_read_u32(np_tim, rk3328_dts_cs1_timing[i], + p + i); + } + if (!ret) + rk3328_de_skew_setting_2_register(de_skew, dts_timing); + + kfree(de_skew); +end: + if (!ret) { + dts_timing->available = 1; + } else { + dts_timing->available = 0; + dev_err(dev, "of_get_ddr_timings: fail\n"); + } + + of_node_put(np_tim); +} + +static int rockchip_ddr_set_auto_self_refresh(uint32_t en) +{ + struct arm_smccc_res res; + + ddr_psci_param->sr_idle_en = en; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, + SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_SET_AT_SR, + 0, 0, 0, 0, &res); + + return res.a0; +} + +static int rk3328_dmc_init(struct platform_device *pdev, + struct rk3328_dmcfreq *dmcfreq) +{ + struct arm_smccc_res res; + u32 size, page_num; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, + 0, 0, ROCKCHIP_SIP_CONFIG_DRAM_GET_VERSION, + 0, 0, 0, 0, &res); + if (res.a0 || (res.a1 < 0x101)) { + dev_err(&pdev->dev, + "trusted firmware need to update or is invalid\n"); + return -ENXIO; + } + + dev_notice(&pdev->dev, "current ATF version 0x%lx\n", res.a1); + + /* + * first 4KB is used for interface parameters + * after 4KB * N is dts parameters + */ + size = sizeof(struct rk3328_ddr_dts_config_timing); + page_num = DIV_ROUND_UP(size, 4096) + 1; + + arm_smccc_smc(ROCKCHIP_SIP_SHARE_MEM, + page_num, SHARE_PAGE_TYPE_DDR, 0, + 0, 0, 0, 0, &res); + if (res.a0 != 0) { + dev_err(&pdev->dev, "no ATF memory for init\n"); + return -ENOMEM; + } + + ddr_psci_param = ioremap(res.a1, page_num << 12); + of_get_rk3328_timings(&pdev->dev, pdev->dev.of_node, + (uint32_t *)ddr_psci_param); + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, + SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_INIT, + 0, 0, 0, 0, &res); + if (res.a0) { + dev_err(&pdev->dev, "Rockchip dram init error %lx\n", res.a0); + return -ENOMEM; + } + + dmcfreq->set_auto_self_refresh = rockchip_ddr_set_auto_self_refresh; + + return 0; +} + +static int rk3328_dmcfreq_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct rk3328_dmcfreq *dmcfreq = dev_get_drvdata(dev); + struct dev_pm_opp *opp; + unsigned long old_clk_rate = dmcfreq->rate; + unsigned long target_volt, target_rate; + int err; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + target_volt = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); + + if (dmcfreq->rate == target_rate) + return 0; + + mutex_lock(&dmcfreq->lock); + + /* + * If frequency scaling from low to high, adjust voltage first. + * If frequency scaling from high to low, adjust frequency first. + */ + if (old_clk_rate < target_rate) { + err = regulator_set_voltage(dmcfreq->vdd_center, target_volt, + target_volt); + if (err) { + dev_err(dev, "Cannot set voltage %lu uV\n", + target_volt); + goto out; + } + } + + err = clk_set_rate(dmcfreq->dmc_clk, target_rate); + if (err) { + dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, + err); + regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt, + dmcfreq->volt); + goto out; + } + + /* + * Check the dpll rate, + * There only two result we will get, + * 1. Ddr frequency scaling fail, we still get the old rate. + * 2. Ddr frequency scaling sucessful, we get the rate we set. + */ + dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk); + + /* If get the incorrect rate, set voltage to old value. */ + if (dmcfreq->rate != target_rate) { + dev_err(dev, "Got wrong frequency, Request %lu, Current %lu\n", + target_rate, dmcfreq->rate); + regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt, + dmcfreq->volt); + goto out; + } else if (old_clk_rate > target_rate) + err = regulator_set_voltage(dmcfreq->vdd_center, target_volt, + target_volt); + if (err) + dev_err(dev, "Cannot set voltage %lu uV\n", target_volt); + + dmcfreq->rate = target_rate; + dmcfreq->volt = target_volt; + +out: + mutex_unlock(&dmcfreq->lock); + return err; +} + +static int rk3328_dmcfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct rk3328_dmcfreq *dmcfreq = dev_get_drvdata(dev); + struct devfreq_event_data edata; + int ret = 0; + + ret = devfreq_event_get_event(dmcfreq->edev, &edata); + if (ret < 0) + return ret; + + stat->current_frequency = dmcfreq->rate; + stat->busy_time = edata.load_count; + stat->total_time = edata.total_count; + + return ret; +} + +static int rk3328_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct rk3328_dmcfreq *dmcfreq = dev_get_drvdata(dev); + + *freq = dmcfreq->rate; + + return 0; +} + +static struct devfreq_dev_profile rk3328_devfreq_dmc_profile = { + .polling_ms = 200, + .target = rk3328_dmcfreq_target, + .get_dev_status = rk3328_dmcfreq_get_dev_status, + .get_cur_freq = rk3328_dmcfreq_get_cur_freq, +}; + +static __maybe_unused int rk3328_dmcfreq_suspend(struct device *dev) +{ + struct rk3328_dmcfreq *dmcfreq = dev_get_drvdata(dev); + int ret = 0; + + ret = devfreq_event_disable_edev(dmcfreq->edev); + if (ret < 0) { + dev_err(dev, "failed to disable the devfreq-event devices\n"); + return ret; + } + + ret = devfreq_suspend_device(dmcfreq->devfreq); + if (ret < 0) { + dev_err(dev, "failed to suspend the devfreq devices\n"); + return ret; + } + + return 0; +} + +static __maybe_unused int rk3328_dmcfreq_resume(struct device *dev) +{ + struct rk3328_dmcfreq *dmcfreq = dev_get_drvdata(dev); + int ret = 0; + + ret = devfreq_event_enable_edev(dmcfreq->edev); + if (ret < 0) { + dev_err(dev, "failed to enable the devfreq-event devices\n"); + return ret; + } + + ret = devfreq_resume_device(dmcfreq->devfreq); + if (ret < 0) { + dev_err(dev, "failed to resume the devfreq devices\n"); + return ret; + } + return ret; +} + +static SIMPLE_DEV_PM_OPS(rk3328_dmcfreq_pm, rk3328_dmcfreq_suspend, + rk3328_dmcfreq_resume); + +static int rk3328_dmcfreq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct rk3328_dmcfreq *data; + struct dev_pm_opp *opp; + int ret; + + data = devm_kzalloc(dev, sizeof(struct rk3328_dmcfreq), GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->lock); + + data->vdd_center = devm_regulator_get(dev, "center"); + if (IS_ERR(data->vdd_center)) { + if (PTR_ERR(data->vdd_center) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(dev, "Cannot get the regulator \"center\"\n"); + return PTR_ERR(data->vdd_center); + } + + data->dmc_clk = devm_clk_get(dev, "dmc_clk"); + if (IS_ERR(data->dmc_clk)) { + if (PTR_ERR(data->dmc_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(dev, "Cannot get the clk dmc_clk\n"); + return PTR_ERR(data->dmc_clk); + } + + data->edev = devfreq_event_get_edev_by_phandle(dev, "devfreq-events", 0); + if (IS_ERR(data->edev)) + return -EPROBE_DEFER; + + ret = devfreq_event_enable_edev(data->edev); + if (ret < 0) { + dev_err(dev, "failed to enable devfreq-event devices\n"); + return ret; + } + + ret = rk3328_dmc_init(pdev, data); + if (ret) + return ret; + + /* + * We add a devfreq driver to our parent since it has a device tree node + * with operating points. + */ + if (dev_pm_opp_of_add_table(dev)) { + dev_err(dev, "Invalid operating-points in device tree.\n"); + return -EINVAL; + } + + of_property_read_u32(np, "upthreshold", + &data->ondemand_data.upthreshold); + of_property_read_u32(np, "downdifferential", + &data->ondemand_data.downdifferential); + + data->rate = clk_get_rate(data->dmc_clk); + + opp = devfreq_recommended_opp(dev, &data->rate, 0); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto err_free_opp; + } + + data->rate = dev_pm_opp_get_freq(opp); + data->volt = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); + + rk3328_devfreq_dmc_profile.initial_freq = data->rate; + + data->devfreq = devm_devfreq_add_device(dev, + &rk3328_devfreq_dmc_profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, + &data->ondemand_data); + if (IS_ERR(data->devfreq)) { + ret = PTR_ERR(data->devfreq); + goto err_free_opp; + } + + devm_devfreq_register_opp_notifier(dev, data->devfreq); + + data->dev = dev; + platform_set_drvdata(pdev, data); + + return 0; + +err_free_opp: + dev_pm_opp_of_remove_table(&pdev->dev); + return ret; +} + +static int rk3328_dmcfreq_remove(struct platform_device *pdev) +{ + struct rk3328_dmcfreq *dmcfreq = dev_get_drvdata(&pdev->dev); + + /* + * Before remove the opp table we need to unregister the opp notifier. + */ + devm_devfreq_unregister_opp_notifier(dmcfreq->dev, dmcfreq->devfreq); + dev_pm_opp_of_remove_table(dmcfreq->dev); + + return 0; +} + +static const struct of_device_id rk3328dmc_devfreq_of_match[] = { + { .compatible = "rockchip,rk3328-dmc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rk3328dmc_devfreq_of_match); + +static struct platform_driver rk3328_dmcfreq_driver = { + .probe = rk3328_dmcfreq_probe, + .remove = rk3328_dmcfreq_remove, + .driver = { + .name = "rk3328-dmc-freq", + .pm = &rk3328_dmcfreq_pm, + .of_match_table = rk3328dmc_devfreq_of_match, + }, +}; +module_platform_driver(rk3328_dmcfreq_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lin Huang "); +MODULE_DESCRIPTION("RK3328 dmcfreq driver with devfreq framework"); diff --git a/include/dt-bindings/clock/rockchip-ddr.h b/include/dt-bindings/clock/rockchip-ddr.h new file mode 100644 index 000000000..b065432e7 --- /dev/null +++ b/include/dt-bindings/clock/rockchip-ddr.h @@ -0,0 +1,63 @@ +/* + * + * Copyright (C) 2017 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _DT_BINDINGS_CLOCK_ROCKCHIP_DDR_H +#define _DT_BINDINGS_CLOCK_ROCKCHIP_DDR_H + +#define DDR2_DEFAULT (0) + +#define DDR3_800D (0) /* 5-5-5 */ +#define DDR3_800E (1) /* 6-6-6 */ +#define DDR3_1066E (2) /* 6-6-6 */ +#define DDR3_1066F (3) /* 7-7-7 */ +#define DDR3_1066G (4) /* 8-8-8 */ +#define DDR3_1333F (5) /* 7-7-7 */ +#define DDR3_1333G (6) /* 8-8-8 */ +#define DDR3_1333H (7) /* 9-9-9 */ +#define DDR3_1333J (8) /* 10-10-10 */ +#define DDR3_1600G (9) /* 8-8-8 */ +#define DDR3_1600H (10) /* 9-9-9 */ +#define DDR3_1600J (11) /* 10-10-10 */ +#define DDR3_1600K (12) /* 11-11-11 */ +#define DDR3_1866J (13) /* 10-10-10 */ +#define DDR3_1866K (14) /* 11-11-11 */ +#define DDR3_1866L (15) /* 12-12-12 */ +#define DDR3_1866M (16) /* 13-13-13 */ +#define DDR3_2133K (17) /* 11-11-11 */ +#define DDR3_2133L (18) /* 12-12-12 */ +#define DDR3_2133M (19) /* 13-13-13 */ +#define DDR3_2133N (20) /* 14-14-14 */ +#define DDR3_DEFAULT (21) +#define DDR_DDR2 (22) +#define DDR_LPDDR (23) +#define DDR_LPDDR2 (24) + +#define DDR4_1600J (0) /* 10-10-10 */ +#define DDR4_1600K (1) /* 11-11-11 */ +#define DDR4_1600L (2) /* 12-12-12 */ +#define DDR4_1866L (3) /* 12-12-12 */ +#define DDR4_1866M (4) /* 13-13-13 */ +#define DDR4_1866N (5) /* 14-14-14 */ +#define DDR4_2133N (6) /* 14-14-14 */ +#define DDR4_2133P (7) /* 15-15-15 */ +#define DDR4_2133R (8) /* 16-16-16 */ +#define DDR4_2400P (9) /* 15-15-15 */ +#define DDR4_2400R (10) /* 16-16-16 */ +#define DDR4_2400U (11) /* 18-18-18 */ +#define DDR4_DEFAULT (12) + +#define PAUSE_CPU_STACK_SIZE 16 + +#endif diff --git a/include/dt-bindings/memory/rockchip,rk322x.h b/include/dt-bindings/memory/rockchip,rk322x.h new file mode 100644 index 000000000..1ab3317d7 --- /dev/null +++ b/include/dt-bindings/memory/rockchip,rk322x.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DT_BINDINGS_DRAM_ROCKCHIP_RK322X_H +#define _DT_BINDINGS_DRAM_ROCKCHIP_RK322X_H + +#define DDR3_DS_34ohm (1 << 1) +#define DDR3_DS_40ohm (0x0) + +#define LP2_DS_34ohm (0x1) +#define LP2_DS_40ohm (0x2) +#define LP2_DS_48ohm (0x3) +#define LP2_DS_60ohm (0x4) +#define LP2_DS_68_6ohm (0x5)/* optional */ +#define LP2_DS_80ohm (0x6) +#define LP2_DS_120ohm (0x7)/* optional */ + +#define LP3_DS_34ohm (0x1) +#define LP3_DS_40ohm (0x2) +#define LP3_DS_48ohm (0x3) +#define LP3_DS_60ohm (0x4) +#define LP3_DS_80ohm (0x6) +#define LP3_DS_34D_40U (0x9) +#define LP3_DS_40D_48U (0xa) +#define LP3_DS_34D_48U (0xb) + +#define DDR3_ODT_DIS (0) +#define DDR3_ODT_40ohm ((1 << 2) | (1 << 6)) +#define DDR3_ODT_60ohm (1 << 2) +#define DDR3_ODT_120ohm (1 << 6) + +#define LP3_ODT_DIS (0) +#define LP3_ODT_60ohm (1) +#define LP3_ODT_120ohm (2) +#define LP3_ODT_240ohm (3) + +#define PHY_DDR3_RON_RTT_DISABLE (0) +#define PHY_DDR3_RON_RTT_451ohm (1) +#define PHY_DDR3_RON_RTT_225ohm (2) +#define PHY_DDR3_RON_RTT_150ohm (3) +#define PHY_DDR3_RON_RTT_112ohm (4) +#define PHY_DDR3_RON_RTT_90ohm (5) +#define PHY_DDR3_RON_RTT_75ohm (6) +#define PHY_DDR3_RON_RTT_64ohm (7) +#define PHY_DDR3_RON_RTT_56ohm (16) +#define PHY_DDR3_RON_RTT_50ohm (17) +#define PHY_DDR3_RON_RTT_45ohm (18) +#define PHY_DDR3_RON_RTT_41ohm (19) +#define PHY_DDR3_RON_RTT_37ohm (20) +#define PHY_DDR3_RON_RTT_34ohm (21) +#define PHY_DDR3_RON_RTT_33ohm (22) +#define PHY_DDR3_RON_RTT_30ohm (23) +#define PHY_DDR3_RON_RTT_28ohm (24) +#define PHY_DDR3_RON_RTT_26ohm (25) +#define PHY_DDR3_RON_RTT_25ohm (26) +#define PHY_DDR3_RON_RTT_23ohm (27) +#define PHY_DDR3_RON_RTT_22ohm (28) +#define PHY_DDR3_RON_RTT_21ohm (29) +#define PHY_DDR3_RON_RTT_20ohm (30) +#define PHY_DDR3_RON_RTT_19ohm (31) + +#define PHY_LP23_RON_RTT_DISABLE (0) +#define PHY_LP23_RON_RTT_480ohm (1) +#define PHY_LP23_RON_RTT_240ohm (2) +#define PHY_LP23_RON_RTT_160ohm (3) +#define PHY_LP23_RON_RTT_120ohm (4) +#define PHY_LP23_RON_RTT_96ohm (5) +#define PHY_LP23_RON_RTT_80ohm (6) +#define PHY_LP23_RON_RTT_68ohm (7) +#define PHY_LP23_RON_RTT_60ohm (16) +#define PHY_LP23_RON_RTT_53ohm (17) +#define PHY_LP23_RON_RTT_48ohm (18) +#define PHY_LP23_RON_RTT_43ohm (19) +#define PHY_LP23_RON_RTT_40ohm (20) +#define PHY_LP23_RON_RTT_37ohm (21) +#define PHY_LP23_RON_RTT_34ohm (22) +#define PHY_LP23_RON_RTT_32ohm (23) +#define PHY_LP23_RON_RTT_30ohm (24) +#define PHY_LP23_RON_RTT_28ohm (25) +#define PHY_LP23_RON_RTT_26ohm (26) +#define PHY_LP23_RON_RTT_25ohm (27) +#define PHY_LP23_RON_RTT_24ohm (28) +#define PHY_LP23_RON_RTT_22ohm (29) +#define PHY_LP23_RON_RTT_21ohm (30) +#define PHY_LP23_RON_RTT_20ohm (31) + +#endif /* _DT_BINDINGS_DRAM_ROCKCHIP_RK322X_H */ diff --git a/drivers/devfreq/rk3228_dmc.c b/drivers/devfreq/rk3228_dmc.c new file mode 100644 index 000000000..3a34cb2b5 --- /dev/null +++ b/drivers/devfreq/rk3228_dmc.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DTS_PAR_OFFSET (4096) + +#define RK3228_GRF_OS_REG2 0x5d0 +#define DDR_PCTL_MCFG 0x80 +#define DDR_PCTL_TCL 0xe8 +#define DDR_PCTL_TRAS 0xf0 +#define DDR_PCTL_TRCD 0xf8 +#define DDR_PCTL_TRP 0xdc + +/* MCFG */ +#define MDDR_LPDDR2_CLK_STOP_IDLE_SHIFT 24 +#define PD_IDLE_SHIFT 8 +#define MDDR_EN (2 << 22) +#define LPDDR2_EN (3 << 22) +#define LPDDR3_EN (1 << 22) +#define DDR2_EN (0 << 5) +#define DDR3_EN (1 << 5) +#define LPDDR2_S2 (0 << 6) +#define LPDDR2_S4 (1 << 6) +#define MDDR_LPDDR2_BL_2 (0 << 20) +#define MDDR_LPDDR2_BL_4 (1 << 20) +#define MDDR_LPDDR2_BL_8 (2 << 20) +#define MDDR_LPDDR2_BL_16 (3 << 20) +#define DDR2_DDR3_BL_4 0 +#define DDR2_DDR3_BL_8 1 +#define TFAW_SHIFT 18 +#define PD_EXIT_SLOW (0 << 17) +#define PD_EXIT_FAST (1 << 17) +#define PD_TYPE_SHIFT 16 +#define BURSTLENGTH_SHIFT 20 + +#define MCFG_CR_2T_BIT(x) ((x & (1 << 3)) >> 3) +#define MCFG_DDR_MASK 0x60 +#define MCFG_DDR_SHIFT 5 +#define MCFG_LPDDR_MASK 0xC00000 +#define MCFG_LPDDR_SHIFT 22 + +#define MCFG_LPDDR2_S2 0x0 +#define MCFG_DDR3 0x1 +#define MCFG_LPDDR2_S4 0x2 + +#define READ_DRAMTYPE_INFO(n) (((n) >> 13) & 0x7) + +enum { + DDR4 = 0, + DDR2 = 2, + DDR3 = 3, + LPDDR2 = 5, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct share_params { + u32 hz; + u32 lcdc_type; + u32 vop; + u32 vop_dclk_mode; + u32 sr_idle_en; + u32 addr_mcu_el3; + /* + * 1: need to wait flag1 + * 0: never wait flag1 + */ + u32 wait_flag1; + /* + * 1: need to wait flag1 + * 0: never wait flag1 + */ + u32 wait_flag0; + u32 complt_hwirq; + /* if need, add parameter after */ +}; + +static struct share_params *ddr_psci_param = NULL; + +static const char * const rk3228_dts_timing[] = { + "dram_spd_bin", + "sr_idle", + "pd_idle", + "dram_dll_disb_freq", + "phy_dll_disb_freq", + "dram_odt_disb_freq", + "phy_odt_disb_freq", + "ddr3_drv", + "ddr3_odt", + "lpddr3_drv", + "lpddr3_odt", + "lpddr2_drv", + "phy_ddr3_clk_drv", + "phy_ddr3_cmd_drv", + "phy_ddr3_dqs_drv", + "phy_ddr3_odt", + "phy_lp23_clk_drv", + "phy_lp23_cmd_drv", + "phy_lp23_dqs_drv", + "phy_lp3_odt" +}; + +struct rk3228_ddr_dts_config_timing { + u32 dram_spd_bin; + u32 sr_idle; + u32 pd_idle; + u32 dram_dll_dis_freq; + u32 phy_dll_dis_freq; + u32 dram_odt_dis_freq; + u32 phy_odt_dis_freq; + u32 ddr3_drv; + u32 ddr3_odt; + u32 lpddr3_drv; + u32 lpddr3_odt; + u32 lpddr2_drv; + u32 phy_ddr3_clk_drv; + u32 phy_ddr3_cmd_drv; + u32 phy_ddr3_dqs_drv; + u32 phy_ddr3_odt; + u32 phy_lp23_clk_drv; + u32 phy_lp23_cmd_drv; + u32 phy_lp23_dqs_drv; + u32 phy_lp3_odt; +}; + +struct rk3228_devfreq { + struct devfreq *devfreq; + struct opp_table *clkname_opp_table; + struct opp_table *regulators_opp_table; + struct thermal_cooling_device *cooling; + bool opp_of_table_added; +}; + +struct rk3228_dmc { + struct device *dev; + void __iomem *iomem; + + int rate; + struct devfreq_simple_ondemand_data ondemand_data; + struct devfreq_event_dev *edev; + struct clk *dmc_clk; + struct rk3228_devfreq devfreq; + + uint32_t dram_type; + + //struct mutex lock; + + int (*set_auto_self_refresh)(u32 en); +}; + +static uint32_t of_get_rk3228_timings(struct device *dev, + struct device_node *np, uint32_t *timing) +{ + struct device_node *np_tim; + uint32_t offset; + int ret = 0; + u32 idx; + + // first 4kb page is reserved for interface parameters, we calculate an offset + // after which the timing parameters start + offset = DTS_PAR_OFFSET / sizeof(uint32_t); + + np_tim = of_parse_phandle(np, "rockchip,dram_timing", 0); + + if (!np_tim) { + ret = -EINVAL; + goto end; + } + + for (idx = 0; idx < ARRAY_SIZE(rk3228_dts_timing); idx++) + ret |= of_property_read_u32(np_tim, rk3228_dts_timing[idx], &timing[offset + idx]); + +end: + if (ret) + dev_err(dev, "of_get_ddr_timings: fail\n"); + + of_node_put(np_tim); + + return ret; + +} + +static int rockchip_ddr_set_auto_self_refresh(uint32_t en) +{ + struct arm_smccc_res res; + + ddr_psci_param->sr_idle_en = en; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, + SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_SET_AT_SR, + 0, 0, 0, 0, &res); + + return res.a0; +} + +static int rk3228_dmc_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + + struct rk3228_dmc *rdev = dev_get_drvdata(dev); + struct dev_pm_opp *opp; + int err; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + dev_pm_opp_put(opp); + + err = dev_pm_opp_set_rate(dev, *freq); + if (err) + return err; + + rdev->rate = *freq; + + return 0; + +} + +static int rk3228_dmc_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct rk3228_dmc *rdev = dev_get_drvdata(dev); + struct devfreq_event_data edata; + int ret = 0; + + ret = devfreq_event_get_event(rdev->edev, &edata); + if (ret < 0) + return ret; + + stat->current_frequency = rdev->rate; + stat->busy_time = edata.load_count; + stat->total_time = edata.total_count; + + return ret; +} + +static int rk3228_dmc_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct rk3228_dmc *rdev = dev_get_drvdata(dev); + + *freq = rdev->rate; + + return 0; +} + +static struct devfreq_dev_profile rk3228_devfreq_profile = { + .polling_ms = 50, + .target = rk3228_dmc_target, + .get_dev_status = rk3228_dmc_get_dev_status, + .get_cur_freq = rk3228_dmc_get_cur_freq, +}; + +void rk3228_devfreq_fini(struct rk3228_dmc *rdev) +{ + struct rk3228_devfreq *devfreq = &rdev->devfreq; + + if (devfreq->cooling) { + devfreq_cooling_unregister(devfreq->cooling); + devfreq->cooling = NULL; + } + + if (devfreq->devfreq) { + devm_devfreq_remove_device(rdev->dev, devfreq->devfreq); + devfreq->devfreq = NULL; + } + + if (devfreq->opp_of_table_added) { + dev_pm_opp_of_remove_table(rdev->dev); + devfreq->opp_of_table_added = false; + } + + if (devfreq->regulators_opp_table) { + dev_pm_opp_put_regulators(devfreq->regulators_opp_table); + devfreq->regulators_opp_table = NULL; + } + + if (devfreq->clkname_opp_table) { + dev_pm_opp_put_clkname(devfreq->clkname_opp_table); + devfreq->clkname_opp_table = NULL; + } +} + +int rk3228_devfreq_init(struct rk3228_dmc *rdev) +{ + struct thermal_cooling_device *cooling; + struct device *dev = rdev->dev; + struct opp_table *opp_table; + struct devfreq *devfreq; + struct rk3228_devfreq *rdevfreq = &rdev->devfreq; + + struct dev_pm_opp *opp; + unsigned long cur_freq; + int ret; + + if (!device_property_present(dev, "operating-points-v2")) + /* Optional, continue without devfreq */ + return 0; + + opp_table = dev_pm_opp_set_clkname(dev, "ddr_sclk"); + if (IS_ERR(opp_table)) { + ret = PTR_ERR(opp_table); + goto err_fini; + } + + rdevfreq->clkname_opp_table = opp_table; + + opp_table = dev_pm_opp_set_regulators(dev, + (const char *[]){ "logic" }); + + if (IS_ERR(opp_table)) { + ret = PTR_ERR(opp_table); + + /* Continue if the optional regulator is missing */ + if (ret != -ENODEV) + goto err_fini; + } else { + rdevfreq->regulators_opp_table = opp_table; + } + + ret = dev_pm_opp_of_add_table(dev); + if (ret) + goto err_fini; + rdevfreq->opp_of_table_added = true; + + cur_freq = 0; + + opp = devfreq_recommended_opp(dev, &cur_freq, 0); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto err_fini; + } + + rk3228_devfreq_profile.initial_freq = cur_freq; + dev_pm_opp_put(opp); + + rdev->ondemand_data.upthreshold = 30; + rdev->ondemand_data.downdifferential = 5; + + devfreq = devm_devfreq_add_device(dev, &rk3228_devfreq_profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, &rdev->ondemand_data); + if (IS_ERR(devfreq)) { + dev_err(dev, "Couldn't initialize GPU devfreq\n"); + ret = PTR_ERR(devfreq); + goto err_fini; + } + + rdevfreq->devfreq = devfreq; + + cooling = of_devfreq_cooling_register(dev->of_node, devfreq); + if (IS_ERR(cooling)) + dev_warn(dev, "Failed to register cooling device\n"); + else + rdevfreq->cooling = cooling; + + return 0; + +err_fini: + rk3228_devfreq_fini(rdev); + return ret; +} + +static int rk3228_dmc_init(struct platform_device *pdev, + struct rk3228_dmc *rdev) +{ + struct arm_smccc_res res; + u32 page_num; + + // Count of pages to request to trust os, in pages of 4kb + page_num = DIV_ROUND_UP(sizeof(struct rk3228_ddr_dts_config_timing), PAGE_SIZE) + 1; + + dev_dbg(&pdev->dev, "trying to allocate %d pages\n", page_num); + + // Do request to trust OS. res.a0 contains error code, res.a1 the *physical* + // initial location of pages + arm_smccc_smc( + ROCKCHIP_SIP_SHARE_MEM, + page_num, SHARE_PAGE_TYPE_DDR, 0, + 0, 0, 0, 0, &res + ); + + if (res.a0) { + dev_err(&pdev->dev, "no ATF memory for init\n"); + return -ENOMEM; + } + + dev_dbg(&pdev->dev, "allocated %d shared memory pages\n", page_num); + + // Remap the physical location to kernel space using ioremap + ddr_psci_param = (struct share_params *)ioremap(res.a1, page_num << PAGE_SHIFT); + + if (of_get_rk3228_timings(&pdev->dev, pdev->dev.of_node, + (uint32_t *)ddr_psci_param)) + return -ENOMEM; + + // Reset Hz value + ddr_psci_param->hz = 0; + + arm_smccc_smc( + ROCKCHIP_SIP_DRAM_FREQ, + SHARE_PAGE_TYPE_DDR, 0, ROCKCHIP_SIP_CONFIG_DRAM_INIT, + 0, 0, 0, 0, &res + ); + + if (res.a0) { + dev_err(&pdev->dev, "rockchip_sip_config_dram_init error:%lx\n", + res.a0); + return -EINVAL; + } + + dev_notice(&pdev->dev, "TEE DRAM configuration initialized\n"); + + rdev->set_auto_self_refresh = rockchip_ddr_set_auto_self_refresh; + + return 0; + +} + +static __maybe_unused int rk3228_dmc_suspend(struct device *dev) +{ + struct rk3228_dmc *rdev = dev_get_drvdata(dev); + int ret = 0; + + ret = devfreq_event_disable_edev(rdev->edev); + if (ret < 0) { + dev_err(dev, "failed to disable the devfreq-event devices\n"); + return ret; + } + + ret = devfreq_suspend_device(rdev->devfreq.devfreq); + if (ret < 0) { + dev_err(dev, "failed to suspend the devfreq devices\n"); + return ret; + } + + return 0; +} + +static __maybe_unused int rk3228_dmc_resume(struct device *dev) +{ + struct rk3228_dmc *rdev = dev_get_drvdata(dev); + int ret = 0; + + ret = devfreq_event_enable_edev(rdev->edev); + if (ret < 0) { + dev_err(dev, "failed to enable the devfreq-event devices\n"); + return ret; + } + + ret = devfreq_resume_device(rdev->devfreq.devfreq); + if (ret < 0) { + dev_err(dev, "failed to resume the devfreq devices\n"); + return ret; + } + return ret; +} + +static uint32_t rk3228_get_dram_type(struct device *dev, struct device_node *node_grf, struct rk3228_dmc *data) +{ + + struct regmap *regmap_grf; + uint32_t dram_type; + uint32_t val; + + dram_type = UNUSED; + + regmap_grf = syscon_node_to_regmap(node_grf); + + if (IS_ERR(regmap_grf)) { + dev_err(dev, "Cannot map rockchip,grf\n"); + goto err; + } + + regmap_read(regmap_grf, RK3228_GRF_OS_REG2, &val); + dram_type = READ_DRAMTYPE_INFO(val); + +err: + + return dram_type; + +} + +static SIMPLE_DEV_PM_OPS(rk3228_dmc_pm, rk3228_dmc_suspend, + rk3228_dmc_resume); + +static int rk3328_dmc_print_info(struct rk3228_dmc *rdev) +{ + + u32 tcl; + u32 tras; + u32 trp; + u32 trcd; + + u32 mcfg; + u32 reg_ddr_type1; + u32 reg_ddr_type2; + + u32 dram_type; + u32 cr; + + const char * const dram_types[] = { + "LPDDR2 S2", + "LPDDR2 S4", + "DDR3", + "LPDDR3", + "Unknown" + }; + + const char * const cr_types[] = { + "1T", + "2T" + }; + + + tcl = readl(rdev->iomem + DDR_PCTL_TCL) & 0xf; + tras = readl(rdev->iomem + DDR_PCTL_TRAS) & 0x3f; + trp = readl(rdev->iomem + DDR_PCTL_TRP) & 0xf; + trcd = readl(rdev->iomem + DDR_PCTL_TRCD) & 0xf; + + mcfg = readl(rdev->iomem + DDR_PCTL_MCFG); + + reg_ddr_type1 = (mcfg & MCFG_DDR_MASK) >> MCFG_DDR_SHIFT; + reg_ddr_type2 = (mcfg & MCFG_LPDDR_MASK) >> MCFG_LPDDR_SHIFT; + cr = MCFG_CR_2T_BIT(mcfg); + + switch (reg_ddr_type1) { + case MCFG_LPDDR2_S2: + dram_type = 0; + break; + case MCFG_LPDDR2_S4: + dram_type = 1; + break; + case MCFG_DDR3: + dram_type = reg_ddr_type2 == LPDDR3_EN ? 3 : 2; + break; + default: + dram_type = 4; + break; + } + + dev_info(rdev->dev, + "memory type %s, timings (tCL, tRCD, tRP, tRAS): CL%d-%d-%d-%d command rate: %s (mcfg register: 0x%x)\n", + dram_types[dram_type], tcl, trcd, trp, tras, cr_types[cr], mcfg); + + return 0; + +} + +static int rk3228_dmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct rk3228_dmc *data; + struct device_node *node_grf; + int ret; + + data = devm_kzalloc(dev, sizeof(struct rk3228_dmc), GFP_KERNEL); + if (!data) + return -ENOMEM; + + //mutex_init(&data->lock); + + data->dmc_clk = devm_clk_get(dev, "ddr_sclk"); + if (IS_ERR(data->dmc_clk)) { + if (PTR_ERR(data->dmc_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_err(dev, "Cannot get the clk dmc_clk\n"); + return PTR_ERR(data->dmc_clk); + } + + data->edev = devfreq_event_get_edev_by_phandle(dev, "devfreq-events", 0); + if (IS_ERR(data->edev)) + return -EPROBE_DEFER; + + data->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->iomem)) { + dev_err(dev, "fail to ioremap iomem\n"); + ret = PTR_ERR(data->iomem); + return ret; + } + + data->dev = dev; + + rk3328_dmc_print_info(data); + + node_grf = of_parse_phandle(np, "rockchip,grf", 0); + if (node_grf) { + + data->dram_type = rk3228_get_dram_type(dev, node_grf, data); + + if (data->dram_type == LPDDR2) { + dev_warn(dev, "detected LPDDR2 memory\n"); + } else if (data->dram_type == DDR2) { + dev_warn(dev, "detected DDR2 memory\n"); + } else if (data->dram_type == DDR3) { + dev_info(dev, "detected DDR3 memory\n"); + } else if (data->dram_type == LPDDR3) { + dev_info(dev, "detected LPDDR3 memory\n"); + } else if (data->dram_type == DDR4) { + dev_info(dev, "detected DDR4 memory\n"); + } else if (data->dram_type == LPDDR4) { + dev_info(dev, "detected LPDDR4 memory\n"); + } else if (data->dram_type == UNUSED) { + dev_info(dev, "memory type not detected\n"); + } else { + dev_info(dev, "unknown memory type: 0x%x\n", data->dram_type); + } + + } else { + + dev_warn(dev, "Cannot get rockchip,grf\n"); + data->dram_type = UNUSED; + + } + + if (data->dram_type == DDR3 || + data->dram_type == LPDDR3 || + data->dram_type == DDR4 || + data->dram_type == LPDDR4) { + + ret = devfreq_event_enable_edev(data->edev); + if (ret < 0) { + dev_err(dev, "failed to enable devfreq-event devices\n"); + return ret; + } + + ret = rk3228_dmc_init(pdev, data); + if (ret) + return ret; + + + + ret = rk3228_devfreq_init(data); + if (ret) + return ret; + + } else { + + dev_warn(dev, "detected memory type does not support clock scaling\n"); + + } + + platform_set_drvdata(pdev, data); + + return 0; + +} + +static int rk3228_dmc_remove(struct platform_device *pdev) +{ + struct rk3228_dmc *rdev = dev_get_drvdata(&pdev->dev); + + /* + * Before remove the opp table we need to unregister the opp notifier. + */ + rk3228_devfreq_fini(rdev); + + if (ddr_psci_param) + iounmap(ddr_psci_param); + + if (rdev->iomem) + iounmap(rdev->iomem); + + return 0; +} + +static const struct of_device_id rk3228_dmc_of_match[] = { + { .compatible = "rockchip,rk3228-dmc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rk3228_dmc_of_match); + +static struct platform_driver rk3228_dmc_driver = { + .probe = rk3228_dmc_probe, + .remove = rk3228_dmc_remove, + .driver = { + .name = "rk3228-dmc", + .pm = &rk3228_dmc_pm, + .of_match_table = rk3228_dmc_of_match, + }, +}; +module_platform_driver(rk3228_dmc_driver); + +#ifdef CONFIG_ARM +static __init int sip_firmware_init(void) +{ + struct arm_smccc_res res; + + /* + * OP-TEE works on kernel 3.10 and 4.4 and we have different sip + * implement. We should tell OP-TEE the current rockchip sip version. + */ + + /* + * + * res = __invoke_sip_fn_smc(ROCKCHIP_SIP_SIP_VERSION, ROCKCHIP_SIP_IMPLEMENT_V2, + SECURE_REG_WR, 0); + + arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + */ + + arm_smccc_smc(ROCKCHIP_SIP_SIP_VERSION, ROCKCHIP_SIP_IMPLEMENT_V2, SECURE_REG_WR, 0, 0, 0, 0, 0, &res); + + if (res.a0) + pr_err("%s: set rockchip sip version v2 failed\n", __func__); + + pr_notice("Rockchip SIP initialized, version 0x%lx\n", res.a1); + + return 0; +} +arch_initcall(sip_firmware_init); +#endif + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lin Huang "); +MODULE_DESCRIPTION("RK3228 dmcfreq driver with devfreq framework"); -- 2.25.1