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:
Greg Kroah-Hartman 2023-06-21 21:12:43 +02:00
commit 92852219a3
14 changed files with 382 additions and 143 deletions

View file

@ -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.

View file

@ -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.

View file

@ -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,
},
};

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>");

View file

@ -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 @@ int qnoc_probe(struct platform_device *pdev)
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 @@ int qnoc_probe(struct platform_device *pdev)
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 @@ int qnoc_probe(struct platform_device *pdev)
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;
}

View file

@ -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;

View file

@ -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");

View file

@ -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,
};

View file

@ -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

View file

@ -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

View file

@ -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)
{