xref: /linux/drivers/devfreq/rk3399_dmc.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25a893e31SLin Huang /*
35a893e31SLin Huang  * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd.
45a893e31SLin Huang  * Author: Lin Huang <hl@rock-chips.com>
55a893e31SLin Huang  */
65a893e31SLin Huang 
75a893e31SLin Huang #include <linux/arm-smccc.h>
8a5ca1854SBrian Norris #include <linux/bitfield.h>
95a893e31SLin Huang #include <linux/clk.h>
105a893e31SLin Huang #include <linux/delay.h>
115a893e31SLin Huang #include <linux/devfreq.h>
125a893e31SLin Huang #include <linux/devfreq-event.h>
135a893e31SLin Huang #include <linux/interrupt.h>
149173c5ceSEnric Balletbo i Serra #include <linux/mfd/syscon.h>
155a893e31SLin Huang #include <linux/module.h>
165a893e31SLin Huang #include <linux/of.h>
175a893e31SLin Huang #include <linux/platform_device.h>
185a893e31SLin Huang #include <linux/pm_opp.h>
199173c5ceSEnric Balletbo i Serra #include <linux/regmap.h>
205a893e31SLin Huang #include <linux/regulator/consumer.h>
215a893e31SLin Huang #include <linux/rwsem.h>
225a893e31SLin Huang #include <linux/suspend.h>
235a893e31SLin Huang 
242e691421SBrian Norris #include <soc/rockchip/pm_domains.h>
2574002e66SSascha Hauer #include <soc/rockchip/rockchip_grf.h>
269173c5ceSEnric Balletbo i Serra #include <soc/rockchip/rk3399_grf.h>
275a893e31SLin Huang #include <soc/rockchip/rockchip_sip.h>
285a893e31SLin Huang 
29fd5b8479SBrian Norris #define NS_TO_CYCLE(NS, MHz)				(((NS) * (MHz)) / NSEC_PER_USEC)
30fd5b8479SBrian Norris 
31a5ca1854SBrian Norris #define RK3399_SET_ODT_PD_0_SR_IDLE			GENMASK(7, 0)
32a5ca1854SBrian Norris #define RK3399_SET_ODT_PD_0_SR_MC_GATE_IDLE		GENMASK(15, 8)
33a5ca1854SBrian Norris #define RK3399_SET_ODT_PD_0_STANDBY_IDLE		GENMASK(31, 16)
34a5ca1854SBrian Norris 
35a5ca1854SBrian Norris #define RK3399_SET_ODT_PD_1_PD_IDLE			GENMASK(11, 0)
36a5ca1854SBrian Norris #define RK3399_SET_ODT_PD_1_SRPD_LITE_IDLE		GENMASK(27, 16)
37a5ca1854SBrian Norris 
38a5ca1854SBrian Norris #define RK3399_SET_ODT_PD_2_ODT_ENABLE			BIT(0)
39a5ca1854SBrian Norris 
405a893e31SLin Huang struct rk3399_dmcfreq {
415a893e31SLin Huang 	struct device *dev;
425a893e31SLin Huang 	struct devfreq *devfreq;
435d521a30SBrian Norris 	struct devfreq_dev_profile profile;
445a893e31SLin Huang 	struct devfreq_simple_ondemand_data ondemand_data;
455a893e31SLin Huang 	struct clk *dmc_clk;
465a893e31SLin Huang 	struct devfreq_event_dev *edev;
475a893e31SLin Huang 	struct mutex lock;
485a893e31SLin Huang 	struct regulator *vdd_center;
499173c5ceSEnric Balletbo i Serra 	struct regmap *regmap_pmu;
505a893e31SLin Huang 	unsigned long rate, target_rate;
515a893e31SLin Huang 	unsigned long volt, target_volt;
529173c5ceSEnric Balletbo i Serra 	unsigned int odt_dis_freq;
53b82acf82SBrian Norris 
54fd5b8479SBrian Norris 	unsigned int pd_idle_ns;
55fd5b8479SBrian Norris 	unsigned int sr_idle_ns;
56fd5b8479SBrian Norris 	unsigned int sr_mc_gate_idle_ns;
57fd5b8479SBrian Norris 	unsigned int srpd_lite_idle_ns;
58fd5b8479SBrian Norris 	unsigned int standby_idle_ns;
59b82acf82SBrian Norris 	unsigned int ddr3_odt_dis_freq;
60b82acf82SBrian Norris 	unsigned int lpddr3_odt_dis_freq;
61b82acf82SBrian Norris 	unsigned int lpddr4_odt_dis_freq;
62e4421721SBrian Norris 
63e4421721SBrian Norris 	unsigned int pd_idle_dis_freq;
64e4421721SBrian Norris 	unsigned int sr_idle_dis_freq;
65e4421721SBrian Norris 	unsigned int sr_mc_gate_idle_dis_freq;
66e4421721SBrian Norris 	unsigned int srpd_lite_idle_dis_freq;
67e4421721SBrian Norris 	unsigned int standby_idle_dis_freq;
685a893e31SLin Huang };
695a893e31SLin Huang 
rk3399_dmcfreq_target(struct device * dev,unsigned long * freq,u32 flags)705a893e31SLin Huang static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
715a893e31SLin Huang 				 u32 flags)
725a893e31SLin Huang {
735a893e31SLin Huang 	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
745a893e31SLin Huang 	struct dev_pm_opp *opp;
755a893e31SLin Huang 	unsigned long old_clk_rate = dmcfreq->rate;
765a893e31SLin Huang 	unsigned long target_volt, target_rate;
77fd5b8479SBrian Norris 	unsigned int ddrcon_mhz;
789173c5ceSEnric Balletbo i Serra 	struct arm_smccc_res res;
795a893e31SLin Huang 	int err;
805a893e31SLin Huang 
81fd5b8479SBrian Norris 	u32 odt_pd_arg0 = 0;
82fd5b8479SBrian Norris 	u32 odt_pd_arg1 = 0;
83fd5b8479SBrian Norris 	u32 odt_pd_arg2 = 0;
84fd5b8479SBrian Norris 
855a893e31SLin Huang 	opp = devfreq_recommended_opp(dev, freq, flags);
868a31d9d9SViresh Kumar 	if (IS_ERR(opp))
875a893e31SLin Huang 		return PTR_ERR(opp);
885a893e31SLin Huang 
895a893e31SLin Huang 	target_rate = dev_pm_opp_get_freq(opp);
905a893e31SLin Huang 	target_volt = dev_pm_opp_get_voltage(opp);
918a31d9d9SViresh Kumar 	dev_pm_opp_put(opp);
925a893e31SLin Huang 
935a893e31SLin Huang 	if (dmcfreq->rate == target_rate)
945a893e31SLin Huang 		return 0;
955a893e31SLin Huang 
965a893e31SLin Huang 	mutex_lock(&dmcfreq->lock);
975a893e31SLin Huang 
98fd5b8479SBrian Norris 	/*
992e691421SBrian Norris 	 * Ensure power-domain transitions don't interfere with ARM Trusted
1002e691421SBrian Norris 	 * Firmware power-domain idling.
1012e691421SBrian Norris 	 */
1022e691421SBrian Norris 	err = rockchip_pmu_block();
1032e691421SBrian Norris 	if (err) {
1042e691421SBrian Norris 		dev_err(dev, "Failed to block PMU: %d\n", err);
1052e691421SBrian Norris 		goto out_unlock;
1062e691421SBrian Norris 	}
1072e691421SBrian Norris 
1082e691421SBrian Norris 	/*
109fd5b8479SBrian Norris 	 * Some idle parameters may be based on the DDR controller clock, which
110fd5b8479SBrian Norris 	 * is half of the DDR frequency.
111fd5b8479SBrian Norris 	 * pd_idle and standby_idle are based on the controller clock cycle.
112fd5b8479SBrian Norris 	 * sr_idle_cycle, sr_mc_gate_idle_cycle, and srpd_lite_idle_cycle
113fd5b8479SBrian Norris 	 * are based on the 1024 controller clock cycle
114fd5b8479SBrian Norris 	 */
115fd5b8479SBrian Norris 	ddrcon_mhz = target_rate / USEC_PER_SEC / 2;
116a5ca1854SBrian Norris 
117fd5b8479SBrian Norris 	u32p_replace_bits(&odt_pd_arg1,
118fd5b8479SBrian Norris 			  NS_TO_CYCLE(dmcfreq->pd_idle_ns, ddrcon_mhz),
119fd5b8479SBrian Norris 			  RK3399_SET_ODT_PD_1_PD_IDLE);
120fd5b8479SBrian Norris 	u32p_replace_bits(&odt_pd_arg0,
121fd5b8479SBrian Norris 			  NS_TO_CYCLE(dmcfreq->standby_idle_ns, ddrcon_mhz),
122fd5b8479SBrian Norris 			  RK3399_SET_ODT_PD_0_STANDBY_IDLE);
123fd5b8479SBrian Norris 	u32p_replace_bits(&odt_pd_arg0,
124fd5b8479SBrian Norris 			  DIV_ROUND_UP(NS_TO_CYCLE(dmcfreq->sr_idle_ns,
125fd5b8479SBrian Norris 						   ddrcon_mhz), 1024),
126fd5b8479SBrian Norris 			  RK3399_SET_ODT_PD_0_SR_IDLE);
127fd5b8479SBrian Norris 	u32p_replace_bits(&odt_pd_arg0,
128fd5b8479SBrian Norris 			  DIV_ROUND_UP(NS_TO_CYCLE(dmcfreq->sr_mc_gate_idle_ns,
129fd5b8479SBrian Norris 						   ddrcon_mhz), 1024),
130fd5b8479SBrian Norris 			  RK3399_SET_ODT_PD_0_SR_MC_GATE_IDLE);
131fd5b8479SBrian Norris 	u32p_replace_bits(&odt_pd_arg1,
132fd5b8479SBrian Norris 			  DIV_ROUND_UP(NS_TO_CYCLE(dmcfreq->srpd_lite_idle_ns,
133fd5b8479SBrian Norris 						   ddrcon_mhz), 1024),
134fd5b8479SBrian Norris 			  RK3399_SET_ODT_PD_1_SRPD_LITE_IDLE);
135fd5b8479SBrian Norris 
136fd5b8479SBrian Norris 	if (dmcfreq->regmap_pmu) {
137e4421721SBrian Norris 		if (target_rate >= dmcfreq->sr_idle_dis_freq)
138e4421721SBrian Norris 			odt_pd_arg0 &= ~RK3399_SET_ODT_PD_0_SR_IDLE;
139e4421721SBrian Norris 
140e4421721SBrian Norris 		if (target_rate >= dmcfreq->sr_mc_gate_idle_dis_freq)
141e4421721SBrian Norris 			odt_pd_arg0 &= ~RK3399_SET_ODT_PD_0_SR_MC_GATE_IDLE;
142e4421721SBrian Norris 
143e4421721SBrian Norris 		if (target_rate >= dmcfreq->standby_idle_dis_freq)
144e4421721SBrian Norris 			odt_pd_arg0 &= ~RK3399_SET_ODT_PD_0_STANDBY_IDLE;
145e4421721SBrian Norris 
146e4421721SBrian Norris 		if (target_rate >= dmcfreq->pd_idle_dis_freq)
147e4421721SBrian Norris 			odt_pd_arg1 &= ~RK3399_SET_ODT_PD_1_PD_IDLE;
148e4421721SBrian Norris 
149e4421721SBrian Norris 		if (target_rate >= dmcfreq->srpd_lite_idle_dis_freq)
150e4421721SBrian Norris 			odt_pd_arg1 &= ~RK3399_SET_ODT_PD_1_SRPD_LITE_IDLE;
151e4421721SBrian Norris 
1529173c5ceSEnric Balletbo i Serra 		if (target_rate >= dmcfreq->odt_dis_freq)
153a5ca1854SBrian Norris 			odt_pd_arg2 |= RK3399_SET_ODT_PD_2_ODT_ENABLE;
1549173c5ceSEnric Balletbo i Serra 
1559173c5ceSEnric Balletbo i Serra 		/*
15663ef91f2SMarc Zyngier 		 * This makes a SMC call to the TF-A to set the DDR PD
15763ef91f2SMarc Zyngier 		 * (power-down) timings and to enable or disable the
15863ef91f2SMarc Zyngier 		 * ODT (on-die termination) resistors.
1599173c5ceSEnric Balletbo i Serra 		 */
160e4421721SBrian Norris 		arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, odt_pd_arg0, odt_pd_arg1,
161e4421721SBrian Norris 			      ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD, odt_pd_arg2,
162e4421721SBrian Norris 			      0, 0, 0, &res);
16363ef91f2SMarc Zyngier 	}
1649173c5ceSEnric Balletbo i Serra 
1655a893e31SLin Huang 	/*
1665a893e31SLin Huang 	 * If frequency scaling from low to high, adjust voltage first.
1675a893e31SLin Huang 	 * If frequency scaling from high to low, adjust frequency first.
1685a893e31SLin Huang 	 */
1695a893e31SLin Huang 	if (old_clk_rate < target_rate) {
1705a893e31SLin Huang 		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
1715a893e31SLin Huang 					    target_volt);
1725a893e31SLin Huang 		if (err) {
173dfa7d764SEnric Balletbo i Serra 			dev_err(dev, "Cannot set voltage %lu uV\n",
1745a893e31SLin Huang 				target_volt);
1755a893e31SLin Huang 			goto out;
1765a893e31SLin Huang 		}
1775a893e31SLin Huang 	}
1785a893e31SLin Huang 
1795a893e31SLin Huang 	err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
1805a893e31SLin Huang 	if (err) {
181dfa7d764SEnric Balletbo i Serra 		dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate,
182dfa7d764SEnric Balletbo i Serra 			err);
1835a893e31SLin Huang 		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
1845a893e31SLin Huang 				      dmcfreq->volt);
1855a893e31SLin Huang 		goto out;
1865a893e31SLin Huang 	}
1875a893e31SLin Huang 
1885a893e31SLin Huang 	/*
1895a893e31SLin Huang 	 * Check the dpll rate,
1905a893e31SLin Huang 	 * There only two result we will get,
1915a893e31SLin Huang 	 * 1. Ddr frequency scaling fail, we still get the old rate.
1925a893e31SLin Huang 	 * 2. Ddr frequency scaling sucessful, we get the rate we set.
1935a893e31SLin Huang 	 */
1945a893e31SLin Huang 	dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
1955a893e31SLin Huang 
1965a893e31SLin Huang 	/* If get the incorrect rate, set voltage to old value. */
1975a893e31SLin Huang 	if (dmcfreq->rate != target_rate) {
198dfa7d764SEnric Balletbo i Serra 		dev_err(dev, "Got wrong frequency, Request %lu, Current %lu\n",
199dfa7d764SEnric Balletbo i Serra 			target_rate, dmcfreq->rate);
2005a893e31SLin Huang 		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
2015a893e31SLin Huang 				      dmcfreq->volt);
2025a893e31SLin Huang 		goto out;
2035a893e31SLin Huang 	} else if (old_clk_rate > target_rate)
2045a893e31SLin Huang 		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
2055a893e31SLin Huang 					    target_volt);
2065a893e31SLin Huang 	if (err)
207dfa7d764SEnric Balletbo i Serra 		dev_err(dev, "Cannot set voltage %lu uV\n", target_volt);
2085a893e31SLin Huang 
209e37d3508SViresh Kumar 	dmcfreq->rate = target_rate;
210e37d3508SViresh Kumar 	dmcfreq->volt = target_volt;
211e37d3508SViresh Kumar 
2125a893e31SLin Huang out:
2132e691421SBrian Norris 	rockchip_pmu_unblock();
2142e691421SBrian Norris out_unlock:
2155a893e31SLin Huang 	mutex_unlock(&dmcfreq->lock);
2165a893e31SLin Huang 	return err;
2175a893e31SLin Huang }
2185a893e31SLin Huang 
rk3399_dmcfreq_get_dev_status(struct device * dev,struct devfreq_dev_status * stat)2195a893e31SLin Huang static int rk3399_dmcfreq_get_dev_status(struct device *dev,
2205a893e31SLin Huang 					 struct devfreq_dev_status *stat)
2215a893e31SLin Huang {
2225a893e31SLin Huang 	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
2235a893e31SLin Huang 	struct devfreq_event_data edata;
2245a893e31SLin Huang 	int ret = 0;
2255a893e31SLin Huang 
2265a893e31SLin Huang 	ret = devfreq_event_get_event(dmcfreq->edev, &edata);
2275a893e31SLin Huang 	if (ret < 0)
2285a893e31SLin Huang 		return ret;
2295a893e31SLin Huang 
2305a893e31SLin Huang 	stat->current_frequency = dmcfreq->rate;
2315a893e31SLin Huang 	stat->busy_time = edata.load_count;
2325a893e31SLin Huang 	stat->total_time = edata.total_count;
2335a893e31SLin Huang 
2345a893e31SLin Huang 	return ret;
2355a893e31SLin Huang }
2365a893e31SLin Huang 
rk3399_dmcfreq_get_cur_freq(struct device * dev,unsigned long * freq)2375a893e31SLin Huang static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
2385a893e31SLin Huang {
2395a893e31SLin Huang 	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
2405a893e31SLin Huang 
2415a893e31SLin Huang 	*freq = dmcfreq->rate;
2425a893e31SLin Huang 
2435a893e31SLin Huang 	return 0;
2445a893e31SLin Huang }
2455a893e31SLin Huang 
rk3399_dmcfreq_suspend(struct device * dev)2465a893e31SLin Huang static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
2475a893e31SLin Huang {
2485a893e31SLin Huang 	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
2495a893e31SLin Huang 	int ret = 0;
2505a893e31SLin Huang 
2515a893e31SLin Huang 	ret = devfreq_event_disable_edev(dmcfreq->edev);
2525a893e31SLin Huang 	if (ret < 0) {
2535a893e31SLin Huang 		dev_err(dev, "failed to disable the devfreq-event devices\n");
2545a893e31SLin Huang 		return ret;
2555a893e31SLin Huang 	}
2565a893e31SLin Huang 
2575a893e31SLin Huang 	ret = devfreq_suspend_device(dmcfreq->devfreq);
2585a893e31SLin Huang 	if (ret < 0) {
2595a893e31SLin Huang 		dev_err(dev, "failed to suspend the devfreq devices\n");
2605a893e31SLin Huang 		return ret;
2615a893e31SLin Huang 	}
2625a893e31SLin Huang 
2635a893e31SLin Huang 	return 0;
2645a893e31SLin Huang }
2655a893e31SLin Huang 
rk3399_dmcfreq_resume(struct device * dev)2665a893e31SLin Huang static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
2675a893e31SLin Huang {
2685a893e31SLin Huang 	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
2695a893e31SLin Huang 	int ret = 0;
2705a893e31SLin Huang 
2715a893e31SLin Huang 	ret = devfreq_event_enable_edev(dmcfreq->edev);
2725a893e31SLin Huang 	if (ret < 0) {
2735a893e31SLin Huang 		dev_err(dev, "failed to enable the devfreq-event devices\n");
2745a893e31SLin Huang 		return ret;
2755a893e31SLin Huang 	}
2765a893e31SLin Huang 
2775a893e31SLin Huang 	ret = devfreq_resume_device(dmcfreq->devfreq);
2785a893e31SLin Huang 	if (ret < 0) {
2795a893e31SLin Huang 		dev_err(dev, "failed to resume the devfreq devices\n");
2805a893e31SLin Huang 		return ret;
2815a893e31SLin Huang 	}
2825a893e31SLin Huang 	return ret;
2835a893e31SLin Huang }
2845a893e31SLin Huang 
2855a893e31SLin Huang static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
2865a893e31SLin Huang 			 rk3399_dmcfreq_resume);
2875a893e31SLin Huang 
rk3399_dmcfreq_of_props(struct rk3399_dmcfreq * data,struct device_node * np)288b82acf82SBrian Norris static int rk3399_dmcfreq_of_props(struct rk3399_dmcfreq *data,
2895a893e31SLin Huang 				   struct device_node *np)
2905a893e31SLin Huang {
2915a893e31SLin Huang 	int ret = 0;
2925a893e31SLin Huang 
293e4421721SBrian Norris 	/*
294e4421721SBrian Norris 	 * These are all optional, and serve as minimum bounds. Give them large
295e4421721SBrian Norris 	 * (i.e., never "disabled") values if the DT doesn't specify one.
296e4421721SBrian Norris 	 */
297e4421721SBrian Norris 	data->pd_idle_dis_freq =
298e4421721SBrian Norris 		data->sr_idle_dis_freq =
299e4421721SBrian Norris 		data->sr_mc_gate_idle_dis_freq =
300e4421721SBrian Norris 		data->srpd_lite_idle_dis_freq =
301e4421721SBrian Norris 		data->standby_idle_dis_freq = UINT_MAX;
302e4421721SBrian Norris 
303fd5b8479SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,pd-idle-ns",
304fd5b8479SBrian Norris 				    &data->pd_idle_ns);
305fd5b8479SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,sr-idle-ns",
306fd5b8479SBrian Norris 				    &data->sr_idle_ns);
307fd5b8479SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,sr-mc-gate-idle-ns",
308fd5b8479SBrian Norris 				    &data->sr_mc_gate_idle_ns);
309fd5b8479SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,srpd-lite-idle-ns",
310fd5b8479SBrian Norris 				    &data->srpd_lite_idle_ns);
311fd5b8479SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,standby-idle-ns",
312fd5b8479SBrian Norris 				    &data->standby_idle_ns);
3135a893e31SLin Huang 	ret |= of_property_read_u32(np, "rockchip,ddr3_odt_dis_freq",
314b82acf82SBrian Norris 				    &data->ddr3_odt_dis_freq);
3155a893e31SLin Huang 	ret |= of_property_read_u32(np, "rockchip,lpddr3_odt_dis_freq",
316b82acf82SBrian Norris 				    &data->lpddr3_odt_dis_freq);
3175a893e31SLin Huang 	ret |= of_property_read_u32(np, "rockchip,lpddr4_odt_dis_freq",
318b82acf82SBrian Norris 				    &data->lpddr4_odt_dis_freq);
3195a893e31SLin Huang 
320e4421721SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,pd-idle-dis-freq-hz",
321e4421721SBrian Norris 				    &data->pd_idle_dis_freq);
322e4421721SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,sr-idle-dis-freq-hz",
323e4421721SBrian Norris 				    &data->sr_idle_dis_freq);
324e4421721SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,sr-mc-gate-idle-dis-freq-hz",
325e4421721SBrian Norris 				    &data->sr_mc_gate_idle_dis_freq);
326e4421721SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,srpd-lite-idle-dis-freq-hz",
327e4421721SBrian Norris 				    &data->srpd_lite_idle_dis_freq);
328e4421721SBrian Norris 	ret |= of_property_read_u32(np, "rockchip,standby-idle-dis-freq-hz",
329e4421721SBrian Norris 				    &data->standby_idle_dis_freq);
330e4421721SBrian Norris 
3315a893e31SLin Huang 	return ret;
3325a893e31SLin Huang }
3335a893e31SLin Huang 
rk3399_dmcfreq_probe(struct platform_device * pdev)3345a893e31SLin Huang static int rk3399_dmcfreq_probe(struct platform_device *pdev)
3355a893e31SLin Huang {
3365a893e31SLin Huang 	struct arm_smccc_res res;
3375a893e31SLin Huang 	struct device *dev = &pdev->dev;
3389173c5ceSEnric Balletbo i Serra 	struct device_node *np = pdev->dev.of_node, *node;
3395a893e31SLin Huang 	struct rk3399_dmcfreq *data;
340b82acf82SBrian Norris 	int ret;
3415a893e31SLin Huang 	struct dev_pm_opp *opp;
3429173c5ceSEnric Balletbo i Serra 	u32 ddr_type;
3439173c5ceSEnric Balletbo i Serra 	u32 val;
3445a893e31SLin Huang 
3455a893e31SLin Huang 	data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
3465a893e31SLin Huang 	if (!data)
3475a893e31SLin Huang 		return -ENOMEM;
3485a893e31SLin Huang 
3495a893e31SLin Huang 	mutex_init(&data->lock);
3505a893e31SLin Huang 
3515a893e31SLin Huang 	data->vdd_center = devm_regulator_get(dev, "center");
352fbf821ecSKrzysztof Kozlowski 	if (IS_ERR(data->vdd_center))
353fbf821ecSKrzysztof Kozlowski 		return dev_err_probe(dev, PTR_ERR(data->vdd_center),
354fbf821ecSKrzysztof Kozlowski 				     "Cannot get the regulator \"center\"\n");
3555a893e31SLin Huang 
3565a893e31SLin Huang 	data->dmc_clk = devm_clk_get(dev, "dmc_clk");
357fbf821ecSKrzysztof Kozlowski 	if (IS_ERR(data->dmc_clk))
358fbf821ecSKrzysztof Kozlowski 		return dev_err_probe(dev, PTR_ERR(data->dmc_clk),
359fbf821ecSKrzysztof Kozlowski 				     "Cannot get the clk dmc_clk\n");
3605a893e31SLin Huang 
36102bdbf7dSChanwoo Choi 	data->edev = devfreq_event_get_edev_by_phandle(dev, "devfreq-events", 0);
3625a893e31SLin Huang 	if (IS_ERR(data->edev))
3635a893e31SLin Huang 		return -EPROBE_DEFER;
3645a893e31SLin Huang 
3655a893e31SLin Huang 	ret = devfreq_event_enable_edev(data->edev);
3665a893e31SLin Huang 	if (ret < 0) {
3675a893e31SLin Huang 		dev_err(dev, "failed to enable devfreq-event devices\n");
3685a893e31SLin Huang 		return ret;
3695a893e31SLin Huang 	}
3705a893e31SLin Huang 
371b82acf82SBrian Norris 	rk3399_dmcfreq_of_props(data, np);
3725a893e31SLin Huang 
3739173c5ceSEnric Balletbo i Serra 	node = of_parse_phandle(np, "rockchip,pmu", 0);
37463ef91f2SMarc Zyngier 	if (!node)
37563ef91f2SMarc Zyngier 		goto no_pmu;
37663ef91f2SMarc Zyngier 
3779173c5ceSEnric Balletbo i Serra 	data->regmap_pmu = syscon_node_to_regmap(node);
37829d867e9SYangtao Li 	of_node_put(node);
37939a6e473SYangtao Li 	if (IS_ERR(data->regmap_pmu)) {
38039a6e473SYangtao Li 		ret = PTR_ERR(data->regmap_pmu);
38139a6e473SYangtao Li 		goto err_edev;
38239a6e473SYangtao Li 	}
3839173c5ceSEnric Balletbo i Serra 
3849173c5ceSEnric Balletbo i Serra 	regmap_read(data->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
38574002e66SSascha Hauer 	ddr_type = FIELD_GET(RK3399_PMUGRF_OS_REG2_DDRTYPE, val);
3869173c5ceSEnric Balletbo i Serra 
3879173c5ceSEnric Balletbo i Serra 	switch (ddr_type) {
38874002e66SSascha Hauer 	case ROCKCHIP_DDRTYPE_DDR3:
389b82acf82SBrian Norris 		data->odt_dis_freq = data->ddr3_odt_dis_freq;
3909173c5ceSEnric Balletbo i Serra 		break;
39174002e66SSascha Hauer 	case ROCKCHIP_DDRTYPE_LPDDR3:
392b82acf82SBrian Norris 		data->odt_dis_freq = data->lpddr3_odt_dis_freq;
3939173c5ceSEnric Balletbo i Serra 		break;
39474002e66SSascha Hauer 	case ROCKCHIP_DDRTYPE_LPDDR4:
395b82acf82SBrian Norris 		data->odt_dis_freq = data->lpddr4_odt_dis_freq;
3969173c5ceSEnric Balletbo i Serra 		break;
3979173c5ceSEnric Balletbo i Serra 	default:
39839a6e473SYangtao Li 		ret = -EINVAL;
39939a6e473SYangtao Li 		goto err_edev;
400fc1745c0SYang Li 	}
4019173c5ceSEnric Balletbo i Serra 
40263ef91f2SMarc Zyngier no_pmu:
4035a893e31SLin Huang 	arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
4045a893e31SLin Huang 		      ROCKCHIP_SIP_CONFIG_DRAM_INIT,
4055a893e31SLin Huang 		      0, 0, 0, 0, &res);
4065a893e31SLin Huang 
4075a893e31SLin Huang 	/*
4085a893e31SLin Huang 	 * We add a devfreq driver to our parent since it has a device tree node
4095a893e31SLin Huang 	 * with operating points.
4105a893e31SLin Huang 	 */
411cb178a95SBrian Norris 	if (devm_pm_opp_of_add_table(dev)) {
4125a893e31SLin Huang 		dev_err(dev, "Invalid operating-points in device tree.\n");
41339a6e473SYangtao Li 		ret = -EINVAL;
41439a6e473SYangtao Li 		goto err_edev;
4155a893e31SLin Huang 	}
4165a893e31SLin Huang 
4175f50c52fSBrian Norris 	data->ondemand_data.upthreshold = 25;
4185f50c52fSBrian Norris 	data->ondemand_data.downdifferential = 15;
4195a893e31SLin Huang 
4205a893e31SLin Huang 	data->rate = clk_get_rate(data->dmc_clk);
4215a893e31SLin Huang 
4225a893e31SLin Huang 	opp = devfreq_recommended_opp(dev, &data->rate, 0);
423d6e98f3eSEnric Balletbo i Serra 	if (IS_ERR(opp)) {
424d6e98f3eSEnric Balletbo i Serra 		ret = PTR_ERR(opp);
425cb178a95SBrian Norris 		goto err_edev;
426d6e98f3eSEnric Balletbo i Serra 	}
4278a31d9d9SViresh Kumar 
428e37d3508SViresh Kumar 	data->rate = dev_pm_opp_get_freq(opp);
429e37d3508SViresh Kumar 	data->volt = dev_pm_opp_get_voltage(opp);
4308a31d9d9SViresh Kumar 	dev_pm_opp_put(opp);
4315a893e31SLin Huang 
4325d521a30SBrian Norris 	data->profile = (struct devfreq_dev_profile) {
4335d521a30SBrian Norris 		.polling_ms	= 200,
4345d521a30SBrian Norris 		.target		= rk3399_dmcfreq_target,
4355d521a30SBrian Norris 		.get_dev_status	= rk3399_dmcfreq_get_dev_status,
4365d521a30SBrian Norris 		.get_cur_freq	= rk3399_dmcfreq_get_cur_freq,
4375d521a30SBrian Norris 		.initial_freq	= data->rate,
4385d521a30SBrian Norris 	};
4395a893e31SLin Huang 
440927b75a6SChanwoo Choi 	data->devfreq = devm_devfreq_add_device(dev,
4415d521a30SBrian Norris 					   &data->profile,
442aa7c352fSChanwoo Choi 					   DEVFREQ_GOV_SIMPLE_ONDEMAND,
4435a893e31SLin Huang 					   &data->ondemand_data);
444d6e98f3eSEnric Balletbo i Serra 	if (IS_ERR(data->devfreq)) {
445d6e98f3eSEnric Balletbo i Serra 		ret = PTR_ERR(data->devfreq);
446cb178a95SBrian Norris 		goto err_edev;
447d6e98f3eSEnric Balletbo i Serra 	}
448d6e98f3eSEnric Balletbo i Serra 
4495a893e31SLin Huang 	devm_devfreq_register_opp_notifier(dev, data->devfreq);
4505a893e31SLin Huang 
4515a893e31SLin Huang 	data->dev = dev;
4525a893e31SLin Huang 	platform_set_drvdata(pdev, data);
4535a893e31SLin Huang 
4545a893e31SLin Huang 	return 0;
455d6e98f3eSEnric Balletbo i Serra 
45639a6e473SYangtao Li err_edev:
45739a6e473SYangtao Li 	devfreq_event_disable_edev(data->edev);
45839a6e473SYangtao Li 
459d6e98f3eSEnric Balletbo i Serra 	return ret;
460d6e98f3eSEnric Balletbo i Serra }
461d6e98f3eSEnric Balletbo i Serra 
rk3399_dmcfreq_remove(struct platform_device * pdev)462*8eba5b69SUwe Kleine-König static void rk3399_dmcfreq_remove(struct platform_device *pdev)
463d6e98f3eSEnric Balletbo i Serra {
464d6e98f3eSEnric Balletbo i Serra 	struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(&pdev->dev);
465d6e98f3eSEnric Balletbo i Serra 
4662fccf9e6SBrian Norris 	devfreq_event_disable_edev(dmcfreq->edev);
4675a893e31SLin Huang }
4685a893e31SLin Huang 
4695a893e31SLin Huang static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
4705a893e31SLin Huang 	{ .compatible = "rockchip,rk3399-dmc" },
4715a893e31SLin Huang 	{ },
4725a893e31SLin Huang };
4732f3f1a26SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, rk3399dmc_devfreq_of_match);
4745a893e31SLin Huang 
4755a893e31SLin Huang static struct platform_driver rk3399_dmcfreq_driver = {
4765a893e31SLin Huang 	.probe	= rk3399_dmcfreq_probe,
477*8eba5b69SUwe Kleine-König 	.remove_new = rk3399_dmcfreq_remove,
4785a893e31SLin Huang 	.driver = {
4795a893e31SLin Huang 		.name	= "rk3399-dmc-freq",
4805a893e31SLin Huang 		.pm	= &rk3399_dmcfreq_pm,
4815a893e31SLin Huang 		.of_match_table = rk3399dmc_devfreq_of_match,
4825a893e31SLin Huang 	},
4835a893e31SLin Huang };
4845a893e31SLin Huang module_platform_driver(rk3399_dmcfreq_driver);
4855a893e31SLin Huang 
4865a893e31SLin Huang MODULE_LICENSE("GPL v2");
4875a893e31SLin Huang MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
4885a893e31SLin Huang MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
489