mirror of https://gitee.com/openkylin/linux.git
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:
commit
c9cf27d9de
|
@ -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>;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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");
|
|
@ -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 */
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue