Re: [PATCH v3 2/2] PM / devfreq: Add devfreq driver for interconnect bandwidth voting

From: Sibi Sankar
Date: Fri Sep 14 2018 - 08:53:39 EST


Hi Saravana,

On 2018-08-02 06:27, Saravana Kannan wrote:
This driver registers itself as a devfreq device that allows devfreq
governors to make bandwidth votes for an interconnect path. This allows
applying various policies for different interconnect paths using devfreq
governors.

Example uses:
* Use the devfreq performance governor to set the CPU to DDR interconnect
path for maximum performance.
* Use the devfreq performance governor to set the GPU to DDR interconnect
path for maximum performance.
* Use the CPU frequency to device frequency mapping governor to scale the
DDR frequency based on the needs of the CPUs' current frequency.

Signed-off-by: Saravana Kannan <skannan@xxxxxxxxxxxxxx>
---
Documentation/devicetree/bindings/devfreq/icbw.txt | 21 ++++
drivers/devfreq/Kconfig | 13 +++
drivers/devfreq/Makefile | 1 +
drivers/devfreq/devfreq_icbw.c | 116 +++++++++++++++++++++
4 files changed, 151 insertions(+)
create mode 100644 Documentation/devicetree/bindings/devfreq/icbw.txt
create mode 100644 drivers/devfreq/devfreq_icbw.c

diff --git a/Documentation/devicetree/bindings/devfreq/icbw.txt
b/Documentation/devicetree/bindings/devfreq/icbw.txt
new file mode 100644
index 0000000..36cf045
--- /dev/null
+++ b/Documentation/devicetree/bindings/devfreq/icbw.txt
@@ -0,0 +1,21 @@
+Interconnect bandwidth device
+
+icbw is a device that represents an interconnect path that connects two
+devices. This device is typically used to vote for BW requirements between
+two devices. Eg: CPU to DDR, GPU to DDR, etc
+
+Required properties:
+- compatible: Must be "devfreq-icbw"
+- interconnects: Pairs of phandles and interconnect provider specifier
+ to denote the edge source and destination ports of
+ the interconnect path. See also:
+ Documentation/devicetree/bindings/interconnect/interconnect.txt
+- interconnect-names: Must have one entry with the name "path".
+
+Example:
+
+ qcom,cpubw {
+ compatible = "devfreq-icbw";
+ interconnects = <&snoc MASTER_APSS_1 &bimc SLAVE_EBI_CH0>;
+ interconnect-names = "path";
+ };
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 3d9ae68..590370e 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -121,6 +121,19 @@ config ARM_RK3399_DMC_DEVFREQ
It sets the frequency for the memory controller and reads
the usage counts
from hardware.

+config DEVFREQ_ICBW
+ bool "DEVFREQ device for making bandwidth votes on interconnect paths"
+ select DEVFREQ_GOV_PERFORMANCE
+ select DEVFREQ_GOV_POWERSAVE
+ select DEVFREQ_GOV_USERSPACE
+ default n
+ help
+ Different devfreq governors use this devfreq device to make
+ bandwidth votes for interconnect paths between different devices
+ (Eg: CPU to DDR, GPU to DDR, etc). This driver provides a generic
+ interface so that the devfreq governors can be shared across SoCs
+ and architectures.
+
source "drivers/devfreq/event/Kconfig"

endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index e9dc3e9..b302c5cf 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_DEVFREQ_GOV_CPUFREQ_MAP) +=
governor_cpufreq_map.o
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
+obj-$(CONFIG_DEVFREQ_ICBW) += devfreq_icbw.o

# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
diff --git a/drivers/devfreq/devfreq_icbw.c b/drivers/devfreq/devfreq_icbw.c
new file mode 100644
index 0000000..231fb21
--- /dev/null
+++ b/drivers/devfreq/devfreq_icbw.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2013-2014, 2018, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "icbw: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/devfreq.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/interconnect.h>
+
+struct dev_data {
+ struct icc_path *path;
+ u32 cur_ab;
+ u32 cur_pb;
+ unsigned long gov_ab;
+ struct devfreq *df;
+ struct devfreq_dev_profile dp;
+};
+
+static int icbw_target(struct device *dev, unsigned long *freq, u32 flags)
+{
+ struct dev_data *d = dev_get_drvdata(dev);
+ int ret;
+ u32 new_pb = *freq, new_ab = d->gov_ab;
+
+ if (d->cur_pb == new_pb && d->cur_ab == new_ab)
+ return 0;
+
+ dev_dbg(dev, "BW KBps: AB: %u PB: %u\n", new_ab, new_pb);
+
+ ret = icc_set(d->path, new_ab, new_pb);
+ if (ret) {
+ dev_err(dev, "bandwidth request failed (%d)\n", ret);
+ } else {
+ d->cur_pb = new_pb;
+ d->cur_ab = new_ab;
+ }
+
+ return ret;
+}
+
+static int icbw_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *stat)
+{
+ struct dev_data *d = dev_get_drvdata(dev);
+
+ stat->private_data = &d->gov_ab;
+ return 0;
+}
+
+static int devfreq_icbw_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dev_data *d;
+ struct devfreq_dev_profile *p;
+
+ d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+ dev_set_drvdata(dev, d);
+
+ p = &d->dp;
+ p->polling_ms = 50;
+ p->target = icbw_target;
+ p->get_dev_status = icbw_get_dev_status;
+
+ d->path = of_icc_get(dev, "path");
+ if (IS_ERR(d->path)) {
+ dev_err(dev, "Unable to register interconnect path\n");
+ return -ENODEV;
+ }

The probe fails if the provider is not registered yet. Worked around it using -EPROBE_DEFER
but this should probably come from of_icc_get itself.

+
+ d->df = devfreq_add_device(dev, p, "performance", NULL);
+ if (IS_ERR(d->df)) {
+ icc_put(d->path);
+ return PTR_ERR(d->df);
+ }

devfreq_add_device will fail with -EINVAL for set_table_freq, find_available_min_freq and
find_available_max_freq. If you end up working your way around it, I still ended up getting
multiple errors like "Couldn't update frequency transition information" after probing.

+
+ return 0;
+}
+
+static int devfreq_icbw_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dev_data *d = dev_get_drvdata(dev);
+
+ devfreq_remove_device(d->df);
+ icc_put(d->path);
+ return 0;
+}
+
+static const struct of_device_id icbw_match_table[] = {
+ { .compatible = "devfreq-icbw" },
+ {}
+};
+
+static struct platform_driver icbw_driver = {
+ .probe = devfreq_icbw_probe,
+ .remove = devfreq_icbw_remove,
+ .driver = {
+ .name = "devfreq-icbw",
+ .of_match_table = icbw_match_table,
+ },
+};
+
+module_platform_driver(icbw_driver);
+MODULE_DESCRIPTION("Interconnect bandwidth voting driver");
+MODULE_LICENSE("GPL v2");

--
-- Sibi Sankar --
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project.