18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci * Author: Lin Huang <hl@rock-chips.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h>
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/devfreq.h>
118c2ecf20Sopenharmony_ci#include <linux/devfreq-event.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_opp.h>
188c2ecf20Sopenharmony_ci#include <linux/regmap.h>
198c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
208c2ecf20Sopenharmony_ci#include <linux/rwsem.h>
218c2ecf20Sopenharmony_ci#include <linux/suspend.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <soc/rockchip/rk3399_grf.h>
248c2ecf20Sopenharmony_ci#include <soc/rockchip/rockchip_sip.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct dram_timing {
278c2ecf20Sopenharmony_ci	unsigned int ddr3_speed_bin;
288c2ecf20Sopenharmony_ci	unsigned int pd_idle;
298c2ecf20Sopenharmony_ci	unsigned int sr_idle;
308c2ecf20Sopenharmony_ci	unsigned int sr_mc_gate_idle;
318c2ecf20Sopenharmony_ci	unsigned int srpd_lite_idle;
328c2ecf20Sopenharmony_ci	unsigned int standby_idle;
338c2ecf20Sopenharmony_ci	unsigned int auto_pd_dis_freq;
348c2ecf20Sopenharmony_ci	unsigned int dram_dll_dis_freq;
358c2ecf20Sopenharmony_ci	unsigned int phy_dll_dis_freq;
368c2ecf20Sopenharmony_ci	unsigned int ddr3_odt_dis_freq;
378c2ecf20Sopenharmony_ci	unsigned int ddr3_drv;
388c2ecf20Sopenharmony_ci	unsigned int ddr3_odt;
398c2ecf20Sopenharmony_ci	unsigned int phy_ddr3_ca_drv;
408c2ecf20Sopenharmony_ci	unsigned int phy_ddr3_dq_drv;
418c2ecf20Sopenharmony_ci	unsigned int phy_ddr3_odt;
428c2ecf20Sopenharmony_ci	unsigned int lpddr3_odt_dis_freq;
438c2ecf20Sopenharmony_ci	unsigned int lpddr3_drv;
448c2ecf20Sopenharmony_ci	unsigned int lpddr3_odt;
458c2ecf20Sopenharmony_ci	unsigned int phy_lpddr3_ca_drv;
468c2ecf20Sopenharmony_ci	unsigned int phy_lpddr3_dq_drv;
478c2ecf20Sopenharmony_ci	unsigned int phy_lpddr3_odt;
488c2ecf20Sopenharmony_ci	unsigned int lpddr4_odt_dis_freq;
498c2ecf20Sopenharmony_ci	unsigned int lpddr4_drv;
508c2ecf20Sopenharmony_ci	unsigned int lpddr4_dq_odt;
518c2ecf20Sopenharmony_ci	unsigned int lpddr4_ca_odt;
528c2ecf20Sopenharmony_ci	unsigned int phy_lpddr4_ca_drv;
538c2ecf20Sopenharmony_ci	unsigned int phy_lpddr4_ck_cs_drv;
548c2ecf20Sopenharmony_ci	unsigned int phy_lpddr4_dq_drv;
558c2ecf20Sopenharmony_ci	unsigned int phy_lpddr4_odt;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistruct rk3399_dmcfreq {
598c2ecf20Sopenharmony_ci	struct device *dev;
608c2ecf20Sopenharmony_ci	struct devfreq *devfreq;
618c2ecf20Sopenharmony_ci	struct devfreq_simple_ondemand_data ondemand_data;
628c2ecf20Sopenharmony_ci	struct clk *dmc_clk;
638c2ecf20Sopenharmony_ci	struct devfreq_event_dev *edev;
648c2ecf20Sopenharmony_ci	struct mutex lock;
658c2ecf20Sopenharmony_ci	struct dram_timing timing;
668c2ecf20Sopenharmony_ci	struct regulator *vdd_center;
678c2ecf20Sopenharmony_ci	struct regmap *regmap_pmu;
688c2ecf20Sopenharmony_ci	unsigned long rate, target_rate;
698c2ecf20Sopenharmony_ci	unsigned long volt, target_volt;
708c2ecf20Sopenharmony_ci	unsigned int odt_dis_freq;
718c2ecf20Sopenharmony_ci	int odt_pd_arg0, odt_pd_arg1;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
758c2ecf20Sopenharmony_ci				 u32 flags)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
788c2ecf20Sopenharmony_ci	struct dev_pm_opp *opp;
798c2ecf20Sopenharmony_ci	unsigned long old_clk_rate = dmcfreq->rate;
808c2ecf20Sopenharmony_ci	unsigned long target_volt, target_rate;
818c2ecf20Sopenharmony_ci	struct arm_smccc_res res;
828c2ecf20Sopenharmony_ci	bool odt_enable = false;
838c2ecf20Sopenharmony_ci	int err;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	opp = devfreq_recommended_opp(dev, freq, flags);
868c2ecf20Sopenharmony_ci	if (IS_ERR(opp))
878c2ecf20Sopenharmony_ci		return PTR_ERR(opp);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	target_rate = dev_pm_opp_get_freq(opp);
908c2ecf20Sopenharmony_ci	target_volt = dev_pm_opp_get_voltage(opp);
918c2ecf20Sopenharmony_ci	dev_pm_opp_put(opp);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (dmcfreq->rate == target_rate)
948c2ecf20Sopenharmony_ci		return 0;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	mutex_lock(&dmcfreq->lock);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (dmcfreq->regmap_pmu) {
998c2ecf20Sopenharmony_ci		if (target_rate >= dmcfreq->odt_dis_freq)
1008c2ecf20Sopenharmony_ci			odt_enable = true;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		/*
1038c2ecf20Sopenharmony_ci		 * This makes a SMC call to the TF-A to set the DDR PD
1048c2ecf20Sopenharmony_ci		 * (power-down) timings and to enable or disable the
1058c2ecf20Sopenharmony_ci		 * ODT (on-die termination) resistors.
1068c2ecf20Sopenharmony_ci		 */
1078c2ecf20Sopenharmony_ci		arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, dmcfreq->odt_pd_arg0,
1088c2ecf20Sopenharmony_ci			      dmcfreq->odt_pd_arg1,
1098c2ecf20Sopenharmony_ci			      ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD,
1108c2ecf20Sopenharmony_ci			      odt_enable, 0, 0, 0, &res);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * If frequency scaling from low to high, adjust voltage first.
1158c2ecf20Sopenharmony_ci	 * If frequency scaling from high to low, adjust frequency first.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	if (old_clk_rate < target_rate) {
1188c2ecf20Sopenharmony_ci		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
1198c2ecf20Sopenharmony_ci					    target_volt);
1208c2ecf20Sopenharmony_ci		if (err) {
1218c2ecf20Sopenharmony_ci			dev_err(dev, "Cannot set voltage %lu uV\n",
1228c2ecf20Sopenharmony_ci				target_volt);
1238c2ecf20Sopenharmony_ci			goto out;
1248c2ecf20Sopenharmony_ci		}
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
1288c2ecf20Sopenharmony_ci	if (err) {
1298c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate,
1308c2ecf20Sopenharmony_ci			err);
1318c2ecf20Sopenharmony_ci		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
1328c2ecf20Sopenharmony_ci				      dmcfreq->volt);
1338c2ecf20Sopenharmony_ci		goto out;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/*
1378c2ecf20Sopenharmony_ci	 * Check the dpll rate,
1388c2ecf20Sopenharmony_ci	 * There only two result we will get,
1398c2ecf20Sopenharmony_ci	 * 1. Ddr frequency scaling fail, we still get the old rate.
1408c2ecf20Sopenharmony_ci	 * 2. Ddr frequency scaling sucessful, we get the rate we set.
1418c2ecf20Sopenharmony_ci	 */
1428c2ecf20Sopenharmony_ci	dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* If get the incorrect rate, set voltage to old value. */
1458c2ecf20Sopenharmony_ci	if (dmcfreq->rate != target_rate) {
1468c2ecf20Sopenharmony_ci		dev_err(dev, "Got wrong frequency, Request %lu, Current %lu\n",
1478c2ecf20Sopenharmony_ci			target_rate, dmcfreq->rate);
1488c2ecf20Sopenharmony_ci		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
1498c2ecf20Sopenharmony_ci				      dmcfreq->volt);
1508c2ecf20Sopenharmony_ci		goto out;
1518c2ecf20Sopenharmony_ci	} else if (old_clk_rate > target_rate)
1528c2ecf20Sopenharmony_ci		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
1538c2ecf20Sopenharmony_ci					    target_volt);
1548c2ecf20Sopenharmony_ci	if (err)
1558c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot set voltage %lu uV\n", target_volt);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	dmcfreq->rate = target_rate;
1588c2ecf20Sopenharmony_ci	dmcfreq->volt = target_volt;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ciout:
1618c2ecf20Sopenharmony_ci	mutex_unlock(&dmcfreq->lock);
1628c2ecf20Sopenharmony_ci	return err;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int rk3399_dmcfreq_get_dev_status(struct device *dev,
1668c2ecf20Sopenharmony_ci					 struct devfreq_dev_status *stat)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
1698c2ecf20Sopenharmony_ci	struct devfreq_event_data edata;
1708c2ecf20Sopenharmony_ci	int ret = 0;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ret = devfreq_event_get_event(dmcfreq->edev, &edata);
1738c2ecf20Sopenharmony_ci	if (ret < 0)
1748c2ecf20Sopenharmony_ci		return ret;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	stat->current_frequency = dmcfreq->rate;
1778c2ecf20Sopenharmony_ci	stat->busy_time = edata.load_count;
1788c2ecf20Sopenharmony_ci	stat->total_time = edata.total_count;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return ret;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	*freq = dmcfreq->rate;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
1938c2ecf20Sopenharmony_ci	.polling_ms	= 200,
1948c2ecf20Sopenharmony_ci	.target		= rk3399_dmcfreq_target,
1958c2ecf20Sopenharmony_ci	.get_dev_status	= rk3399_dmcfreq_get_dev_status,
1968c2ecf20Sopenharmony_ci	.get_cur_freq	= rk3399_dmcfreq_get_cur_freq,
1978c2ecf20Sopenharmony_ci};
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
2028c2ecf20Sopenharmony_ci	int ret = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ret = devfreq_event_disable_edev(dmcfreq->edev);
2058c2ecf20Sopenharmony_ci	if (ret < 0) {
2068c2ecf20Sopenharmony_ci		dev_err(dev, "failed to disable the devfreq-event devices\n");
2078c2ecf20Sopenharmony_ci		return ret;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	ret = devfreq_suspend_device(dmcfreq->devfreq);
2118c2ecf20Sopenharmony_ci	if (ret < 0) {
2128c2ecf20Sopenharmony_ci		dev_err(dev, "failed to suspend the devfreq devices\n");
2138c2ecf20Sopenharmony_ci		return ret;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
2228c2ecf20Sopenharmony_ci	int ret = 0;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	ret = devfreq_event_enable_edev(dmcfreq->edev);
2258c2ecf20Sopenharmony_ci	if (ret < 0) {
2268c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable the devfreq-event devices\n");
2278c2ecf20Sopenharmony_ci		return ret;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = devfreq_resume_device(dmcfreq->devfreq);
2318c2ecf20Sopenharmony_ci	if (ret < 0) {
2328c2ecf20Sopenharmony_ci		dev_err(dev, "failed to resume the devfreq devices\n");
2338c2ecf20Sopenharmony_ci		return ret;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	return ret;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
2398c2ecf20Sopenharmony_ci			 rk3399_dmcfreq_resume);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int of_get_ddr_timings(struct dram_timing *timing,
2428c2ecf20Sopenharmony_ci			      struct device_node *np)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	int ret = 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "rockchip,ddr3_speed_bin",
2478c2ecf20Sopenharmony_ci				   &timing->ddr3_speed_bin);
2488c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,pd_idle",
2498c2ecf20Sopenharmony_ci				    &timing->pd_idle);
2508c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,sr_idle",
2518c2ecf20Sopenharmony_ci				    &timing->sr_idle);
2528c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,sr_mc_gate_idle",
2538c2ecf20Sopenharmony_ci				    &timing->sr_mc_gate_idle);
2548c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,srpd_lite_idle",
2558c2ecf20Sopenharmony_ci				    &timing->srpd_lite_idle);
2568c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,standby_idle",
2578c2ecf20Sopenharmony_ci				    &timing->standby_idle);
2588c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,auto_pd_dis_freq",
2598c2ecf20Sopenharmony_ci				    &timing->auto_pd_dis_freq);
2608c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,dram_dll_dis_freq",
2618c2ecf20Sopenharmony_ci				    &timing->dram_dll_dis_freq);
2628c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_dll_dis_freq",
2638c2ecf20Sopenharmony_ci				    &timing->phy_dll_dis_freq);
2648c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,ddr3_odt_dis_freq",
2658c2ecf20Sopenharmony_ci				    &timing->ddr3_odt_dis_freq);
2668c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,ddr3_drv",
2678c2ecf20Sopenharmony_ci				    &timing->ddr3_drv);
2688c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,ddr3_odt",
2698c2ecf20Sopenharmony_ci				    &timing->ddr3_odt);
2708c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_ddr3_ca_drv",
2718c2ecf20Sopenharmony_ci				    &timing->phy_ddr3_ca_drv);
2728c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_ddr3_dq_drv",
2738c2ecf20Sopenharmony_ci				    &timing->phy_ddr3_dq_drv);
2748c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_ddr3_odt",
2758c2ecf20Sopenharmony_ci				    &timing->phy_ddr3_odt);
2768c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr3_odt_dis_freq",
2778c2ecf20Sopenharmony_ci				    &timing->lpddr3_odt_dis_freq);
2788c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr3_drv",
2798c2ecf20Sopenharmony_ci				    &timing->lpddr3_drv);
2808c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr3_odt",
2818c2ecf20Sopenharmony_ci				    &timing->lpddr3_odt);
2828c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_ca_drv",
2838c2ecf20Sopenharmony_ci				    &timing->phy_lpddr3_ca_drv);
2848c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_dq_drv",
2858c2ecf20Sopenharmony_ci				    &timing->phy_lpddr3_dq_drv);
2868c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_odt",
2878c2ecf20Sopenharmony_ci				    &timing->phy_lpddr3_odt);
2888c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr4_odt_dis_freq",
2898c2ecf20Sopenharmony_ci				    &timing->lpddr4_odt_dis_freq);
2908c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr4_drv",
2918c2ecf20Sopenharmony_ci				    &timing->lpddr4_drv);
2928c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr4_dq_odt",
2938c2ecf20Sopenharmony_ci				    &timing->lpddr4_dq_odt);
2948c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,lpddr4_ca_odt",
2958c2ecf20Sopenharmony_ci				    &timing->lpddr4_ca_odt);
2968c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_ca_drv",
2978c2ecf20Sopenharmony_ci				    &timing->phy_lpddr4_ca_drv);
2988c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_ck_cs_drv",
2998c2ecf20Sopenharmony_ci				    &timing->phy_lpddr4_ck_cs_drv);
3008c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_dq_drv",
3018c2ecf20Sopenharmony_ci				    &timing->phy_lpddr4_dq_drv);
3028c2ecf20Sopenharmony_ci	ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_odt",
3038c2ecf20Sopenharmony_ci				    &timing->phy_lpddr4_odt);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return ret;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int rk3399_dmcfreq_probe(struct platform_device *pdev)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct arm_smccc_res res;
3118c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3128c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node, *node;
3138c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *data;
3148c2ecf20Sopenharmony_ci	int ret, index, size;
3158c2ecf20Sopenharmony_ci	uint32_t *timing;
3168c2ecf20Sopenharmony_ci	struct dev_pm_opp *opp;
3178c2ecf20Sopenharmony_ci	u32 ddr_type;
3188c2ecf20Sopenharmony_ci	u32 val;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
3218c2ecf20Sopenharmony_ci	if (!data)
3228c2ecf20Sopenharmony_ci		return -ENOMEM;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	mutex_init(&data->lock);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	data->vdd_center = devm_regulator_get(dev, "center");
3278c2ecf20Sopenharmony_ci	if (IS_ERR(data->vdd_center)) {
3288c2ecf20Sopenharmony_ci		if (PTR_ERR(data->vdd_center) == -EPROBE_DEFER)
3298c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot get the regulator \"center\"\n");
3328c2ecf20Sopenharmony_ci		return PTR_ERR(data->vdd_center);
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	data->dmc_clk = devm_clk_get(dev, "dmc_clk");
3368c2ecf20Sopenharmony_ci	if (IS_ERR(data->dmc_clk)) {
3378c2ecf20Sopenharmony_ci		if (PTR_ERR(data->dmc_clk) == -EPROBE_DEFER)
3388c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		dev_err(dev, "Cannot get the clk dmc_clk\n");
3418c2ecf20Sopenharmony_ci		return PTR_ERR(data->dmc_clk);
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	data->edev = devfreq_event_get_edev_by_phandle(dev, "devfreq-events", 0);
3458c2ecf20Sopenharmony_ci	if (IS_ERR(data->edev))
3468c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret = devfreq_event_enable_edev(data->edev);
3498c2ecf20Sopenharmony_ci	if (ret < 0) {
3508c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable devfreq-event devices\n");
3518c2ecf20Sopenharmony_ci		return ret;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/*
3558c2ecf20Sopenharmony_ci	 * Get dram timing and pass it to arm trust firmware,
3568c2ecf20Sopenharmony_ci	 * the dram driver in arm trust firmware will get these
3578c2ecf20Sopenharmony_ci	 * timing and to do dram initial.
3588c2ecf20Sopenharmony_ci	 */
3598c2ecf20Sopenharmony_ci	if (!of_get_ddr_timings(&data->timing, np)) {
3608c2ecf20Sopenharmony_ci		timing = &data->timing.ddr3_speed_bin;
3618c2ecf20Sopenharmony_ci		size = sizeof(struct dram_timing) / 4;
3628c2ecf20Sopenharmony_ci		for (index = 0; index < size; index++) {
3638c2ecf20Sopenharmony_ci			arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, *timing++, index,
3648c2ecf20Sopenharmony_ci				      ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM,
3658c2ecf20Sopenharmony_ci				      0, 0, 0, 0, &res);
3668c2ecf20Sopenharmony_ci			if (res.a0) {
3678c2ecf20Sopenharmony_ci				dev_err(dev, "Failed to set dram param: %ld\n",
3688c2ecf20Sopenharmony_ci					res.a0);
3698c2ecf20Sopenharmony_ci				ret = -EINVAL;
3708c2ecf20Sopenharmony_ci				goto err_edev;
3718c2ecf20Sopenharmony_ci			}
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	node = of_parse_phandle(np, "rockchip,pmu", 0);
3768c2ecf20Sopenharmony_ci	if (!node)
3778c2ecf20Sopenharmony_ci		goto no_pmu;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	data->regmap_pmu = syscon_node_to_regmap(node);
3808c2ecf20Sopenharmony_ci	of_node_put(node);
3818c2ecf20Sopenharmony_ci	if (IS_ERR(data->regmap_pmu)) {
3828c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->regmap_pmu);
3838c2ecf20Sopenharmony_ci		goto err_edev;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	regmap_read(data->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
3878c2ecf20Sopenharmony_ci	ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) &
3888c2ecf20Sopenharmony_ci		    RK3399_PMUGRF_DDRTYPE_MASK;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (ddr_type) {
3918c2ecf20Sopenharmony_ci	case RK3399_PMUGRF_DDRTYPE_DDR3:
3928c2ecf20Sopenharmony_ci		data->odt_dis_freq = data->timing.ddr3_odt_dis_freq;
3938c2ecf20Sopenharmony_ci		break;
3948c2ecf20Sopenharmony_ci	case RK3399_PMUGRF_DDRTYPE_LPDDR3:
3958c2ecf20Sopenharmony_ci		data->odt_dis_freq = data->timing.lpddr3_odt_dis_freq;
3968c2ecf20Sopenharmony_ci		break;
3978c2ecf20Sopenharmony_ci	case RK3399_PMUGRF_DDRTYPE_LPDDR4:
3988c2ecf20Sopenharmony_ci		data->odt_dis_freq = data->timing.lpddr4_odt_dis_freq;
3998c2ecf20Sopenharmony_ci		break;
4008c2ecf20Sopenharmony_ci	default:
4018c2ecf20Sopenharmony_ci		ret = -EINVAL;
4028c2ecf20Sopenharmony_ci		goto err_edev;
4038c2ecf20Sopenharmony_ci	};
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cino_pmu:
4068c2ecf20Sopenharmony_ci	arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
4078c2ecf20Sopenharmony_ci		      ROCKCHIP_SIP_CONFIG_DRAM_INIT,
4088c2ecf20Sopenharmony_ci		      0, 0, 0, 0, &res);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/*
4118c2ecf20Sopenharmony_ci	 * In TF-A there is a platform SIP call to set the PD (power-down)
4128c2ecf20Sopenharmony_ci	 * timings and to enable or disable the ODT (on-die termination).
4138c2ecf20Sopenharmony_ci	 * This call needs three arguments as follows:
4148c2ecf20Sopenharmony_ci	 *
4158c2ecf20Sopenharmony_ci	 * arg0:
4168c2ecf20Sopenharmony_ci	 *     bit[0-7]   : sr_idle
4178c2ecf20Sopenharmony_ci	 *     bit[8-15]  : sr_mc_gate_idle
4188c2ecf20Sopenharmony_ci	 *     bit[16-31] : standby idle
4198c2ecf20Sopenharmony_ci	 * arg1:
4208c2ecf20Sopenharmony_ci	 *     bit[0-11]  : pd_idle
4218c2ecf20Sopenharmony_ci	 *     bit[16-27] : srpd_lite_idle
4228c2ecf20Sopenharmony_ci	 * arg2:
4238c2ecf20Sopenharmony_ci	 *     bit[0]     : odt enable
4248c2ecf20Sopenharmony_ci	 */
4258c2ecf20Sopenharmony_ci	data->odt_pd_arg0 = (data->timing.sr_idle & 0xff) |
4268c2ecf20Sopenharmony_ci			    ((data->timing.sr_mc_gate_idle & 0xff) << 8) |
4278c2ecf20Sopenharmony_ci			    ((data->timing.standby_idle & 0xffff) << 16);
4288c2ecf20Sopenharmony_ci	data->odt_pd_arg1 = (data->timing.pd_idle & 0xfff) |
4298c2ecf20Sopenharmony_ci			    ((data->timing.srpd_lite_idle & 0xfff) << 16);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/*
4328c2ecf20Sopenharmony_ci	 * We add a devfreq driver to our parent since it has a device tree node
4338c2ecf20Sopenharmony_ci	 * with operating points.
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	if (dev_pm_opp_of_add_table(dev)) {
4368c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid operating-points in device tree.\n");
4378c2ecf20Sopenharmony_ci		ret = -EINVAL;
4388c2ecf20Sopenharmony_ci		goto err_edev;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	of_property_read_u32(np, "upthreshold",
4428c2ecf20Sopenharmony_ci			     &data->ondemand_data.upthreshold);
4438c2ecf20Sopenharmony_ci	of_property_read_u32(np, "downdifferential",
4448c2ecf20Sopenharmony_ci			     &data->ondemand_data.downdifferential);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	data->rate = clk_get_rate(data->dmc_clk);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	opp = devfreq_recommended_opp(dev, &data->rate, 0);
4498c2ecf20Sopenharmony_ci	if (IS_ERR(opp)) {
4508c2ecf20Sopenharmony_ci		ret = PTR_ERR(opp);
4518c2ecf20Sopenharmony_ci		goto err_free_opp;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	data->rate = dev_pm_opp_get_freq(opp);
4558c2ecf20Sopenharmony_ci	data->volt = dev_pm_opp_get_voltage(opp);
4568c2ecf20Sopenharmony_ci	dev_pm_opp_put(opp);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	rk3399_devfreq_dmc_profile.initial_freq = data->rate;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	data->devfreq = devm_devfreq_add_device(dev,
4618c2ecf20Sopenharmony_ci					   &rk3399_devfreq_dmc_profile,
4628c2ecf20Sopenharmony_ci					   DEVFREQ_GOV_SIMPLE_ONDEMAND,
4638c2ecf20Sopenharmony_ci					   &data->ondemand_data);
4648c2ecf20Sopenharmony_ci	if (IS_ERR(data->devfreq)) {
4658c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->devfreq);
4668c2ecf20Sopenharmony_ci		goto err_free_opp;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	devm_devfreq_register_opp_notifier(dev, data->devfreq);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	data->dev = dev;
4728c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return 0;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cierr_free_opp:
4778c2ecf20Sopenharmony_ci	dev_pm_opp_of_remove_table(&pdev->dev);
4788c2ecf20Sopenharmony_cierr_edev:
4798c2ecf20Sopenharmony_ci	devfreq_event_disable_edev(data->edev);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	return ret;
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int rk3399_dmcfreq_remove(struct platform_device *pdev)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(&pdev->dev);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	devfreq_event_disable_edev(dmcfreq->edev);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/*
4918c2ecf20Sopenharmony_ci	 * Before remove the opp table we need to unregister the opp notifier.
4928c2ecf20Sopenharmony_ci	 */
4938c2ecf20Sopenharmony_ci	devm_devfreq_unregister_opp_notifier(dmcfreq->dev, dmcfreq->devfreq);
4948c2ecf20Sopenharmony_ci	dev_pm_opp_of_remove_table(dmcfreq->dev);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic const struct of_device_id rk3399dmc_devfreq_of_match[] = {
5008c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,rk3399-dmc" },
5018c2ecf20Sopenharmony_ci	{ },
5028c2ecf20Sopenharmony_ci};
5038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rk3399dmc_devfreq_of_match);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic struct platform_driver rk3399_dmcfreq_driver = {
5068c2ecf20Sopenharmony_ci	.probe	= rk3399_dmcfreq_probe,
5078c2ecf20Sopenharmony_ci	.remove = rk3399_dmcfreq_remove,
5088c2ecf20Sopenharmony_ci	.driver = {
5098c2ecf20Sopenharmony_ci		.name	= "rk3399-dmc-freq",
5108c2ecf20Sopenharmony_ci		.pm	= &rk3399_dmcfreq_pm,
5118c2ecf20Sopenharmony_ci		.of_match_table = rk3399dmc_devfreq_of_match,
5128c2ecf20Sopenharmony_ci	},
5138c2ecf20Sopenharmony_ci};
5148c2ecf20Sopenharmony_cimodule_platform_driver(rk3399_dmcfreq_driver);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
5178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
5188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
519