From a3adae63972b5ea2e7fa24020cdc9ed883bedf14 Mon Sep 17 00:00:00 2001 From: Pranav Patel Date: Thu, 30 Jul 2020 12:37:30 +0530 Subject: [PATCH] msm: kgsl: Add GPU minimum bandwidth vote state The GPU is usually clock gated when idle i.e NAP state. Introduce a new state after NAP which lowers the GPU bandwidth vote to minimum non-zero value so that memory related components can run at a lower frequency corner, if possible when GPU is idle. Change-Id: Iee568e8c3039b35d6b58d7fc0fe72880eee2cace Signed-off-by: Pranav Patel --- drivers/gpu/msm/adreno.c | 2 + drivers/gpu/msm/adreno_coresight.c | 4 +- drivers/gpu/msm/kgsl_bus.c | 16 +++-- drivers/gpu/msm/kgsl_bus.h | 8 ++- drivers/gpu/msm/kgsl_device.h | 13 +++- drivers/gpu/msm/kgsl_pwrctrl.c | 106 ++++++++++++++++++++++++++--- drivers/gpu/msm/kgsl_pwrctrl.h | 4 ++ drivers/gpu/msm/kgsl_pwrscale.c | 2 +- 8 files changed, 137 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index ffa411cc1451..b59ba5056b1d 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1077,6 +1077,8 @@ static int adreno_of_get_power(struct adreno_device *adreno_dev, /* Default timeout is 80 ms across all targets */ device->pwrctrl.interval_timeout = msecs_to_jiffies(80); + device->pwrctrl.minbw_timeout = 10; + /* Set default bus control to true on all targets */ device->pwrctrl.bus_control = true; diff --git a/drivers/gpu/msm/adreno_coresight.c b/drivers/gpu/msm/adreno_coresight.c index faa4006c2a93..83a755a6065e 100644 --- a/drivers/gpu/msm/adreno_coresight.c +++ b/drivers/gpu/msm/adreno_coresight.c @@ -60,7 +60,7 @@ ssize_t adreno_coresight_show_register(struct device *dev, */ if (device->state == KGSL_STATE_ACTIVE || - device->state == KGSL_STATE_NAP) { + kgsl_state_is_nap_or_minbw(device)) { if (!adreno_active_count_get(adreno_dev)) { if (!is_cx) kgsl_regread(device, cattr->reg->offset, @@ -115,7 +115,7 @@ ssize_t adreno_coresight_store_register(struct device *dev, /* Program the hardware if it is not power collapsed */ if (device->state == KGSL_STATE_ACTIVE || - device->state == KGSL_STATE_NAP) { + kgsl_state_is_nap_or_minbw(device)) { if (!adreno_active_count_get(adreno_dev)) { if (!is_cx) kgsl_regwrite(device, cattr->reg->offset, diff --git a/drivers/gpu/msm/kgsl_bus.c b/drivers/gpu/msm/kgsl_bus.c index 648c81152d0b..e21a6899d1e0 100644 --- a/drivers/gpu/msm/kgsl_bus.c +++ b/drivers/gpu/msm/kgsl_bus.c @@ -31,7 +31,8 @@ static u32 _ab_buslevel_update(struct kgsl_pwrctrl *pwr, } -int kgsl_bus_update(struct kgsl_device *device, bool on) +int kgsl_bus_update(struct kgsl_device *device, + enum kgsl_bus_vote vote_state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; /* FIXME: this might be wrong? */ @@ -40,17 +41,24 @@ int kgsl_bus_update(struct kgsl_device *device, bool on) u32 ab; /* the bus should be ON to update the active frequency */ - if (on && !(test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags))) + if ((vote_state != KGSL_BUS_VOTE_OFF) && + !(test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags))) return 0; /* * If the bus should remain on calculate our request and submit it, * otherwise request bus level 0, off. */ - if (on) { + if (vote_state == KGSL_BUS_VOTE_ON) { buslevel = min_t(int, pwr->pwrlevels[0].bus_max, cur + pwr->bus_mod); buslevel = max_t(int, buslevel, 1); - } else { + } else if (vote_state == KGSL_BUS_VOTE_MINIMUM) { + /* Request bus level 1, minimum non-zero value */ + buslevel = 1; + pwr->bus_mod = 0; + pwr->bus_percent_ab = 0; + pwr->bus_ab_mbytes = 0; + } else if (vote_state == KGSL_BUS_VOTE_OFF) { /* If the bus is being turned off, reset to default level */ pwr->bus_mod = 0; pwr->bus_percent_ab = 0; diff --git a/drivers/gpu/msm/kgsl_bus.h b/drivers/gpu/msm/kgsl_bus.h index 8fd6d6d8ef08..181423365878 100644 --- a/drivers/gpu/msm/kgsl_bus.h +++ b/drivers/gpu/msm/kgsl_bus.h @@ -6,12 +6,18 @@ #ifndef _KGSL_BUS_H #define _KGSL_BUS_H +enum kgsl_bus_vote { + KGSL_BUS_VOTE_OFF = 0, + KGSL_BUS_VOTE_ON, + KGSL_BUS_VOTE_MINIMUM, +}; + struct kgsl_device; struct platform_device; int kgsl_bus_init(struct kgsl_device *device, struct platform_device *pdev); void kgsl_bus_close(struct kgsl_device *device); -int kgsl_bus_update(struct kgsl_device *device, bool on); +int kgsl_bus_update(struct kgsl_device *device, enum kgsl_bus_vote vote_state); u32 *kgsl_bus_get_table(struct platform_device *pdev, const char *name, int *count); diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index eafb9b342a5d..335321b4f819 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -25,7 +25,8 @@ * past its timer) and all system resources are released. SUSPEND is * * requested by the kernel and will be enforced upon all open devices. * * RESET indicates that GPU or GMU hang happens. KGSL is handling * - * snapshot or recover GPU from hang. * + * snapshot or recover GPU from hang. MINBW implies that DDR BW vote is * + * set to non-zero minimum value. */ #define KGSL_STATE_NONE 0x00000000 @@ -35,6 +36,7 @@ #define KGSL_STATE_SUSPEND 0x00000010 #define KGSL_STATE_AWARE 0x00000020 #define KGSL_STATE_SLUMBER 0x00000080 +#define KGSL_STATE_MINBW 0x00000100 /** * enum kgsl_event_results - result codes passed to an event callback when the @@ -596,6 +598,15 @@ static inline int kgsl_state_is_awake(struct kgsl_device *device) return false; } +static inline bool kgsl_state_is_nap_or_minbw(struct kgsl_device *device) +{ + if (device->state == KGSL_STATE_NAP || + device->state == KGSL_STATE_MINBW) + return true; + + return false; +} + int kgsl_readtimestamp(struct kgsl_device *device, void *priv, enum kgsl_timestamp_type type, unsigned int *timestamp); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index e07c165a3fb1..554f9b051677 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -196,7 +196,7 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, * Update the bus before the GPU clock to prevent underrun during * frequency increases. */ - kgsl_bus_update(device, true); + kgsl_bus_update(device, KGSL_BUS_VOTE_ON); pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel]; /* Change register settings if any BEFORE pwrlevel change*/ @@ -550,6 +550,33 @@ static ssize_t idle_timer_show(struct device *dev, jiffies_to_msecs(device->pwrctrl.interval_timeout)); } +static ssize_t minbw_timer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct kgsl_device *device = dev_get_drvdata(dev); + u32 val; + int ret; + + if (device->pwrctrl.ctrl_flags & BIT(KGSL_PWRFLAGS_NAP_OFF)) + return -EINVAL; + + ret = kstrtou32(buf, 0, &val); + if (ret) + return ret; + + device->pwrctrl.minbw_timeout = val; + return count; +} + +static ssize_t minbw_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct kgsl_device *device = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", + device->pwrctrl.minbw_timeout); +} + static ssize_t gpubusy_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1043,6 +1070,7 @@ static DEVICE_ATTR_RO(temp); static DEVICE_ATTR_RW(gpuclk); static DEVICE_ATTR_RW(max_gpuclk); static DEVICE_ATTR_RW(idle_timer); +static DEVICE_ATTR_RW(minbw_timer); static DEVICE_ATTR_RO(gpubusy); static DEVICE_ATTR_RO(gpu_available_frequencies); static DEVICE_ATTR_RO(gpu_clock_stats); @@ -1070,6 +1098,7 @@ static const struct attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk.attr, &dev_attr_max_gpuclk.attr, &dev_attr_idle_timer.attr, + &dev_attr_minbw_timer.attr, &dev_attr_gpubusy.attr, &dev_attr_gpu_available_frequencies.attr, &dev_attr_gpu_clock_stats.attr, @@ -1181,7 +1210,8 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, clk_disable(pwr->grp_clks[i]); /* High latency clock maintenance. */ if ((pwr->pwrlevels[0].gpu_freq > 0) && - (requested_state != KGSL_STATE_NAP)) { + (requested_state != KGSL_STATE_NAP) && + (requested_state != KGSL_STATE_MINBW)) { for (i = KGSL_MAX_CLKS - 1; i > 0; i--) clk_unprepare(pwr->grp_clks[i]); device->ftbl->gpu_clock_set(device, @@ -1209,7 +1239,8 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, trace_kgsl_clk(device, state, kgsl_pwrctrl_active_freq(pwr)); /* High latency clock maintenance. */ - if (device->state != KGSL_STATE_NAP) { + if ((device->state != KGSL_STATE_NAP) && + (device->state != KGSL_STATE_MINBW)) { if (pwr->pwrlevels[0].gpu_freq > 0) { device->ftbl->gpu_clock_set(device, pwr->active_pwrlevel); @@ -1255,13 +1286,13 @@ int kgsl_pwrctrl_axi(struct kgsl_device *device, int state) if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { trace_kgsl_bus(device, state); - return kgsl_bus_update(device, false); + return kgsl_bus_update(device, KGSL_BUS_VOTE_OFF); } } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { trace_kgsl_bus(device, state); - return kgsl_bus_update(device, true); + return kgsl_bus_update(device, KGSL_BUS_VOTE_ON); } } @@ -1352,6 +1383,18 @@ void kgsl_pwrctrl_irq(struct kgsl_device *device, int state) } } +static void kgsl_minbw_timer(struct timer_list *t) +{ + struct kgsl_pwrctrl *pwr = from_timer(pwr, t, minbw_timer); + struct kgsl_device *device = container_of(pwr, + struct kgsl_device, pwrctrl); + + if (device->state == KGSL_STATE_NAP) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_MINBW); + kgsl_schedule_work(&device->idle_check_ws); + } +} + static int _get_clocks(struct kgsl_device *device) { struct device *dev = &device->pdev->dev; @@ -1415,7 +1458,7 @@ static void _gpu_clk_prepare_enable(struct kgsl_device *device, { int ret; - if (device->state == KGSL_STATE_NAP) { + if (kgsl_state_is_nap_or_minbw(device)) { ret = clk_enable(clk); if (ret) goto err; @@ -1527,6 +1570,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pm_runtime_enable(&pdev->dev); + timer_setup(&pwr->minbw_timer, kgsl_minbw_timer, 0); + /* temperature sensor name */ of_property_read_string(pdev->dev.of_node, "qcom,tzone-name", &pwr->tzone_name); @@ -1557,7 +1602,7 @@ void kgsl_idle_check(struct work_struct *work) requested_state = device->requested_state; if (device->state == KGSL_STATE_ACTIVE - || device->state == KGSL_STATE_NAP) { + || kgsl_state_is_nap_or_minbw(device)) { if (!atomic_read(&device->active_cnt)) { spin_lock(&device->submit_lock); @@ -1597,7 +1642,9 @@ done: jiffies + device->pwrctrl.interval_timeout); } - kgsl_pwrscale_update(device); + + if (device->state != KGSL_STATE_MINBW) + kgsl_pwrscale_update(device); mutex_unlock(&device->mutex); } @@ -1709,7 +1756,10 @@ static int _init(struct kgsl_device *device) int status = 0; switch (device->state) { + case KGSL_STATE_MINBW: + /* fall through */ case KGSL_STATE_NAP: + del_timer_sync(&device->pwrctrl.minbw_timer); /* Force power on to do the stop */ status = kgsl_pwrctrl_enable(device); /* fall through */ @@ -1762,6 +1812,9 @@ static int _wake(struct kgsl_device *device) kgsl_pwrscale_wake(device); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); /* fall through */ + case KGSL_STATE_MINBW: + kgsl_bus_update(device, KGSL_BUS_VOTE_ON); + /* fall through */ case KGSL_STATE_NAP: /* Turn on the core clocks */ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); @@ -1783,6 +1836,7 @@ static int _wake(struct kgsl_device *device) pwr->previous_pwrlevel = pwr->active_pwrlevel; mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); + del_timer_sync(&device->pwrctrl.minbw_timer); break; case KGSL_STATE_AWARE: kgsl_pwrctrl_clk_set_options(device, true); @@ -1791,6 +1845,7 @@ static int _wake(struct kgsl_device *device) kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); + del_timer_sync(&device->pwrctrl.minbw_timer); break; default: dev_warn(device->dev, "unhandled state %s\n", @@ -1821,7 +1876,9 @@ _aware(struct kgsl_device *device) case KGSL_STATE_INIT: status = kgsl_pwrctrl_enable(device); break; - /* The following 3 cases shouldn't occur, but don't panic. */ + /* The following 4 cases shouldn't occur, but don't panic. */ + case KGSL_STATE_MINBW: + /* Fall through */ case KGSL_STATE_NAP: status = _wake(device); /* Fall through */ @@ -1865,6 +1922,9 @@ _nap(struct kgsl_device *device) */ kgsl_pwrscale_update_stats(device); + mod_timer(&device->pwrctrl.minbw_timer, jiffies + + msecs_to_jiffies(device->pwrctrl.minbw_timeout)); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_NAP); kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP); /* fallthrough */ @@ -1881,6 +1941,26 @@ _nap(struct kgsl_device *device) return 0; } +static int +_minbw(struct kgsl_device *device) +{ + switch (device->state) { + /* + * Device is expected to be clock gated to move to + * a deeper low power state. No other transition is + * permitted + */ + case KGSL_STATE_NAP: + kgsl_bus_update(device, KGSL_BUS_VOTE_MINIMUM); + kgsl_pwrctrl_set_state(device, KGSL_STATE_MINBW); + break; + default: + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + break; + } + return 0; +} + static int _slumber(struct kgsl_device *device) { @@ -1894,6 +1974,9 @@ _slumber(struct kgsl_device *device) } /* fall through */ case KGSL_STATE_NAP: + /* fall through */ + case KGSL_STATE_MINBW: + del_timer_sync(&device->pwrctrl.minbw_timer); del_timer_sync(&device->idle_timer); kgsl_pwrscale_midframe_timer_cancel(device); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); @@ -1997,6 +2080,9 @@ int kgsl_pwrctrl_change_state(struct kgsl_device *device, int state) case KGSL_STATE_NAP: status = _nap(device); break; + case KGSL_STATE_MINBW: + status = _minbw(device); + break; case KGSL_STATE_SLUMBER: status = _slumber(device); break; @@ -2049,6 +2135,8 @@ const char *kgsl_pwrstate_to_str(unsigned int state) return "ACTIVE"; case KGSL_STATE_NAP: return "NAP"; + case KGSL_STATE_MINBW: + return "MINBW"; case KGSL_STATE_SUSPEND: return "SUSPEND"; case KGSL_STATE_SLUMBER: diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index eeae3cec3568..6747b7d39555 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -163,6 +163,10 @@ struct kgsl_pwrctrl { struct icc_path *icc_path; /** cur_ab: The last ab voted by the driver */ u32 cur_ab; + /** @minbw_timer - Timer struct for entering minimum bandwidth state */ + struct timer_list minbw_timer; + /** @minbw_timeout - Timeout for entering minimum bandwidth state */ + u32 minbw_timeout; }; int kgsl_pwrctrl_init(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index a0644d228ca9..995aa28143c2 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -588,7 +588,7 @@ int kgsl_busmon_target(struct device *dev, unsigned long *freq, u32 flags) if ((pwr->bus_mod != b) || (pwr->bus_ab_mbytes != ab_mbytes)) { pwr->bus_percent_ab = device->pwrscale.bus_profile.percent_ab; pwr->bus_ab_mbytes = ab_mbytes; - kgsl_bus_update(device, true); + kgsl_bus_update(device, KGSL_BUS_VOTE_ON); } mutex_unlock(&device->mutex);