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 ++++++++++++++
  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,18 @@ config ARM_TEGRA20_DEVFREQ
 	  It reads Memory Controller counters and adjusts the operating
 	  frequencies and voltages with OPP support.
 
+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 bf40d04928d..059712bfe5f 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ)	+= imx-bus.o
 obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ)	+= imx8m-ddrc.o
 obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ)	+= mtk-cci-devfreq.o
 obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_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/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 00000000000..e8106f36404
--- /dev/null
+++ b/drivers/devfreq/rk3228_dmc.c
@@ -0,0 +1,709 @@
+// 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 thermal_cooling_device *cooling;
+};
+
+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;
+	}
+
+}
+
+int rk3228_devfreq_init(struct rk3228_dmc *rdev)
+{
+	struct thermal_cooling_device *cooling;
+	struct device *dev = rdev->dev;
+	struct devfreq *devfreq;
+	struct rk3228_devfreq *rdevfreq = &rdev->devfreq;
+	const char *regulator_names[] = { "logic", NULL };
+
+	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;
+
+	ret = devm_pm_opp_set_clkname(dev, "ddr_sclk");
+	if (ret)
+		goto err_fini;
+
+	ret = devm_pm_opp_set_regulators(dev, regulator_names);
+
+	/* Continue if the optional regulator is missing */
+	if (ret && ret != -ENODEV)
+		goto err_fini;
+
+	ret = devm_pm_opp_of_add_table(dev);
+	if (ret)
+		goto err_fini;
+
+	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
