interconnect changes for 6.5
This pull request contains the interconnect changes for the 6.5-rc1 merge window which is a mix of core and driver changes with the following highlights: - Support for configuring QoS on the Qualcomm's RPM-based platforms, that required special handling of some interface (non-scaling) clocks. - Support for clock-based interconnect providers for cases when clock corresponds to bus bandwidth. This is used to enable CPU cluster bandwidth scaling on MSM8996 platforms. One patch is touching a file in the clock subsystem that has been acked by the maintainer. Core changes: interconnect: add clk-based icc provider support interconnect: icc-clk: fix modular build interconnect: drop unused icc_get() interface Driver changes: interconnect: qcom: rpm: Rename icc desc clocks to bus_blocks interconnect: qcom: rpm: Rename icc provider num_clocks to num_bus_clocks interconnect: qcom: rpm: Drop unused parameters interconnect: qcom: rpm: Set QoS registers only once interconnect: qcom: rpm: Handle interface clocks interconnect: qcom: icc-rpm: Enforce 2 or 0 bus clocks interconnect: qcom: rpm: Don't use clk_get_optional for bus clocks anymore interconnect: qcom: msm8996: Promote to core_initcall interconnect: qcom: rpm: allocate enough data in probe() dt-bindings: interconnect/msm8996-cbf: add defines to be used by CBF clk: qcom: cbf-msm8996: scale CBF clock according to the CPUfreq dt-bindings: interconnect: fsl,imx8m-noc: drop unneeded quotes Signed-off-by: Georgi Djakov <djakov@kernel.org> -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJkkyQsAAoJEIDQzArG2BZj7GQQAMLrW2sZcxJhq5Fe2wKV4W5M ItIE7xME1Vk9PvuulZIJ57tZIKfOTJpXwwbh6qWJOejYGePrmgtT89iS0fadO81f yCKv2O2hD+Xukv+gFzyuX3AYEfur7myaCTfmRx93xVDYUz0d95Kj4BlYA84xkjXU i+wte+nX/nw9W78s+Y9BHcs389a3HTre0WR1c0eOboPmt8D0U9cBOdiZMHkSUc+4 /8RDUYRdsTBR0AblpPExm2JjoSRKUGEw7N8ZFZhOXaejCjmGoeVXeTdnHO+tjXaq HQ9290C9Pz0BZWdKXaFFfjc4Wqu3RYjdXJmHNo74a4sFHE+H/j33eRSgC24qMWg5 5hRsH8+gv0ZhoyLv6Ucd2MRQQvvUYCLNNeTlQ2/RkOFuqewLKqpXCiihbuKUpOi0 CLeWKTDjNlIM5murJURXX88+xjZ1UvpuBXe/U+i9jrhjSQ6IjnAppoDw7anrrxTE ldLGFPzJoWL8VO1H0povS08/kd25+fgkjL/3pZHagSMLjDWNOXA+xDLkRYGBCNi7 rZpLT/4nBFTcrcYEsJ2EPAqHYK19kD76NVrz+Fj2gzF9Ych3q+2MSkLb132Qkyzf qLn3SqWJQoPAhy0kQbOt3XBnYon8QjVEpcZIWf9J3Qr2au4SdHi7hr3ki86a5Pfz ne03bJDC237hO6q4jY0b =Smk9 -----END PGP SIGNATURE----- Merge tag 'icc-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc into char-misc-next Georgi writes: interconnect changes for 6.5 This pull request contains the interconnect changes for the 6.5-rc1 merge window which is a mix of core and driver changes with the following highlights: - Support for configuring QoS on the Qualcomm's RPM-based platforms, that required special handling of some interface (non-scaling) clocks. - Support for clock-based interconnect providers for cases when clock corresponds to bus bandwidth. This is used to enable CPU cluster bandwidth scaling on MSM8996 platforms. One patch is touching a file in the clock subsystem that has been acked by the maintainer. Core changes: interconnect: add clk-based icc provider support interconnect: icc-clk: fix modular build interconnect: drop unused icc_get() interface Driver changes: interconnect: qcom: rpm: Rename icc desc clocks to bus_blocks interconnect: qcom: rpm: Rename icc provider num_clocks to num_bus_clocks interconnect: qcom: rpm: Drop unused parameters interconnect: qcom: rpm: Set QoS registers only once interconnect: qcom: rpm: Handle interface clocks interconnect: qcom: icc-rpm: Enforce 2 or 0 bus clocks interconnect: qcom: rpm: Don't use clk_get_optional for bus clocks anymore interconnect: qcom: msm8996: Promote to core_initcall interconnect: qcom: rpm: allocate enough data in probe() dt-bindings: interconnect/msm8996-cbf: add defines to be used by CBF clk: qcom: cbf-msm8996: scale CBF clock according to the CPUfreq dt-bindings: interconnect: fsl,imx8m-noc: drop unneeded quotes Signed-off-by: Georgi Djakov <djakov@kernel.org> * tag 'icc-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/djakov/icc: dt-bindings: interconnect: fsl,imx8m-noc: drop unneeded quotes interconnect: icc-clk: fix modular build clk: qcom: cbf-msm8996: scale CBF clock according to the CPUfreq interconnect: drop unused icc_get() interface interconnect: qcom: rpm: allocate enough data in probe() interconnect: qcom: msm8996: Promote to core_initcall interconnect: qcom: rpm: Don't use clk_get_optional for bus clocks anymore interconnect: qcom: icc-rpm: Enforce 2 or 0 bus clocks interconnect: qcom: rpm: Handle interface clocks interconnect: add clk-based icc provider support dt-bindings: interconnect/msm8996-cbf: add defines to be used by CBF interconnect: qcom: rpm: Set QoS registers only once interconnect: qcom: rpm: Drop unused parameters interconnect: qcom: rpm: Rename icc provider num_clocks to num_bus_clocks interconnect: qcom: rpm: Rename icc desc clocks to bus_blocks
This commit is contained in:
commit
92852219a3
|
@ -51,7 +51,7 @@ properties:
|
|||
type: object
|
||||
|
||||
fsl,ddrc:
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle"
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description:
|
||||
Phandle to DDR Controller.
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ config QCOM_CLK_APCS_MSM8916
|
|||
config QCOM_CLK_APCC_MSM8996
|
||||
tristate "MSM8996 CPU Clock Controller"
|
||||
select QCOM_KRYO_L2_ACCESSORS
|
||||
select INTERCONNECT_CLK if INTERCONNECT
|
||||
depends on ARM64
|
||||
help
|
||||
Support for the CPU clock controller on msm8996 devices.
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/interconnect-clk.h>
|
||||
#include <linux/interconnect-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/interconnect/qcom,msm8996-cbf.h>
|
||||
|
||||
#include "clk-alpha-pll.h"
|
||||
#include "clk-regmap.h"
|
||||
|
||||
|
@ -223,6 +227,49 @@ static const struct regmap_config cbf_msm8996_regmap_config = {
|
|||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_INTERCONNECT
|
||||
|
||||
/* Random ID that doesn't clash with main qnoc and OSM */
|
||||
#define CBF_MASTER_NODE 2000
|
||||
|
||||
static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct clk *clk = devm_clk_hw_get_clk(dev, cbf_hw, "cbf");
|
||||
const struct icc_clk_data data[] = {
|
||||
{ .clk = clk, .name = "cbf", },
|
||||
};
|
||||
struct icc_provider *provider;
|
||||
|
||||
provider = icc_clk_register(dev, CBF_MASTER_NODE, ARRAY_SIZE(data), data);
|
||||
if (IS_ERR(provider))
|
||||
return PTR_ERR(provider);
|
||||
|
||||
platform_set_drvdata(pdev, provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_msm8996_cbf_icc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct icc_provider *provider = platform_get_drvdata(pdev);
|
||||
|
||||
icc_clk_unregister(provider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define qcom_msm8996_cbf_icc_sync_state icc_sync_state
|
||||
#else
|
||||
static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw)
|
||||
{
|
||||
dev_warn(&pdev->dev, "CONFIG_INTERCONNECT is disabled, CBF clock is fixed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define qcom_msm8996_cbf_icc_remove(pdev) (0)
|
||||
#define qcom_msm8996_cbf_icc_sync_state NULL
|
||||
#endif
|
||||
|
||||
static int qcom_msm8996_cbf_probe(struct platform_device *pdev)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
@ -281,7 +328,16 @@ static int qcom_msm8996_cbf_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
|
||||
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return qcom_msm8996_cbf_icc_register(pdev, &cbf_mux.clkr.hw);
|
||||
}
|
||||
|
||||
static int qcom_msm8996_cbf_remove(struct platform_device *pdev)
|
||||
{
|
||||
return qcom_msm8996_cbf_icc_remove(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_msm8996_cbf_match_table[] = {
|
||||
|
@ -292,9 +348,11 @@ MODULE_DEVICE_TABLE(of, qcom_msm8996_cbf_match_table);
|
|||
|
||||
static struct platform_driver qcom_msm8996_cbf_driver = {
|
||||
.probe = qcom_msm8996_cbf_probe,
|
||||
.remove = qcom_msm8996_cbf_remove,
|
||||
.driver = {
|
||||
.name = "qcom-msm8996-cbf",
|
||||
.of_match_table = qcom_msm8996_cbf_match_table,
|
||||
.sync_state = qcom_msm8996_cbf_icc_sync_state,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -15,4 +15,10 @@ source "drivers/interconnect/imx/Kconfig"
|
|||
source "drivers/interconnect/qcom/Kconfig"
|
||||
source "drivers/interconnect/samsung/Kconfig"
|
||||
|
||||
config INTERCONNECT_CLK
|
||||
tristate
|
||||
depends on COMMON_CLK
|
||||
help
|
||||
Support for wrapping clocks into the interconnect nodes.
|
||||
|
||||
endif
|
||||
|
|
|
@ -7,3 +7,5 @@ obj-$(CONFIG_INTERCONNECT) += icc-core.o
|
|||
obj-$(CONFIG_INTERCONNECT_IMX) += imx/
|
||||
obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
|
||||
obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/
|
||||
|
||||
obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o
|
||||
|
|
|
@ -587,7 +587,7 @@ EXPORT_SYMBOL_GPL(icc_set_tag);
|
|||
|
||||
/**
|
||||
* icc_get_name() - Get name of the icc path
|
||||
* @path: reference to the path returned by icc_get()
|
||||
* @path: interconnect path
|
||||
*
|
||||
* This function is used by an interconnect consumer to get the name of the icc
|
||||
* path.
|
||||
|
@ -605,7 +605,7 @@ EXPORT_SYMBOL_GPL(icc_get_name);
|
|||
|
||||
/**
|
||||
* icc_set_bw() - set bandwidth constraints on an interconnect path
|
||||
* @path: reference to the path returned by icc_get()
|
||||
* @path: interconnect path
|
||||
* @avg_bw: average bandwidth in kilobytes per second
|
||||
* @peak_bw: peak bandwidth in kilobytes per second
|
||||
*
|
||||
|
@ -704,54 +704,6 @@ int icc_disable(struct icc_path *path)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(icc_disable);
|
||||
|
||||
/**
|
||||
* icc_get() - return a handle for path between two endpoints
|
||||
* @dev: the device requesting the path
|
||||
* @src_id: source device port id
|
||||
* @dst_id: destination device port id
|
||||
*
|
||||
* 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, ERR_PTR() on error or NULL if the
|
||||
* interconnect API is disabled.
|
||||
*/
|
||||
struct icc_path *icc_get(struct device *dev, const int src_id, const int dst_id)
|
||||
{
|
||||
struct icc_node *src, *dst;
|
||||
struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
mutex_lock(&icc_lock);
|
||||
|
||||
src = node_find(src_id);
|
||||
if (!src)
|
||||
goto out;
|
||||
|
||||
dst = node_find(dst_id);
|
||||
if (!dst)
|
||||
goto out;
|
||||
|
||||
path = path_find(dev, src, dst);
|
||||
if (IS_ERR(path)) {
|
||||
dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
|
||||
goto out;
|
||||
}
|
||||
|
||||
path->name = kasprintf(GFP_KERNEL, "%s-%s", src->name, dst->name);
|
||||
if (!path->name) {
|
||||
kfree(path);
|
||||
path = ERR_PTR(-ENOMEM);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&icc_lock);
|
||||
return path;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(icc_get);
|
||||
|
||||
/**
|
||||
* icc_put() - release the reference to the icc_path
|
||||
* @path: interconnect path
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023, Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interconnect-clk.h>
|
||||
#include <linux/interconnect-provider.h>
|
||||
|
||||
struct icc_clk_node {
|
||||
struct clk *clk;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct icc_clk_provider {
|
||||
struct icc_provider provider;
|
||||
int num_clocks;
|
||||
struct icc_clk_node clocks[];
|
||||
};
|
||||
|
||||
#define to_icc_clk_provider(_provider) \
|
||||
container_of(_provider, struct icc_clk_provider, provider)
|
||||
|
||||
static int icc_clk_set(struct icc_node *src, struct icc_node *dst)
|
||||
{
|
||||
struct icc_clk_node *qn = src->data;
|
||||
int ret;
|
||||
|
||||
if (!qn || !qn->clk)
|
||||
return 0;
|
||||
|
||||
if (!src->peak_bw) {
|
||||
if (qn->enabled)
|
||||
clk_disable_unprepare(qn->clk);
|
||||
qn->enabled = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!qn->enabled) {
|
||||
ret = clk_prepare_enable(qn->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
qn->enabled = true;
|
||||
}
|
||||
|
||||
return clk_set_rate(qn->clk, icc_units_to_bps(src->peak_bw));
|
||||
}
|
||||
|
||||
static int icc_clk_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
|
||||
{
|
||||
struct icc_clk_node *qn = node->data;
|
||||
|
||||
if (!qn || !qn->clk)
|
||||
*peak = INT_MAX;
|
||||
else
|
||||
*peak = Bps_to_icc(clk_get_rate(qn->clk));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* icc_clk_register() - register a new clk-based interconnect provider
|
||||
* @dev: device supporting this provider
|
||||
* @first_id: an ID of the first provider's node
|
||||
* @num_clocks: number of instances of struct icc_clk_data
|
||||
* @data: data for the provider
|
||||
*
|
||||
* Registers and returns a clk-based interconnect provider. It is a simple
|
||||
* wrapper around COMMON_CLK framework, allowing other devices to vote on the
|
||||
* clock rate.
|
||||
*
|
||||
* Return: 0 on success, or an error code otherwise
|
||||
*/
|
||||
struct icc_provider *icc_clk_register(struct device *dev,
|
||||
unsigned int first_id,
|
||||
unsigned int num_clocks,
|
||||
const struct icc_clk_data *data)
|
||||
{
|
||||
struct icc_clk_provider *qp;
|
||||
struct icc_provider *provider;
|
||||
struct icc_onecell_data *onecell;
|
||||
struct icc_node *node;
|
||||
int ret, i, j;
|
||||
|
||||
onecell = devm_kzalloc(dev, struct_size(onecell, nodes, 2 * num_clocks), GFP_KERNEL);
|
||||
if (!onecell)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
qp = devm_kzalloc(dev, struct_size(qp, clocks, num_clocks), GFP_KERNEL);
|
||||
if (!qp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
qp->num_clocks = num_clocks;
|
||||
|
||||
provider = &qp->provider;
|
||||
provider->dev = dev;
|
||||
provider->get_bw = icc_clk_get_bw;
|
||||
provider->set = icc_clk_set;
|
||||
provider->aggregate = icc_std_aggregate;
|
||||
provider->xlate = of_icc_xlate_onecell;
|
||||
INIT_LIST_HEAD(&provider->nodes);
|
||||
provider->data = onecell;
|
||||
|
||||
icc_provider_init(provider);
|
||||
|
||||
for (i = 0, j = 0; i < num_clocks; i++) {
|
||||
qp->clocks[i].clk = data[i].clk;
|
||||
|
||||
node = icc_node_create(first_id + j);
|
||||
if (IS_ERR(node)) {
|
||||
ret = PTR_ERR(node);
|
||||
goto err;
|
||||
}
|
||||
|
||||
node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_master", data[i].name);
|
||||
node->data = &qp->clocks[i];
|
||||
icc_node_add(node, provider);
|
||||
/* link to the next node, slave */
|
||||
icc_link_create(node, first_id + j + 1);
|
||||
onecell->nodes[j++] = node;
|
||||
|
||||
node = icc_node_create(first_id + j);
|
||||
if (IS_ERR(node)) {
|
||||
ret = PTR_ERR(node);
|
||||
goto err;
|
||||
}
|
||||
|
||||
node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_slave", data[i].name);
|
||||
/* no data for slave node */
|
||||
icc_node_add(node, provider);
|
||||
onecell->nodes[j++] = node;
|
||||
}
|
||||
|
||||
onecell->num_nodes = j;
|
||||
|
||||
ret = icc_provider_register(provider);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return provider;
|
||||
|
||||
err:
|
||||
icc_nodes_remove(provider);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(icc_clk_register);
|
||||
|
||||
/**
|
||||
* icc_clk_unregister() - unregister a previously registered clk interconnect provider
|
||||
* @provider: provider returned by icc_clk_register()
|
||||
*/
|
||||
void icc_clk_unregister(struct icc_provider *provider)
|
||||
{
|
||||
struct icc_clk_provider *qp = container_of(provider, struct icc_clk_provider, provider);
|
||||
int i;
|
||||
|
||||
icc_provider_deregister(&qp->provider);
|
||||
icc_nodes_remove(&qp->provider);
|
||||
|
||||
for (i = 0; i < qp->num_clocks; i++) {
|
||||
struct icc_clk_node *qn = &qp->clocks[i];
|
||||
|
||||
if (qn->enabled)
|
||||
clk_disable_unprepare(qn->clk);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(icc_clk_unregister);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Interconnect wrapper for clocks");
|
||||
MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
|
|
@ -50,7 +50,7 @@
|
|||
#define NOC_QOS_MODE_FIXED_VAL 0x0
|
||||
#define NOC_QOS_MODE_BYPASS_VAL 0x2
|
||||
|
||||
static int qcom_icc_set_qnoc_qos(struct icc_node *src, u64 max_bw)
|
||||
static int qcom_icc_set_qnoc_qos(struct icc_node *src)
|
||||
{
|
||||
struct icc_provider *provider = src->provider;
|
||||
struct qcom_icc_provider *qp = to_qcom_provider(provider);
|
||||
|
@ -95,7 +95,7 @@ static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp,
|
|||
mask, val);
|
||||
}
|
||||
|
||||
static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw)
|
||||
static int qcom_icc_set_bimc_qos(struct icc_node *src)
|
||||
{
|
||||
struct qcom_icc_provider *qp;
|
||||
struct qcom_icc_node *qn;
|
||||
|
@ -150,7 +150,7 @@ static int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp,
|
|||
NOC_QOS_PRIORITY_P0_MASK, qos->prio_level);
|
||||
}
|
||||
|
||||
static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw)
|
||||
static int qcom_icc_set_noc_qos(struct icc_node *src)
|
||||
{
|
||||
struct qcom_icc_provider *qp;
|
||||
struct qcom_icc_node *qn;
|
||||
|
@ -187,7 +187,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw)
|
|||
NOC_QOS_MODEn_MASK, mode);
|
||||
}
|
||||
|
||||
static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw)
|
||||
static int qcom_icc_qos_set(struct icc_node *node)
|
||||
{
|
||||
struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
|
||||
struct qcom_icc_node *qn = node->data;
|
||||
|
@ -196,38 +196,41 @@ static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw)
|
|||
|
||||
switch (qp->type) {
|
||||
case QCOM_ICC_BIMC:
|
||||
return qcom_icc_set_bimc_qos(node, sum_bw);
|
||||
return qcom_icc_set_bimc_qos(node);
|
||||
case QCOM_ICC_QNOC:
|
||||
return qcom_icc_set_qnoc_qos(node, sum_bw);
|
||||
return qcom_icc_set_qnoc_qos(node);
|
||||
default:
|
||||
return qcom_icc_set_noc_qos(node, sum_bw);
|
||||
return qcom_icc_set_noc_qos(node);
|
||||
}
|
||||
}
|
||||
|
||||
static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw)
|
||||
static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 sum_bw)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (mas_rpm_id != -1) {
|
||||
if (qn->qos.ap_owned)
|
||||
return 0;
|
||||
|
||||
if (qn->mas_rpm_id != -1) {
|
||||
ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
|
||||
RPM_BUS_MASTER_REQ,
|
||||
mas_rpm_id,
|
||||
qn->mas_rpm_id,
|
||||
sum_bw);
|
||||
if (ret) {
|
||||
pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
|
||||
mas_rpm_id, ret);
|
||||
qn->mas_rpm_id, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (slv_rpm_id != -1) {
|
||||
if (qn->slv_rpm_id != -1) {
|
||||
ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE,
|
||||
RPM_BUS_SLAVE_REQ,
|
||||
slv_rpm_id,
|
||||
qn->slv_rpm_id,
|
||||
sum_bw);
|
||||
if (ret) {
|
||||
pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
|
||||
slv_rpm_id, ret);
|
||||
qn->slv_rpm_id, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -235,26 +238,6 @@ static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int __qcom_icc_set(struct icc_node *n, struct qcom_icc_node *qn,
|
||||
u64 sum_bw)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!qn->qos.ap_owned) {
|
||||
/* send bandwidth request message to the RPM processor */
|
||||
ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) {
|
||||
/* set bandwidth directly from the AP */
|
||||
ret = qcom_icc_qos_set(n, sum_bw);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_icc_pre_bw_aggregate - cleans up values before re-aggregate requests
|
||||
* @node: icc node to operate on
|
||||
|
@ -370,16 +353,17 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
|
|||
|
||||
sum_bw = icc_units_to_bps(max_agg_avg);
|
||||
|
||||
ret = __qcom_icc_set(src, src_qn, sum_bw);
|
||||
ret = qcom_icc_rpm_set(src_qn, sum_bw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dst_qn) {
|
||||
ret = __qcom_icc_set(dst, dst_qn, sum_bw);
|
||||
ret = qcom_icc_rpm_set(dst_qn, sum_bw);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < qp->num_clks; i++) {
|
||||
for (i = 0; i < qp->num_bus_clks; i++) {
|
||||
/*
|
||||
* Use WAKE bucket for active clock, otherwise, use SLEEP bucket
|
||||
* for other clocks. If a platform doesn't set interconnect
|
||||
|
@ -425,7 +409,7 @@ int qnoc_probe(struct platform_device *pdev)
|
|||
struct qcom_icc_provider *qp;
|
||||
struct icc_node *node;
|
||||
size_t num_nodes, i;
|
||||
const char * const *cds;
|
||||
const char * const *cds = NULL;
|
||||
int cd_num;
|
||||
int ret;
|
||||
|
||||
|
@ -440,21 +424,20 @@ int qnoc_probe(struct platform_device *pdev)
|
|||
qnodes = desc->nodes;
|
||||
num_nodes = desc->num_nodes;
|
||||
|
||||
if (desc->num_clocks) {
|
||||
cds = desc->clocks;
|
||||
cd_num = desc->num_clocks;
|
||||
if (desc->num_intf_clocks) {
|
||||
cds = desc->intf_clocks;
|
||||
cd_num = desc->num_intf_clocks;
|
||||
} else {
|
||||
cds = bus_clocks;
|
||||
cd_num = ARRAY_SIZE(bus_clocks);
|
||||
/* 0 intf clocks is perfectly fine */
|
||||
cd_num = 0;
|
||||
}
|
||||
|
||||
qp = devm_kzalloc(dev, struct_size(qp, bus_clks, cd_num), GFP_KERNEL);
|
||||
qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
|
||||
if (!qp)
|
||||
return -ENOMEM;
|
||||
|
||||
qp->bus_clk_rate = devm_kcalloc(dev, cd_num, sizeof(*qp->bus_clk_rate),
|
||||
GFP_KERNEL);
|
||||
if (!qp->bus_clk_rate)
|
||||
qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL);
|
||||
if (!qp->intf_clks)
|
||||
return -ENOMEM;
|
||||
|
||||
data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
|
||||
|
@ -462,9 +445,13 @@ int qnoc_probe(struct platform_device *pdev)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
qp->num_intf_clks = cd_num;
|
||||
for (i = 0; i < cd_num; i++)
|
||||
qp->bus_clks[i].id = cds[i];
|
||||
qp->num_clks = cd_num;
|
||||
qp->intf_clks[i].id = cds[i];
|
||||
|
||||
qp->num_bus_clks = desc->no_clk_scaling ? 0 : NUM_BUS_CLKS;
|
||||
for (i = 0; i < qp->num_bus_clks; i++)
|
||||
qp->bus_clks[i].id = bus_clocks[i];
|
||||
|
||||
qp->type = desc->type;
|
||||
qp->qos_offset = desc->qos_offset;
|
||||
|
@ -494,11 +481,15 @@ int qnoc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
regmap_done:
|
||||
ret = devm_clk_bulk_get_optional(dev, qp->num_clks, qp->bus_clks);
|
||||
ret = devm_clk_bulk_get(dev, qp->num_bus_clks, qp->bus_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks);
|
||||
ret = clk_bulk_prepare_enable(qp->num_bus_clks, qp->bus_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_clk_bulk_get(dev, qp->num_intf_clks, qp->intf_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -512,6 +503,11 @@ regmap_done:
|
|||
|
||||
icc_provider_init(provider);
|
||||
|
||||
/* If this fails, bus accesses will crash the platform! */
|
||||
ret = clk_bulk_prepare_enable(qp->num_intf_clks, qp->intf_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num_nodes; i++) {
|
||||
size_t j;
|
||||
|
||||
|
@ -528,10 +524,20 @@ regmap_done:
|
|||
for (j = 0; j < qnodes[i]->num_links; j++)
|
||||
icc_link_create(node, qnodes[i]->links[j]);
|
||||
|
||||
/* Set QoS registers (we only need to do it once, generally) */
|
||||
if (qnodes[i]->qos.ap_owned &&
|
||||
qnodes[i]->qos.qos_mode != NOC_QOS_MODE_INVALID) {
|
||||
ret = qcom_icc_qos_set(node);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->nodes[i] = node;
|
||||
}
|
||||
data->num_nodes = num_nodes;
|
||||
|
||||
clk_bulk_disable_unprepare(qp->num_intf_clks, qp->intf_clks);
|
||||
|
||||
ret = icc_provider_register(provider);
|
||||
if (ret)
|
||||
goto err_remove_nodes;
|
||||
|
@ -551,7 +557,7 @@ err_deregister_provider:
|
|||
icc_provider_deregister(provider);
|
||||
err_remove_nodes:
|
||||
icc_nodes_remove(provider);
|
||||
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
|
||||
clk_bulk_disable_unprepare(qp->num_bus_clks, qp->bus_clks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -563,7 +569,7 @@ int qnoc_remove(struct platform_device *pdev)
|
|||
|
||||
icc_provider_deregister(&qp->provider);
|
||||
icc_nodes_remove(&qp->provider);
|
||||
clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks);
|
||||
clk_bulk_disable_unprepare(qp->num_bus_clks, qp->bus_clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -20,24 +20,32 @@ enum qcom_icc_type {
|
|||
QCOM_ICC_QNOC,
|
||||
};
|
||||
|
||||
#define NUM_BUS_CLKS 2
|
||||
|
||||
/**
|
||||
* struct qcom_icc_provider - Qualcomm specific interconnect provider
|
||||
* @provider: generic interconnect provider
|
||||
* @num_clks: the total number of clk_bulk_data entries
|
||||
* @num_bus_clks: the total number of bus_clks clk_bulk_data entries (0 or 2)
|
||||
* @num_intf_clks: the total number of intf_clks clk_bulk_data entries
|
||||
* @type: the ICC provider type
|
||||
* @regmap: regmap for QoS registers read/write access
|
||||
* @qos_offset: offset to QoS registers
|
||||
* @bus_clk_rate: bus clock rate in Hz
|
||||
* @bus_clks: the clk_bulk_data table of bus clocks
|
||||
* @intf_clks: a clk_bulk_data array of interface clocks
|
||||
* @is_on: whether the bus is powered on
|
||||
*/
|
||||
struct qcom_icc_provider {
|
||||
struct icc_provider provider;
|
||||
int num_clks;
|
||||
int num_bus_clks;
|
||||
int num_intf_clks;
|
||||
enum qcom_icc_type type;
|
||||
struct regmap *regmap;
|
||||
unsigned int qos_offset;
|
||||
u64 *bus_clk_rate;
|
||||
struct clk_bulk_data bus_clks[];
|
||||
u64 bus_clk_rate[NUM_BUS_CLKS];
|
||||
struct clk_bulk_data bus_clks[NUM_BUS_CLKS];
|
||||
struct clk_bulk_data *intf_clks;
|
||||
bool is_on;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -91,8 +99,10 @@ struct qcom_icc_node {
|
|||
struct qcom_icc_desc {
|
||||
struct qcom_icc_node * const *nodes;
|
||||
size_t num_nodes;
|
||||
const char * const *clocks;
|
||||
size_t num_clocks;
|
||||
const char * const *bus_clocks;
|
||||
const char * const *intf_clocks;
|
||||
size_t num_intf_clocks;
|
||||
bool no_clk_scaling;
|
||||
enum qcom_icc_type type;
|
||||
const struct regmap_config *regmap_cfg;
|
||||
unsigned int qos_offset;
|
||||
|
|
|
@ -21,21 +21,17 @@
|
|||
#include "smd-rpm.h"
|
||||
#include "msm8996.h"
|
||||
|
||||
static const char * const bus_mm_clocks[] = {
|
||||
"bus",
|
||||
"bus_a",
|
||||
static const char * const mm_intf_clocks[] = {
|
||||
"iface"
|
||||
};
|
||||
|
||||
static const char * const bus_a0noc_clocks[] = {
|
||||
static const char * const a0noc_intf_clocks[] = {
|
||||
"aggre0_snoc_axi",
|
||||
"aggre0_cnoc_ahb",
|
||||
"aggre0_noc_mpu_cfg"
|
||||
};
|
||||
|
||||
static const char * const bus_a2noc_clocks[] = {
|
||||
"bus",
|
||||
"bus_a",
|
||||
static const char * const a2noc_intf_clocks[] = {
|
||||
"aggre2_ufs_axi",
|
||||
"ufs_axi"
|
||||
};
|
||||
|
@ -1821,8 +1817,9 @@ static const struct qcom_icc_desc msm8996_a0noc = {
|
|||
.type = QCOM_ICC_NOC,
|
||||
.nodes = a0noc_nodes,
|
||||
.num_nodes = ARRAY_SIZE(a0noc_nodes),
|
||||
.clocks = bus_a0noc_clocks,
|
||||
.num_clocks = ARRAY_SIZE(bus_a0noc_clocks),
|
||||
.intf_clocks = a0noc_intf_clocks,
|
||||
.num_intf_clocks = ARRAY_SIZE(a0noc_intf_clocks),
|
||||
.no_clk_scaling = true,
|
||||
.regmap_cfg = &msm8996_a0noc_regmap_config
|
||||
};
|
||||
|
||||
|
@ -1865,8 +1862,8 @@ static const struct qcom_icc_desc msm8996_a2noc = {
|
|||
.type = QCOM_ICC_NOC,
|
||||
.nodes = a2noc_nodes,
|
||||
.num_nodes = ARRAY_SIZE(a2noc_nodes),
|
||||
.clocks = bus_a2noc_clocks,
|
||||
.num_clocks = ARRAY_SIZE(bus_a2noc_clocks),
|
||||
.intf_clocks = a2noc_intf_clocks,
|
||||
.num_intf_clocks = ARRAY_SIZE(a2noc_intf_clocks),
|
||||
.regmap_cfg = &msm8996_a2noc_regmap_config
|
||||
};
|
||||
|
||||
|
@ -2004,8 +2001,8 @@ static const struct qcom_icc_desc msm8996_mnoc = {
|
|||
.type = QCOM_ICC_NOC,
|
||||
.nodes = mnoc_nodes,
|
||||
.num_nodes = ARRAY_SIZE(mnoc_nodes),
|
||||
.clocks = bus_mm_clocks,
|
||||
.num_clocks = ARRAY_SIZE(bus_mm_clocks),
|
||||
.intf_clocks = mm_intf_clocks,
|
||||
.num_intf_clocks = ARRAY_SIZE(mm_intf_clocks),
|
||||
.regmap_cfg = &msm8996_mnoc_regmap_config
|
||||
};
|
||||
|
||||
|
@ -2111,7 +2108,17 @@ static struct platform_driver qnoc_driver = {
|
|||
.sync_state = icc_sync_state,
|
||||
}
|
||||
};
|
||||
module_platform_driver(qnoc_driver);
|
||||
static int __init qnoc_driver_init(void)
|
||||
{
|
||||
return platform_driver_register(&qnoc_driver);
|
||||
}
|
||||
core_initcall(qnoc_driver_init);
|
||||
|
||||
static void __exit qnoc_driver_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qnoc_driver);
|
||||
}
|
||||
module_exit(qnoc_driver_exit);
|
||||
|
||||
MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
|
||||
MODULE_DESCRIPTION("Qualcomm MSM8996 NoC driver");
|
||||
|
|
|
@ -127,15 +127,11 @@ enum {
|
|||
SDM660_SNOC,
|
||||
};
|
||||
|
||||
static const char * const bus_mm_clocks[] = {
|
||||
"bus",
|
||||
"bus_a",
|
||||
static const char * const mm_intf_clocks[] = {
|
||||
"iface",
|
||||
};
|
||||
|
||||
static const char * const bus_a2noc_clocks[] = {
|
||||
"bus",
|
||||
"bus_a",
|
||||
static const char * const a2noc_intf_clocks[] = {
|
||||
"ipa",
|
||||
"ufs_axi",
|
||||
"aggre2_ufs_axi",
|
||||
|
@ -1516,8 +1512,8 @@ static const struct qcom_icc_desc sdm660_a2noc = {
|
|||
.type = QCOM_ICC_NOC,
|
||||
.nodes = sdm660_a2noc_nodes,
|
||||
.num_nodes = ARRAY_SIZE(sdm660_a2noc_nodes),
|
||||
.clocks = bus_a2noc_clocks,
|
||||
.num_clocks = ARRAY_SIZE(bus_a2noc_clocks),
|
||||
.intf_clocks = a2noc_intf_clocks,
|
||||
.num_intf_clocks = ARRAY_SIZE(a2noc_intf_clocks),
|
||||
.regmap_cfg = &sdm660_a2noc_regmap_config,
|
||||
};
|
||||
|
||||
|
@ -1620,6 +1616,7 @@ static const struct qcom_icc_desc sdm660_gnoc = {
|
|||
.nodes = sdm660_gnoc_nodes,
|
||||
.num_nodes = ARRAY_SIZE(sdm660_gnoc_nodes),
|
||||
.regmap_cfg = &sdm660_gnoc_regmap_config,
|
||||
.no_clk_scaling = true,
|
||||
};
|
||||
|
||||
static struct qcom_icc_node * const sdm660_mnoc_nodes[] = {
|
||||
|
@ -1659,8 +1656,8 @@ static const struct qcom_icc_desc sdm660_mnoc = {
|
|||
.type = QCOM_ICC_NOC,
|
||||
.nodes = sdm660_mnoc_nodes,
|
||||
.num_nodes = ARRAY_SIZE(sdm660_mnoc_nodes),
|
||||
.clocks = bus_mm_clocks,
|
||||
.num_clocks = ARRAY_SIZE(bus_mm_clocks),
|
||||
.intf_clocks = mm_intf_clocks,
|
||||
.num_intf_clocks = ARRAY_SIZE(mm_intf_clocks),
|
||||
.regmap_cfg = &sdm660_mnoc_regmap_config,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
/*
|
||||
* Copyright (C) 2023 Linaro Ltd. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8996_CBF_H
|
||||
#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8996_CBF_H
|
||||
|
||||
#define MASTER_CBF_M4M 0
|
||||
#define SLAVE_CBF_M4M 1
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023, Linaro Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_INTERCONNECT_CLK_H
|
||||
#define __LINUX_INTERCONNECT_CLK_H
|
||||
|
||||
struct device;
|
||||
|
||||
struct icc_clk_data {
|
||||
struct clk *clk;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct icc_provider *icc_clk_register(struct device *dev,
|
||||
unsigned int first_id,
|
||||
unsigned int num_clocks,
|
||||
const struct icc_clk_data *data);
|
||||
void icc_clk_unregister(struct icc_provider *provider);
|
||||
|
||||
#endif
|
|
@ -40,8 +40,6 @@ struct icc_bulk_data {
|
|||
|
||||
#if IS_ENABLED(CONFIG_INTERCONNECT)
|
||||
|
||||
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);
|
||||
int devm_of_icc_bulk_get(struct device *dev, int num_paths, struct icc_bulk_data *paths);
|
||||
|
@ -61,12 +59,6 @@ void icc_bulk_disable(int num_paths, const struct icc_bulk_data *paths);
|
|||
|
||||
#else
|
||||
|
||||
static inline struct icc_path *icc_get(struct device *dev, const int src_id,
|
||||
const int dst_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct icc_path *of_icc_get(struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue