diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 14a7c6ec8b03..ad196347b448 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1034,13 +1034,11 @@ l3_pwrlevel_probe(struct kgsl_device *device, struct device_node *node) &device->l3_freq[index]); } - device->l3_clk = devm_clk_get(&device->pdev->dev, "l3_vote"); + device->l3_icc = of_icc_get(&device->pdev->dev, "l3_path"); - if (IS_ERR_OR_NULL(device->l3_clk)) { + if (IS_ERR(device->l3_icc)) dev_err(&device->pdev->dev, - "Unable to get the l3_vote clock\n"); - device->l3_clk = NULL; - } + "Unable to get the l3 icc path\n"); } static int adreno_of_get_power(struct adreno_device *adreno_dev, diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c index 89dd85db28d1..bee7f499a6f4 100644 --- a/drivers/gpu/msm/adreno_a6xx.c +++ b/drivers/gpu/msm/adreno_a6xx.c @@ -129,6 +129,36 @@ static void a6xx_gmu_wrapper_init(struct adreno_device *adreno_dev) dev_warn(device->dev, "gmu_wrapper ioremap failed\n"); } +static int match_name(struct device *dev, void *data) +{ + struct device *parent = data; + + return (!strcmp(dev_name(dev), dev_name(parent))); +} + +static void find_ddr_qos_device(struct adreno_device *adreno_dev) +{ + struct device *devfreq_dev, *ddr_qos_dev; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + if (device->pwrctrl.ddr_qos_devfreq) + return; + + ddr_qos_dev = bus_find_device_by_name(&platform_bus_type, NULL, + "soc:qcom,kgsl-ddr-qos"); + + if (!ddr_qos_dev) + return; + + /* Devfreq device has the same name as its parent device */ + devfreq_dev = device_find_child(ddr_qos_dev, ddr_qos_dev, match_name); + if (!devfreq_dev) + return; + + device->pwrctrl.ddr_qos_devfreq = container_of(devfreq_dev, + struct devfreq, dev); +} + int a6xx_init(struct adreno_device *adreno_dev) { const struct adreno_a6xx_core *a6xx_core = to_a6xx_core(adreno_dev); @@ -157,6 +187,8 @@ int a6xx_init(struct adreno_device *adreno_dev) return PTR_ERR(adreno_dev->pwrup_reglist); } + find_ddr_qos_device(adreno_dev); + return a6xx_get_cp_init_cmds(adreno_dev); } diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c index 69877654c764..c47a00d7c211 100644 --- a/drivers/gpu/msm/adreno_a6xx_gmu.c +++ b/drivers/gpu/msm/adreno_a6xx_gmu.c @@ -3103,6 +3103,8 @@ no_gx_power: kgsl_pwrscale_sleep(device); + kgsl_pwrctrl_clear_l3_vote(device); + trace_kgsl_pwr_set_state(device, KGSL_STATE_SLUMBER); return ret; diff --git a/drivers/gpu/msm/adreno_a6xx_hwsched.c b/drivers/gpu/msm/adreno_a6xx_hwsched.c index 8f40f625a3bc..433454a70619 100644 --- a/drivers/gpu/msm/adreno_a6xx_hwsched.c +++ b/drivers/gpu/msm/adreno_a6xx_hwsched.c @@ -654,6 +654,8 @@ no_gx_power: kgsl_pwrscale_sleep(device); + kgsl_pwrctrl_clear_l3_vote(device); + trace_kgsl_pwr_set_state(device, KGSL_STATE_SLUMBER); return ret; diff --git a/drivers/gpu/msm/adreno_a6xx_rgmu.c b/drivers/gpu/msm/adreno_a6xx_rgmu.c index 3fec3e030674..42a96726809a 100644 --- a/drivers/gpu/msm/adreno_a6xx_rgmu.c +++ b/drivers/gpu/msm/adreno_a6xx_rgmu.c @@ -711,6 +711,7 @@ static void a6xx_rgmu_power_off(struct adreno_device *adreno_dev) a6xx_rgmu_disable_clks(adreno_dev); a6xx_rgmu_disable_gdsc(adreno_dev); + kgsl_pwrctrl_clear_l3_vote(device); } static int a6xx_rgmu_clock_set(struct adreno_device *adreno_dev, diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index dc3d1bd93976..59da31ad2b88 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -3,6 +3,7 @@ * Copyright (c) 2002,2007-2020, The Linux Foundation. All rights reserved. */ +#include #include #include @@ -753,9 +754,9 @@ static void adreno_ringbuffer_set_constraint(struct kgsl_device *device, ((context->flags & KGSL_CONTEXT_PWR_CONSTRAINT) || (flags & KGSL_CONTEXT_PWR_CONSTRAINT))) { - if (device->l3_clk == NULL) { + if (IS_ERR(device->l3_icc)) { dev_err_once(device->dev, - "l3_vote clk not available\n"); + "l3_icc path not available\n"); return; } @@ -776,15 +777,21 @@ static void adreno_ringbuffer_set_constraint(struct kgsl_device *device, new_l3 = min_t(unsigned int, new_l3, device->num_l3_pwrlevels - 1); - ret = clk_set_rate(device->l3_clk, + if (device->cur_l3_pwrlevel == new_l3) + return; + + ret = icc_set_bw(device->l3_icc, 0, device->l3_freq[new_l3]); - if (!ret) + if (!ret) { + trace_kgsl_constraint(device, + KGSL_CONSTRAINT_L3_PWRLEVEL, new_l3, 1); device->cur_l3_pwrlevel = new_l3; - else + } else { dev_err_ratelimited(device->dev, "Could not set l3_vote: %d\n", ret); + } break; } } diff --git a/drivers/gpu/msm/kgsl_bus.c b/drivers/gpu/msm/kgsl_bus.c index 2320ae603430..810a16bbca5e 100644 --- a/drivers/gpu/msm/kgsl_bus.c +++ b/drivers/gpu/msm/kgsl_bus.c @@ -5,7 +5,9 @@ #include #include +#include +#include "../../devfreq/governor.h" #include "kgsl_bus.h" #include "kgsl_device.h" #include "kgsl_trace.h" @@ -30,6 +32,36 @@ static u32 _ab_buslevel_update(struct kgsl_pwrctrl *pwr, return (pwr->bus_percent_ab * pwr->bus_max) / 100; } +static void set_ddr_qos(struct kgsl_device *device, int buslevel) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct devfreq *dev = pwr->ddr_qos_devfreq; + static unsigned long cur_min_freq; + unsigned long new_min_freq = DEVFREQ_MIN_FREQ; + int ret; + + if (!dev) + return; + + /* + * Instead of creating a new dummy governor, we are using powersave + * governor for this devfreq device. Therefore, modify the minimum + * frequency to point to the desired QOS level. + */ + if (buslevel == pwr->pwrlevels[0].bus_max) + new_min_freq = DEVFREQ_MAX_FREQ; + + if (new_min_freq == cur_min_freq) + return; + + mutex_lock(&dev->lock); + dev->min_freq = new_min_freq; + ret = update_devfreq(dev); + mutex_unlock(&dev->lock); + + if (!ret) + cur_min_freq = new_min_freq; +} int kgsl_bus_update(struct kgsl_device *device, enum kgsl_bus_vote vote_state) @@ -37,7 +69,7 @@ int kgsl_bus_update(struct kgsl_device *device, struct kgsl_pwrctrl *pwr = &device->pwrctrl; /* FIXME: this might be wrong? */ int cur = pwr->pwrlevels[pwr->active_pwrlevel].bus_freq; - int buslevel = 0; + int ret, buslevel = 0; u32 ab; /* the bus should be ON to update the active frequency */ @@ -68,7 +100,13 @@ int kgsl_bus_update(struct kgsl_device *device, /* buslevel is the IB vote, update the AB */ ab = _ab_buslevel_update(pwr, pwr->ddr_table[buslevel]); - return device->ftbl->gpu_bus_set(device, buslevel, ab); + ret = device->ftbl->gpu_bus_set(device, buslevel, ab); + if (ret) + return ret; + + set_ddr_qos(device, buslevel); + + return 0; } static void validate_pwrlevels(struct kgsl_device *device, u32 *ibs, @@ -158,7 +196,7 @@ done: validate_pwrlevels(device, pwr->ddr_table, pwr->ddr_table_count); - pwr->icc_path = of_icc_get(&pdev->dev, NULL); + pwr->icc_path = of_icc_get(&pdev->dev, "gpu_icc_path"); if (IS_ERR(pwr->icc_path) && !gmu_core_scales_bandwidth(device)) { WARN(1, "The CPU has no way to set the GPU bus levels\n"); @@ -175,4 +213,6 @@ void kgsl_bus_close(struct kgsl_device *device) kfree(device->pwrctrl.ddr_table); device->pwrctrl.ddr_table = NULL; icc_put(device->pwrctrl.icc_path); + if (device->pwrctrl.ddr_qos_devfreq) + put_device(&device->pwrctrl.ddr_qos_devfreq->dev); } diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 544cabeb3890..4cd2f5e3e5a1 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -288,7 +288,8 @@ struct kgsl_device { /* Number of active contexts seen globally for this device */ int active_context_count; struct kobject gpu_sysfs_kobj; - struct clk *l3_clk; + /** @icc_path: Interconnect path for scaling l3 frequency */ + struct icc_path *l3_icc; unsigned int l3_freq[MAX_L3_LEVELS]; unsigned int num_l3_pwrlevels; /* store current L3 vote to determine if we should change our vote */ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 44c09c3f1dec..00ae40fd4977 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -3,6 +3,7 @@ * Copyright (c) 2010-2020, The Linux Foundation. All rights reserved. */ +#include #include #include #include @@ -1617,6 +1618,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) kgsl_bus_close(device); pm_runtime_disable(&device->pdev->dev); + + icc_put(device->l3_icc); } void kgsl_idle_check(struct work_struct *work) @@ -1752,16 +1755,24 @@ static int kgsl_pwrctrl_enable(struct kgsl_device *device) return device->ftbl->regulator_enable(device); } -static void kgsl_pwrctrl_disable(struct kgsl_device *device) +void kgsl_pwrctrl_clear_l3_vote(struct kgsl_device *device) { int status; - status = clk_set_rate(device->l3_clk, device->l3_freq[0]); + if (IS_ERR(device->l3_icc)) + return; + + status = icc_set_bw(device->l3_icc, 0, device->l3_freq[0]); if (!status) device->cur_l3_pwrlevel = 0; else dev_err(device->dev, "Could not clear l3_vote: %d\n", status); +} + +static void kgsl_pwrctrl_disable(struct kgsl_device *device) +{ + kgsl_pwrctrl_clear_l3_vote(device); /* Order pwrrail/clk sequence based upon platform */ device->ftbl->regulator_disable(device); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index c0666596617a..ad97e33ffea9 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -169,6 +169,8 @@ struct kgsl_pwrctrl { struct timer_list minbw_timer; /** @minbw_timeout - Timeout for entering minimum bandwidth state */ u32 minbw_timeout; + /** @ddr_qos_devfreq: Devfreq device for setting DDR qos policy */ + struct devfreq *ddr_qos_devfreq; }; int kgsl_pwrctrl_init(struct kgsl_device *device); @@ -251,4 +253,11 @@ void kgsl_idle_check(struct work_struct *work); * */ void kgsl_pwrctrl_irq(struct kgsl_device *device, int state); +/** + * kgsl_pwrctrl_clear_l3_vote - Relinquish l3 vote + * @device: Handle to the kgsl device + * + * Clear the l3 vote when going into slumber + */ +void kgsl_pwrctrl_clear_l3_vote(struct kgsl_device *device); #endif /* __KGSL_PWRCTRL_H */ diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h index 320330e456ca..f6097c06d007 100644 --- a/drivers/gpu/msm/kgsl_trace.h +++ b/drivers/gpu/msm/kgsl_trace.h @@ -28,7 +28,9 @@ #define show_constraint(type) \ __print_symbolic(type, \ { KGSL_CONSTRAINT_NONE, "None" }, \ - { KGSL_CONSTRAINT_PWRLEVEL, "Pwrlevel" }) + { KGSL_CONSTRAINT_PWRLEVEL, "Pwrlevel" }, \ + { KGSL_CONSTRAINT_L3_NONE, "L3_none" }, \ + { KGSL_CONSTRAINT_L3_PWRLEVEL, "L3_pwrlevel" }) struct kgsl_ringbuffer_issueibcmds; struct kgsl_device_waittimestamp;