mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
Merge "msm: kgsl: Add GPU minimum bandwidth vote state"
This commit is contained in:
commit
8295c46e6b
8 changed files with 137 additions and 18 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
@ -552,6 +552,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)
|
||||
{
|
||||
|
|
@ -1045,6 +1072,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);
|
||||
|
|
@ -1072,6 +1100,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,
|
||||
|
|
@ -1183,7 +1212,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,
|
||||
|
|
@ -1211,7 +1241,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);
|
||||
|
|
@ -1257,13 +1288,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1354,6 +1385,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;
|
||||
|
|
@ -1417,7 +1460,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;
|
||||
|
|
@ -1529,6 +1572,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);
|
||||
|
|
@ -1559,7 +1604,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);
|
||||
|
|
@ -1599,7 +1644,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);
|
||||
}
|
||||
|
||||
|
|
@ -1711,7 +1758,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 */
|
||||
|
|
@ -1764,6 +1814,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);
|
||||
|
|
@ -1785,6 +1838,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);
|
||||
|
|
@ -1793,6 +1847,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",
|
||||
|
|
@ -1823,7 +1878,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 */
|
||||
|
|
@ -1867,6 +1924,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 */
|
||||
|
|
@ -1883,6 +1943,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)
|
||||
{
|
||||
|
|
@ -1896,6 +1976,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);
|
||||
|
|
@ -1999,6 +2082,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;
|
||||
|
|
@ -2051,6 +2137,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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue