interconnect changes for 5.8

These are the interconnect changes for the 5.8-rc1 merge window:
 
 Core changes:
 - Convert the framework core from tristate to bool to make handling
 dependencies between other core frameworks easier
 - Add of_icc_get_by_index()
 - Add devm_of_icc_get() helper function
 - Add icc_enable() and icc_disable() helpers
 
 New drivers:
 - Platform driver for NXP i.MX8MM SoC
 - Platform driver for NXP i.MX8MN SoC
 - Platform driver for NXP i.MX8MQ SoC
 
 Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJexm0fAAoJEIDQzArG2BZj1A4QAJzbhmuIvX9wbLg4ZWVsUgz7
 CXKH+AKdK1hhdA3uspthjHWl9nV4goEzT8Ia1ZHd79+64ksIvP4jz95Ae6+7maMP
 ffrQaArnscbVL+1FX5UMLdHRNhZ9AOsaYX/CbYugY1f6WJ+VrbsVbaKPiPWUv7jh
 uPuGVsRvwMfT23gAqGLKnHqV3LjCHnG0xjWalBb0cZNbdJu7WP2DiZnk7qM28GhA
 L4xSudtTVwq91Zij1EoIMIm1ZqgYpQtTVEtg0OebZebYMFakg3xLGO/EtLziEcI3
 qXRLleaRrEjhQ/6Wbuxh+kCTcEUXTG/DUbGzrqKspzE7jnM4MQdC38Wqrs4tZvoi
 m43K5SQi0P5UpPnYnpsb6XvlkXgryw0PIKZLLcAaVWitNSLE2LUSlmBMO7EOyuag
 TftcYcqILo93mPWiEBnrBS1DtheN9W529akOIESrA9y6DrFpHHGR9sEz34ZAIA/R
 Hn29yFLPEGAdoCgs+mQatXIfPU66vFxALh5V1YLFLImErYtA3+HNDeXg/xLBEbk+
 NRXkURLPjMIIGtgIEA8laEeMX6rgJA6DFF2H4B6uizLpfccVLipwBafIJIjksLSf
 PJHySyE0yuWSQslRR09FguSvwTbtamOxrK/Aqc/w3XYfz2UwNZDyI8ecz195noW0
 ylKcFX+HVcl2rQ/hPN7C
 =+f9u
 -----END PGP SIGNATURE-----

Merge tag 'icc-5.8-rc1' of https://git.linaro.org/people/georgi.djakov/linux into char-misc-next

Georgi writes:

interconnect changes for 5.8

These are the interconnect changes for the 5.8-rc1 merge window:

Core changes:
- Convert the framework core from tristate to bool to make handling
dependencies between other core frameworks easier
- Add of_icc_get_by_index()
- Add devm_of_icc_get() helper function
- Add icc_enable() and icc_disable() helpers

New drivers:
- Platform driver for NXP i.MX8MM SoC
- Platform driver for NXP i.MX8MN SoC
- Platform driver for NXP i.MX8MQ SoC

Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org>

* tag 'icc-5.8-rc1' of https://git.linaro.org/people/georgi.djakov/linux:
  interconnect: Remove unused module exit code from core
  interconnect: Disallow interconnect core to be built as a module
  interconnect: Add of_icc_get_by_index() helper function
  interconnect: Add helpers for enabling/disabling a path
  interconnect: imx: Fix return value check in imx_icc_node_init_qos()
  interconnect: imx: Add platform driver for imx8mn
  interconnect: imx: Add platform driver for imx8mq
  interconnect: imx: Add platform driver for imx8mm
  interconnect: Add imx core driver
  dt-bindings: interconnect: Add bindings for imx8m noc
  interconnect: Add devm_of_icc_get() as exported API for users
This commit is contained in:
Greg Kroah-Hartman 2020-05-22 09:14:03 +02:00
commit c9cf27d9de
16 changed files with 1061 additions and 26 deletions

View file

@ -0,0 +1,101 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/fsl,imx8m-noc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic i.MX bus frequency device
maintainers:
- Leonard Crestez <leonard.crestez@nxp.com>
description: |
The i.MX SoC family has multiple buses for which clock frequency (and
sometimes voltage) can be adjusted.
Some of those buses expose register areas mentioned in the memory maps as GPV
("Global Programmers View") but not all. Access to this area might be denied
for normal (non-secure) world.
The buses are based on externally licensed IPs such as ARM NIC-301 and
Arteris FlexNOC but DT bindings are specific to the integration of these bus
interconnect IPs into imx SOCs.
properties:
compatible:
oneOf:
- items:
- enum:
- fsl,imx8mn-nic
- fsl,imx8mm-nic
- fsl,imx8mq-nic
- const: fsl,imx8m-nic
- items:
- enum:
- fsl,imx8mn-noc
- fsl,imx8mm-noc
- fsl,imx8mq-noc
- const: fsl,imx8m-noc
- const: fsl,imx8m-nic
reg:
maxItems: 1
clocks:
maxItems: 1
operating-points-v2: true
opp-table: true
fsl,ddrc:
$ref: "/schemas/types.yaml#/definitions/phandle"
description:
Phandle to DDR Controller.
'#interconnect-cells':
description:
If specified then also act as an interconnect provider. Should only be
set once per soc on the main noc.
const: 1
required:
- compatible
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx8mm-clock.h>
#include <dt-bindings/interconnect/imx8mm.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
noc: interconnect@32700000 {
compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc";
reg = <0x32700000 0x100000>;
clocks = <&clk IMX8MM_CLK_NOC>;
#interconnect-cells = <1>;
fsl,ddrc = <&ddrc>;
operating-points-v2 = <&noc_opp_table>;
noc_opp_table: opp-table {
compatible = "operating-points-v2";
opp-133M {
opp-hz = /bits/ 64 <133333333>;
};
opp-800M {
opp-hz = /bits/ 64 <800000000>;
};
};
};
ddrc: memory-controller@3d400000 {
compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc";
reg = <0x3d400000 0x400000>;
clock-names = "core", "pll", "alt", "apb";
clocks = <&clk IMX8MM_CLK_DRAM_CORE>,
<&clk IMX8MM_DRAM_PLL>,
<&clk IMX8MM_CLK_DRAM_ALT>,
<&clk IMX8MM_CLK_DRAM_APB>;
};

View file

@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig INTERCONNECT
tristate "On-Chip Interconnect management support"
bool "On-Chip Interconnect management support"
help
Support for management of the on-chip interconnects.
@ -11,6 +11,7 @@ menuconfig INTERCONNECT
if INTERCONNECT
source "drivers/interconnect/imx/Kconfig"
source "drivers/interconnect/qcom/Kconfig"
endif

View file

@ -4,4 +4,5 @@ CFLAGS_core.o := -I$(src)
icc-core-objs := core.o
obj-$(CONFIG_INTERCONNECT) += icc-core.o
obj-$(CONFIG_INTERCONNECT_IMX) += imx/
obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/

View file

@ -158,6 +158,7 @@ static struct icc_path *path_init(struct device *dev, struct icc_node *dst,
hlist_add_head(&path->reqs[i].req_node, &node->req_list);
path->reqs[i].node = node;
path->reqs[i].dev = dev;
path->reqs[i].enabled = true;
/* reference to previous node was saved during path traversal */
node = node->reverse;
}
@ -249,9 +250,12 @@ static int aggregate_requests(struct icc_node *node)
if (p->pre_aggregate)
p->pre_aggregate(node);
hlist_for_each_entry(r, &node->req_list, req_node)
hlist_for_each_entry(r, &node->req_list, req_node) {
if (!r->enabled)
continue;
p->aggregate(node, r->tag, r->avg_bw, r->peak_bw,
&node->avg_bw, &node->peak_bw);
}
return 0;
}
@ -350,10 +354,35 @@ static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec)
return node;
}
static void devm_icc_release(struct device *dev, void *res)
{
icc_put(*(struct icc_path **)res);
}
struct icc_path *devm_of_icc_get(struct device *dev, const char *name)
{
struct icc_path **ptr, *path;
ptr = devres_alloc(devm_icc_release, sizeof(**ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
path = of_icc_get(dev, name);
if (!IS_ERR(path)) {
*ptr = path;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return path;
}
EXPORT_SYMBOL_GPL(devm_of_icc_get);
/**
* of_icc_get() - get a path handle from a DT node based on name
* of_icc_get_by_index() - get a path handle from a DT node based on index
* @dev: device pointer for the consumer device
* @name: interconnect path name
* @idx: interconnect path index
*
* This function will search for a path between two endpoints and return an
* icc_path handle on success. Use icc_put() to release constraints when they
@ -365,13 +394,12 @@ static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec)
* Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
* when the API is disabled or the "interconnects" DT property is missing.
*/
struct icc_path *of_icc_get(struct device *dev, const char *name)
struct icc_path *of_icc_get_by_index(struct device *dev, int idx)
{
struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
struct icc_path *path;
struct icc_node *src_node, *dst_node;
struct device_node *np = NULL;
struct device_node *np;
struct of_phandle_args src_args, dst_args;
int idx = 0;
int ret;
if (!dev || !dev->of_node)
@ -391,12 +419,6 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
* lets support only global ids and extend this in the future if needed
* without breaking DT compatibility.
*/
if (name) {
idx = of_property_match_string(np, "interconnect-names", name);
if (idx < 0)
return ERR_PTR(idx);
}
ret = of_parse_phandle_with_args(np, "interconnects",
"#interconnect-cells", idx * 2,
&src_args);
@ -439,12 +461,8 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
return path;
}
if (name)
path->name = kstrdup_const(name, GFP_KERNEL);
else
path->name = kasprintf(GFP_KERNEL, "%s-%s",
src_node->name, dst_node->name);
path->name = kasprintf(GFP_KERNEL, "%s-%s",
src_node->name, dst_node->name);
if (!path->name) {
kfree(path);
return ERR_PTR(-ENOMEM);
@ -452,6 +470,53 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
return path;
}
EXPORT_SYMBOL_GPL(of_icc_get_by_index);
/**
* of_icc_get() - get a path handle from a DT node based on name
* @dev: device pointer for the consumer device
* @name: interconnect path name
*
* This function will search for a path between two endpoints and return an
* icc_path handle on success. Use icc_put() to release constraints when they
* are not needed anymore.
* If the interconnect API is disabled, NULL is returned and the consumer
* drivers will still build. Drivers are free to handle this specifically,
* but they don't have to.
*
* Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
* when the API is disabled or the "interconnects" DT property is missing.
*/
struct icc_path *of_icc_get(struct device *dev, const char *name)
{
struct device_node *np;
int idx = 0;
if (!dev || !dev->of_node)
return ERR_PTR(-ENODEV);
np = dev->of_node;
/*
* When the consumer DT node do not have "interconnects" property
* return a NULL path to skip setting constraints.
*/
if (!of_find_property(np, "interconnects", NULL))
return NULL;
/*
* We use a combination of phandle and specifier for endpoint. For now
* lets support only global ids and extend this in the future if needed
* without breaking DT compatibility.
*/
if (name) {
idx = of_property_match_string(np, "interconnect-names", name);
if (idx < 0)
return ERR_PTR(idx);
}
return of_icc_get_by_index(dev, idx);
}
EXPORT_SYMBOL_GPL(of_icc_get);
/**
@ -546,6 +611,39 @@ int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
}
EXPORT_SYMBOL_GPL(icc_set_bw);
static int __icc_enable(struct icc_path *path, bool enable)
{
int i;
if (!path)
return 0;
if (WARN_ON(IS_ERR(path) || !path->num_nodes))
return -EINVAL;
mutex_lock(&icc_lock);
for (i = 0; i < path->num_nodes; i++)
path->reqs[i].enabled = enable;
mutex_unlock(&icc_lock);
return icc_set_bw(path, path->reqs[0].avg_bw,
path->reqs[0].peak_bw);
}
int icc_enable(struct icc_path *path)
{
return __icc_enable(path, true);
}
EXPORT_SYMBOL_GPL(icc_enable);
int icc_disable(struct icc_path *path)
{
return __icc_enable(path, false);
}
EXPORT_SYMBOL_GPL(icc_disable);
/**
* icc_get() - return a handle for path between two endpoints
* @dev: the device requesting the path
@ -908,12 +1006,7 @@ static int __init icc_init(void)
return 0;
}
static void __exit icc_exit(void)
{
debugfs_remove_recursive(icc_debugfs_dir);
}
module_init(icc_init);
module_exit(icc_exit);
device_initcall(icc_init);
MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
MODULE_DESCRIPTION("Interconnect Driver Core");

View file

@ -0,0 +1,17 @@
config INTERCONNECT_IMX
tristate "i.MX interconnect drivers"
depends on ARCH_MXC || COMPILE_TEST
help
Generic interconnect drivers for i.MX SOCs
config INTERCONNECT_IMX8MM
tristate "i.MX8MM interconnect driver"
depends on INTERCONNECT_IMX
config INTERCONNECT_IMX8MN
tristate "i.MX8MN interconnect driver"
depends on INTERCONNECT_IMX
config INTERCONNECT_IMX8MQ
tristate "i.MX8MQ interconnect driver"
depends on INTERCONNECT_IMX

View file

@ -0,0 +1,9 @@
imx-interconnect-objs := imx.o
imx8mm-interconnect-objs := imx8mm.o
imx8mq-interconnect-objs := imx8mq.o
imx8mn-interconnect-objs := imx8mn.o
obj-$(CONFIG_INTERCONNECT_IMX) += imx-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MM) += imx8mm-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MQ) += imx8mq-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MN) += imx8mn-interconnect.o

View file

@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, NXP
* 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_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);
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_dbg(dev, "icc node %s[%d] is main noc itself\n",
node->name, node->id);
} else {
dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
if (!dn) {
dev_warn(dev, "Failed to parse %s\n",
adj->phandle_name);
return -ENODEV;
}
/* 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);
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);
return -EPROBE_DEFER;
}
node_data->qos_dev = &pdev->dev;
dev_dbg(dev, "node %s[%d] has device node %pOF\n",
node->name, node->id, dn);
}
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);
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 = 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;
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");

View file

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, 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;
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 */

View file

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MM SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, NXP
* Author: Alexandre Bailon <abailon@baylibre.com>
* Author: Leonard Crestez <leonard.crestez@nxp.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/imx8mm.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mm_dram_adj = {
.bw_mul = 1,
.bw_div = 16,
.phandle_name = "fsl,ddrc",
};
static const struct imx_icc_node_adj_desc imx8mm_noc_adj = {
.bw_mul = 1,
.bw_div = 16,
.main_noc = true,
};
/*
* Describe bus masters, slaves and connections between them
*
* This is a simplified subset of the bus diagram, there are several other
* PL301 nics which are skipped/merged into PL301_MAIN
*/
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MM_ICN_NOC, &imx8mm_noc_adj,
IMX8MM_ICS_DRAM, IMX8MM_ICN_MAIN),
DEFINE_BUS_SLAVE("DRAM", IMX8MM_ICS_DRAM, &imx8mm_dram_adj),
DEFINE_BUS_SLAVE("OCRAM", IMX8MM_ICS_OCRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MM_ICM_A53, IMX8MM_ICN_NOC),
/* VPUMIX */
DEFINE_BUS_MASTER("VPU H1", IMX8MM_ICM_VPU_H1, IMX8MM_ICN_VIDEO),
DEFINE_BUS_MASTER("VPU G1", IMX8MM_ICM_VPU_G1, IMX8MM_ICN_VIDEO),
DEFINE_BUS_MASTER("VPU G2", IMX8MM_ICM_VPU_G2, IMX8MM_ICN_VIDEO),
DEFINE_BUS_INTERCONNECT("PL301_VIDEO", IMX8MM_ICN_VIDEO, NULL, IMX8MM_ICN_NOC),
/* GPUMIX */
DEFINE_BUS_MASTER("GPU 2D", IMX8MM_ICM_GPU2D, IMX8MM_ICN_GPU),
DEFINE_BUS_MASTER("GPU 3D", IMX8MM_ICM_GPU3D, IMX8MM_ICN_GPU),
DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MM_ICN_GPU, NULL, IMX8MM_ICN_NOC),
/* DISPLAYMIX */
DEFINE_BUS_MASTER("CSI", IMX8MM_ICM_CSI, IMX8MM_ICN_MIPI),
DEFINE_BUS_MASTER("LCDIF", IMX8MM_ICM_LCDIF, IMX8MM_ICN_MIPI),
DEFINE_BUS_INTERCONNECT("PL301_MIPI", IMX8MM_ICN_MIPI, NULL, IMX8MM_ICN_NOC),
/* HSIO */
DEFINE_BUS_MASTER("USB1", IMX8MM_ICM_USB1, IMX8MM_ICN_HSIO),
DEFINE_BUS_MASTER("USB2", IMX8MM_ICM_USB2, IMX8MM_ICN_HSIO),
DEFINE_BUS_MASTER("PCIE", IMX8MM_ICM_PCIE, IMX8MM_ICN_HSIO),
DEFINE_BUS_INTERCONNECT("PL301_HSIO", IMX8MM_ICN_HSIO, NULL, IMX8MM_ICN_NOC),
/* Audio */
DEFINE_BUS_MASTER("SDMA2", IMX8MM_ICM_SDMA2, IMX8MM_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA3", IMX8MM_ICM_SDMA3, IMX8MM_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MM_ICN_AUDIO, NULL, IMX8MM_ICN_MAIN),
/* Ethernet */
DEFINE_BUS_MASTER("ENET", IMX8MM_ICM_ENET, IMX8MM_ICN_ENET),
DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MM_ICN_ENET, NULL, IMX8MM_ICN_MAIN),
/* Other */
DEFINE_BUS_MASTER("SDMA1", IMX8MM_ICM_SDMA1, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("NAND", IMX8MM_ICM_NAND, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC1", IMX8MM_ICM_USDHC1, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC2", IMX8MM_ICM_USDHC2, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC3", IMX8MM_ICM_USDHC3, IMX8MM_ICN_MAIN),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MM_ICN_MAIN, NULL,
IMX8MM_ICN_NOC, IMX8MM_ICS_OCRAM),
};
static int imx8mm_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes));
}
static int imx8mm_icc_remove(struct platform_device *pdev)
{
return imx_icc_unregister(pdev);
}
static struct platform_driver imx8mm_icc_driver = {
.probe = imx8mm_icc_probe,
.remove = imx8mm_icc_remove,
.driver = {
.name = "imx8mm-interconnect",
},
};
module_platform_driver(imx8mm_icc_driver);
MODULE_AUTHOR("Alexandre Bailon <abailon@baylibre.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:imx8mm-interconnect");

View file

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MN SoC
*
* Copyright (c) 2019-2020, NXP
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/imx8mn.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mn_dram_adj = {
.bw_mul = 1,
.bw_div = 4,
.phandle_name = "fsl,ddrc",
};
static const struct imx_icc_node_adj_desc imx8mn_noc_adj = {
.bw_mul = 1,
.bw_div = 4,
.main_noc = true,
};
/*
* Describe bus masters, slaves and connections between them
*
* This is a simplified subset of the bus diagram, there are several other
* PL301 nics which are skipped/merged into PL301_MAIN
*/
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MN_ICN_NOC, &imx8mn_noc_adj,
IMX8MN_ICS_DRAM, IMX8MN_ICN_MAIN),
DEFINE_BUS_SLAVE("DRAM", IMX8MN_ICS_DRAM, &imx8mn_dram_adj),
DEFINE_BUS_SLAVE("OCRAM", IMX8MN_ICS_OCRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MN_ICM_A53, IMX8MN_ICN_NOC),
/* GPUMIX */
DEFINE_BUS_MASTER("GPU", IMX8MN_ICM_GPU, IMX8MN_ICN_GPU),
DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MN_ICN_GPU, NULL, IMX8MN_ICN_NOC),
/* DISPLAYMIX */
DEFINE_BUS_MASTER("CSI1", IMX8MN_ICM_CSI1, IMX8MN_ICN_MIPI),
DEFINE_BUS_MASTER("CSI2", IMX8MN_ICM_CSI2, IMX8MN_ICN_MIPI),
DEFINE_BUS_MASTER("ISI", IMX8MN_ICM_ISI, IMX8MN_ICN_MIPI),
DEFINE_BUS_MASTER("LCDIF", IMX8MN_ICM_LCDIF, IMX8MN_ICN_MIPI),
DEFINE_BUS_INTERCONNECT("PL301_MIPI", IMX8MN_ICN_MIPI, NULL, IMX8MN_ICN_NOC),
/* USB goes straight to NOC */
DEFINE_BUS_MASTER("USB", IMX8MN_ICM_USB, IMX8MN_ICN_NOC),
/* Audio */
DEFINE_BUS_MASTER("SDMA2", IMX8MN_ICM_SDMA2, IMX8MN_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA3", IMX8MN_ICM_SDMA3, IMX8MN_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MN_ICN_AUDIO, NULL, IMX8MN_ICN_MAIN),
/* Ethernet */
DEFINE_BUS_MASTER("ENET", IMX8MN_ICM_ENET, IMX8MN_ICN_ENET),
DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MN_ICN_ENET, NULL, IMX8MN_ICN_MAIN),
/* Other */
DEFINE_BUS_MASTER("SDMA1", IMX8MN_ICM_SDMA1, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("NAND", IMX8MN_ICM_NAND, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC1", IMX8MN_ICM_USDHC1, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC2", IMX8MN_ICM_USDHC2, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC3", IMX8MN_ICM_USDHC3, IMX8MN_ICN_MAIN),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MN_ICN_MAIN, NULL,
IMX8MN_ICN_NOC, IMX8MN_ICS_OCRAM),
};
static int imx8mn_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes));
}
static int imx8mn_icc_remove(struct platform_device *pdev)
{
return imx_icc_unregister(pdev);
}
static struct platform_driver imx8mn_icc_driver = {
.probe = imx8mn_icc_probe,
.remove = imx8mn_icc_remove,
.driver = {
.name = "imx8mn-interconnect",
},
};
module_platform_driver(imx8mn_icc_driver);
MODULE_ALIAS("platform:imx8mn-interconnect");
MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MQ SoC
*
* Copyright (c) 2019-2020, NXP
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/imx8mq.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mq_dram_adj = {
.bw_mul = 1,
.bw_div = 4,
.phandle_name = "fsl,ddrc",
};
static const struct imx_icc_node_adj_desc imx8mq_noc_adj = {
.bw_mul = 1,
.bw_div = 4,
.main_noc = true,
};
/*
* Describe bus masters, slaves and connections between them
*
* This is a simplified subset of the bus diagram, there are several other
* PL301 nics which are skipped/merged into PL301_MAIN
*/
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MQ_ICN_NOC, &imx8mq_noc_adj,
IMX8MQ_ICS_DRAM, IMX8MQ_ICN_MAIN),
DEFINE_BUS_SLAVE("DRAM", IMX8MQ_ICS_DRAM, &imx8mq_dram_adj),
DEFINE_BUS_SLAVE("OCRAM", IMX8MQ_ICS_OCRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MQ_ICM_A53, IMX8MQ_ICN_NOC),
/* VPUMIX */
DEFINE_BUS_MASTER("VPU", IMX8MQ_ICM_VPU, IMX8MQ_ICN_VIDEO),
DEFINE_BUS_INTERCONNECT("PL301_VIDEO", IMX8MQ_ICN_VIDEO, NULL, IMX8MQ_ICN_NOC),
/* GPUMIX */
DEFINE_BUS_MASTER("GPU", IMX8MQ_ICM_GPU, IMX8MQ_ICN_GPU),
DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MQ_ICN_GPU, NULL, IMX8MQ_ICN_NOC),
/* DISPMIX (only for DCSS) */
DEFINE_BUS_MASTER("DC", IMX8MQ_ICM_DCSS, IMX8MQ_ICN_DCSS),
DEFINE_BUS_INTERCONNECT("PL301_DC", IMX8MQ_ICN_DCSS, NULL, IMX8MQ_ICN_NOC),
/* USBMIX */
DEFINE_BUS_MASTER("USB1", IMX8MQ_ICM_USB1, IMX8MQ_ICN_USB),
DEFINE_BUS_MASTER("USB2", IMX8MQ_ICM_USB2, IMX8MQ_ICN_USB),
DEFINE_BUS_INTERCONNECT("PL301_USB", IMX8MQ_ICN_USB, NULL, IMX8MQ_ICN_NOC),
/* PL301_DISPLAY (IPs other than DCSS, inside SUPERMIX) */
DEFINE_BUS_MASTER("CSI1", IMX8MQ_ICM_CSI1, IMX8MQ_ICN_DISPLAY),
DEFINE_BUS_MASTER("CSI2", IMX8MQ_ICM_CSI2, IMX8MQ_ICN_DISPLAY),
DEFINE_BUS_MASTER("LCDIF", IMX8MQ_ICM_LCDIF, IMX8MQ_ICN_DISPLAY),
DEFINE_BUS_INTERCONNECT("PL301_DISPLAY", IMX8MQ_ICN_DISPLAY, NULL, IMX8MQ_ICN_MAIN),
/* AUDIO */
DEFINE_BUS_MASTER("SDMA2", IMX8MQ_ICM_SDMA2, IMX8MQ_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MQ_ICN_AUDIO, NULL, IMX8MQ_ICN_DISPLAY),
/* ENET */
DEFINE_BUS_MASTER("ENET", IMX8MQ_ICM_ENET, IMX8MQ_ICN_ENET),
DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MQ_ICN_ENET, NULL, IMX8MQ_ICN_MAIN),
/* OTHER */
DEFINE_BUS_MASTER("SDMA1", IMX8MQ_ICM_SDMA1, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("NAND", IMX8MQ_ICM_NAND, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC1", IMX8MQ_ICM_USDHC1, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC2", IMX8MQ_ICM_USDHC2, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("PCIE1", IMX8MQ_ICM_PCIE1, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("PCIE2", IMX8MQ_ICM_PCIE2, IMX8MQ_ICN_MAIN),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MQ_ICN_MAIN, NULL,
IMX8MQ_ICN_NOC, IMX8MQ_ICS_OCRAM),
};
static int imx8mq_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes));
}
static int imx8mq_icc_remove(struct platform_device *pdev)
{
return imx_icc_unregister(pdev);
}
static struct platform_driver imx8mq_icc_driver = {
.probe = imx8mq_icc_probe,
.remove = imx8mq_icc_remove,
.driver = {
.name = "imx8mq-interconnect",
},
};
module_platform_driver(imx8mq_icc_driver);
MODULE_ALIAS("platform:imx8mq-interconnect");
MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
MODULE_LICENSE("GPL v2");

View file

@ -14,6 +14,7 @@
* @req_node: entry in list of requests for the particular @node
* @node: the interconnect node to which this constraint applies
* @dev: reference to the device that sets the constraints
* @enabled: indicates whether the path with this request is enabled
* @tag: path tag (optional)
* @avg_bw: an integer describing the average bandwidth in kBps
* @peak_bw: an integer describing the peak bandwidth in kBps
@ -22,6 +23,7 @@ struct icc_req {
struct hlist_node req_node;
struct icc_node *node;
struct device *dev;
bool enabled;
u32 tag;
u32 avg_bw;
u32 peak_bw;

View file

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, NXP
* Author: Alexandre Bailon <abailon@baylibre.com>
*/
#ifndef __DT_BINDINGS_INTERCONNECT_IMX8MM_H
#define __DT_BINDINGS_INTERCONNECT_IMX8MM_H
#define IMX8MM_ICN_NOC 1
#define IMX8MM_ICS_DRAM 2
#define IMX8MM_ICS_OCRAM 3
#define IMX8MM_ICM_A53 4
#define IMX8MM_ICM_VPU_H1 5
#define IMX8MM_ICM_VPU_G1 6
#define IMX8MM_ICM_VPU_G2 7
#define IMX8MM_ICN_VIDEO 8
#define IMX8MM_ICM_GPU2D 9
#define IMX8MM_ICM_GPU3D 10
#define IMX8MM_ICN_GPU 11
#define IMX8MM_ICM_CSI 12
#define IMX8MM_ICM_LCDIF 13
#define IMX8MM_ICN_MIPI 14
#define IMX8MM_ICM_USB1 15
#define IMX8MM_ICM_USB2 16
#define IMX8MM_ICM_PCIE 17
#define IMX8MM_ICN_HSIO 18
#define IMX8MM_ICM_SDMA2 19
#define IMX8MM_ICM_SDMA3 20
#define IMX8MM_ICN_AUDIO 21
#define IMX8MM_ICN_ENET 22
#define IMX8MM_ICM_ENET 23
#define IMX8MM_ICN_MAIN 24
#define IMX8MM_ICM_NAND 25
#define IMX8MM_ICM_SDMA1 26
#define IMX8MM_ICM_USDHC1 27
#define IMX8MM_ICM_USDHC2 28
#define IMX8MM_ICM_USDHC3 29
#endif /* __DT_BINDINGS_INTERCONNECT_IMX8MM_H */

View file

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019-2020, NXP
*/
#ifndef __DT_BINDINGS_INTERCONNECT_IMX8MN_H
#define __DT_BINDINGS_INTERCONNECT_IMX8MN_H
#define IMX8MN_ICN_NOC 1
#define IMX8MN_ICS_DRAM 2
#define IMX8MN_ICS_OCRAM 3
#define IMX8MN_ICM_A53 4
#define IMX8MN_ICM_GPU 5
#define IMX8MN_ICN_GPU 6
#define IMX8MN_ICM_CSI1 7
#define IMX8MN_ICM_CSI2 8
#define IMX8MN_ICM_ISI 9
#define IMX8MN_ICM_LCDIF 10
#define IMX8MN_ICN_MIPI 11
#define IMX8MN_ICM_USB 12
#define IMX8MN_ICM_SDMA2 13
#define IMX8MN_ICM_SDMA3 14
#define IMX8MN_ICN_AUDIO 15
#define IMX8MN_ICN_ENET 16
#define IMX8MN_ICM_ENET 17
#define IMX8MN_ICM_NAND 18
#define IMX8MN_ICM_SDMA1 19
#define IMX8MN_ICM_USDHC1 20
#define IMX8MN_ICM_USDHC2 21
#define IMX8MN_ICM_USDHC3 22
#define IMX8MN_ICN_MAIN 23
#endif /* __DT_BINDINGS_INTERCONNECT_IMX8MN_H */

View file

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019-2020, NXP
*/
#ifndef __DT_BINDINGS_INTERCONNECT_IMX8MQ_H
#define __DT_BINDINGS_INTERCONNECT_IMX8MQ_H
#define IMX8MQ_ICN_NOC 1
#define IMX8MQ_ICS_DRAM 2
#define IMX8MQ_ICS_OCRAM 3
#define IMX8MQ_ICM_A53 4
#define IMX8MQ_ICM_VPU 5
#define IMX8MQ_ICN_VIDEO 6
#define IMX8MQ_ICM_GPU 7
#define IMX8MQ_ICN_GPU 8
#define IMX8MQ_ICM_DCSS 9
#define IMX8MQ_ICN_DCSS 10
#define IMX8MQ_ICM_USB1 11
#define IMX8MQ_ICM_USB2 12
#define IMX8MQ_ICN_USB 13
#define IMX8MQ_ICM_CSI1 14
#define IMX8MQ_ICM_CSI2 15
#define IMX8MQ_ICM_LCDIF 16
#define IMX8MQ_ICN_DISPLAY 17
#define IMX8MQ_ICM_SDMA2 18
#define IMX8MQ_ICN_AUDIO 19
#define IMX8MQ_ICN_ENET 20
#define IMX8MQ_ICM_ENET 21
#define IMX8MQ_ICM_SDMA1 22
#define IMX8MQ_ICM_NAND 23
#define IMX8MQ_ICM_USDHC1 24
#define IMX8MQ_ICM_USDHC2 25
#define IMX8MQ_ICM_PCIE1 26
#define IMX8MQ_ICM_PCIE2 27
#define IMX8MQ_ICN_MAIN 28
#endif /* __DT_BINDINGS_INTERCONNECT_IMX8MQ_H */

View file

@ -28,7 +28,11 @@ struct device;
struct icc_path *icc_get(struct device *dev, const int src_id,
const int dst_id);
struct icc_path *of_icc_get(struct device *dev, const char *name);
struct icc_path *devm_of_icc_get(struct device *dev, const char *name);
struct icc_path *of_icc_get_by_index(struct device *dev, int idx);
void icc_put(struct icc_path *path);
int icc_enable(struct icc_path *path);
int icc_disable(struct icc_path *path);
int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
void icc_set_tag(struct icc_path *path, u32 tag);
@ -46,10 +50,31 @@ static inline struct icc_path *of_icc_get(struct device *dev,
return NULL;
}
static inline struct icc_path *devm_of_icc_get(struct device *dev,
const char *name)
{
return NULL;
}
static inline struct icc_path *of_icc_get_by_index(struct device *dev, int idx)
{
return NULL;
}
static inline void icc_put(struct icc_path *path)
{
}
static inline int icc_enable(struct icc_path *path)
{
return 0;
}
static inline int icc_disable(struct icc_path *path)
{
return 0;
}
static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
{
return 0;