build/patch/kernel/archive/rk322x-5.18/03-0004-add-dmc-driver.patch

1938 lines
49 KiB
Diff

From e7c97c57e2d5040e90662459239bc28c8ea89be5 Mon Sep 17 00:00:00 2001
From: Paolo Sabatino <paolo.sabatino@gmail.com>
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 <dt-bindings/clock/rk3228-cru.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/power/rk3228-power.h>
+#include <dt-bindings/clock/rockchip-ddr.h>
+#include <dt-bindings/memory/rockchip,rk322x.h>
/ {
#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 = <DDR3_DEFAULT>;
+ 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_DS_40ohm>;
+ ddr3_odt = <DDR3_ODT_120ohm>;
+ lpddr3_drv = <LP3_DS_34ohm>;
+ lpddr3_odt = <LP3_ODT_240ohm>;
+ lpddr2_drv = <LP2_DS_34ohm>;
+ /* lpddr2 not supported odt */
+ phy_ddr3_clk_drv = <PHY_DDR3_RON_RTT_45ohm>;
+ phy_ddr3_cmd_drv = <PHY_DDR3_RON_RTT_45ohm>;
+ phy_ddr3_dqs_drv = <PHY_DDR3_RON_RTT_34ohm>;
+ phy_ddr3_odt = <PHY_DDR3_RON_RTT_225ohm>;
+ phy_lp23_clk_drv = <PHY_LP23_RON_RTT_43ohm>;
+ phy_lp23_cmd_drv = <PHY_LP23_RON_RTT_34ohm>;
+ phy_lp23_dqs_drv = <PHY_LP23_RON_RTT_34ohm>;
+ phy_lp3_odt = <PHY_LP23_RON_RTT_240ohm>;
+ };
+
arm-pmu {
compatible = "arm,cortex-a7-pmu";
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>,
@@ -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 <hl@rock-chips.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rwsem.h>
+#include <linux/suspend.h>
+
+#include <soc/rockchip/rockchip_sip.h>
+
+#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 <hl@rock-chips.com>");
+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 <hl@rock-chips.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
+#include <linux/devfreq_cooling.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rwsem.h>
+#include <linux/suspend.h>
+
+#include <soc/rockchip/rockchip_sip.h>
+
+#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" },
+ 1);
+ 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 <hl@rock-chips.com>");
+MODULE_DESCRIPTION("RK3228 dmcfreq driver with devfreq framework");
--
2.25.1