mbox series

[v2,0/8] interconnect: Add imx support via devfreq

Message ID cover.1585751281.git.leonard.crestez@nxp.com
Headers show
Series interconnect: Add imx support via devfreq | expand

Message

Leonard Crestez April 1, 2020, 2:32 p.m. UTC
This series adds interconnect scaling support for imx8m series chips. It uses a
per-SOC interconnect provider layered on top of multiple instances of devfreq
for scalable nodes along the interconnect.

Existing qcom interconnect providers mostly translate bandwidth requests into
firmware calls but equivalent firmware on imx8m is much thinner. Scaling
support for individual nodes is implemented as distinct devfreq drivers
instead.

The imx interconnect provider doesn't communicate with devfreq directly
but rather computes "minimum frequencies" for nodes along the path and
creates dev_pm_qos requests.

Since there is no single devicetree node that can represent the
"interconnect" the main NOC is picked as the "interconnect provider" and
will probe the interconnect platform device if #interconnect-cells is
present. This avoids introducing "virtual" devices but it means that DT
bindings of main NOC includes properties for both devfreq and
interconnect.

Only the ddrc and main noc are scalable right now but more can be added.

Also available on a github branch (with few unrelated changes):
	https://github.com/cdleonard/linux/tree/next

Changes since v1:
* Fix dt_bindings_check for yaml and reduce example to fit current
features
* Fix comment spelling in imx-bus
* Drop mentions of passive governor from imx-bus (will repost later)
* Improve error message in imx_bus_init_icc
* Use dev_pm_opp_set_rate
Link: https://patchwork.kernel.org/cover/11458971/

Changes since RFCv6:
* Allow building interconnect drivers as modules
* Handle icc_provider_del errors in imx_icc_unregister (like EBUSY).
* Rename imx-devfreq to imx-bus, similar to exynos-bus
* Explain why imx bus clock enabling is not required
Link: https://patchwork.kernel.org/cover/11244421/

Changes since RFCv5:
* Replace scanning for interconnect-node-id with explicit
scalable-nodes/scalable-node-ids property on NoC.
* Now passes make `dtbs_check`
* Remove struct imx_icc_provider
* Switch to of_icc_xlate_onecell
* Use of_find_device_by_node to fetch QoS target, this causes fewer probe
deferrals, removes dependency on devfreq API and even allows reloading ddrc
module at runtime
* Add imx_icc_node_destroy helper
* Remove 0/1 on DEFINE_BUS_SLAVE/MASTER which created spurious links
Link: https://patchwork.kernel.org/cover/11222015/

Changes since RFCv4:
* Drop icc proxy nonsense
* Make devfreq driver for NOC probe the ICC driver if
#interconnect-cells is present
* Move NOC support to interconnect series and rename the node in DT
* Add support for all chips at once, differences are not intereseting
and there is more community interest for 8mq than 8mm.
Link: https://patchwork.kernel.org/cover/11111865/

Changes since RFCv3:
* Remove the virtual "icc" node and add devfreq nodes as proxy providers
* Fix build on 32-bit arm (reported by kbuilt test robot)
* Remove ARCH_MXC_ARM64 (never existed in upstream)
* Remove _numlinks, calculate instead
* Replace __BUSFREQ_H header guard
* Improve commit message and comment spelling
* Fix checkpatch issues
Link to RFCv3: https://patchwork.kernel.org/cover/11078671/

Changes since RFCv2 and initial work by Alexandre Bailon:
* Relying on devfreq and dev_pm_qos instead of CLK
* No more "platform opp" stuff
* No more special suspend handling: use suspend-opp on devfreq instead
* Replace all mentions of "busfreq" with "interconnect"
Link to v2: https://patchwork.kernel.org/cover/11021563/

Leonard Crestez (8):
  dt-bindings: interconnect: Add bindings for imx8m noc
  PM / devfreq: Add generic imx bus scaling driver
  PM / devfreq: imx: Register interconnect device
  interconnect: Add imx core driver
  interconnect: imx: Add platform driver for imx8mm
  interconnect: imx: Add platform driver for imx8mq
  interconnect: imx: Add platform driver for imx8mn
  arm64: dts: imx8m: Add NOC nodes

 .../bindings/interconnect/fsl,imx8m-noc.yaml  | 101 ++++++
 arch/arm64/boot/dts/freescale/imx8mm.dtsi     |  24 ++
 arch/arm64/boot/dts/freescale/imx8mn.dtsi     |  24 ++
 arch/arm64/boot/dts/freescale/imx8mq.dtsi     |  24 ++
 drivers/devfreq/Kconfig                       |   8 +
 drivers/devfreq/Makefile                      |   1 +
 drivers/devfreq/imx-bus.c                     | 180 +++++++++++
 drivers/interconnect/Kconfig                  |   1 +
 drivers/interconnect/Makefile                 |   1 +
 drivers/interconnect/imx/Kconfig              |  17 +
 drivers/interconnect/imx/Makefile             |   9 +
 drivers/interconnect/imx/imx.c                | 298 ++++++++++++++++++
 drivers/interconnect/imx/imx.h                |  62 ++++
 drivers/interconnect/imx/imx8mm.c             | 108 +++++++
 drivers/interconnect/imx/imx8mn.c             |  97 ++++++
 drivers/interconnect/imx/imx8mq.c             | 106 +++++++
 include/dt-bindings/interconnect/imx8mm.h     |  49 +++
 include/dt-bindings/interconnect/imx8mn.h     |  41 +++
 include/dt-bindings/interconnect/imx8mq.h     |  48 +++
 19 files changed, 1199 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interconnect/fsl,imx8m-noc.yaml
 create mode 100644 drivers/devfreq/imx-bus.c
 create mode 100644 drivers/interconnect/imx/Kconfig
 create mode 100644 drivers/interconnect/imx/Makefile
 create mode 100644 drivers/interconnect/imx/imx.c
 create mode 100644 drivers/interconnect/imx/imx.h
 create mode 100644 drivers/interconnect/imx/imx8mm.c
 create mode 100644 drivers/interconnect/imx/imx8mn.c
 create mode 100644 drivers/interconnect/imx/imx8mq.c
 create mode 100644 include/dt-bindings/interconnect/imx8mm.h
 create mode 100644 include/dt-bindings/interconnect/imx8mn.h
 create mode 100644 include/dt-bindings/interconnect/imx8mq.h

Comments

Chanwoo Choi April 2, 2020, 12:21 a.m. UTC | #1
Hi Leonard,

As I replied on v1 mail thread, it needs a dt-binding document
under Documentation/devicetree/bindings/devfreq.

And I add one comment below. Except for them, looks good to me.

On 4/1/20 11:33 PM, Leonard Crestez wrote:
> Add initial support for dynamic frequency switching on pieces of the imx
> interconnect fabric.
> 
> All this driver does is set a clk rate based on an opp table, it does
> not map register areas.
> 
> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
> Tested-by: Martin Kepplinger <martin.kepplinger@puri.sm>
> ---
>  drivers/devfreq/Kconfig   |   8 +++
>  drivers/devfreq/Makefile  |   1 +
>  drivers/devfreq/imx-bus.c | 139 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 148 insertions(+)
>  create mode 100644 drivers/devfreq/imx-bus.c
> 
> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
> index 0b1df12e0f21..37dc40d1fcfb 100644
> --- a/drivers/devfreq/Kconfig
> +++ b/drivers/devfreq/Kconfig
> @@ -89,10 +89,18 @@ config ARM_EXYNOS_BUS_DEVFREQ
>  	  Each memory bus group could contain many memoby bus block. It reads
>  	  PPMU counters of memory controllers by using DEVFREQ-event device
>  	  and adjusts the operating frequencies and voltages with OPP support.
>  	  This does not yet operate with optimal voltages.
>  
> +config ARM_IMX_BUS_DEVFREQ
> +	tristate "i.MX Generic Bus DEVFREQ Driver"
> +	depends on ARCH_MXC || COMPILE_TEST
> +	select DEVFREQ_GOV_USERSPACE
> +	help
> +	  This adds the generic DEVFREQ driver for i.MX interconnects. It
> +	  allows adjusting NIC/NOC frequency.
> +
>  config ARM_IMX8M_DDRC_DEVFREQ
>  	tristate "i.MX8M DDRC DEVFREQ Driver"
>  	depends on (ARCH_MXC && HAVE_ARM_SMCCC) || \
>  		(COMPILE_TEST && HAVE_ARM_SMCCC)
>  	select DEVFREQ_GOV_SIMPLE_ONDEMAND
> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
> index 3eb4d5e6635c..3ca1ad0ecb97 100644
> --- a/drivers/devfreq/Makefile
> +++ b/drivers/devfreq/Makefile
> @@ -7,10 +7,11 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE)	+= governor_powersave.o
>  obj-$(CONFIG_DEVFREQ_GOV_USERSPACE)	+= governor_userspace.o
>  obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)	+= governor_passive.o
>  
>  # DEVFREQ Drivers
>  obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
> +obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ)	+= imx-bus.o
>  obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ)	+= imx8m-ddrc.o
>  obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
>  obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra30-devfreq.o
>  obj-$(CONFIG_ARM_TEGRA20_DEVFREQ)	+= tegra20-devfreq.o
>  
> diff --git a/drivers/devfreq/imx-bus.c b/drivers/devfreq/imx-bus.c
> new file mode 100644
> index 000000000000..7915d7277349
> --- /dev/null
> +++ b/drivers/devfreq/imx-bus.c
> @@ -0,0 +1,139 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/devfreq.h>
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +struct imx_bus {
> +	struct devfreq_dev_profile profile;
> +	struct devfreq *devfreq;
> +	struct clk *clk;
> +};
> +
> +static int imx_bus_target(struct device *dev,
> +		unsigned long *freq, u32 flags)
> +{
> +	struct imx_bus *priv = dev_get_drvdata(dev);

It is not used. Need to remove it.

> +	struct dev_pm_opp *new_opp;
> +	int ret;
> +
> +	new_opp = devfreq_recommended_opp(dev, freq, flags);
> +	if (IS_ERR(new_opp)) {
> +		ret = PTR_ERR(new_opp);
> +		dev_err(dev, "failed to get recommended opp: %d\n", ret);
> +		return ret;
> +	}
> +	dev_pm_opp_put(new_opp);
> +
> +	return dev_pm_opp_set_rate(dev, *freq);
> +}
> +
> +static int imx_bus_get_cur_freq(struct device *dev, unsigned long *freq)
> +{
> +	struct imx_bus *priv = dev_get_drvdata(dev);
> +
> +	*freq = clk_get_rate(priv->clk);
> +
> +	return 0;
> +}
> +
> +static int imx_bus_get_dev_status(struct device *dev,
> +		struct devfreq_dev_status *stat)
> +{
> +	struct imx_bus *priv = dev_get_drvdata(dev);
> +
> +	stat->busy_time = 0;
> +	stat->total_time = 0;
> +	stat->current_frequency = clk_get_rate(priv->clk);
> +
> +	return 0;
> +}
> +
> +static void imx_bus_exit(struct device *dev)
> +{
> +	dev_pm_opp_of_remove_table(dev);
> +}
> +
> +static int imx_bus_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct imx_bus *priv;
> +	const char *gov = DEVFREQ_GOV_USERSPACE;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Fetch the clock to adjust but don't explicitly enable.
> +	 *
> +	 * For imx bus clock clk_set_rate is safe no matter if the clock is on
> +	 * or off and some peripheral side-buses might be off unless enabled by
> +	 * drivers for devices on those specific buses.
> +	 *
> +	 * Rate adjustment on a disabled bus clock just takes effect later.
> +	 */
> +	priv->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(priv->clk)) {
> +		ret = PTR_ERR(priv->clk);
> +		dev_err(dev, "failed to fetch clk: %d\n", ret);
> +		return ret;
> +	}
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = dev_pm_opp_of_add_table(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to get OPP table\n");
> +		return ret;
> +	}
> +
> +	priv->profile.polling_ms = 1000;
> +	priv->profile.target = imx_bus_target;
> +	priv->profile.get_dev_status = imx_bus_get_dev_status;
> +	priv->profile.exit = imx_bus_exit;
> +	priv->profile.get_cur_freq = imx_bus_get_cur_freq;
> +	priv->profile.initial_freq = clk_get_rate(priv->clk);
> +
> +	priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
> +						gov, NULL);
> +	if (IS_ERR(priv->devfreq)) {
> +		ret = PTR_ERR(priv->devfreq);
> +		dev_err(dev, "failed to add devfreq device: %d\n", ret);
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	dev_pm_opp_of_remove_table(dev);
> +	return ret;
> +}
> +
> +static const struct of_device_id imx_bus_of_match[] = {
> +	{ .compatible = "fsl,imx8m-noc", },
> +	{ .compatible = "fsl,imx8m-nic", },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, imx_bus_of_match);
> +
> +static struct platform_driver imx_bus_platdrv = {
> +	.probe		= imx_bus_probe,
> +	.driver = {
> +		.name	= "imx-bus-devfreq",
> +		.of_match_table = of_match_ptr(imx_bus_of_match),
> +	},
> +};
> +module_platform_driver(imx_bus_platdrv);
> +
> +MODULE_DESCRIPTION("Generic i.MX bus frequency scaling driver");
> +MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
> +MODULE_LICENSE("GPL v2");
>
Chanwoo Choi April 2, 2020, 12:23 a.m. UTC | #2
Hi Leonard,

On 4/1/20 11:33 PM, Leonard Crestez wrote:
> There is no single device which can represent the imx interconnect.
> Instead of adding a virtual one just make the main &noc act as the
> global interconnect provider.
> 
> The imx interconnect provider driver will scale the NOC and DDRC based
> on bandwidth request. More scalable nodes can be added in the future,
> for example for audio/display/vpu/gpu NICs.
> 
> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
> Tested-by: Martin Kepplinger <martin.kepplinger@puri.sm>
> ---
>  drivers/devfreq/imx-bus.c | 41 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
> 
> diff --git a/drivers/devfreq/imx-bus.c b/drivers/devfreq/imx-bus.c
> index 7915d7277349..240eeea66f13 100644
> --- a/drivers/devfreq/imx-bus.c
> +++ b/drivers/devfreq/imx-bus.c
> @@ -14,10 +14,11 @@
>  
>  struct imx_bus {
>  	struct devfreq_dev_profile profile;
>  	struct devfreq *devfreq;
>  	struct clk *clk;
> +	struct platform_device *icc_pdev;
>  };
>  
>  static int imx_bus_target(struct device *dev,
>  		unsigned long *freq, u32 flags)
>  {
> @@ -57,11 +58,44 @@ static int imx_bus_get_dev_status(struct device *dev,
>  	return 0;
>  }
>  
>  static void imx_bus_exit(struct device *dev)
>  {
> +	struct imx_bus *priv = dev_get_drvdata(dev);
> +
>  	dev_pm_opp_of_remove_table(dev);
> +	platform_device_unregister(priv->icc_pdev);
> +}
> +
> +/* imx_bus_init_icc() - register matching icc provider if required */
> +static int imx_bus_init_icc(struct device *dev)
> +{
> +	struct imx_bus *priv = dev_get_drvdata(dev);
> +	const char *icc_driver_name;
> +
> +	if (!of_get_property(dev->of_node, "#interconnect-cells", 0))
> +		return 0;
> +	if (!IS_ENABLED(CONFIG_INTERCONNECT_IMX)) {
> +		dev_warn(dev, "imx interconnect drivers disabled\n");
> +		return 0;
> +	}
> +
> +	icc_driver_name = of_device_get_match_data(dev);
> +	if (!icc_driver_name) {
> +		dev_err(dev, "unknown interconnect driver\n");
> +		return 0;
> +	}
> +
> +	priv->icc_pdev = platform_device_register_data(
> +			dev, icc_driver_name, -1, NULL, 0);
> +	if (IS_ERR(priv->icc_pdev)) {
> +		dev_err(dev, "failed to register icc provider %s: %ld\n",
> +				icc_driver_name, PTR_ERR(priv->devfreq));
> +		return PTR_ERR(priv->devfreq);
> +	}
> +
> +	return 0;
>  }
>  
>  static int imx_bus_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> @@ -109,18 +143,25 @@ static int imx_bus_probe(struct platform_device *pdev)
>  		ret = PTR_ERR(priv->devfreq);
>  		dev_err(dev, "failed to add devfreq device: %d\n", ret);
>  		goto err;
>  	}
>  
> +	ret = imx_bus_init_icc(dev);
> +	if (ret)
> +		goto err;
> +
>  	return 0;
>  
>  err:
>  	dev_pm_opp_of_remove_table(dev);
>  	return ret;
>  }
>  
>  static const struct of_device_id imx_bus_of_match[] = {
> +	{ .compatible = "fsl,imx8mq-noc", .data = "imx8mq-interconnect", },
> +	{ .compatible = "fsl,imx8mm-noc", .data = "imx8mm-interconnect", },
> +	{ .compatible = "fsl,imx8mn-noc", .data = "imx8mn-interconnect", },
>  	{ .compatible = "fsl,imx8m-noc", },
>  	{ .compatible = "fsl,imx8m-nic", },
>  	{ /* sentinel */ },
>  };
>  MODULE_DEVICE_TABLE(of, imx_bus_of_match);
> 

Acked-by: Chanwoo Choi <cw00.choi@samsung.com>
Georgi Djakov April 2, 2020, 11:05 a.m. UTC | #3
Hi Leonard,

Thank you for updating the patches!

On 4/1/20 17:33, Leonard Crestez wrote:
> This adds support for i.MX SoC family to interconnect framework.
> 
> Platform drivers can describe the interconnect graph and several
> adjustment knobs where icc node bandwidth is converted to a
> DEV_PM_QOS_MIN_FREQUENCY request.
> 
> The interconnect provider is probed through the main NOC device and
> other adjustable nodes on the same graph are found from a
> fsl,scalable-nodes phandle array property.
> 
> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
> Tested-by: Martin Kepplinger <martin.kepplinger@puri.sm>
> ---
>  drivers/interconnect/Kconfig      |   1 +
>  drivers/interconnect/Makefile     |   1 +
>  drivers/interconnect/imx/Kconfig  |   5 +
>  drivers/interconnect/imx/Makefile |   3 +
>  drivers/interconnect/imx/imx.c    | 298 ++++++++++++++++++++++++++++++
>  drivers/interconnect/imx/imx.h    |  62 +++++++
>  6 files changed, 370 insertions(+)
>  create mode 100644 drivers/interconnect/imx/Kconfig
>  create mode 100644 drivers/interconnect/imx/Makefile
>  create mode 100644 drivers/interconnect/imx/imx.c
>  create mode 100644 drivers/interconnect/imx/imx.h
> 
> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
> index bfa4ca3ab7a9..e61802230f90 100644
> --- a/drivers/interconnect/Kconfig
> +++ b/drivers/interconnect/Kconfig
> @@ -10,7 +10,8 @@ menuconfig INTERCONNECT
>  	  If unsure, say no.
>  
>  if INTERCONNECT
>  
>  source "drivers/interconnect/qcom/Kconfig"
> +source "drivers/interconnect/imx/Kconfig"

Nit: Please move it up to make it sorted.

>  
>  endif
> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
> index 725029ae7a2c..6998288a7d98 100644
> --- a/drivers/interconnect/Makefile
> +++ b/drivers/interconnect/Makefile
> @@ -3,5 +3,6 @@
>  CFLAGS_core.o				:= -I$(src)
>  icc-core-objs				:= core.o
>  
>  obj-$(CONFIG_INTERCONNECT)		+= icc-core.o
>  obj-$(CONFIG_INTERCONNECT_QCOM)		+= qcom/
> +obj-$(CONFIG_INTERCONNECT_IMX)		+= imx/

Ditto.

> diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig
> new file mode 100644
> index 000000000000..f39336f8d603
> --- /dev/null
> +++ b/drivers/interconnect/imx/Kconfig
> @@ -0,0 +1,5 @@
> +config INTERCONNECT_IMX
> +	tristate "i.MX interconnect drivers"
> +	depends on ARCH_MXC || COMPILE_TEST
> +	help
> +	  Generic interconnect drivers for i.MX SOCs
> diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile
> new file mode 100644
> index 000000000000..86ae0bd28d8c
> --- /dev/null
> +++ b/drivers/interconnect/imx/Makefile
> @@ -0,0 +1,3 @@
> +imx-interconnect-objs			:= imx.o
> +
> +obj-$(CONFIG_INTERCONNECT_IMX)		+= imx-interconnect.o
> diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c
> new file mode 100644
> index 000000000000..527b1de1c41a
> --- /dev/null
> +++ b/drivers/interconnect/imx/imx.c
> @@ -0,0 +1,298 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Interconnect framework driver for i.MX SoC
> + *
> + * Copyright (c) 2019, BayLibre
> + * Copyright (c) 2019, NXP

Maybe update it to 2020.

> + * Author: Alexandre Bailon <abailon@baylibre.com>
> + * Author: Leonard Crestez <leonard.crestez@nxp.com>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/interconnect-provider.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_qos.h>
> +
> +#include "imx.h"
> +
> +/* private icc_node data */
> +struct imx_icc_node {
> +	const struct imx_icc_node_desc *desc;
> +	struct device *qos_dev;
> +	struct dev_pm_qos_request qos_req;
> +};
> +
> +static int imx_icc_aggregate(struct icc_node *node, u32 tag,
> +			     u32 avg_bw, u32 peak_bw,
> +			     u32 *agg_avg, u32 *agg_peak)
> +{
> +	*agg_avg += avg_bw;
> +	*agg_peak = max(*agg_peak, peak_bw);
> +
> +	return 0;
> +}

Please remove and use the common library function:
icc_std_aggregate().

> +
> +static int imx_icc_node_set(struct icc_node *node)
> +{
> +	struct device *dev = node->provider->dev;
> +	struct imx_icc_node *node_data = node->data;
> +	u64 freq;
> +
> +	if (!node_data->qos_dev)
> +		return 0;
> +
> +	freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul;
> +	do_div(freq, node_data->desc->adj->bw_div);
> +	dev_dbg(dev, "node %s device %s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n",
> +		node->name, dev_name(node_data->qos_dev),
> +		node->avg_bw, node->peak_bw, freq);
> +
> +	if (freq > S32_MAX) {
> +		dev_err(dev, "%s can't request more than S32_MAX freq\n",
> +				node->name);

Please align with the open parenthesis.

> +		return -ERANGE;
> +	}
> +
> +	dev_pm_qos_update_request(&node_data->qos_req, freq);
> +
> +	return 0;
> +}
> +
> +static int imx_icc_set(struct icc_node *src, struct icc_node *dst)
> +{
> +	return imx_icc_node_set(dst);
> +}
> +
> +/* imx_icc_node_destroy() - Destroy an imx icc_node, including private data */
> +static void imx_icc_node_destroy(struct icc_node *node)
> +{
> +	struct imx_icc_node *node_data = node->data;
> +	int ret;
> +
> +	if (dev_pm_qos_request_active(&node_data->qos_req)) {
> +		ret = dev_pm_qos_remove_request(&node_data->qos_req);
> +		if (ret)
> +			dev_warn(node->provider->dev, "failed to remove qos request for %s\n",
> +				 dev_name(node_data->qos_dev));
> +	}
> +
> +	put_device(node_data->qos_dev);
> +	icc_node_del(node);
> +	icc_node_destroy(node->id);
> +}
> +
> +static int imx_icc_node_init_qos(struct icc_provider *provider,
> +				 struct icc_node *node)
> +{
> +	struct imx_icc_node *node_data = node->data;
> +	const struct imx_icc_node_adj_desc *adj = node_data->desc->adj;
> +	struct device *dev = provider->dev;
> +	struct device_node *dn = NULL;
> +	struct platform_device *pdev;
> +
> +	if (adj->main_noc) {
> +		node_data->qos_dev = dev;
> +		dev_info(dev, "icc node %s[%d] is main noc itself\n",
> +			 node->name, node->id);

Should this be dev_dbg()?

> +	} else {
> +		dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
> +		if (IS_ERR(dn)) {
> +			dev_warn(dev, "Failed to parse %s: %ld\n",
> +					adj->phandle_name, PTR_ERR(dn));

Please align with the open parenthesis.

> +			return PTR_ERR(dn);
> +		}
> +		/* Allow scaling to be disabled on a per-node basis */
> +		if (!dn || !of_device_is_available(dn)) {
> +			dev_warn(dev, "Missing property %s, skip scaling %s\n",
> +					adj->phandle_name, node->name);

Please align with the open parenthesis.

> +			return 0;
> +		}
> +
> +		pdev = of_find_device_by_node(dn);
> +		of_node_put(dn);
> +		if (!pdev) {
> +			dev_warn(dev, "node %s[%d] missing device for %pOF\n",
> +					node->name, node->id, dn);

Please align with the open parenthesis.

> +			return -EPROBE_DEFER;
> +		}
> +		node_data->qos_dev = &pdev->dev;
> +		dev_info(dev, "node %s[%d] has device node %pOF\n",
> +			 node->name, node->id, dn);

dev_dbg() again maybe? Unless you think that it make sense to print this
information to the user?

> +	}
> +
> +	return dev_pm_qos_add_request(node_data->qos_dev,
> +				      &node_data->qos_req,
> +				      DEV_PM_QOS_MIN_FREQUENCY, 0);
> +}
> +
> +static struct icc_node *imx_icc_node_add(struct icc_provider *provider,
> +					 const struct imx_icc_node_desc *node_desc)
> +{
> +	struct device *dev = provider->dev;
> +	struct imx_icc_node *node_data;
> +	struct icc_node *node;
> +	int ret;
> +
> +	node = icc_node_create(node_desc->id);
> +	if (IS_ERR(node)) {
> +		dev_err(dev, "failed to create node %d\n", node_desc->id);
> +		return node;
> +	}
> +
> +	if (node->data) {
> +		dev_err(dev, "already created node %s id=%d\n",
> +				node_desc->name, node_desc->id);

Please align with the open parenthesis.

> +		return ERR_PTR(-EEXIST);
> +	}
> +
> +	node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL);
> +	if (!node_data) {
> +		icc_node_destroy(node->id);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	node->name = node_desc->name;
> +	node->data = node_data;
> +	node_data->desc = node_desc;
> +	icc_node_add(node, provider);
> +
> +	if (node_desc->adj) {
> +		ret = imx_icc_node_init_qos(provider, node);
> +		if (ret < 0) {
> +			imx_icc_node_destroy(node);
> +			return ERR_PTR(ret);
> +		}
> +	}
> +
> +	return node;
> +}
> +
> +static void imx_icc_unregister_nodes(struct icc_provider *provider)
> +{
> +	struct icc_node *node, *tmp;
> +
> +	list_for_each_entry_safe(node, tmp, &provider->nodes, node_list)
> +		imx_icc_node_destroy(node);
> +}
> +
> +static int imx_icc_register_nodes(struct icc_provider *provider,
> +				  const struct imx_icc_node_desc *descs,
> +				  int count)
> +{
> +	struct icc_onecell_data *provider_data = provider->data;
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < count; i++) {
> +		struct icc_node *node;
> +		const struct imx_icc_node_desc *node_desc = &descs[i];
> +		size_t j;
> +
> +		node = imx_icc_node_add(provider, node_desc);
> +		if (IS_ERR(node)) {
> +			ret = PTR_ERR(node);
> +			if (ret != -EPROBE_DEFER)
> +				dev_err(provider->dev, "failed to add %s: %d\n",
> +					node_desc->name, ret);
> +			goto err;
> +		}
> +		provider_data->nodes[node->id] = node;
> +
> +		for (j = 0; j < node_desc->num_links; j++) {
> +			ret = icc_link_create(node, node_desc->links[j]);
> +			if (ret) {
> +				dev_err(provider->dev, "failed to link node %d to %d: %d\n",
> +					node->id, node_desc->links[j], ret);
> +				goto err;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	imx_icc_unregister_nodes(provider);
> +
> +	return ret;
> +}
> +
> +static int get_max_node_id(struct imx_icc_node_desc *nodes, int nodes_count)
> +{
> +	int i, ret = 0;
> +
> +	for (i = 0; i < nodes_count; ++i)
> +		if (nodes[i].id > ret)
> +			ret = nodes[i].id;
> +
> +	return ret;
> +}
> +
> +int imx_icc_register(struct platform_device *pdev,
> +		     struct imx_icc_node_desc *nodes, int nodes_count)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct icc_onecell_data *data;
> +	struct icc_provider *provider;
> +	int max_node_id;
> +	int ret;
> +
> +	/* icc_onecell_data is indexed by node_id, unlike nodes param */
> +	max_node_id = get_max_node_id(nodes, nodes_count);
> +	data = devm_kzalloc(dev, struct_size(data, nodes, max_node_id),
> +			    GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +	data->num_nodes = max_node_id;
> +
> +	provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
> +	if (!provider)
> +		return -ENOMEM;
> +	provider->set = imx_icc_set;
> +	provider->aggregate = imx_icc_aggregate;

provider->aggregate = icc_std_aggregate;

> +	provider->xlate = of_icc_xlate_onecell;
> +	provider->data = data;
> +	provider->dev = dev->parent;
> +	platform_set_drvdata(pdev, provider);
> +
> +	ret = icc_provider_add(provider);
> +	if (ret) {
> +		dev_err(dev, "error adding interconnect provider: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = imx_icc_register_nodes(provider, nodes, nodes_count);
> +	if (ret)
> +		goto provider_del;
> +
> +	return 0;
> +
> +provider_del:
> +	icc_provider_del(provider);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(imx_icc_register);
> +
> +int imx_icc_unregister(struct platform_device *pdev)
> +{
> +	struct icc_provider *provider = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	if (provider->users) {
> +		dev_warn(&pdev->dev, "interconnect provider still has %d users\n",
> +			provider->users);
> +		return -EBUSY;
> +	}

The above check already exists in icc_provider_del(). But i assume you don't
want to delete any nodes from the provider if it still has users. Maybe it will
be sensible to add this check into icc_nodes_remove() instead, so that such
cases are handled on a framework level.

> +	imx_icc_unregister_nodes(provider);
> +
> +	ret = icc_provider_del(provider);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(imx_icc_unregister);
> +
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/interconnect/imx/imx.h b/drivers/interconnect/imx/imx.h
> new file mode 100644
> index 000000000000..aa811e4ebb7e
> --- /dev/null
> +++ b/drivers/interconnect/imx/imx.h
> @@ -0,0 +1,62 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Interconnect framework driver for i.MX SoC
> + *
> + * Copyright (c) 2019, BayLibre
> + * Copyright (c) 2019, NXP
> + * Author: Alexandre Bailon <abailon@baylibre.com>
> + * Author: Leonard Crestez <leonard.crestez@nxp.com>
> + */
> +#ifndef __DRIVERS_INTERCONNECT_IMX_H
> +#define __DRIVERS_INTERCONNECT_IMX_H
> +
> +#include <linux/kernel.h>
> +
> +#define IMX_ICC_MAX_LINKS	4
> +
> +/*
> + * struct imx_icc_node_adj - Describe a dynamic adjustable node
> + */
> +struct imx_icc_node_adj_desc {
> +	unsigned int bw_mul, bw_div;
> +	const char *phandle_name;
> +	bool main_noc;
> +};
> +
> +/*
> + * struct imx_icc_node - Describe an interconnect node
> + * @name: name of the node
> + * @id: an unique id to identify the node
> + * @links: an array of slaves' node id
> + * @num_links: number of id defined in links
> + */
> +struct imx_icc_node_desc {
> +	const char *name;
> +	u16 id;
> +	u16 links[IMX_ICC_MAX_LINKS];
> +	u16 num_links;
> +

Why the blank line?

> +	const struct imx_icc_node_adj_desc *adj;
> +};
> +
> +#define DEFINE_BUS_INTERCONNECT(_name, _id, _adj, ...)			\
> +	{								\
> +		.id = _id,						\
> +		.name = _name,						\
> +		.adj = _adj,						\
> +		.num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })),	\
> +		.links = { __VA_ARGS__ },				\
> +	}
> +
> +#define DEFINE_BUS_MASTER(_name, _id, _dest_id)				\
> +	DEFINE_BUS_INTERCONNECT(_name, _id, NULL, _dest_id)
> +
> +#define DEFINE_BUS_SLAVE(_name, _id, _adj)				\
> +	DEFINE_BUS_INTERCONNECT(_name, _id, _adj)
> +
> +int imx_icc_register(struct platform_device *pdev,
> +		     struct imx_icc_node_desc *nodes,
> +		     int nodes_count);
> +int imx_icc_unregister(struct platform_device *pdev);
> +
> +#endif /* __DRIVERS_INTERCONNECT_IMX_H */
> 
Thanks,
Georgi
Leonard Crestez April 2, 2020, 10 p.m. UTC | #4
On 2020-04-02 2:06 PM, Georgi Djakov wrote:
> Hi Leonard,
> 
> Thank you for updating the patches!
> 
> On 4/1/20 17:33, Leonard Crestez wrote:
>> This adds support for i.MX SoC family to interconnect framework.
>>
>> Platform drivers can describe the interconnect graph and several
>> adjustment knobs where icc node bandwidth is converted to a
>> DEV_PM_QOS_MIN_FREQUENCY request.
>>
>> The interconnect provider is probed through the main NOC device and
>> other adjustable nodes on the same graph are found from a
>> fsl,scalable-nodes phandle array property.
>>
>> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
>> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
>> Tested-by: Martin Kepplinger <martin.kepplinger@puri.sm>
>> ---
>>   drivers/interconnect/Kconfig      |   1 +
>>   drivers/interconnect/Makefile     |   1 +
>>   drivers/interconnect/imx/Kconfig  |   5 +
>>   drivers/interconnect/imx/Makefile |   3 +
>>   drivers/interconnect/imx/imx.c    | 298 ++++++++++++++++++++++++++++++
>>   drivers/interconnect/imx/imx.h    |  62 +++++++
>>   6 files changed, 370 insertions(+)
>>   create mode 100644 drivers/interconnect/imx/Kconfig
>>   create mode 100644 drivers/interconnect/imx/Makefile
>>   create mode 100644 drivers/interconnect/imx/imx.c
>>   create mode 100644 drivers/interconnect/imx/imx.h
>>
>> diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
>> index bfa4ca3ab7a9..e61802230f90 100644
>> --- a/drivers/interconnect/Kconfig
>> +++ b/drivers/interconnect/Kconfig
>> @@ -10,7 +10,8 @@ menuconfig INTERCONNECT
>>   	  If unsure, say no.
>>   
>>   if INTERCONNECT
>>   
>>   source "drivers/interconnect/qcom/Kconfig"
>> +source "drivers/interconnect/imx/Kconfig"
> 
> Nit: Please move it up to make it sorted.
> 
>>   
>>   endif
>> diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
>> index 725029ae7a2c..6998288a7d98 100644
>> --- a/drivers/interconnect/Makefile
>> +++ b/drivers/interconnect/Makefile
>> @@ -3,5 +3,6 @@
>>   CFLAGS_core.o				:= -I$(src)
>>   icc-core-objs				:= core.o
>>   
>>   obj-$(CONFIG_INTERCONNECT)		+= icc-core.o
>>   obj-$(CONFIG_INTERCONNECT_QCOM)		+= qcom/
>> +obj-$(CONFIG_INTERCONNECT_IMX)		+= imx/
> 
> Ditto.
> 
>> diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig
>> new file mode 100644
>> index 000000000000..f39336f8d603
>> --- /dev/null
>> +++ b/drivers/interconnect/imx/Kconfig
>> @@ -0,0 +1,5 @@
>> +config INTERCONNECT_IMX
>> +	tristate "i.MX interconnect drivers"
>> +	depends on ARCH_MXC || COMPILE_TEST
>> +	help
>> +	  Generic interconnect drivers for i.MX SOCs
>> diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile
>> new file mode 100644
>> index 000000000000..86ae0bd28d8c
>> --- /dev/null
>> +++ b/drivers/interconnect/imx/Makefile
>> @@ -0,0 +1,3 @@
>> +imx-interconnect-objs			:= imx.o
>> +
>> +obj-$(CONFIG_INTERCONNECT_IMX)		+= imx-interconnect.o
>> diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c
>> new file mode 100644
>> index 000000000000..527b1de1c41a
>> --- /dev/null
>> +++ b/drivers/interconnect/imx/imx.c
>> @@ -0,0 +1,298 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Interconnect framework driver for i.MX SoC
>> + *
>> + * Copyright (c) 2019, BayLibre
>> + * Copyright (c) 2019, NXP
> 
> Maybe update it to 2020.
> 
>> + * Author: Alexandre Bailon <abailon@baylibre.com>
>> + * Author: Leonard Crestez <leonard.crestez@nxp.com>
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/interconnect-provider.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_qos.h>
>> +
>> +#include "imx.h"
>> +
>> +/* private icc_node data */
>> +struct imx_icc_node {
>> +	const struct imx_icc_node_desc *desc;
>> +	struct device *qos_dev;
>> +	struct dev_pm_qos_request qos_req;
>> +};
>> +
>> +static int imx_icc_aggregate(struct icc_node *node, u32 tag,
>> +			     u32 avg_bw, u32 peak_bw,
>> +			     u32 *agg_avg, u32 *agg_peak)
>> +{
>> +	*agg_avg += avg_bw;
>> +	*agg_peak = max(*agg_peak, peak_bw);
>> +
>> +	return 0;
>> +}
> 
> Please remove and use the common library function:
> icc_std_aggregate().

OK

>> +
>> +static int imx_icc_node_set(struct icc_node *node)
>> +{
>> +	struct device *dev = node->provider->dev;
>> +	struct imx_icc_node *node_data = node->data;
>> +	u64 freq;
>> +
>> +	if (!node_data->qos_dev)
>> +		return 0;
>> +
>> +	freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul;
>> +	do_div(freq, node_data->desc->adj->bw_div);
>> +	dev_dbg(dev, "node %s device %s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n",
>> +		node->name, dev_name(node_data->qos_dev),
>> +		node->avg_bw, node->peak_bw, freq);
>> +
>> +	if (freq > S32_MAX) {
>> +		dev_err(dev, "%s can't request more than S32_MAX freq\n",
>> +				node->name);
> 
> Please align with the open parenthesis.

Fixed all instances.

>> +		return -ERANGE;
>> +	}
>> +
>> +	dev_pm_qos_update_request(&node_data->qos_req, freq);
>> +
>> +	return 0;
>> +}
>> +
>> +static int imx_icc_set(struct icc_node *src, struct icc_node *dst)
>> +{
>> +	return imx_icc_node_set(dst);
>> +}
>> +
>> +/* imx_icc_node_destroy() - Destroy an imx icc_node, including private data */
>> +static void imx_icc_node_destroy(struct icc_node *node)
>> +{
>> +	struct imx_icc_node *node_data = node->data;
>> +	int ret;
>> +
>> +	if (dev_pm_qos_request_active(&node_data->qos_req)) {
>> +		ret = dev_pm_qos_remove_request(&node_data->qos_req);
>> +		if (ret)
>> +			dev_warn(node->provider->dev, "failed to remove qos request for %s\n",
>> +				 dev_name(node_data->qos_dev));
>> +	}
>> +
>> +	put_device(node_data->qos_dev);
>> +	icc_node_del(node);
>> +	icc_node_destroy(node->id);
>> +}
>> +
>> +static int imx_icc_node_init_qos(struct icc_provider *provider,
>> +				 struct icc_node *node)
>> +{
>> +	struct imx_icc_node *node_data = node->data;
>> +	const struct imx_icc_node_adj_desc *adj = node_data->desc->adj;
>> +	struct device *dev = provider->dev;
>> +	struct device_node *dn = NULL;
>> +	struct platform_device *pdev;
>> +
>> +	if (adj->main_noc) {
>> +		node_data->qos_dev = dev;
>> +		dev_info(dev, "icc node %s[%d] is main noc itself\n",
>> +			 node->name, node->id);
> 
> Should this be dev_dbg()?
> 
>> +	} else {
>> +		dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
>> +		if (IS_ERR(dn)) {
>> +			dev_warn(dev, "Failed to parse %s: %ld\n",
>> +					adj->phandle_name, PTR_ERR(dn));
> 
> Please align with the open parenthesis.
> 
>> +			return PTR_ERR(dn);
>> +		}
>> +		/* Allow scaling to be disabled on a per-node basis */
>> +		if (!dn || !of_device_is_available(dn)) {
>> +			dev_warn(dev, "Missing property %s, skip scaling %s\n",
>> +					adj->phandle_name, node->name);
> 
> Please align with the open parenthesis.
> 
>> +			return 0;
>> +		}
>> +
>> +		pdev = of_find_device_by_node(dn);
>> +		of_node_put(dn);
>> +		if (!pdev) {
>> +			dev_warn(dev, "node %s[%d] missing device for %pOF\n",
>> +					node->name, node->id, dn);
> 
> Please align with the open parenthesis.
> 
>> +			return -EPROBE_DEFER;
>> +		}
>> +		node_data->qos_dev = &pdev->dev;
>> +		dev_info(dev, "node %s[%d] has device node %pOF\n",
>> +			 node->name, node->id, dn);
> 
> dev_dbg() again maybe? Unless you think that it make sense to print this
> information to the user?
> 
>> +	}
>> +
>> +	return dev_pm_qos_add_request(node_data->qos_dev,
>> +				      &node_data->qos_req,
>> +				      DEV_PM_QOS_MIN_FREQUENCY, 0);
>> +}
>> +
>> +static struct icc_node *imx_icc_node_add(struct icc_provider *provider,
>> +					 const struct imx_icc_node_desc *node_desc)
>> +{
>> +	struct device *dev = provider->dev;
>> +	struct imx_icc_node *node_data;
>> +	struct icc_node *node;
>> +	int ret;
>> +
>> +	node = icc_node_create(node_desc->id);
>> +	if (IS_ERR(node)) {
>> +		dev_err(dev, "failed to create node %d\n", node_desc->id);
>> +		return node;
>> +	}
>> +
>> +	if (node->data) {
>> +		dev_err(dev, "already created node %s id=%d\n",
>> +				node_desc->name, node_desc->id);
> 
> Please align with the open parenthesis.
> 
>> +		return ERR_PTR(-EEXIST);
>> +	}
>> +
>> +	node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL);
>> +	if (!node_data) {
>> +		icc_node_destroy(node->id);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	node->name = node_desc->name;
>> +	node->data = node_data;
>> +	node_data->desc = node_desc;
>> +	icc_node_add(node, provider);
>> +
>> +	if (node_desc->adj) {
>> +		ret = imx_icc_node_init_qos(provider, node);
>> +		if (ret < 0) {
>> +			imx_icc_node_destroy(node);
>> +			return ERR_PTR(ret);
>> +		}
>> +	}
>> +
>> +	return node;
>> +}
>> +
>> +static void imx_icc_unregister_nodes(struct icc_provider *provider)
>> +{
>> +	struct icc_node *node, *tmp;
>> +
>> +	list_for_each_entry_safe(node, tmp, &provider->nodes, node_list)
>> +		imx_icc_node_destroy(node);
>> +}
>> +
>> +static int imx_icc_register_nodes(struct icc_provider *provider,
>> +				  const struct imx_icc_node_desc *descs,
>> +				  int count)
>> +{
>> +	struct icc_onecell_data *provider_data = provider->data;
>> +	int ret;
>> +	int i;
>> +
>> +	for (i = 0; i < count; i++) {
>> +		struct icc_node *node;
>> +		const struct imx_icc_node_desc *node_desc = &descs[i];
>> +		size_t j;
>> +
>> +		node = imx_icc_node_add(provider, node_desc);
>> +		if (IS_ERR(node)) {
>> +			ret = PTR_ERR(node);
>> +			if (ret != -EPROBE_DEFER)
>> +				dev_err(provider->dev, "failed to add %s: %d\n",
>> +					node_desc->name, ret);
>> +			goto err;
>> +		}
>> +		provider_data->nodes[node->id] = node;
>> +
>> +		for (j = 0; j < node_desc->num_links; j++) {
>> +			ret = icc_link_create(node, node_desc->links[j]);
>> +			if (ret) {
>> +				dev_err(provider->dev, "failed to link node %d to %d: %d\n",
>> +					node->id, node_desc->links[j], ret);
>> +				goto err;
>> +			}
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +err:
>> +	imx_icc_unregister_nodes(provider);
>> +
>> +	return ret;
>> +}
>> +
>> +static int get_max_node_id(struct imx_icc_node_desc *nodes, int nodes_count)
>> +{
>> +	int i, ret = 0;
>> +
>> +	for (i = 0; i < nodes_count; ++i)
>> +		if (nodes[i].id > ret)
>> +			ret = nodes[i].id;
>> +
>> +	return ret;
>> +}
>> +
>> +int imx_icc_register(struct platform_device *pdev,
>> +		     struct imx_icc_node_desc *nodes, int nodes_count)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct icc_onecell_data *data;
>> +	struct icc_provider *provider;
>> +	int max_node_id;
>> +	int ret;
>> +
>> +	/* icc_onecell_data is indexed by node_id, unlike nodes param */
>> +	max_node_id = get_max_node_id(nodes, nodes_count);
>> +	data = devm_kzalloc(dev, struct_size(data, nodes, max_node_id),
>> +			    GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +	data->num_nodes = max_node_id;
>> +
>> +	provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
>> +	if (!provider)
>> +		return -ENOMEM;
>> +	provider->set = imx_icc_set;
>> +	provider->aggregate = imx_icc_aggregate;
> 
> provider->aggregate = icc_std_aggregate;
> 
>> +	provider->xlate = of_icc_xlate_onecell;
>> +	provider->data = data;
>> +	provider->dev = dev->parent;
>> +	platform_set_drvdata(pdev, provider);
>> +
>> +	ret = icc_provider_add(provider);
>> +	if (ret) {
>> +		dev_err(dev, "error adding interconnect provider: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = imx_icc_register_nodes(provider, nodes, nodes_count);
>> +	if (ret)
>> +		goto provider_del;
>> +
>> +	return 0;
>> +
>> +provider_del:
>> +	icc_provider_del(provider);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(imx_icc_register);
>> +
>> +int imx_icc_unregister(struct platform_device *pdev)
>> +{
>> +	struct icc_provider *provider = platform_get_drvdata(pdev);
>> +	int ret;
>> +
>> +	if (provider->users) {
>> +		dev_warn(&pdev->dev, "interconnect provider still has %d users\n",
>> +			provider->users);
>> +		return -EBUSY;
>> +	}
> 
> The above check already exists in icc_provider_del(). But i assume you don't
> want to delete any nodes from the provider if it still has users. Maybe it will
> be sensible to add this check into icc_nodes_remove() instead, so that such
> cases are handled on a framework level.

Actually device removal is not allowed to fail, for example doing an 
explicit unbind will ignore errors coming from the unregister function:

echo imx8mm-interconnect > 
/sys/bus/platform/drivers/imx8mm-interconnect/unbind

results in the following stack trace where device_driver_detach actually 
continues even if remove returns an error:

[   24.017901]  imx_icc_unregister+0x24/0x80
[   24.021925]  imx8mm_icc_remove+0x18/0x28
[   24.025863]  platform_drv_remove+0x34/0x58
[   24.029974]  device_release_driver_internal+0x104/0x1d8
[   24.035213]  device_driver_detach+0x20/0x30
[   24.039409]  unbind_store+0xe8/0x110
[   24.042999]  drv_attr_store+0x2c/0x40
[   24.046677]  sysfs_kf_write+0x54/0x80

I'm not sure how the framework should handle provider removal. Right now 
icc_path holds icc_node pointers so when nodes are freed any consumer 
calling into icc will corrupt memory.

One option would be to reference count the allocation of struct 
icc_node, this way icc_path functions could safely check for 
node->provider == NULL and return an error.

I'll remove this check from the imx driver because it does not solve 
anything.

> 
>> +	imx_icc_unregister_nodes(provider);
>> +
>> +	ret = icc_provider_del(provider);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(imx_icc_unregister);
>> +
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/interconnect/imx/imx.h b/drivers/interconnect/imx/imx.h
>> new file mode 100644
>> index 000000000000..aa811e4ebb7e
>> --- /dev/null
>> +++ b/drivers/interconnect/imx/imx.h
>> @@ -0,0 +1,62 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Interconnect framework driver for i.MX SoC
>> + *
>> + * Copyright (c) 2019, BayLibre
>> + * Copyright (c) 2019, NXP
>> + * Author: Alexandre Bailon <abailon@baylibre.com>
>> + * Author: Leonard Crestez <leonard.crestez@nxp.com>
>> + */
>> +#ifndef __DRIVERS_INTERCONNECT_IMX_H
>> +#define __DRIVERS_INTERCONNECT_IMX_H
>> +
>> +#include <linux/kernel.h>
>> +
>> +#define IMX_ICC_MAX_LINKS	4
>> +
>> +/*
>> + * struct imx_icc_node_adj - Describe a dynamic adjustable node
>> + */
>> +struct imx_icc_node_adj_desc {
>> +	unsigned int bw_mul, bw_div;
>> +	const char *phandle_name;
>> +	bool main_noc;
>> +};
>> +
>> +/*
>> + * struct imx_icc_node - Describe an interconnect node
>> + * @name: name of the node
>> + * @id: an unique id to identify the node
>> + * @links: an array of slaves' node id
>> + * @num_links: number of id defined in links
>> + */
>> +struct imx_icc_node_desc {
>> +	const char *name;
>> +	u16 id;
>> +	u16 links[IMX_ICC_MAX_LINKS];
>> +	u16 num_links;
>> +
> 
> Why the blank line?

Removed

>> +	const struct imx_icc_node_adj_desc *adj;
>> +};
>> +
>> +#define DEFINE_BUS_INTERCONNECT(_name, _id, _adj, ...)			\
>> +	{								\
>> +		.id = _id,						\
>> +		.name = _name,						\
>> +		.adj = _adj,						\
>> +		.num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })),	\
>> +		.links = { __VA_ARGS__ },				\
>> +	}
>> +
>> +#define DEFINE_BUS_MASTER(_name, _id, _dest_id)				\
>> +	DEFINE_BUS_INTERCONNECT(_name, _id, NULL, _dest_id)
>> +
>> +#define DEFINE_BUS_SLAVE(_name, _id, _adj)				\
>> +	DEFINE_BUS_INTERCONNECT(_name, _id, _adj)
>> +
>> +int imx_icc_register(struct platform_device *pdev,
>> +		     struct imx_icc_node_desc *nodes,
>> +		     int nodes_count);
>> +int imx_icc_unregister(struct platform_device *pdev);
>> +
>> +#endif /* __DRIVERS_INTERCONNECT_IMX_H */
Adam Ford April 4, 2020, 1:25 p.m. UTC | #5
On Wed, Apr 1, 2020 at 9:35 AM Leonard Crestez <leonard.crestez@nxp.com> wrote:
>
> Add nodes for the main interconnect of the imx8m series chips.
>
> These nodes are bound to by devfreq and interconnect drivers.
>
> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
> ---
>  arch/arm64/boot/dts/freescale/imx8mm.dtsi | 24 +++++++++++++++++++++++
>  arch/arm64/boot/dts/freescale/imx8mn.dtsi | 24 +++++++++++++++++++++++
>  arch/arm64/boot/dts/freescale/imx8mq.dtsi | 24 +++++++++++++++++++++++
>  3 files changed, 72 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> index 175c28ae10cf..41047b6709b6 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> @@ -6,10 +6,11 @@
>  #include <dt-bindings/clock/imx8mm-clock.h>
>  #include <dt-bindings/gpio/gpio.h>
>  #include <dt-bindings/input/input.h>
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/thermal/thermal.h>
> +#include <dt-bindings/interconnect/imx8mm.h>
>
>  #include "imx8mm-pinfunc.h"
>
>  / {
>         interrupt-parent = <&gic>;
> @@ -860,10 +861,33 @@
>                                 status = "disabled";
>                         };
>
>                 };
>
> +               noc: interconnect@32700000 {
> +                       compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc";
> +                       reg = <0x32700000 0x100000>;
> +                       clocks = <&clk IMX8MM_CLK_NOC>;
> +                       fsl,ddrc = <&ddrc>;
> +                       #interconnect-cells = <1>;
> +                       operating-points-v2 = <&noc_opp_table>;
> +
> +                       noc_opp_table: opp-table {
> +                               compatible = "operating-points-v2";
> +
> +                               opp-150M {
> +                                       opp-hz = /bits/ 64 <150000000>;
> +                               };
> +                               opp-375M {
> +                                       opp-hz = /bits/ 64 <375000000>;
> +                               };
> +                               opp-750M {
> +                                       opp-hz = /bits/ 64 <750000000>;

Out of curiosity, the 8M Mini runs up to 750M, and  the 8M Nano and
8MQ run up to 800.  The 8MQ had a patch to increase the assigned clock
speed for the NOC to 800MHz

See: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/patch/arch/arm64/boot/dts/freescale?id=912b9dacf3f0ffad55e1a1b3c5af0e433ebdb5dd)

The 8M Mini and 8M Nano appear to be setting the default speed to 0.

Should the 8M Mini or 8M Nano do something similar to what the 8MQ
did, or does this series negate the need for such a patch?

thanks

adam

> +                               };
> +                       };
> +               };
> +
>                 aips4: bus@32c00000 {
>                         compatible = "fsl,aips-bus", "simple-bus";
>                         reg = <0x32df0000 0x10000>;
>                         #address-cells = <1>;
>                         #size-cells = <1>;
> diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> index 88e7d74e077f..e8a55956813f 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> @@ -6,10 +6,11 @@
>  #include <dt-bindings/clock/imx8mn-clock.h>
>  #include <dt-bindings/gpio/gpio.h>
>  #include <dt-bindings/input/input.h>
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/thermal/thermal.h>
> +#include <dt-bindings/interconnect/imx8mn.h>
>
>  #include "imx8mn-pinfunc.h"
>
>  / {
>         interrupt-parent = <&gic>;
> @@ -751,10 +752,33 @@
>                                 status = "disabled";
>                         };
>
>                 };
>
> +               noc: interconnect@32700000 {
> +                       compatible = "fsl,imx8mn-noc", "fsl,imx8m-noc";
> +                       reg = <0x32700000 0x100000>;
> +                       clocks = <&clk IMX8MN_CLK_NOC>;
> +                       fsl,ddrc = <&ddrc>;
> +                       #interconnect-cells = <1>;
> +                       operating-points-v2 = <&noc_opp_table>;
> +
> +                       noc_opp_table: opp-table {
> +                               compatible = "operating-points-v2";
> +
> +                               opp-100M {
> +                                       opp-hz = /bits/ 64 <100000000>;
> +                               };
> +                               opp-600M {
> +                                       opp-hz = /bits/ 64 <600000000>;
> +                               };
> +                               opp-800M {
> +                                       opp-hz = /bits/ 64 <800000000>;
> +                               };
> +                       };
> +               };
> +
>                 aips4: bus@32c00000 {
>                         compatible = "fsl,aips-bus", "simple-bus";
>                         reg = <0x32df0000 0x10000>;
>                         #address-cells = <1>;
>                         #size-cells = <1>;
> diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> index ea93bc4b7d7e..3a208feec74c 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> @@ -9,10 +9,11 @@
>  #include <dt-bindings/reset/imx8mq-reset.h>
>  #include <dt-bindings/gpio/gpio.h>
>  #include "dt-bindings/input/input.h"
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/thermal/thermal.h>
> +#include <dt-bindings/interconnect/imx8mq.h>
>  #include "imx8mq-pinfunc.h"
>
>  / {
>         interrupt-parent = <&gpc>;
>
> @@ -1026,10 +1027,33 @@
>                                 fsl,num-rx-queues = <3>;
>                                 status = "disabled";
>                         };
>                 };
>
> +               noc: interconnect@32700000 {
> +                       compatible = "fsl,imx8mq-noc", "fsl,imx8m-noc";
> +                       reg = <0x32700000 0x100000>;
> +                       clocks = <&clk IMX8MQ_CLK_NOC>;
> +                       fsl,ddrc = <&ddrc>;
> +                       #interconnect-cells = <1>;
> +                       operating-points-v2 = <&noc_opp_table>;
> +
> +                       noc_opp_table: opp-table {
> +                               compatible = "operating-points-v2";
> +
> +                               opp-133M {
> +                                       opp-hz = /bits/ 64 <133333333>;
> +                               };
> +                               opp-400M {
> +                                       opp-hz = /bits/ 64 <400000000>;
> +                               };
> +                               opp-800M {
> +                                       opp-hz = /bits/ 64 <800000000>;
> +                               };
> +                       };
> +               };
> +
>                 bus@32c00000 { /* AIPS4 */
>                         compatible = "fsl,aips-bus", "simple-bus";
>                         reg = <0x32df0000 0x10000>;
>                         #address-cells = <1>;
>                         #size-cells = <1>;
> --
> 2.17.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Leonard Crestez April 7, 2020, 9:10 a.m. UTC | #6
On 2020-04-04 4:25 PM, Adam Ford wrote:
> On Wed, Apr 1, 2020 at 9:35 AM Leonard Crestez <leonard.crestez@nxp.com> wrote:
>>
>> Add nodes for the main interconnect of the imx8m series chips.
>>
>> These nodes are bound to by devfreq and interconnect drivers.
>>
>> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
>> ---
>>   arch/arm64/boot/dts/freescale/imx8mm.dtsi | 24 +++++++++++++++++++++++
>>   arch/arm64/boot/dts/freescale/imx8mn.dtsi | 24 +++++++++++++++++++++++
>>   arch/arm64/boot/dts/freescale/imx8mq.dtsi | 24 +++++++++++++++++++++++
>>   3 files changed, 72 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
>> index 175c28ae10cf..41047b6709b6 100644
>> --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
>> +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
>> @@ -6,10 +6,11 @@
>>   #include <dt-bindings/clock/imx8mm-clock.h>
>>   #include <dt-bindings/gpio/gpio.h>
>>   #include <dt-bindings/input/input.h>
>>   #include <dt-bindings/interrupt-controller/arm-gic.h>
>>   #include <dt-bindings/thermal/thermal.h>
>> +#include <dt-bindings/interconnect/imx8mm.h>
>>
>>   #include "imx8mm-pinfunc.h"
>>
>>   / {
>>          interrupt-parent = <&gic>;
>> @@ -860,10 +861,33 @@
>>                                  status = "disabled";
>>                          };
>>
>>                  };
>>
>> +               noc: interconnect@32700000 {
>> +                       compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc";
>> +                       reg = <0x32700000 0x100000>;
>> +                       clocks = <&clk IMX8MM_CLK_NOC>;
>> +                       fsl,ddrc = <&ddrc>;
>> +                       #interconnect-cells = <1>;
>> +                       operating-points-v2 = <&noc_opp_table>;
>> +
>> +                       noc_opp_table: opp-table {
>> +                               compatible = "operating-points-v2";
>> +
>> +                               opp-150M {
>> +                                       opp-hz = /bits/ 64 <150000000>;
>> +                               };
>> +                               opp-375M {
>> +                                       opp-hz = /bits/ 64 <375000000>;
>> +                               };
>> +                               opp-750M {
>> +                                       opp-hz = /bits/ 64 <750000000>;
> 
> Out of curiosity, the 8M Mini runs up to 750M, and  the 8M Nano and
> 8MQ run up to 800.  The 8MQ had a patch to increase the assigned clock
> speed for the NOC to 800MHz
> 
> See: https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fnext%2Flinux-next.git%2Fpatch%2Farch%2Farm64%2Fboot%2Fdts%2Ffreescale%3Fid%3D912b9dacf3f0ffad55e1a1b3c5af0e433ebdb5dd&amp;data=02%7C01%7Cleonard.crestez%40nxp.com%7C32c3655718e4459028e008d7d89baa31%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637216035403876452&amp;sdata=z%2B5afsPGbCk4HkRp4nR6QepOrm70Fi5B5dohyvaquxo%3D&amp;reserved=0)
> 
> The 8M Mini and 8M Nano appear to be setting the default speed to 0.

I'm not sure what you mean about this, the noc clock is required for 
mostly everything.

> Should the 8M Mini or 8M Nano do something similar to what the 8MQ
> did, or does this series negate the need for such a patch?

Instead of doing assigned-clocks noc frequency needs to be tweaked by 
adjusting OPPs in this list. The devfreq device for noc will overwrite 
other frequencies set for the noc.

--
Regards,
Leonard
Adam Ford April 7, 2020, 6:39 p.m. UTC | #7
On Tue, Apr 7, 2020 at 4:10 AM Leonard Crestez <leonard.crestez@nxp.com> wrote:
>
> On 2020-04-04 4:25 PM, Adam Ford wrote:
> > On Wed, Apr 1, 2020 at 9:35 AM Leonard Crestez <leonard.crestez@nxp.com> wrote:
> >>
> >> Add nodes for the main interconnect of the imx8m series chips.
> >>
> >> These nodes are bound to by devfreq and interconnect drivers.
> >>
> >> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
> >> ---
> >>   arch/arm64/boot/dts/freescale/imx8mm.dtsi | 24 +++++++++++++++++++++++
> >>   arch/arm64/boot/dts/freescale/imx8mn.dtsi | 24 +++++++++++++++++++++++
> >>   arch/arm64/boot/dts/freescale/imx8mq.dtsi | 24 +++++++++++++++++++++++
> >>   3 files changed, 72 insertions(+)
> >>
> >> diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> >> index 175c28ae10cf..41047b6709b6 100644
> >> --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> >> +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> >> @@ -6,10 +6,11 @@
> >>   #include <dt-bindings/clock/imx8mm-clock.h>
> >>   #include <dt-bindings/gpio/gpio.h>
> >>   #include <dt-bindings/input/input.h>
> >>   #include <dt-bindings/interrupt-controller/arm-gic.h>
> >>   #include <dt-bindings/thermal/thermal.h>
> >> +#include <dt-bindings/interconnect/imx8mm.h>
> >>
> >>   #include "imx8mm-pinfunc.h"
> >>
> >>   / {
> >>          interrupt-parent = <&gic>;
> >> @@ -860,10 +861,33 @@
> >>                                  status = "disabled";
> >>                          };
> >>
> >>                  };
> >>
> >> +               noc: interconnect@32700000 {
> >> +                       compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc";
> >> +                       reg = <0x32700000 0x100000>;
> >> +                       clocks = <&clk IMX8MM_CLK_NOC>;
> >> +                       fsl,ddrc = <&ddrc>;
> >> +                       #interconnect-cells = <1>;
> >> +                       operating-points-v2 = <&noc_opp_table>;
> >> +
> >> +                       noc_opp_table: opp-table {
> >> +                               compatible = "operating-points-v2";
> >> +
> >> +                               opp-150M {
> >> +                                       opp-hz = /bits/ 64 <150000000>;
> >> +                               };
> >> +                               opp-375M {
> >> +                                       opp-hz = /bits/ 64 <375000000>;
> >> +                               };
> >> +                               opp-750M {
> >> +                                       opp-hz = /bits/ 64 <750000000>;
> >
> > Out of curiosity, the 8M Mini runs up to 750M, and  the 8M Nano and
> > 8MQ run up to 800.  The 8MQ had a patch to increase the assigned clock
> > speed for the NOC to 800MHz
> >
> > See: https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kernel.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fnext%2Flinux-next.git%2Fpatch%2Farch%2Farm64%2Fboot%2Fdts%2Ffreescale%3Fid%3D912b9dacf3f0ffad55e1a1b3c5af0e433ebdb5dd&amp;data=02%7C01%7Cleonard.crestez%40nxp.com%7C32c3655718e4459028e008d7d89baa31%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637216035403876452&amp;sdata=z%2B5afsPGbCk4HkRp4nR6QepOrm70Fi5B5dohyvaquxo%3D&amp;reserved=0)
> >
> > The 8M Mini and 8M Nano appear to be setting the default speed to 0.
>
> I'm not sure what you mean about this, the noc clock is required for
> mostly everything.

As an example, the i.MX8MM looks like this:

assigned-clocks = <&clk IMX8MM_CLK_NOC>,
                              <&clk IMX8MM_CLK_AUDIO_AHB>,
                              <&clk IMX8MM_CLK_IPG_AUDIO_ROOT>,
                              <&clk IMX8MM_SYS_PLL3>,
                              <&clk IMX8MM_VIDEO_PLL1>,
                              <&clk IMX8MM_AUDIO_PLL1>,
                              <&clk IMX8MM_AUDIO_PLL2>;
assigned-clock-parents = <&clk IMX8MM_SYS_PLL3_OUT>,
                                         <&clk IMX8MM_SYS_PLL1_800M>;
assigned-clock-rates = <0>,
                                    <400000000>,
                                    <400000000>,
                                    <750000000>,
                                    <594000000>,
                                    <393216000>,
                                    <361267200>;

If I am reading this correctly, it appears to me that IMX8MM_CLK_NOC
is set to 0.

The i.MX8MN is similar, but the patch above shows IMX8MQ_CLK_NOC used
to be 0, but was updated for better performance.

>
> > Should the 8M Mini or 8M Nano do something similar to what the 8MQ
> > did, or does this series negate the need for such a patch?
>
> Instead of doing assigned-clocks noc frequency needs to be tweaked by
> adjusting OPPs in this list. The devfreq device for noc will overwrite
> other frequencies set for the noc.

My question was whether or not we should consider a patch to made the
default assigned-clock-rate for IMX8MM_CLK_NOC to be 750000000.  Based
on your response, it sounds like the answer might be that it's not
necessary.

thanks

adam
>
> --
> Regards,
> Leonard