6186cb14cc
When kgsl_device_platform_probe fails with EPROBE_DEFER, do proper cleanup to avoid issues in next probe call. Change-Id: Icd998b1299a64d53458bcd452f754903710c4ca0 Signed-off-by: Druva Kumar Charipalli <quic_dcharipa@quicinc.com> Signed-off-by: Pankaj Gupta <quic_gpankaj@quicinc.com>
232 lines
5.8 KiB
C
232 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/interconnect.h>
|
|
#include <linux/of.h>
|
|
#include <linux/devfreq.h>
|
|
|
|
#include "../../devfreq/governor.h"
|
|
#include "kgsl_bus.h"
|
|
#include "kgsl_device.h"
|
|
#include "kgsl_trace.h"
|
|
|
|
|
|
static u32 _ab_buslevel_update(struct kgsl_pwrctrl *pwr,
|
|
u32 ib)
|
|
{
|
|
if (!ib)
|
|
return 0;
|
|
|
|
/*
|
|
* In the absence of any other settings, make ab 25% of ib
|
|
* where the ib vote is in kbps
|
|
*/
|
|
if ((!pwr->bus_percent_ab) && (!pwr->bus_ab_mbytes))
|
|
return 25 * ib / 100000;
|
|
|
|
if (pwr->bus_width)
|
|
return pwr->bus_ab_mbytes;
|
|
|
|
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;
|
|
|
|
/*
|
|
* We need the event lock to protect against concurrent governor
|
|
* re-assignments.
|
|
*/
|
|
event_mutex_lock(dev);
|
|
mutex_lock(&dev->lock);
|
|
/*
|
|
* Update both min/max to make sure correct vote is set regardless
|
|
* of the governor, which can be changed from sysfs
|
|
*/
|
|
dev->min_freq = new_min_freq;
|
|
dev->max_freq = new_min_freq;
|
|
ret = update_devfreq(dev);
|
|
mutex_unlock(&dev->lock);
|
|
event_mutex_unlock(dev);
|
|
|
|
if (!ret)
|
|
cur_min_freq = new_min_freq;
|
|
}
|
|
|
|
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? */
|
|
int cur = pwr->pwrlevels[pwr->active_pwrlevel].bus_freq;
|
|
int ret, buslevel = 0;
|
|
u32 ab;
|
|
|
|
/* the bus should be ON to update the active frequency */
|
|
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 (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 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;
|
|
pwr->bus_ab_mbytes = 0;
|
|
}
|
|
|
|
/* buslevel is the IB vote, update the AB */
|
|
ab = _ab_buslevel_update(pwr, pwr->ddr_table[buslevel]);
|
|
|
|
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,
|
|
int count)
|
|
{
|
|
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
|
|
int i;
|
|
|
|
for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
|
|
struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[i];
|
|
|
|
if (pwrlevel->bus_freq >= count) {
|
|
dev_err(device->dev, "Bus setting for GPU freq %d is out of bounds\n",
|
|
pwrlevel->gpu_freq);
|
|
pwrlevel->bus_freq = count - 1;
|
|
}
|
|
|
|
if (pwrlevel->bus_max >= count) {
|
|
dev_err(device->dev, "Bus max for GPU freq %d is out of bounds\n",
|
|
pwrlevel->gpu_freq);
|
|
pwrlevel->bus_max = count - 1;
|
|
}
|
|
|
|
if (pwrlevel->bus_min >= count) {
|
|
dev_err(device->dev, "Bus min for GPU freq %d is out of bounds\n",
|
|
pwrlevel->gpu_freq);
|
|
pwrlevel->bus_min = count - 1;
|
|
}
|
|
|
|
if (pwrlevel->bus_min > pwrlevel->bus_max) {
|
|
dev_err(device->dev, "Bus min is bigger than bus max for GPU freq %d\n",
|
|
pwrlevel->gpu_freq);
|
|
pwrlevel->bus_min = pwrlevel->bus_max;
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 *kgsl_bus_get_table(struct platform_device *pdev,
|
|
const char *name, int *count)
|
|
{
|
|
u32 *levels;
|
|
int i, num = of_property_count_elems_of_size(pdev->dev.of_node,
|
|
name, sizeof(u32));
|
|
|
|
/* If the bus wasn't specified, then build a static table */
|
|
if (num <= 0)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
levels = kcalloc(num, sizeof(*levels), GFP_KERNEL);
|
|
if (!levels)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
for (i = 0; i < num; i++)
|
|
of_property_read_u32_index(pdev->dev.of_node,
|
|
name, i, &levels[i]);
|
|
|
|
*count = num;
|
|
return levels;
|
|
}
|
|
|
|
int kgsl_bus_init(struct kgsl_device *device, struct platform_device *pdev)
|
|
{
|
|
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
|
|
int count;
|
|
int ddr = of_fdt_get_ddrtype();
|
|
|
|
if (ddr >= 0) {
|
|
char str[32];
|
|
|
|
snprintf(str, sizeof(str), "qcom,bus-table-ddr%d", ddr);
|
|
|
|
pwr->ddr_table = kgsl_bus_get_table(pdev, str, &count);
|
|
if (!IS_ERR(pwr->ddr_table))
|
|
goto done;
|
|
}
|
|
|
|
/* Look if a generic table is present */
|
|
pwr->ddr_table = kgsl_bus_get_table(pdev, "qcom,bus-table-ddr", &count);
|
|
if (IS_ERR(pwr->ddr_table)) {
|
|
int ret = PTR_ERR(pwr->ddr_table);
|
|
|
|
pwr->ddr_table = NULL;
|
|
return ret;
|
|
}
|
|
done:
|
|
pwr->ddr_table_count = count;
|
|
|
|
validate_pwrlevels(device, pwr->ddr_table, pwr->ddr_table_count);
|
|
|
|
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");
|
|
|
|
kfree(pwr->ddr_table);
|
|
pwr->ddr_table = NULL;
|
|
return PTR_ERR(pwr->icc_path);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void kgsl_bus_close(struct kgsl_device *device)
|
|
{
|
|
kfree(device->pwrctrl.ddr_table);
|
|
device->pwrctrl.ddr_table = NULL;
|
|
icc_put(device->pwrctrl.icc_path);
|
|
device->pwrctrl.icc_path = NULL;
|
|
if (device->pwrctrl.ddr_qos_devfreq)
|
|
put_device(&device->pwrctrl.ddr_qos_devfreq->dev);
|
|
}
|