Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / devfreq: rk3399_dmc: Pass ODT and auto power down parameters to TF-A.

Trusted Firmware-A (TF-A) for rk3399 implements a SiP call to get the
on-die termination (ODT) and auto power down parameters from kernel,
this patch adds the functionality to do this. Also, if DDR clock
frequency is lower than the on-die termination (ODT) disable frequency
this driver should disable the DDR ODT.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Gaël PORTAY <gael.portay@collabora.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>

authored by

Enric Balletbo i Serra and committed by
MyungJoo Ham
9173c5ce adfe3b76

+71 -1
+70 -1
drivers/devfreq/rk3399_dmc.c
··· 18 18 #include <linux/devfreq.h> 19 19 #include <linux/devfreq-event.h> 20 20 #include <linux/interrupt.h> 21 + #include <linux/mfd/syscon.h> 21 22 #include <linux/module.h> 22 23 #include <linux/of.h> 23 24 #include <linux/platform_device.h> 24 25 #include <linux/pm_opp.h> 26 + #include <linux/regmap.h> 25 27 #include <linux/regulator/consumer.h> 26 28 #include <linux/rwsem.h> 27 29 #include <linux/suspend.h> 28 30 31 + #include <soc/rockchip/rk3399_grf.h> 29 32 #include <soc/rockchip/rockchip_sip.h> 30 33 31 34 struct dram_timing { ··· 72 69 struct mutex lock; 73 70 struct dram_timing timing; 74 71 struct regulator *vdd_center; 72 + struct regmap *regmap_pmu; 75 73 unsigned long rate, target_rate; 76 74 unsigned long volt, target_volt; 75 + unsigned int odt_dis_freq; 76 + int odt_pd_arg0, odt_pd_arg1; 77 77 }; 78 78 79 79 static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, ··· 86 80 struct dev_pm_opp *opp; 87 81 unsigned long old_clk_rate = dmcfreq->rate; 88 82 unsigned long target_volt, target_rate; 83 + struct arm_smccc_res res; 84 + bool odt_enable = false; 89 85 int err; 90 86 91 87 opp = devfreq_recommended_opp(dev, freq, flags); ··· 102 94 return 0; 103 95 104 96 mutex_lock(&dmcfreq->lock); 97 + 98 + if (target_rate >= dmcfreq->odt_dis_freq) 99 + odt_enable = true; 100 + 101 + /* 102 + * This makes a SMC call to the TF-A to set the DDR PD (power-down) 103 + * timings and to enable or disable the ODT (on-die termination) 104 + * resistors. 105 + */ 106 + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, dmcfreq->odt_pd_arg0, 107 + dmcfreq->odt_pd_arg1, 108 + ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD, 109 + odt_enable, 0, 0, 0, &res); 105 110 106 111 /* 107 112 * If frequency scaling from low to high, adjust voltage first. ··· 315 294 { 316 295 struct arm_smccc_res res; 317 296 struct device *dev = &pdev->dev; 318 - struct device_node *np = pdev->dev.of_node; 297 + struct device_node *np = pdev->dev.of_node, *node; 319 298 struct rk3399_dmcfreq *data; 320 299 int ret, index, size; 321 300 uint32_t *timing; 322 301 struct dev_pm_opp *opp; 302 + u32 ddr_type; 303 + u32 val; 323 304 324 305 data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL); 325 306 if (!data) ··· 377 354 } 378 355 } 379 356 357 + node = of_parse_phandle(np, "rockchip,pmu", 0); 358 + if (node) { 359 + data->regmap_pmu = syscon_node_to_regmap(node); 360 + if (IS_ERR(data->regmap_pmu)) 361 + return PTR_ERR(data->regmap_pmu); 362 + } 363 + 364 + regmap_read(data->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val); 365 + ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) & 366 + RK3399_PMUGRF_DDRTYPE_MASK; 367 + 368 + switch (ddr_type) { 369 + case RK3399_PMUGRF_DDRTYPE_DDR3: 370 + data->odt_dis_freq = data->timing.ddr3_odt_dis_freq; 371 + break; 372 + case RK3399_PMUGRF_DDRTYPE_LPDDR3: 373 + data->odt_dis_freq = data->timing.lpddr3_odt_dis_freq; 374 + break; 375 + case RK3399_PMUGRF_DDRTYPE_LPDDR4: 376 + data->odt_dis_freq = data->timing.lpddr4_odt_dis_freq; 377 + break; 378 + default: 379 + return -EINVAL; 380 + }; 381 + 380 382 arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, 381 383 ROCKCHIP_SIP_CONFIG_DRAM_INIT, 382 384 0, 0, 0, 0, &res); 385 + 386 + /* 387 + * In TF-A there is a platform SIP call to set the PD (power-down) 388 + * timings and to enable or disable the ODT (on-die termination). 389 + * This call needs three arguments as follows: 390 + * 391 + * arg0: 392 + * bit[0-7] : sr_idle 393 + * bit[8-15] : sr_mc_gate_idle 394 + * bit[16-31] : standby idle 395 + * arg1: 396 + * bit[0-11] : pd_idle 397 + * bit[16-27] : srpd_lite_idle 398 + * arg2: 399 + * bit[0] : odt enable 400 + */ 401 + data->odt_pd_arg0 = (data->timing.sr_idle & 0xff) | 402 + ((data->timing.sr_mc_gate_idle & 0xff) << 8) | 403 + ((data->timing.standby_idle & 0xffff) << 16); 404 + data->odt_pd_arg1 = (data->timing.pd_idle & 0xfff) | 405 + ((data->timing.srpd_lite_idle & 0xfff) << 16); 383 406 384 407 /* 385 408 * We add a devfreq driver to our parent since it has a device tree node
+1
include/soc/rockchip/rockchip_sip.h
··· 23 23 #define ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE 0x05 24 24 #define ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ 0x06 25 25 #define ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM 0x07 26 + #define ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD 0x08 26 27 27 28 #endif