Re: [PATCH v2 2/3] interconnect: qcom: Add CAMNOC interconnect provider driver
From: Gjorgji Rosikopulos (Consultant)
Date: Wed Sep 30 2026 - 04:37:42 EST
Hi Bryan,
On 9/29/2026 11:36 AM, Bryan O'Donoghue wrote:
> On 19/08/2026 10:32, Atanas Filipov wrote:
>> Add an ICC provider for the Qualcomm Camera NOC (CAMNOC). Multiple
>> camera sub-devices share CAM_CC_CAMNOC_AXI_CLK; direct clk_set_rate()
>> calls from each consumer cause a last-writer-wins race.
>>
>> Each consumer obtains an ICC path to SLAVE_CAMNOC_AXI and votes via
>> icc_set_bw(). The ICC core aggregates votes (max peak_bw) and the
>> set() callback converts the result to clk_set_rate(). Consumers
>> encode the required frequency as peak_bw in kBps.
>>
>> Signed-off-by: Atanas Filipov <atanas.filipov@xxxxxxxxxxxxxxxx>
>> ---
>> drivers/interconnect/qcom/Kconfig | 10 +
>> drivers/interconnect/qcom/Makefile | 2 +
>> drivers/interconnect/qcom/qcom-camnoc.c | 279 ++++++++++++++++++++++++
>> 3 files changed, 291 insertions(+)
>> create mode 100644 drivers/interconnect/qcom/qcom-camnoc.c
>>
>> diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
>> index 5b8a094ca4ed..9d9d238019a0 100644
>> --- a/drivers/interconnect/qcom/Kconfig
>> +++ b/drivers/interconnect/qcom/Kconfig
>> @@ -135,6 +135,16 @@ config INTERCONNECT_QCOM_NORD
>> the RPMh hardware and communicates via Bus Clock Manager (BCM)
>> through the Resource State Coordinator (RSC).
>>
>> +config INTERCONNECT_QCOM_CAMNOC
>> + tristate "Qualcomm CAMNOC interconnect driver"
>> + depends on INTERCONNECT_QCOM || COMPILE_TEST
>> + depends on ARM64 || COMPILE_TEST
>> + help
>> + Say y here to support the Camera Network-on-Chip (CAMNOC)
>> + interconnect driver for Qualcomm SoCs. It aggregates bandwidth
>> + requests from camera sub-devices (IFE, JPEG, BPS, etc.) and
>> + scales CAM_CC_CAMNOC_AXI_CLK accordingly.
>> +
>> config INTERCONNECT_QCOM_OSM_L3
>> tristate "Qualcomm OSM L3 interconnect driver"
>> depends on INTERCONNECT_QCOM || COMPILE_TEST
>> diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
>> index 1c7d410b40cc..52a5998ae6a8 100644
>> --- a/drivers/interconnect/qcom/Makefile
>> +++ b/drivers/interconnect/qcom/Makefile
>> @@ -18,6 +18,7 @@ qnoc-msm8974-objs := msm8974.o
>> qnoc-msm8976-objs := msm8976.o
>> qnoc-msm8996-objs := msm8996.o
>> qnoc-nord-objs := nord.o
>> +icc-camnoc-objs := qcom-camnoc.o
>> icc-osm-l3-objs := osm-l3.o
>> qnoc-qcm2290-objs := qcm2290.o
>> qnoc-qcs404-objs := qcs404.o
>> @@ -66,6 +67,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o
>> obj-$(CONFIG_INTERCONNECT_QCOM_MSM8976) += qnoc-msm8976.o
>> obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o
>> obj-$(CONFIG_INTERCONNECT_QCOM_NORD) += qnoc-nord.o
>> +obj-$(CONFIG_INTERCONNECT_QCOM_CAMNOC) += icc-camnoc.o
>> obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o
>> obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o
>> obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o
>> diff --git a/drivers/interconnect/qcom/qcom-camnoc.c b/drivers/interconnect/qcom/qcom-camnoc.c
>> new file mode 100644
>> index 000000000000..9a703b052b2d
>> --- /dev/null
>> +++ b/drivers/interconnect/qcom/qcom-camnoc.c
>> @@ -0,0 +1,279 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>> + *
>> + * Qualcomm Camera NOC (CAMNOC) interconnect provider.
>> + *
>> + * CAMNOC is the internal AXI interconnect within the Qualcomm camera
>> + * subsystem. Multiple camera sub-devices (IFE, JPEG, BPS, etc.) share
>> + * CAM_CC_CAMNOC_AXI_CLK. This driver acts as an ICC provider so that
>> + * each sub-device can independently vote for bandwidth; the ICC core
>> + * aggregates the votes (max of peak_bw across all consumers) and this
>> + * driver translates the result into a clk_set_rate() call, avoiding
>> + * the last-writer-wins race that occurs with direct clk_set_rate().
>> + *
>> + * Consumers express their required clock rate directly as peak_bw in
>> + * kBps (e.g. 400000 for 400 MHz). The driver converts kBps → Hz:
>> + * rate_hz = peak_bw_kBps * 1000
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/interconnect-provider.h>
>> +#include <linux/interconnect.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include <dt-bindings/interconnect/qcom,camnoc.h>
>> +
>> +#define to_camnoc_provider(_p) \
>> + container_of(_p, struct qcom_camnoc_icc_provider, provider)
>> +
>> +struct qcom_camnoc_icc_provider {
>> + struct clk *clk;
>> + struct icc_provider provider;
>> +};
>> +
>> +struct qcom_camnoc_node {
>> + const char *name;
>> + u16 buswidth;
>> +};
>> +
>> +/* IFE */
>> +static const struct qcom_camnoc_node camnoc_master_ife_0 = {
>> + .name = "master-camnoc-ife-0", .buswidth = 16
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_0_rdi = {
>> + .name = "master-camnoc-ife-0-rdi", .buswidth = 16
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_1 = {
>> + .name = "master-camnoc-ife-1", .buswidth = 16
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_1_rdi = {
>> + .name = "master-camnoc-ife-1-rdi", .buswidth = 16
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_lite_0 = {
>> + .name = "master-camnoc-ife-lite-0", .buswidth = 8
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_lite_1 = {
>> + .name = "master-camnoc-ife-lite-1", .buswidth = 8
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_lite_2 = {
>> + .name = "master-camnoc-ife-lite-2", .buswidth = 8
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_lite_3 = {
>> + .name = "master-camnoc-ife-lite-3", .buswidth = 8
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ife_lite_4 = {
>> + .name = "master-camnoc-ife-lite-4", .buswidth = 8
>> +};
>> +
>> +/* BPS */
>> +static const struct qcom_camnoc_node camnoc_master_bps = {
>> + .name = "master-camnoc-bps", .buswidth = 16
>> +};
>> +
>> +/* IPE */
>> +static const struct qcom_camnoc_node camnoc_master_ipe_0 = {
>> + .name = "master-camnoc-ipe-0", .buswidth = 16
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_ipe_1 = {
>> + .name = "master-camnoc-ipe-1", .buswidth = 16
>> +};
>> +
>> +/* JPEG */
>> +static const struct qcom_camnoc_node camnoc_master_jpeg = {
>> + .name = "master-camnoc-jpeg", .buswidth = 16
>> +};
>> +
>> +static const struct qcom_camnoc_node camnoc_master_jpeg_dma = {
>> + .name = "master-camnoc-jpeg-dma", .buswidth = 16
>> +};
>> +
>> +/* CDM */
>> +static const struct qcom_camnoc_node camnoc_master_cdm = {
>> + .name = "master-camnoc-cdm", .buswidth = 4
>> +};
>> +
>> +/* FD */
>> +static const struct qcom_camnoc_node camnoc_master_fd = {
>> + .name = "master-camnoc-fd", .buswidth = 16
>> +};
>> +
>> +/* SBI */
>> +static const struct qcom_camnoc_node camnoc_master_sbi = {
>> + .name = "master-camnoc-sbi", .buswidth = 16
>> +};
>> +
>> +/* ICP */
>> +static const struct qcom_camnoc_node camnoc_master_icp = {
>> + .name = "master-camnoc-icp", .buswidth = 8
>> +};
>> +
>> +/* Slave */
>> +static const struct qcom_camnoc_node camnoc_slave_axi = {
>> + .name = "slave-camnoc-axi", .buswidth = 32
>> +};
>> +
>> +static const struct qcom_camnoc_node * const camnoc_nodes[] = {
>> + [MASTER_CAMNOC_IFE_0] = &camnoc_master_ife_0,
>> + [MASTER_CAMNOC_IFE_0_RDI] = &camnoc_master_ife_0_rdi,
>> + [MASTER_CAMNOC_IFE_1] = &camnoc_master_ife_1,
>> + [MASTER_CAMNOC_IFE_1_RDI] = &camnoc_master_ife_1_rdi,
>> + [MASTER_CAMNOC_IFE_LITE_0] = &camnoc_master_ife_lite_0,
>> + [MASTER_CAMNOC_IFE_LITE_1] = &camnoc_master_ife_lite_1,
>> + [MASTER_CAMNOC_IFE_LITE_2] = &camnoc_master_ife_lite_2,
>> + [MASTER_CAMNOC_IFE_LITE_3] = &camnoc_master_ife_lite_3,
>> + [MASTER_CAMNOC_IFE_LITE_4] = &camnoc_master_ife_lite_4,
>> + [MASTER_CAMNOC_BPS] = &camnoc_master_bps,
>> + [MASTER_CAMNOC_IPE_0] = &camnoc_master_ipe_0,
>> + [MASTER_CAMNOC_IPE_1] = &camnoc_master_ipe_1,
>> + [MASTER_CAMNOC_JPEG] = &camnoc_master_jpeg,
>> + [MASTER_CAMNOC_JPEG_DMA] = &camnoc_master_jpeg_dma,
>> + [MASTER_CAMNOC_CDM] = &camnoc_master_cdm,
>> + [MASTER_CAMNOC_FD] = &camnoc_master_fd,
>> + [MASTER_CAMNOC_SBI] = &camnoc_master_sbi,
>> + [MASTER_CAMNOC_ICP] = &camnoc_master_icp,
>> + [SLAVE_CAMNOC_AXI] = &camnoc_slave_axi,
>> +};
>
> This will fill up quickly given the number of entries. This should be
> moved to a separate file.
Yes that make sense. BTW we plan to move to icc-clk helper so we are evaluating
how the things will look like.
>
>> +
>> +#define CAMNOC_NUM_NODES ARRAY_SIZE(camnoc_nodes)
>> +
>> +static int qcom_camnoc_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
>> +{
>> + *avg = 0;
>> + *peak = 0;
>> +
>> + return 0;
>> +}
>
> What's going on here ? This sets everything to zero always ?
Yes that should be fixed maybe as part of using icc-clk helper.
~Gjorgji