Commit 63f13b2e authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-ipa-use-bulk-interconnect-interfaces'

Alex Elder says:

====================
net: ipa: use bulk interconnect interfaces

The IPA code currently enables and disables interconnects by setting
the bandwidth of each to a non-zero value, or to zero.  The
interconnect API now supports enable/disable functions, so we can
use those instead.  In addition, the interconnect API provides bulk
interfaces that allow all interconnects to be operated on at once.

This series converts the IPA driver to use the bulk enable and
disable interfaces.  In the process it uses some existing data
structures rather than defining new ones.
====================

Link: https://lore.kernel.org/r/20220309192037.667879-1-elder@linaro.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents c17c4059 37e0cf33
......@@ -34,18 +34,6 @@
#define IPA_AUTOSUSPEND_DELAY 500 /* milliseconds */
/**
* struct ipa_interconnect - IPA interconnect information
* @path: Interconnect path
* @average_bandwidth: Average interconnect bandwidth (KB/second)
* @peak_bandwidth: Peak interconnect bandwidth (KB/second)
*/
struct ipa_interconnect {
struct icc_path *path;
u32 average_bandwidth;
u32 peak_bandwidth;
};
/**
* enum ipa_power_flag - IPA power flags
* @IPA_POWER_FLAG_RESUMED: Whether resume from suspend has been signaled
......@@ -79,164 +67,78 @@ struct ipa_power {
spinlock_t spinlock; /* used with STOPPED/STARTED power flags */
DECLARE_BITMAP(flags, IPA_POWER_FLAG_COUNT);
u32 interconnect_count;
struct ipa_interconnect *interconnect;
struct icc_bulk_data interconnect[];
};
static int ipa_interconnect_init_one(struct device *dev,
struct ipa_interconnect *interconnect,
const struct ipa_interconnect_data *data)
{
struct icc_path *path;
path = of_icc_get(dev, data->name);
if (IS_ERR(path)) {
int ret = PTR_ERR(path);
dev_err_probe(dev, ret, "error getting %s interconnect\n",
data->name);
return ret;
}
interconnect->path = path;
interconnect->average_bandwidth = data->average_bandwidth;
interconnect->peak_bandwidth = data->peak_bandwidth;
return 0;
}
static void ipa_interconnect_exit_one(struct ipa_interconnect *interconnect)
{
icc_put(interconnect->path);
memset(interconnect, 0, sizeof(*interconnect));
}
/* Initialize interconnects required for IPA operation */
static int ipa_interconnect_init(struct ipa_power *power, struct device *dev,
static int ipa_interconnect_init(struct ipa_power *power,
const struct ipa_interconnect_data *data)
{
struct ipa_interconnect *interconnect;
u32 count;
int ret;
count = power->interconnect_count;
interconnect = kcalloc(count, sizeof(*interconnect), GFP_KERNEL);
if (!interconnect)
return -ENOMEM;
power->interconnect = interconnect;
while (count--) {
ret = ipa_interconnect_init_one(dev, interconnect, data++);
if (ret)
goto out_unwind;
interconnect++;
}
return 0;
out_unwind:
while (interconnect-- > power->interconnect)
ipa_interconnect_exit_one(interconnect);
kfree(power->interconnect);
power->interconnect = NULL;
return ret;
}
/* Inverse of ipa_interconnect_init() */
static void ipa_interconnect_exit(struct ipa_power *power)
{
struct ipa_interconnect *interconnect;
interconnect = power->interconnect + power->interconnect_count;
while (interconnect-- > power->interconnect)
ipa_interconnect_exit_one(interconnect);
kfree(power->interconnect);
power->interconnect = NULL;
}
/* Currently we only use one bandwidth level, so just "enable" interconnects */
static int ipa_interconnect_enable(struct ipa *ipa)
{
struct ipa_interconnect *interconnect;
struct ipa_power *power = ipa->power;
struct icc_bulk_data *interconnect;
int ret;
u32 i;
interconnect = power->interconnect;
/* Initialize our interconnect data array for bulk operations */
interconnect = &power->interconnect[0];
for (i = 0; i < power->interconnect_count; i++) {
ret = icc_set_bw(interconnect->path,
interconnect->average_bandwidth,
interconnect->peak_bandwidth);
if (ret) {
dev_err(&ipa->pdev->dev,
"error %d enabling %s interconnect\n",
ret, icc_get_name(interconnect->path));
goto out_unwind;
}
/* interconnect->path is filled in by of_icc_bulk_get() */
interconnect->name = data->name;
interconnect->avg_bw = data->average_bandwidth;
interconnect->peak_bw = data->peak_bandwidth;
data++;
interconnect++;
}
return 0;
ret = of_icc_bulk_get(power->dev, power->interconnect_count,
power->interconnect);
if (ret)
return ret;
out_unwind:
while (interconnect-- > power->interconnect)
(void)icc_set_bw(interconnect->path, 0, 0);
/* All interconnects are initially disabled */
icc_bulk_disable(power->interconnect_count, power->interconnect);
/* Set the bandwidth values to be used when enabled */
ret = icc_bulk_set_bw(power->interconnect_count, power->interconnect);
if (ret)
icc_bulk_put(power->interconnect_count, power->interconnect);
return ret;
}
/* To disable an interconnect, we just its bandwidth to 0 */
static int ipa_interconnect_disable(struct ipa *ipa)
/* Inverse of ipa_interconnect_init() */
static void ipa_interconnect_exit(struct ipa_power *power)
{
struct ipa_interconnect *interconnect;
struct ipa_power *power = ipa->power;
struct device *dev = &ipa->pdev->dev;
int result = 0;
u32 count;
int ret;
count = power->interconnect_count;
interconnect = power->interconnect + count;
while (count--) {
interconnect--;
ret = icc_set_bw(interconnect->path, 0, 0);
if (ret) {
dev_err(dev, "error %d disabling %s interconnect\n",
ret, icc_get_name(interconnect->path));
/* Try to disable all; record only the first error */
if (!result)
result = ret;
}
}
return result;
icc_bulk_put(power->interconnect_count, power->interconnect);
}
/* Enable IPA power, enabling interconnects and the core clock */
static int ipa_power_enable(struct ipa *ipa)
{
struct ipa_power *power = ipa->power;
int ret;
ret = ipa_interconnect_enable(ipa);
ret = icc_bulk_enable(power->interconnect_count, power->interconnect);
if (ret)
return ret;
ret = clk_prepare_enable(ipa->power->core);
ret = clk_prepare_enable(power->core);
if (ret) {
dev_err(&ipa->pdev->dev, "error %d enabling core clock\n", ret);
(void)ipa_interconnect_disable(ipa);
dev_err(power->dev, "error %d enabling core clock\n", ret);
icc_bulk_disable(power->interconnect_count,
power->interconnect);
}
return ret;
}
/* Inverse of ipa_power_enable() */
static int ipa_power_disable(struct ipa *ipa)
static void ipa_power_disable(struct ipa *ipa)
{
clk_disable_unprepare(ipa->power->core);
struct ipa_power *power = ipa->power;
return ipa_interconnect_disable(ipa);
clk_disable_unprepare(power->core);
icc_bulk_disable(power->interconnect_count, power->interconnect);
}
static int ipa_runtime_suspend(struct device *dev)
......@@ -250,7 +152,9 @@ static int ipa_runtime_suspend(struct device *dev)
gsi_suspend(&ipa->gsi);
}
return ipa_power_disable(ipa);
ipa_power_disable(ipa);
return 0;
}
static int ipa_runtime_resume(struct device *dev)
......@@ -453,6 +357,7 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data)
{
struct ipa_power *power;
struct clk *clk;
size_t size;
int ret;
clk = clk_get(dev, "core");
......@@ -469,7 +374,8 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data)
goto err_clk_put;
}
power = kzalloc(sizeof(*power), GFP_KERNEL);
size = data->interconnect_count * sizeof(power->interconnect[0]);
power = kzalloc(sizeof(*power) + size, GFP_KERNEL);
if (!power) {
ret = -ENOMEM;
goto err_clk_put;
......@@ -479,7 +385,7 @@ ipa_power_init(struct device *dev, const struct ipa_power_data *data)
spin_lock_init(&power->spinlock);
power->interconnect_count = data->interconnect_count;
ret = ipa_interconnect_init(power, dev, data->interconnect_data);
ret = ipa_interconnect_init(power, data->interconnect_data);
if (ret)
goto err_kfree;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment