From 7e0c383b72c810d018606cd952e39934bd1459ee Mon Sep 17 00:00:00 2001 From: Amir Vajid Date: Wed, 6 May 2020 09:11:13 -0700 Subject: [PATCH] PM / devfreq: icc: add support for L3 BW devices Add support for L3 bandwidth devices that vote in units of MBPS. This includes creating OPP tables in units of MBPS as well as requiring that these devices define a width in DT which is used to convert the votes to Hz before submitting them to the L3 provider. Change-Id: I47a569f76b9e57caac57788592784921c689505b Signed-off-by: Amir Vajid --- drivers/devfreq/devfreq_icc.c | 75 ++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/drivers/devfreq/devfreq_icc.c b/drivers/devfreq/devfreq_icc.c index 9f893237b232..248a0b97c07e 100644 --- a/drivers/devfreq/devfreq_icc.c +++ b/drivers/devfreq/devfreq_icc.c @@ -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 #include #include +#include #include #include #include @@ -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] }, {} };