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