Merge "PM / devfreq: icc: add support for L3 BW devices"
This commit is contained in:
commit
9ee4a9e6ec
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2013-2014, 2018-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2013-2014, 2018-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "devfreq-icc: " fmt
|
||||
@ -21,6 +21,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <trace/events/power.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interconnect.h>
|
||||
@ -28,13 +29,27 @@
|
||||
|
||||
/* Has to be ULL to prevent overflow where this macro is used. */
|
||||
#define MBYTE (1ULL << 20)
|
||||
#define HZ_TO_MBPS(hz, w) (mult_frac(w, hz, MBYTE))
|
||||
#define MBPS_TO_HZ(mbps, w) (mult_frac(mbps, MBYTE, w))
|
||||
|
||||
enum dev_type {
|
||||
STD_MBPS_DEV,
|
||||
L3_HZ_DEV,
|
||||
L3_MBPS_DEV,
|
||||
NUM_DEV_TYPES
|
||||
};
|
||||
|
||||
struct devfreq_icc_spec {
|
||||
enum dev_type type;
|
||||
};
|
||||
|
||||
struct dev_data {
|
||||
struct icc_path *icc_path;
|
||||
u32 cur_ab;
|
||||
u32 cur_ib;
|
||||
unsigned long gov_ab;
|
||||
bool is_l3;
|
||||
const struct devfreq_icc_spec *spec;
|
||||
unsigned int width;
|
||||
struct devfreq *df;
|
||||
struct devfreq_dev_profile dp;
|
||||
};
|
||||
@ -53,7 +68,10 @@ static int set_bw(struct device *dev, u32 new_ib, u32 new_ab)
|
||||
if (d->cur_ib == new_ib && d->cur_ab == new_ab)
|
||||
return 0;
|
||||
|
||||
if (!d->is_l3) {
|
||||
if (d->spec->type == L3_MBPS_DEV) {
|
||||
icc_ib = MBPS_TO_HZ(new_ib, d->width);
|
||||
icc_ab = MBPS_TO_HZ(new_ab, d->width);
|
||||
} else if (d->spec->type == STD_MBPS_DEV) {
|
||||
icc_ib = Bps_to_icc(new_ib * MBYTE);
|
||||
icc_ab = Bps_to_icc(new_ab * MBYTE);
|
||||
}
|
||||
@ -101,6 +119,7 @@ static int icc_get_dev_status(struct device *dev,
|
||||
|
||||
static int populate_l3_opp_table(struct device *dev)
|
||||
{
|
||||
struct dev_data *d = dev_get_drvdata(dev);
|
||||
int idx, ret;
|
||||
u32 data, src, mult, i;
|
||||
unsigned long freq, prev_freq = 0;
|
||||
@ -139,7 +158,10 @@ static int populate_l3_opp_table(struct device *dev)
|
||||
if (i > 0 && prev_freq == freq)
|
||||
break;
|
||||
|
||||
dev_pm_opp_add(dev, freq, 0);
|
||||
if (d->spec->type == L3_MBPS_DEV)
|
||||
dev_pm_opp_add(dev, HZ_TO_MBPS(freq, d->width), 0);
|
||||
else
|
||||
dev_pm_opp_add(dev, freq, 0);
|
||||
l3_freqs[i] = freq;
|
||||
prev_freq = freq;
|
||||
}
|
||||
@ -152,13 +174,18 @@ static int populate_l3_opp_table(struct device *dev)
|
||||
|
||||
static int copy_l3_opp_table(struct device *dev)
|
||||
{
|
||||
struct dev_data *d = dev_get_drvdata(dev);
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < MAX_L3_ENTRIES; idx++) {
|
||||
if (l3_freqs[idx])
|
||||
dev_pm_opp_add(dev, l3_freqs[idx], 0);
|
||||
else
|
||||
if (!l3_freqs[idx])
|
||||
break;
|
||||
|
||||
if (d->spec->type == L3_MBPS_DEV)
|
||||
dev_pm_opp_add(dev,
|
||||
HZ_TO_MBPS(l3_freqs[idx], d->width), 0);
|
||||
else
|
||||
dev_pm_opp_add(dev, l3_freqs[idx], 0);
|
||||
}
|
||||
|
||||
if (!idx) {
|
||||
@ -186,6 +213,12 @@ int devfreq_add_icc(struct device *dev)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, d);
|
||||
|
||||
d->spec = of_device_get_match_data(dev);
|
||||
if (!d->spec) {
|
||||
dev_err(dev, "Unknown device type!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
p = &d->dp;
|
||||
p->polling_ms = 50;
|
||||
p->target = icc_target;
|
||||
@ -200,7 +233,16 @@ int devfreq_add_icc(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,devfreq-icc-l3")) {
|
||||
if (d->spec->type == L3_MBPS_DEV) {
|
||||
ret = of_property_read_u32(dev->of_node, "qcom,bus-width",
|
||||
&d->width);
|
||||
if (ret < 0 || !d->width) {
|
||||
dev_err(dev, "Missing or invalid bus-width: %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (d->spec->type == L3_HZ_DEV || d->spec->type == L3_MBPS_DEV) {
|
||||
mutex_lock(&l3_freqs_lock);
|
||||
if (use_cached_l3_freqs) {
|
||||
mutex_unlock(&l3_freqs_lock);
|
||||
@ -209,10 +251,8 @@ int devfreq_add_icc(struct device *dev)
|
||||
ret = populate_l3_opp_table(dev);
|
||||
mutex_unlock(&l3_freqs_lock);
|
||||
}
|
||||
d->is_l3 = true;
|
||||
} else {
|
||||
ret = dev_pm_opp_of_add_table(dev);
|
||||
d->is_l3 = false;
|
||||
}
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Couldn't parse OPP table:%d\n", ret);
|
||||
@ -273,11 +313,18 @@ static int devfreq_icc_remove(struct platform_device *pdev)
|
||||
return devfreq_remove_icc(&pdev->dev);
|
||||
}
|
||||
|
||||
static const struct devfreq_icc_spec spec[] = {
|
||||
[0] = { STD_MBPS_DEV },
|
||||
[1] = { L3_HZ_DEV },
|
||||
[2] = { L3_MBPS_DEV },
|
||||
};
|
||||
|
||||
static const struct of_device_id devfreq_icc_match_table[] = {
|
||||
{ .compatible = "qcom,devfreq-icc-l3" },
|
||||
{ .compatible = "qcom,devfreq-icc-llcc" },
|
||||
{ .compatible = "qcom,devfreq-icc-ddr" },
|
||||
{ .compatible = "qcom,devfreq-icc" },
|
||||
{ .compatible = "qcom,devfreq-icc-l3bw", .data = &spec[2] },
|
||||
{ .compatible = "qcom,devfreq-icc-l3", .data = &spec[1] },
|
||||
{ .compatible = "qcom,devfreq-icc-llcc", .data = &spec[0] },
|
||||
{ .compatible = "qcom,devfreq-icc-ddr", .data = &spec[0] },
|
||||
{ .compatible = "qcom,devfreq-icc", .data = &spec[0] },
|
||||
{}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user