* Re: [PATCH net v3] net: airoha: Fix skb->priority underflow in airoha_dev_select_queue()
From: Lorenzo Bianconi @ 2026-06-18 10:03 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Wayen Yan, netdev, horms, pabeni, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260617161951.52abe413@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2515 bytes --]
> On Sun, 14 Jun 2026 07:30:54 +0800 Wayen Yan wrote:
> > In airoha_dev_select_queue(), the expression:
> >
> > queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES;
> >
> > implicitly converts to unsigned arithmetic: when skb->priority is 0
> > (the default for unclassified traffic), (0u - 1u) wraps to UINT_MAX,
> > and UINT_MAX % 8 = 7, routing default best-effort packets to the
> > highest-priority QoS queue. This causes QoS inversion where the
> > majority of traffic on a PON gateway starves actual high-priority
> > flows (VoIP, gaming, etc.).
> >
> > Fix by guarding the subtraction: when priority is 0, map to queue 0
> > (lowest priority), otherwise apply the original (priority - 1) % 8
> > mapping.
> >
> > Fixes: 2b288b81560b ("net: airoha: Introduce ndo_select_queue callback")
> > Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
> > Reviewed-by: Joe Damato <joe@dama.to>
> > Signed-off-by: Wayen Yan <win847@gmail.com>
> > ---
> > drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 31cdb11cd7..d476ef83c3 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -1933,7 +1933,7 @@ static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb,
> > */
> > channel = netdev_uses_dsa(dev) ? skb_get_queue_mapping(skb) : port->id;
> > channel = channel % AIROHA_NUM_QOS_CHANNELS;
> > - queue = (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
> > + queue = skb->priority ? (skb->priority - 1) % AIROHA_NUM_QOS_QUEUES : 0;
>
> Hi Lorenzo, is there a reason we're subtracting 1 here in the first
> place? Could be just me, but may be worth adding a comment here.
>
> Intuitively if we are "narrowing" 16 prios to 8 queues it'd make most
> sense to group the adjacent ones -- divide by two.
>
> Please respin with some sort of an explanation..
IIRC this is a leftover of the ETS offload support.
I agree it is righ to just do:
queue = skb->priority % AIROHA_NUM_QOS_QUEUES; /* QoS queue */
@Wayen: can you please respin fixing the issue? Please add even my Acked-by:
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Regards,
Lorenzo
>
> > queue = channel * AIROHA_NUM_QOS_QUEUES + queue;
> >
> > return queue < dev->num_tx_queues ? queue : 0;
> --
> pw-bot: cr
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: Question: SPEAr PLGPIO irq_enable on PREEMPT_RT and regmap updates
From: Herve Codina @ 2026-06-18 9:54 UTC (permalink / raw)
To: Viresh Kumar
Cc: Runyu Xiao, Viresh Kumar, Linus Walleij,
Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
linux-arm-kernel, soc, linux-gpio, linux-rt-devel, linux-kernel,
jianhao.xu
In-Reply-To: <sj3oxg5ymbe2ac2geznsidsxz23rkqzqc4ir3pkjc7bsrzaorw@cw3waaj6xxkx>
Hi,
On Thu, 18 Jun 2026 13:10:31 +0530
Viresh Kumar <viresh.kumar@linaro.org> wrote:
> + Herve (the last guy to work on this driver).
>
> On 18-06-26, 10:34, Runyu Xiao wrote:
> > Hi,
> >
> > While auditing GPIO/pinctrl irqchip callbacks, our static analysis tool
> > flagged the SPEAr PLGPIO irq_enable path, and we manually reviewed it
> > against the current tree.
> >
> > The path is:
> >
> > irq_startup()
> > -> plgpio_irq_enable()
> > -> gpiochip_enable_irq()
> > -> spin_lock_irqsave(&plgpio->lock)
> > -> plgpio_reg_reset()
> > -> regmap_update_bits()
> >
> > On PREEMPT_RT, plgpio->lock is a regular spinlock_t and can become a
> > sleeping lock. Since irq_enable/irq_disable can be called from IRQ
> > management paths while the IRQ descriptor raw lock is held, taking that
> > regular spinlock there looks unsafe.
> >
> > A minimal Lockdep reproducer preserving this irq_chip::irq_enable carrier
> > reports:
> >
> > BUG: sleeping function called from invalid context
> > irqs_disabled(): 1
> > plgpio_rt_spin_lock_irqsave
> > plgpio_irq_enable
> > request_threaded_irq_probe_path
> >
> > My first thought was to convert the PLGPIO register lock to
> > raw_spinlock_t. However, that does not seem sufficient because the IE/EIT
> > updates go through regmap_update_bits()/regmap_read()/regmap_write(). For
> > the syscon/MMIO regmap used here, regmap may still take its own regular
> > fast-IO lock unless the regmap was created with use_raw_spinlock. So a
> > raw_spinlock_t conversion in the PLGPIO driver alone may just move the
> > PREEMPT_RT problem one level down into regmap.
> >
> > The repair I am considering is to keep the gpiolib resource updates in
> > the fast irq_enable/irq_disable callbacks, but defer the actual PLGPIO
> > IE/EIT register writes to irq_bus_sync_unlock(), after the IRQ core has
> > dropped desc->lock. The driver would keep per-line shadow state for:
> >
> > - IRQ disabled/enabled state
> > - pending IE update
> > - edge direction state
> > - pending EIT update
> >
> > and then synchronize those shadow updates from irq_bus_sync_unlock()
> > under a mutex.
> >
> > In other words, the fast callbacks would only update local shadow state
> > and call gpiochip_enable_irq()/gpiochip_disable_irq(), while the sleepable
> > regmap writes would be batched into the irq bus sync phase.
> >
> > Does that sound like an acceptable direction for SPEAr PLGPIO, or would
> > you prefer a different fix, such as changing the underlying syscon regmap
> > locking model or handling only the IE register path?
> >
> > The draft patch I have locally is roughly:
> >
> > pinctrl: spear: defer PLGPIO IRQ updates to bus sync
> >
> > and it changes only drivers/pinctrl/spear/pinctrl-plgpio.c.
>
> I haven't worked on this for a very long time now (15 yrs). There are some
> people who use this hardware, and so it is not removed until now.
>
> Also I am not sure if RT kernel is a valid use case here for this SoC family.
>
I know some users and they don't use RT kernel.
But well, isn't the pattern present in some other gpio controller drivers ?
How it is done in others ?
What is specific in this controller compare to others ?
We take and release spinlock in gpio chip .irq_enable(). I think we can
find other drivers doing that and probably drivers using a regmap as well.
Best regards,
Hervé
^ permalink raw reply
* [PATCH v4 2/2] clk: amlogic: Add A9 peripherals clock controller driver
From: Jian Hu via B4 Relay @ 2026-06-18 9:51 UTC (permalink / raw)
To: Neil Armstrong, Jerome Brunet, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao,
Kevin Hilman, Martin Blumenstingl
Cc: linux-amlogic, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Jian Hu
In-Reply-To: <20260618-a9_peripherals-v4-0-fe120de44e77@amlogic.com>
From: Jian Hu <jian.hu@amlogic.com>
Add the peripherals clock controller driver for the Amlogic A9 SoC family.
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
drivers/clk/meson/Kconfig | 15 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/a9-peripherals.c | 2096 ++++++++++++++++++++++++++++++++++++
3 files changed, 2112 insertions(+)
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index cf8cf3f9e4ee..86d2e270e1b8 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -132,6 +132,21 @@ config COMMON_CLK_A1_PERIPHERALS
device, A1 SoC Family. Say Y if you want A1 Peripherals clock
controller to work.
+config COMMON_CLK_A9_PERIPHERALS
+ tristate "Amlogic A9 SoC peripherals clock controller support"
+ depends on ARM64 || COMPILE_TEST
+ default ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_DUALDIV
+ select COMMON_CLK_MESON_VID_PLL_DIV
+ imply COMMON_CLK_SCMI
+ imply COMMON_CLK_A9_PLL
+ help
+ Support for the peripherals clock controller on Amlogic A311Y3 based
+ device, AKA A9. Peripherals are required by most peripheral to operate.
+ Say Y if you want A9 peripherals clock controller to work.
+
config COMMON_CLK_C3_PLL
tristate "Amlogic C3 PLL clock controller"
depends on ARM64
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index c6719694a242..bccd9ace9201 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
+obj-$(CONFIG_COMMON_CLK_A9_PERIPHERALS) += a9-peripherals.o
obj-$(CONFIG_COMMON_CLK_C3_PLL) += c3-pll.o
obj-$(CONFIG_COMMON_CLK_C3_PERIPHERALS) += c3-peripherals.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
diff --git a/drivers/clk/meson/a9-peripherals.c b/drivers/clk/meson/a9-peripherals.c
new file mode 100644
index 000000000000..8e0ae3205d77
--- /dev/null
+++ b/drivers/clk/meson/a9-peripherals.c
@@ -0,0 +1,2096 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2026 Amlogic, Inc. All rights reserved
+ */
+
+#include <dt-bindings/clock/amlogic,a9-peripherals-clkc.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+#include "meson-clkc-utils.h"
+#include "vid-pll-div.h"
+
+#define SYS_CLK_EN0_REG0 0x30
+#define SYS_CLK_EN0_REG1 0x34
+#define SYS_CLK_EN0_REG2 0x38
+#define SYS_CLK_EN0_REG3 0x3c
+#define SD_EMMC_CLK_CTRL0 0x90
+#define SD_EMMC_CLK_CTRL1 0x94
+#define PWM_CLK_H_CTRL 0xbc
+#define PWM_CLK_I_CTRL 0xc0
+#define PWM_CLK_J_CTRL 0xc4
+#define PWM_CLK_K_CTRL 0xc8
+#define PWM_CLK_L_CTRL 0xcc
+#define PWM_CLK_M_CTRL 0xd0
+#define PWM_CLK_N_CTRL 0xd4
+#define SPISG_CLK_CTRL 0x100
+#define SPISG_CLK_CTRL1 0x104
+#define SAR_CLK_CTRL 0x150
+#define AMFC_CLK_CTRL 0x154
+#define NNA_CLK_CTRL 0x15c
+#define USB_CLK_CTRL 0x160
+#define PCIE_TL_CLK_CTRL 0x164
+#define CMPR_CLK_CTRL 0x168
+#define DEWARP_CLK_CTRL 0x16c
+#define SC_CLK_CTRL 0x170
+#define DPTX_CLK_CTRL 0x178
+#define ISP_CLK_CTRL 0x17c
+#define CVE_CLK_CTRL 0x180
+#define PP_CLK_CTRL 0x184
+#define GLB_CLK_CTRL 0x188
+#define USB_CLK_CTRL0 0x18c
+#define USB_CLK_CTRL1 0x190
+#define CAN_CLK_CTRL 0x194
+#define CAN_CLK_CTRL1 0x198
+#define I3C_CLK_CTRL 0x19c
+#define TS_CLK_CTRL 0x1a0
+#define ETH_CLK_CTRL 0x1a4
+#define GEN_CLK_CTRL 0x1a8
+#define CLK12_24_CTRL 0x1ac
+#define MALI_CLK_CTRL 0x200
+#define MALI_STACK_CLK_CTRL 0x204
+#define DSPA_CLK_CTRL 0x220
+#define HEVCF_CLK_CTRL 0x240
+#define HCODEC_CLK_CTRL 0x244
+#define VPU_CLK_CTRL 0x260
+#define VAPB_CLK_CTRL 0x268
+#define VPU_CLKB_CTRL 0x280
+#define HDMI_CLK_CTRL 0x284
+#define HTX_CLK_CTRL 0x28c
+#define HTX_CLK_CTRL1 0x290
+#define HRX_CLK_CTRL 0x294
+#define HRX_CLK_CTRL1 0x298
+#define HRX_CLK_CTRL2 0x29c
+#define HRX_CLK_CTRL3 0x2a0
+#define VID_LOCK_CLK_CTRL 0x2a4
+#define VDIN_MEAS_CLK_CTRL 0x2a8
+#define VID_PLL_CLK_DIV 0x2b0
+#define VID_CLK_CTRL 0x2c0
+#define VID_CLK_CTRL2 0x2c4
+#define VID_CLK_DIV 0x2c8
+#define VIID_CLK_DIV 0x2cc
+#define VIID_CLK_CTRL 0x2d0
+#define MIPI_CSI_PHY_CLK_CTRL 0x2e0
+#define DSI_MEAS_CLK_CTRL 0x2f4
+
+#define A9_COMP_SEL(_name, _reg, _shift, _mask, _pdata, _table) \
+ MESON_COMP_SEL(a9_, _name, _reg, _shift, _mask, _pdata, _table, 0, 0)
+
+#define A9_COMP_DIV(_name, _reg, _shift, _width) \
+ MESON_COMP_DIV(a9_, _name, _reg, _shift, _width, 0, CLK_SET_RATE_PARENT)
+
+#define A9_COMP_GATE(_name, _reg, _bit, _iflags) \
+ MESON_COMP_GATE(a9_, _name, _reg, _bit, CLK_SET_RATE_PARENT | (_iflags))
+
+static const struct clk_parent_data a9_sys_pclk_parents = { .fw_name = "sys" };
+
+#define A9_SYS_PCLK(_name, _reg, _bit) \
+ MESON_PCLK(a9_##_name, _reg, _bit, &a9_sys_pclk_parents, 0)
+
+static A9_SYS_PCLK(sys_am_axi, SYS_CLK_EN0_REG0, 0);
+static A9_SYS_PCLK(sys_dos, SYS_CLK_EN0_REG0, 1);
+static A9_SYS_PCLK(sys_mipi_dsi, SYS_CLK_EN0_REG0, 3);
+static A9_SYS_PCLK(sys_eth_phy, SYS_CLK_EN0_REG0, 4);
+static A9_SYS_PCLK(sys_amfc, SYS_CLK_EN0_REG0, 5);
+static A9_SYS_PCLK(sys_mali, SYS_CLK_EN0_REG0, 6);
+static A9_SYS_PCLK(sys_nna, SYS_CLK_EN0_REG0, 7);
+static A9_SYS_PCLK(sys_eth_axi, SYS_CLK_EN0_REG0, 8);
+static A9_SYS_PCLK(sys_dp_apb, SYS_CLK_EN0_REG0, 9);
+static A9_SYS_PCLK(sys_edptx_apb, SYS_CLK_EN0_REG0, 10);
+static A9_SYS_PCLK(sys_u3hsg, SYS_CLK_EN0_REG0, 11);
+static A9_SYS_PCLK(sys_aucpu, SYS_CLK_EN0_REG0, 14);
+static A9_SYS_PCLK(sys_glb, SYS_CLK_EN0_REG0, 15);
+static A9_SYS_PCLK(sys_combo_dphy_apb, SYS_CLK_EN0_REG0, 17);
+static A9_SYS_PCLK(sys_hdmirx_apb, SYS_CLK_EN0_REG0, 18);
+static A9_SYS_PCLK(sys_hdmirx_pclk, SYS_CLK_EN0_REG0, 19);
+static A9_SYS_PCLK(sys_mipi_dsi_phy, SYS_CLK_EN0_REG0, 20);
+static A9_SYS_PCLK(sys_can0, SYS_CLK_EN0_REG0, 21);
+static A9_SYS_PCLK(sys_can1, SYS_CLK_EN0_REG0, 22);
+static A9_SYS_PCLK(sys_sd_emmc_a, SYS_CLK_EN0_REG0, 24);
+static A9_SYS_PCLK(sys_sd_emmc_b, SYS_CLK_EN0_REG0, 25);
+static A9_SYS_PCLK(sys_sd_emmc_c, SYS_CLK_EN0_REG0, 26);
+static A9_SYS_PCLK(sys_sc, SYS_CLK_EN0_REG0, 27);
+static A9_SYS_PCLK(sys_acodec, SYS_CLK_EN0_REG0, 28);
+static A9_SYS_PCLK(sys_mipi_isp, SYS_CLK_EN0_REG0, 29);
+static A9_SYS_PCLK(sys_msr, SYS_CLK_EN0_REG0, 30);
+static A9_SYS_PCLK(sys_audio, SYS_CLK_EN0_REG1, 0);
+static A9_SYS_PCLK(sys_mipi_dsi_b, SYS_CLK_EN0_REG1, 1);
+static A9_SYS_PCLK(sys_mipi_dsi1_phy, SYS_CLK_EN0_REG1, 2);
+static A9_SYS_PCLK(sys_eth, SYS_CLK_EN0_REG1, 3);
+static A9_SYS_PCLK(sys_eth_1g_mac, SYS_CLK_EN0_REG1, 4);
+static A9_SYS_PCLK(sys_uart_a, SYS_CLK_EN0_REG1, 5);
+static A9_SYS_PCLK(sys_uart_f, SYS_CLK_EN0_REG1, 10);
+static A9_SYS_PCLK(sys_ts_a55, SYS_CLK_EN0_REG1, 11);
+static A9_SYS_PCLK(sys_eth_1g_axi, SYS_CLK_EN0_REG1, 12);
+static A9_SYS_PCLK(sys_ts_dos, SYS_CLK_EN0_REG1, 13);
+static A9_SYS_PCLK(sys_u3drd_b, SYS_CLK_EN0_REG1, 14);
+static A9_SYS_PCLK(sys_ts_core, SYS_CLK_EN0_REG1, 15);
+static A9_SYS_PCLK(sys_ts_pll, SYS_CLK_EN0_REG1, 16);
+static A9_SYS_PCLK(sys_csi_dig_clkin, SYS_CLK_EN0_REG1, 18);
+static A9_SYS_PCLK(sys_cve, SYS_CLK_EN0_REG1, 19);
+static A9_SYS_PCLK(sys_ge2d, SYS_CLK_EN0_REG1, 20);
+static A9_SYS_PCLK(sys_spisg, SYS_CLK_EN0_REG1, 21);
+static A9_SYS_PCLK(sys_u3drd_1, SYS_CLK_EN0_REG1, 22);
+static A9_SYS_PCLK(sys_u2h, SYS_CLK_EN0_REG1, 23);
+static A9_SYS_PCLK(sys_pcie_mac_a, SYS_CLK_EN0_REG1, 24);
+static A9_SYS_PCLK(sys_u3drd_a, SYS_CLK_EN0_REG1, 25);
+static A9_SYS_PCLK(sys_u2drd, SYS_CLK_EN0_REG1, 26);
+static A9_SYS_PCLK(sys_pcie_phy, SYS_CLK_EN0_REG1, 27);
+static A9_SYS_PCLK(sys_pcie_mac_b, SYS_CLK_EN0_REG1, 28);
+static A9_SYS_PCLK(sys_periph, SYS_CLK_EN0_REG1, 29);
+static A9_SYS_PCLK(sys_pio, SYS_CLK_EN0_REG2, 0);
+static A9_SYS_PCLK(sys_i3c, SYS_CLK_EN0_REG2, 1);
+static A9_SYS_PCLK(sys_i2c_m_e, SYS_CLK_EN0_REG2, 2);
+static A9_SYS_PCLK(sys_i2c_m_f, SYS_CLK_EN0_REG2, 3);
+static A9_SYS_PCLK(sys_hdmitx_apb, SYS_CLK_EN0_REG2, 4);
+static A9_SYS_PCLK(sys_i2c_m_i, SYS_CLK_EN0_REG2, 5);
+static A9_SYS_PCLK(sys_i2c_m_g, SYS_CLK_EN0_REG2, 6);
+static A9_SYS_PCLK(sys_i2c_m_h, SYS_CLK_EN0_REG2, 7);
+static A9_SYS_PCLK(sys_hdmi20_aes, SYS_CLK_EN0_REG2, 9);
+static A9_SYS_PCLK(sys_csi2_host, SYS_CLK_EN0_REG2, 16);
+static A9_SYS_PCLK(sys_csi2_adapt, SYS_CLK_EN0_REG2, 17);
+static A9_SYS_PCLK(sys_dspa, SYS_CLK_EN0_REG2, 21);
+static A9_SYS_PCLK(sys_pp_dma, SYS_CLK_EN0_REG2, 22);
+static A9_SYS_PCLK(sys_pp_wrapper, SYS_CLK_EN0_REG2, 23);
+static A9_SYS_PCLK(sys_vpu_intr, SYS_CLK_EN0_REG2, 25);
+static A9_SYS_PCLK(sys_csi2_phy, SYS_CLK_EN0_REG2, 27);
+static A9_SYS_PCLK(sys_saradc, SYS_CLK_EN0_REG2, 28);
+static A9_SYS_PCLK(sys_pwm_j, SYS_CLK_EN0_REG2, 30);
+static A9_SYS_PCLK(sys_pwm_i, SYS_CLK_EN0_REG2, 31);
+static A9_SYS_PCLK(sys_pwm_h, SYS_CLK_EN0_REG3, 0);
+static A9_SYS_PCLK(sys_pwm_n, SYS_CLK_EN0_REG3, 8);
+static A9_SYS_PCLK(sys_pwm_m, SYS_CLK_EN0_REG3, 9);
+static A9_SYS_PCLK(sys_pwm_l, SYS_CLK_EN0_REG3, 10);
+static A9_SYS_PCLK(sys_pwm_k, SYS_CLK_EN0_REG3, 11);
+
+/* Channel 5 is unconnected. */
+static u32 a9_sd_emmc_parents_val_table[] = { 0, 1, 2, 3, 4, 6, 7 };
+static const struct clk_parent_data a9_sd_emmc_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "gp1", },
+ { .fw_name = "gp0", }
+};
+
+static A9_COMP_SEL(sd_emmc_a, SD_EMMC_CLK_CTRL0, 9, 0x7, a9_sd_emmc_parents,
+ a9_sd_emmc_parents_val_table);
+static A9_COMP_DIV(sd_emmc_a, SD_EMMC_CLK_CTRL0, 0, 7);
+static A9_COMP_GATE(sd_emmc_a, SD_EMMC_CLK_CTRL0, 8, 0);
+
+static A9_COMP_SEL(sd_emmc_b, SD_EMMC_CLK_CTRL0, 25, 0x7, a9_sd_emmc_parents,
+ a9_sd_emmc_parents_val_table);
+static A9_COMP_DIV(sd_emmc_b, SD_EMMC_CLK_CTRL0, 16, 7);
+static A9_COMP_GATE(sd_emmc_b, SD_EMMC_CLK_CTRL0, 24, 0);
+
+static A9_COMP_SEL(sd_emmc_c, SD_EMMC_CLK_CTRL1, 9, 0x7, a9_sd_emmc_parents,
+ a9_sd_emmc_parents_val_table);
+static A9_COMP_DIV(sd_emmc_c, SD_EMMC_CLK_CTRL1, 0, 7);
+static A9_COMP_GATE(sd_emmc_c, SD_EMMC_CLK_CTRL1, 8, 0);
+
+static const struct clk_parent_data a9_pwm_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", }
+};
+
+static A9_COMP_SEL(pwm_h, PWM_CLK_H_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_h, PWM_CLK_H_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_h, PWM_CLK_H_CTRL, 8, 0);
+
+static A9_COMP_SEL(pwm_i, PWM_CLK_I_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_i, PWM_CLK_I_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_i, PWM_CLK_I_CTRL, 8, 0);
+
+static A9_COMP_SEL(pwm_j, PWM_CLK_J_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_j, PWM_CLK_J_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_j, PWM_CLK_J_CTRL, 8, 0);
+
+static A9_COMP_SEL(pwm_k, PWM_CLK_K_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_k, PWM_CLK_K_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_k, PWM_CLK_K_CTRL, 8, 0);
+
+static A9_COMP_SEL(pwm_l, PWM_CLK_L_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_l, PWM_CLK_L_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_l, PWM_CLK_L_CTRL, 8, 0);
+
+static A9_COMP_SEL(pwm_m, PWM_CLK_M_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_m, PWM_CLK_M_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_m, PWM_CLK_M_CTRL, 8, 0);
+
+static A9_COMP_SEL(pwm_n, PWM_CLK_N_CTRL, 9, 0x7, a9_pwm_parents, NULL);
+static A9_COMP_DIV(pwm_n, PWM_CLK_N_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_n, PWM_CLK_N_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_spisg_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "sys", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "gp0", }
+};
+
+static A9_COMP_SEL(spisg0, SPISG_CLK_CTRL, 9, 0x7, a9_spisg_parents, NULL);
+static A9_COMP_DIV(spisg0, SPISG_CLK_CTRL, 0, 6);
+static A9_COMP_GATE(spisg0, SPISG_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(spisg1, SPISG_CLK_CTRL, 25, 0x7, a9_spisg_parents, NULL);
+static A9_COMP_DIV(spisg1, SPISG_CLK_CTRL, 16, 6);
+static A9_COMP_GATE(spisg1, SPISG_CLK_CTRL, 24, 0);
+
+static A9_COMP_SEL(spisg2, SPISG_CLK_CTRL1, 9, 0x7, a9_spisg_parents, NULL);
+static A9_COMP_DIV(spisg2, SPISG_CLK_CTRL1, 0, 6);
+static A9_COMP_GATE(spisg2, SPISG_CLK_CTRL1, 8, 0);
+
+static const struct clk_parent_data a9_saradc_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "sys", }
+};
+
+static A9_COMP_SEL(saradc, SAR_CLK_CTRL, 9, 0x7, a9_saradc_parents, NULL);
+static A9_COMP_DIV(saradc, SAR_CLK_CTRL, 0, 8);
+static A9_COMP_GATE(saradc, SAR_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_amfc_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "sys", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", }
+};
+
+static A9_COMP_SEL(amfc, AMFC_CLK_CTRL, 9, 0x7, a9_amfc_parents, NULL);
+static A9_COMP_DIV(amfc, AMFC_CLK_CTRL, 0, 6);
+static A9_COMP_GATE(amfc, AMFC_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_nna_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "gp2", },
+ { .fw_name = "hifi0", }
+};
+
+static A9_COMP_SEL(nna, NNA_CLK_CTRL, 9, 0x7, a9_nna_parents, NULL);
+static A9_COMP_DIV(nna, NNA_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(nna, NNA_CLK_CTRL, 8, 0);
+
+/* Channel 5 and 6 are unconnected. */
+static u32 a9_usb_250m_parents_val_table[] = { 0, 1, 2, 3, 4, 7 };
+static const struct clk_parent_data a9_usb_250m_parents[] = {
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "fdiv2p5", }
+};
+
+static A9_COMP_SEL(usb_250m, USB_CLK_CTRL, 9, 0x7, a9_usb_250m_parents,
+ a9_usb_250m_parents_val_table);
+static A9_COMP_DIV(usb_250m, USB_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(usb_250m, USB_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_usb_48m_pre_parents[] = {
+ { .fw_name = "gp0", },
+ { .fw_name = "gp1", },
+ { .fw_name = "gp2", },
+ { .fw_name = "fdiv2", },
+};
+
+static A9_COMP_SEL(usb_48m_pre, USB_CLK_CTRL, 25, 0x3, a9_usb_48m_pre_parents,
+ NULL);
+static A9_COMP_DIV(usb_48m_pre, USB_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(usb_48m_pre, USB_CLK_CTRL, 24, 0);
+
+static const struct clk_parent_data a9_pcie_tl_parents[] = {
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "gp0", },
+ { .fw_name = "sys", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(pcie0_tl, PCIE_TL_CLK_CTRL, 9, 0x7, a9_pcie_tl_parents,
+ NULL);
+static A9_COMP_DIV(pcie0_tl, PCIE_TL_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(pcie0_tl, PCIE_TL_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(pcie1_tl, PCIE_TL_CLK_CTRL, 25, 0x7, a9_pcie_tl_parents,
+ NULL);
+static A9_COMP_DIV(pcie1_tl, PCIE_TL_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(pcie1_tl, PCIE_TL_CLK_CTRL, 24, 0);
+
+static const struct clk_parent_data a9_cmpr_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "gp1", }
+};
+
+static A9_COMP_SEL(cmpr, CMPR_CLK_CTRL, 25, 0x7, a9_cmpr_parents, NULL);
+static A9_COMP_DIV(cmpr, CMPR_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(cmpr, CMPR_CLK_CTRL, 24, 0);
+
+static const struct clk_parent_data a9_dewarpa_parents[] = {
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "gp0", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "gp1", }
+};
+
+static A9_COMP_SEL(dewarpa, DEWARP_CLK_CTRL, 9, 0x7, a9_dewarpa_parents, NULL);
+static A9_COMP_DIV(dewarpa, DEWARP_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(dewarpa, DEWARP_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_sc_parents[] = {
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(sc_pre, SC_CLK_CTRL, 9, 0x7, a9_sc_parents, NULL);
+static A9_COMP_DIV(sc_pre, SC_CLK_CTRL, 0, 8);
+static A9_COMP_GATE(sc_pre, SC_CLK_CTRL, 8, 0);
+
+static struct clk_regmap a9_sc = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = SC_CLK_CTRL,
+ .shift = 16,
+ .width = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sc",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_sc_pre.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_dptx_apb2_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "sys", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", }
+};
+
+static A9_COMP_SEL(dptx_apb2, DPTX_CLK_CTRL, 9, 0x7, a9_dptx_apb2_parents, NULL);
+static A9_COMP_DIV(dptx_apb2, DPTX_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(dptx_apb2, DPTX_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_dptx_aud_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "sys", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", }
+};
+
+static A9_COMP_SEL(dptx_aud, DPTX_CLK_CTRL, 25, 0x7, a9_dptx_aud_parents, NULL);
+static A9_COMP_DIV(dptx_aud, DPTX_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(dptx_aud, DPTX_CLK_CTRL, 24, 0);
+
+static const struct clk_parent_data a9_isp_parents[] = {
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "gp0", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(isp, ISP_CLK_CTRL, 9, 0x7, a9_isp_parents, NULL);
+static A9_COMP_DIV(isp, ISP_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(isp, ISP_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_cve_vge_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "gp0", },
+ { .fw_name = "rtc", }
+};
+
+static A9_COMP_SEL(cve, CVE_CLK_CTRL, 9, 0x7, a9_cve_vge_parents, NULL);
+static A9_COMP_DIV(cve, CVE_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(cve, CVE_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(vge, CVE_CLK_CTRL, 25, 0x7, a9_cve_vge_parents, NULL);
+static A9_COMP_DIV(vge, CVE_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(vge, CVE_CLK_CTRL, 24, 0);
+
+static const struct clk_parent_data a9_pp_parents[] = {
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "gp0", },
+ { .fw_name = "sys", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(pp, PP_CLK_CTRL, 9, 0x7, a9_pp_parents, NULL);
+static A9_COMP_DIV(pp, PP_CLK_CTRL, 0, 6);
+static A9_COMP_GATE(pp, PP_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_dspa_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "gp2", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "rtc", }
+};
+
+static A9_COMP_SEL(dspa_0, DSPA_CLK_CTRL, 9, 0x7, a9_dspa_parents, NULL);
+static A9_COMP_DIV(dspa_0, DSPA_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(dspa_0, DSPA_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(dspa_1, DSPA_CLK_CTRL, 25, 0x7, a9_dspa_parents, NULL);
+static A9_COMP_DIV(dspa_1, DSPA_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(dspa_1, DSPA_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_dspa = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = DSPA_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "dspa",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_dspa_0.hw,
+ &a9_dspa_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* Channel 6 is unconnected. */
+static u32 a9_glb_parents_val_table[] = { 0, 1, 2, 3, 4, 5, 7 };
+static const struct clk_parent_data a9_glb_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &a9_dspa.hw },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .hw = &a9_isp.hw },
+ { .fw_name = "rtc", }
+};
+
+static A9_COMP_SEL(glb, GLB_CLK_CTRL, 9, 0x7, a9_glb_parents, a9_glb_parents_val_table);
+static A9_COMP_DIV(glb, GLB_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(glb, GLB_CLK_CTRL, 8, 0);
+
+static struct clk_regmap a9_usb_48m_dualdiv_in = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = USB_CLK_CTRL,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_48m_dualdiv_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_usb_48m_pre.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param a9_usb_48m_dualdiv_div_table[] = {
+ { 733, 732, 8, 11, 1 },
+ { /* sentinel */ }
+};
+
+static struct clk_regmap a9_usb_48m_dualdiv_div = {
+ .data = &(struct meson_clk_dualdiv_data) {
+ .n1 = {
+ .reg_off = USB_CLK_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = USB_CLK_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = USB_CLK_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = USB_CLK_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = USB_CLK_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = a9_usb_48m_dualdiv_div_table,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_48m_dualdiv_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_usb_48m_dualdiv_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_usb_48m_dualdiv_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = USB_CLK_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_48m_dualdiv_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_usb_48m_dualdiv_in.hw,
+ &a9_usb_48m_dualdiv_div.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_usb_48m_dualdiv = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = USB_CLK_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_48m_dualdiv",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_usb_48m_dualdiv_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_usb_48m = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = USB_CLK_CTRL1,
+ .mask = 0x3,
+ .shift = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "usb_48m",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_usb_48m_pre.hw,
+ &a9_usb_48m_dualdiv.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/* Channel 2 is unconnected. */
+static u32 a9_can_pe_parents_val_table[] = { 0, 1, 3 };
+static const struct clk_parent_data a9_can_pe_parents[] = {
+ { .fw_name = "sys", },
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv5", }
+};
+
+static A9_COMP_SEL(can0_pe, CAN_CLK_CTRL, 9, 0x7, a9_can_pe_parents, a9_can_pe_parents_val_table);
+static A9_COMP_DIV(can0_pe, CAN_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(can0_pe, CAN_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(can1_pe, CAN_CLK_CTRL, 25, 0x7, a9_can_pe_parents, a9_can_pe_parents_val_table);
+static A9_COMP_DIV(can1_pe, CAN_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(can1_pe, CAN_CLK_CTRL, 24, 0);
+
+static const struct clk_parent_data a9_can_filter_parents[] = {
+ { .fw_name = "sys", },
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", }
+};
+
+static A9_COMP_SEL(can0_filter, CAN_CLK_CTRL1, 9, 0x7, a9_can_filter_parents, NULL);
+static A9_COMP_DIV(can0_filter, CAN_CLK_CTRL1, 0, 7);
+static A9_COMP_GATE(can0_filter, CAN_CLK_CTRL1, 8, 0);
+
+static A9_COMP_SEL(can1_filter, CAN_CLK_CTRL1, 25, 0x7, a9_can_filter_parents, NULL);
+static A9_COMP_DIV(can1_filter, CAN_CLK_CTRL1, 16, 7);
+static A9_COMP_GATE(can1_filter, CAN_CLK_CTRL1, 24, 0);
+
+static const struct clk_parent_data a9_i3c_parents[] = {
+ { .fw_name = "sys", },
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv5", }
+};
+
+static A9_COMP_SEL(i3c, I3C_CLK_CTRL, 9, 0x7, a9_i3c_parents, NULL);
+static A9_COMP_DIV(i3c, I3C_CLK_CTRL, 0, 8);
+static A9_COMP_GATE(i3c, I3C_CLK_CTRL, 8, 0);
+
+static struct clk_regmap a9_ts_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = TS_CLK_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ts_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_ts = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = TS_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ts",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ts_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_fixed_factor a9_eth_125m_div = {
+ .mult = 1,
+ .div = 8,
+ .hw.init = &(struct clk_init_data) {
+ .name = "eth_125m_div",
+ .ops = &clk_fixed_factor_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "fdiv2",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_eth_125m = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ETH_CLK_CTRL,
+ .bit_idx = 7,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "eth_125m",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_eth_125m_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * Channel 1, 2, 3, 4, 5 and 6 are unconnected,
+ * Channel 7(ext_rmii) connects external PAD. Do not automatically reparent.
+ */
+static u32 a9_eth_rmii_parents_val_table[] = { 0, 7 };
+static const struct clk_parent_data a9_eth_rmii_parents[] = {
+ { .fw_name = "fdiv2", },
+ { .fw_name = "ext_rmii", }
+};
+
+static struct clk_regmap a9_eth_rmii_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = ETH_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 9,
+ .table = a9_eth_rmii_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "eth_rmii_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a9_eth_rmii_parents,
+ .num_parents = ARRAY_SIZE(a9_eth_rmii_parents),
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap a9_eth_rmii_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = ETH_CLK_CTRL,
+ .shift = 0,
+ .width = 7,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "eth_rmii_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_eth_rmii_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_eth_rmii = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = ETH_CLK_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "eth_rmii",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_eth_rmii_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+/*
+ * Channel 12 (msr_clk) is managed by the clock measurement module and is not part of the clock
+ * tree. It depends on the measurement source selected through the measurement control registers.
+ *
+ * Channel 10, 11, 13, 14 and 16 are unconnected.
+ */
+static u32 a9_gen_parents_val_table[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26};
+static struct clk_regmap a9_vid_pll;
+
+static const struct clk_parent_data a9_gen_parents[] = {
+ { .fw_name = "xtal" },
+ { .fw_name = "rtc" },
+ { .fw_name = "sysplldiv16" },
+ { .fw_name = "ddr_test" },
+ { .hw = &a9_vid_pll.hw },
+ { .fw_name = "gp0" },
+ { .fw_name = "hifi1" },
+ { .fw_name = "hifi0" },
+ { .fw_name = "gp1" },
+ { .fw_name = "gp2" },
+ { .fw_name = "dsudiv16" },
+ { .fw_name = "cpudiv16" },
+ { .fw_name = "a78div16" },
+ { .fw_name = "fdiv2" },
+ { .fw_name = "fdiv2p5" },
+ { .fw_name = "fdiv3" },
+ { .fw_name = "fdiv4" },
+ { .fw_name = "fdiv5" },
+ { .fw_name = "fdiv7" },
+ { .fw_name = "mclk0" },
+ { .fw_name = "mclk1" }
+};
+
+static A9_COMP_SEL(gen, GEN_CLK_CTRL, 12, 0x1f, a9_gen_parents, a9_gen_parents_val_table);
+static A9_COMP_DIV(gen, GEN_CLK_CTRL, 0, 11);
+static A9_COMP_GATE(gen, GEN_CLK_CTRL, 11, 0);
+
+static struct clk_regmap a9_24m_in = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = CLK12_24_CTRL,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "24m_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_12_24m = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = CLK12_24_CTRL,
+ .shift = 10,
+ .width = 1,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "12_24m",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_24m_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct clk_parent_data a9_mali_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "gp1", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", }
+};
+
+static A9_COMP_SEL(mali_0, MALI_CLK_CTRL, 9, 0x7, a9_mali_parents, NULL);
+static A9_COMP_DIV(mali_0, MALI_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(mali_0, MALI_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(mali_1, MALI_CLK_CTRL, 25, 0x7, a9_mali_parents, NULL);
+static A9_COMP_DIV(mali_1, MALI_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(mali_1, MALI_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_mali = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = MALI_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_mali_0.hw,
+ &a9_mali_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static A9_COMP_SEL(mali_stack_0, MALI_STACK_CLK_CTRL, 9, 0x7, a9_mali_parents, NULL);
+static A9_COMP_DIV(mali_stack_0, MALI_STACK_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(mali_stack_0, MALI_STACK_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(mali_stack_1, MALI_STACK_CLK_CTRL, 25, 0x7, a9_mali_parents, NULL);
+static A9_COMP_DIV(mali_stack_1, MALI_STACK_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(mali_stack_1, MALI_STACK_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_mali_stack = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = MALI_STACK_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "mali_stack",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_mali_stack_0.hw,
+ &a9_mali_stack_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_hevcf_parents[] = {
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "gp1", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(hevcf_0, HEVCF_CLK_CTRL, 9, 0x7, a9_hevcf_parents, NULL);
+static A9_COMP_DIV(hevcf_0, HEVCF_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(hevcf_0, HEVCF_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(hevcf_1, HEVCF_CLK_CTRL, 25, 0x7, a9_hevcf_parents, NULL);
+static A9_COMP_DIV(hevcf_1, HEVCF_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(hevcf_1, HEVCF_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_hevcf = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HEVCF_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hevcf",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_hevcf_0.hw,
+ &a9_hevcf_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_hcodec_parents[] = {
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "gp0", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(hcodec_0, HCODEC_CLK_CTRL, 9, 0x7, a9_hcodec_parents, NULL);
+static A9_COMP_DIV(hcodec_0, HCODEC_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(hcodec_0, HCODEC_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(hcodec_1, HCODEC_CLK_CTRL, 25, 0x7, a9_hcodec_parents, NULL);
+static A9_COMP_DIV(hcodec_1, HCODEC_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(hcodec_1, HCODEC_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_hcodec = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HCODEC_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hcodec",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_hcodec_0.hw,
+ &a9_hcodec_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_vpu_parents[] = {
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "vid1", },
+ { .fw_name = "fdiv2", },
+ { .hw = &a9_vid_pll.hw },
+ { .fw_name = "vid2", },
+ { .fw_name = "gp1", }
+};
+
+static A9_COMP_SEL(vpu_0, VPU_CLK_CTRL, 9, 0x7, a9_vpu_parents, NULL);
+static A9_COMP_DIV(vpu_0, VPU_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(vpu_0, VPU_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(vpu_1, VPU_CLK_CTRL, 25, 0x7, a9_vpu_parents, NULL);
+static A9_COMP_DIV(vpu_1, VPU_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(vpu_1, VPU_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_vpu = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VPU_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vpu",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vpu_0.hw,
+ &a9_vpu_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_vapb_parents[] = {
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", },
+ { .fw_name = "fdiv2", },
+ { .hw = &a9_vid_pll.hw },
+ { .fw_name = "hifi0", },
+ { .fw_name = "fdiv2p5", }
+};
+
+static A9_COMP_SEL(vapb_0, VAPB_CLK_CTRL, 9, 0x7, a9_vapb_parents, NULL);
+static A9_COMP_DIV(vapb_0, VAPB_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(vapb_0, VAPB_CLK_CTRL, 8, CLK_SET_RATE_GATE);
+
+static A9_COMP_SEL(vapb_1, VAPB_CLK_CTRL, 25, 0x7, a9_vapb_parents, NULL);
+static A9_COMP_DIV(vapb_1, VAPB_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(vapb_1, VAPB_CLK_CTRL, 24, CLK_SET_RATE_GATE);
+
+static struct clk_regmap a9_vapb = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VAPB_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 31,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vapb",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vapb_0.hw,
+ &a9_vapb_1.hw
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_ge2d = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = VAPB_CLK_CTRL,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ge2d",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vapb.hw,
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct clk_parent_data a9_vpu_clkb_tmp_parents[] = {
+ { .hw = &a9_vpu.hw },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv7", }
+};
+
+static A9_COMP_SEL(vpu_clkb_tmp, VPU_CLKB_CTRL, 25, 0x7, a9_vpu_clkb_tmp_parents, NULL);
+static A9_COMP_DIV(vpu_clkb_tmp, VPU_CLKB_CTRL, 16, 4);
+static A9_COMP_GATE(vpu_clkb_tmp, VPU_CLKB_CTRL, 24, 0);
+
+static struct clk_regmap a9_vpu_clkb_div = {
+ .data = &(struct clk_regmap_div_data) {
+ .offset = VPU_CLKB_CTRL,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkb_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vpu_clkb_tmp.hw,
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_vpu_clkb = {
+ .data = &(struct clk_regmap_gate_data) {
+ .offset = VPU_CLKB_CTRL,
+ .bit_idx = 8,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vpu_clkb",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vpu_clkb_div.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_hdmi_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", }
+};
+
+static A9_COMP_SEL(hdmitx_sys, HDMI_CLK_CTRL, 9, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmitx_sys, HDMI_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(hdmitx_sys, HDMI_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(hdmitx_prif, HTX_CLK_CTRL, 9, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmitx_prif, HTX_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(hdmitx_prif, HTX_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(hdmitx_200m, HTX_CLK_CTRL, 25, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmitx_200m, HTX_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(hdmitx_200m, HTX_CLK_CTRL, 24, 0);
+
+static A9_COMP_SEL(hdmitx_aud, HTX_CLK_CTRL1, 9, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmitx_aud, HTX_CLK_CTRL1, 0, 7);
+static A9_COMP_GATE(hdmitx_aud, HTX_CLK_CTRL1, 8, 0);
+
+static A9_COMP_SEL(hdmirx_5m, HRX_CLK_CTRL, 9, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmirx_5m, HRX_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(hdmirx_5m, HRX_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(hdmirx_2m, HRX_CLK_CTRL, 25, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmirx_2m, HRX_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(hdmirx_2m, HRX_CLK_CTRL, 24, 0);
+
+static A9_COMP_SEL(hdmirx_cfg, HRX_CLK_CTRL1, 9, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmirx_cfg, HRX_CLK_CTRL1, 0, 7);
+static A9_COMP_GATE(hdmirx_cfg, HRX_CLK_CTRL1, 8, 0);
+
+static A9_COMP_SEL(hdmirx_hdcp2x, HRX_CLK_CTRL1, 25, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmirx_hdcp2x, HRX_CLK_CTRL1, 16, 7);
+static A9_COMP_GATE(hdmirx_hdcp2x, HRX_CLK_CTRL1, 24, 0);
+
+static A9_COMP_SEL(hdmirx_acr_ref, HRX_CLK_CTRL2, 25, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmirx_acr_ref, HRX_CLK_CTRL2, 16, 7);
+static A9_COMP_GATE(hdmirx_acr_ref, HRX_CLK_CTRL2, 24, 0);
+
+static A9_COMP_SEL(hdmirx_meter, HRX_CLK_CTRL3, 9, 0x7, a9_hdmi_parents, NULL);
+static A9_COMP_DIV(hdmirx_meter, HRX_CLK_CTRL3, 0, 7);
+static A9_COMP_GATE(hdmirx_meter, HRX_CLK_CTRL3, 8, 0);
+
+
+static struct clk_regmap a9_vid_pll_div = {
+ .data = &(struct meson_vid_pll_div_data){
+ .val = {
+ .reg_off = VID_PLL_CLK_DIV,
+ .shift = 0,
+ .width = 15,
+ },
+ .sel = {
+ .reg_off = VID_PLL_CLK_DIV,
+ .shift = 16,
+ .width = 2,
+ },
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vid_pll_div",
+ .ops = &meson_vid_pll_div_ro_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .fw_name = "hdmiout2", }
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_vid_pll_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VID_PLL_CLK_DIV,
+ .mask = 0x1,
+ .shift = 18,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vid_pll_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a9_vid_pll_div.hw },
+ { .fw_name = "hdmiout2", }
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_vid_pll = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_PLL_CLK_DIV,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vid_pll",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vid_pll_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_vid_pll_vclk = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HDMI_CLK_CTRL,
+ .mask = 0x1,
+ .shift = 15,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vid_pll_vclk",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a9_vid_pll.hw },
+ { .fw_name = "hdmipix", }
+ },
+ .num_parents = 2,
+ },
+};
+
+static const struct clk_parent_data a9_vclk_parents[] = {
+ { .hw = &a9_vid_pll_vclk.hw },
+ { .fw_name = "pix0", },
+ { .fw_name = "vid1", },
+ { .fw_name = "pix1", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "vid2", }
+};
+
+static struct clk_regmap a9_vclk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VID_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a9_vclk_parents,
+ .num_parents = ARRAY_SIZE(a9_vclk_parents),
+ },
+};
+
+static struct clk_regmap a9_vclk0_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_DIV,
+ .bit_idx = 16,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk0_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &a9_vclk0_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_vclk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = VID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk0_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vclk0_in.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_vclk0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk0",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &a9_vclk0_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+#define A9_VCLK_GATE(_name, _reg, _bit, _parent) \
+struct clk_regmap a9_##_name##_en = { \
+ .data = &(struct clk_regmap_gate_data) { \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name "_en", \
+ .ops = &clk_regmap_gate_ops, \
+ .parent_hws = (const struct clk_hw *[]) { \
+ &(_parent).hw \
+ }, \
+ .num_parents = 1, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+}
+
+#define A9_VCLK_DIV(_name, _div) \
+struct clk_fixed_factor a9_##_name = { \
+ .mult = 1, \
+ .div = (_div), \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &clk_fixed_factor_ops, \
+ .parent_hws = (const struct clk_hw *[]) { \
+ &a9_##_name##_en.hw \
+ }, \
+ .num_parents = 1, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+}
+
+static A9_VCLK_GATE(vclk0_div1, VID_CLK_CTRL, 0, a9_vclk0);
+static A9_VCLK_GATE(vclk0_div2, VID_CLK_CTRL, 1, a9_vclk0);
+static A9_VCLK_DIV(vclk0_div2, 2);
+static A9_VCLK_GATE(vclk0_div4, VID_CLK_CTRL, 2, a9_vclk0);
+static A9_VCLK_DIV(vclk0_div4, 4);
+static A9_VCLK_GATE(vclk0_div6, VID_CLK_CTRL, 3, a9_vclk0);
+static A9_VCLK_DIV(vclk0_div6, 6);
+static A9_VCLK_GATE(vclk0_div12, VID_CLK_CTRL, 4, a9_vclk0);
+static A9_VCLK_DIV(vclk0_div12, 12);
+
+static struct clk_regmap a9_vclk1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VIID_CLK_CTRL,
+ .mask = 0x7,
+ .shift = 16,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = a9_vclk_parents,
+ .num_parents = ARRAY_SIZE(a9_vclk_parents),
+ },
+};
+
+static struct clk_regmap a9_vclk1_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VIID_CLK_DIV,
+ .bit_idx = 16,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk1_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &a9_vclk1_sel.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_vclk1_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = VIID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vclk1_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vclk1_in.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_vclk1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VIID_CLK_CTRL,
+ .bit_idx = 19,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vclk1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) { &a9_vclk1_div.hw },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static A9_VCLK_GATE(vclk1_div1, VIID_CLK_CTRL, 0, a9_vclk1);
+static A9_VCLK_GATE(vclk1_div2, VIID_CLK_CTRL, 1, a9_vclk1);
+static A9_VCLK_DIV(vclk1_div2, 2);
+static A9_VCLK_GATE(vclk1_div4, VIID_CLK_CTRL, 2, a9_vclk1);
+static A9_VCLK_DIV(vclk1_div4, 4);
+static A9_VCLK_GATE(vclk1_div6, VIID_CLK_CTRL, 3, a9_vclk1);
+static A9_VCLK_DIV(vclk1_div6, 6);
+static A9_VCLK_GATE(vclk1_div12, VIID_CLK_CTRL, 4, a9_vclk1);
+static A9_VCLK_DIV(vclk1_div12, 12);
+
+/* Channel 5, 6 and 7 are unconnected */
+static u32 a9_vid_parents_val_table[] = { 0, 1, 2, 3, 4, 8, 9, 10, 11, 12 };
+static const struct clk_hw *a9_vid_parents[] = {
+ &a9_vclk0_div1_en.hw,
+ &a9_vclk0_div2.hw,
+ &a9_vclk0_div4.hw,
+ &a9_vclk0_div6.hw,
+ &a9_vclk0_div12.hw,
+ &a9_vclk1_div1_en.hw,
+ &a9_vclk1_div2.hw,
+ &a9_vclk1_div4.hw,
+ &a9_vclk1_div6.hw,
+ &a9_vclk1_div12.hw
+};
+
+static struct clk_regmap a9_encoder0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 12,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "encoder0_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_encoder0 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 10,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "encoder",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_encoder0_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_encoder1_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 8,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "encoder1_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_encoder1 = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 11,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "encorder1",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_encoder1_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_vid_lock_parents[] = {
+ { .fw_name = "xtal", },
+ { .hw = &a9_encoder0.hw },
+ { .hw = &a9_encoder1.hw }
+};
+
+static A9_COMP_SEL(vid_lock, VID_LOCK_CLK_CTRL, 9, 0x7, a9_vid_lock_parents, NULL);
+static A9_COMP_DIV(vid_lock, VID_LOCK_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(vid_lock, VID_LOCK_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_vdin_meas_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", }
+};
+
+static A9_COMP_SEL(vdin_meas, VDIN_MEAS_CLK_CTRL, 9, 0x7, a9_vdin_meas_parents, NULL);
+static A9_COMP_DIV(vdin_meas, VDIN_MEAS_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(vdin_meas, VDIN_MEAS_CLK_CTRL, 8, 0);
+
+static struct clk_regmap a9_vdac_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 28,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "vdac_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_vdac = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 4,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "vdac",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_vdac_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_hdmitx_pixel_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HDMI_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 16,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmitx_pixel_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_hdmitx_pixel = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 5,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmitx_pixel",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_hdmitx_pixel_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_hdmitx_fe_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HDMI_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 20,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmitx_fe_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_hdmitx_fe = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 9,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmitx_fe",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_hdmitx_fe_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_hdmitx1_pixel_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HDMI_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 24,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmitx1_pixel_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_hdmitx1_pixel = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 12,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmitx1_pixel",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_hdmitx_pixel_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_hdmitx1_fe_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HDMI_CLK_CTRL,
+ .mask = 0xf,
+ .shift = 28,
+ .table = a9_vid_parents_val_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "hdmitx1_fe_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = a9_vid_parents,
+ .num_parents = ARRAY_SIZE(a9_vid_parents),
+ },
+};
+
+static struct clk_regmap a9_hdmitx1_fe = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = VID_CLK_CTRL2,
+ .bit_idx = 13,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "hdmitx1_fe",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_hdmitx1_fe_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct clk_parent_data a9_csi_phy_parents[] = {
+ { .fw_name = "fdiv2p5", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "gp0", },
+ { .fw_name = "hifi0", },
+ { .fw_name = "fdiv2", },
+ { .fw_name = "xtal", }
+};
+
+static A9_COMP_SEL(csi_phy, MIPI_CSI_PHY_CLK_CTRL, 9, 0x7, a9_csi_phy_parents, NULL);
+static A9_COMP_DIV(csi_phy, MIPI_CSI_PHY_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(csi_phy, MIPI_CSI_PHY_CLK_CTRL, 8, 0);
+
+static const struct clk_parent_data a9_dsi_meas_parents[] = {
+ { .fw_name = "xtal", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", },
+ { .fw_name = "fdiv5", },
+ { .hw = &a9_vid_pll.hw },
+ { .fw_name = "gp0", },
+ { .fw_name = "vid1", },
+ { .fw_name = "vid2", }
+};
+
+static A9_COMP_SEL(dsi_meas, DSI_MEAS_CLK_CTRL, 9, 0x7, a9_dsi_meas_parents, NULL);
+static A9_COMP_DIV(dsi_meas, DSI_MEAS_CLK_CTRL, 0, 7);
+static A9_COMP_GATE(dsi_meas, DSI_MEAS_CLK_CTRL, 8, 0);
+
+static A9_COMP_SEL(dsi_b_meas, DSI_MEAS_CLK_CTRL, 25, 0x7, a9_dsi_meas_parents, NULL);
+static A9_COMP_DIV(dsi_b_meas, DSI_MEAS_CLK_CTRL, 16, 7);
+static A9_COMP_GATE(dsi_b_meas, DSI_MEAS_CLK_CTRL, 24, 0);
+
+static struct clk_hw *a9_peripherals_hw_clks[] = {
+ [CLKID_SYS_AM_AXI] = &a9_sys_am_axi.hw,
+ [CLKID_SYS_DOS] = &a9_sys_dos.hw,
+ [CLKID_SYS_MIPI_DSI] = &a9_sys_mipi_dsi.hw,
+ [CLKID_SYS_ETH_PHY] = &a9_sys_eth_phy.hw,
+ [CLKID_SYS_AMFC] = &a9_sys_amfc.hw,
+ [CLKID_SYS_MALI] = &a9_sys_mali.hw,
+ [CLKID_SYS_NNA] = &a9_sys_nna.hw,
+ [CLKID_SYS_ETH_AXI] = &a9_sys_eth_axi.hw,
+ [CLKID_SYS_DP_APB] = &a9_sys_dp_apb.hw,
+ [CLKID_SYS_EDPTX_APB] = &a9_sys_edptx_apb.hw,
+ [CLKID_SYS_U3HSG] = &a9_sys_u3hsg.hw,
+ [CLKID_SYS_AUCPU] = &a9_sys_aucpu.hw,
+ [CLKID_SYS_GLB] = &a9_sys_glb.hw,
+ [CLKID_SYS_COMBO_DPHY_APB] = &a9_sys_combo_dphy_apb.hw,
+ [CLKID_SYS_HDMIRX_APB] = &a9_sys_hdmirx_apb.hw,
+ [CLKID_SYS_HDMIRX_PCLK] = &a9_sys_hdmirx_pclk.hw,
+ [CLKID_SYS_MIPI_DSI_PHY] = &a9_sys_mipi_dsi_phy.hw,
+ [CLKID_SYS_CAN0] = &a9_sys_can0.hw,
+ [CLKID_SYS_CAN1] = &a9_sys_can1.hw,
+ [CLKID_SYS_SD_EMMC_A] = &a9_sys_sd_emmc_a.hw,
+ [CLKID_SYS_SD_EMMC_B] = &a9_sys_sd_emmc_b.hw,
+ [CLKID_SYS_SD_EMMC_C] = &a9_sys_sd_emmc_c.hw,
+ [CLKID_SYS_SC] = &a9_sys_sc.hw,
+ [CLKID_SYS_ACODEC] = &a9_sys_acodec.hw,
+ [CLKID_SYS_MIPI_ISP] = &a9_sys_mipi_isp.hw,
+ [CLKID_SYS_MSR] = &a9_sys_msr.hw,
+ [CLKID_SYS_AUDIO] = &a9_sys_audio.hw,
+ [CLKID_SYS_MIPI_DSI_B] = &a9_sys_mipi_dsi_b.hw,
+ [CLKID_SYS_MIPI_DSI1_PHY] = &a9_sys_mipi_dsi1_phy.hw,
+ [CLKID_SYS_ETH] = &a9_sys_eth.hw,
+ [CLKID_SYS_ETH_1G_MAC] = &a9_sys_eth_1g_mac.hw,
+ [CLKID_SYS_UART_A] = &a9_sys_uart_a.hw,
+ [CLKID_SYS_UART_F] = &a9_sys_uart_f.hw,
+ [CLKID_SYS_TS_A55] = &a9_sys_ts_a55.hw,
+ [CLKID_SYS_ETH_1G_AXI] = &a9_sys_eth_1g_axi.hw,
+ [CLKID_SYS_TS_DOS] = &a9_sys_ts_dos.hw,
+ [CLKID_SYS_U3DRD_B] = &a9_sys_u3drd_b.hw,
+ [CLKID_SYS_TS_CORE] = &a9_sys_ts_core.hw,
+ [CLKID_SYS_TS_PLL] = &a9_sys_ts_pll.hw,
+ [CLKID_SYS_CSI_DIG_CLKIN] = &a9_sys_csi_dig_clkin.hw,
+ [CLKID_SYS_CVE] = &a9_sys_cve.hw,
+ [CLKID_SYS_GE2D] = &a9_sys_ge2d.hw,
+ [CLKID_SYS_SPISG] = &a9_sys_spisg.hw,
+ [CLKID_SYS_U3DRD_1] = &a9_sys_u3drd_1.hw,
+ [CLKID_SYS_U2H] = &a9_sys_u2h.hw,
+ [CLKID_SYS_PCIE_MAC_A] = &a9_sys_pcie_mac_a.hw,
+ [CLKID_SYS_U3DRD_A] = &a9_sys_u3drd_a.hw,
+ [CLKID_SYS_U2DRD] = &a9_sys_u2drd.hw,
+ [CLKID_SYS_PCIE_PHY] = &a9_sys_pcie_phy.hw,
+ [CLKID_SYS_PCIE_MAC_B] = &a9_sys_pcie_mac_b.hw,
+ [CLKID_SYS_PERIPH] = &a9_sys_periph.hw,
+ [CLKID_SYS_PIO] = &a9_sys_pio.hw,
+ [CLKID_SYS_I3C] = &a9_sys_i3c.hw,
+ [CLKID_SYS_I2C_M_E] = &a9_sys_i2c_m_e.hw,
+ [CLKID_SYS_I2C_M_F] = &a9_sys_i2c_m_f.hw,
+ [CLKID_SYS_HDMITX_APB] = &a9_sys_hdmitx_apb.hw,
+ [CLKID_SYS_I2C_M_I] = &a9_sys_i2c_m_i.hw,
+ [CLKID_SYS_I2C_M_G] = &a9_sys_i2c_m_g.hw,
+ [CLKID_SYS_I2C_M_H] = &a9_sys_i2c_m_h.hw,
+ [CLKID_SYS_HDMI20_AES] = &a9_sys_hdmi20_aes.hw,
+ [CLKID_SYS_CSI2_HOST] = &a9_sys_csi2_host.hw,
+ [CLKID_SYS_CSI2_ADAPT] = &a9_sys_csi2_adapt.hw,
+ [CLKID_SYS_DSPA] = &a9_sys_dspa.hw,
+ [CLKID_SYS_PP_DMA] = &a9_sys_pp_dma.hw,
+ [CLKID_SYS_PP_WRAPPER] = &a9_sys_pp_wrapper.hw,
+ [CLKID_SYS_VPU_INTR] = &a9_sys_vpu_intr.hw,
+ [CLKID_SYS_CSI2_PHY] = &a9_sys_csi2_phy.hw,
+ [CLKID_SYS_SARADC] = &a9_sys_saradc.hw,
+ [CLKID_SYS_PWM_J] = &a9_sys_pwm_j.hw,
+ [CLKID_SYS_PWM_I] = &a9_sys_pwm_i.hw,
+ [CLKID_SYS_PWM_H] = &a9_sys_pwm_h.hw,
+ [CLKID_SYS_PWM_N] = &a9_sys_pwm_n.hw,
+ [CLKID_SYS_PWM_M] = &a9_sys_pwm_m.hw,
+ [CLKID_SYS_PWM_L] = &a9_sys_pwm_l.hw,
+ [CLKID_SYS_PWM_K] = &a9_sys_pwm_k.hw,
+ [CLKID_SD_EMMC_A_SEL] = &a9_sd_emmc_a_sel.hw,
+ [CLKID_SD_EMMC_A_DIV] = &a9_sd_emmc_a_div.hw,
+ [CLKID_SD_EMMC_A] = &a9_sd_emmc_a.hw,
+ [CLKID_SD_EMMC_B_SEL] = &a9_sd_emmc_b_sel.hw,
+ [CLKID_SD_EMMC_B_DIV] = &a9_sd_emmc_b_div.hw,
+ [CLKID_SD_EMMC_B] = &a9_sd_emmc_b.hw,
+ [CLKID_SD_EMMC_C_SEL] = &a9_sd_emmc_c_sel.hw,
+ [CLKID_SD_EMMC_C_DIV] = &a9_sd_emmc_c_div.hw,
+ [CLKID_SD_EMMC_C] = &a9_sd_emmc_c.hw,
+ [CLKID_PWM_H_SEL] = &a9_pwm_h_sel.hw,
+ [CLKID_PWM_H_DIV] = &a9_pwm_h_div.hw,
+ [CLKID_PWM_H] = &a9_pwm_h.hw,
+ [CLKID_PWM_I_SEL] = &a9_pwm_i_sel.hw,
+ [CLKID_PWM_I_DIV] = &a9_pwm_i_div.hw,
+ [CLKID_PWM_I] = &a9_pwm_i.hw,
+ [CLKID_PWM_J_SEL] = &a9_pwm_j_sel.hw,
+ [CLKID_PWM_J_DIV] = &a9_pwm_j_div.hw,
+ [CLKID_PWM_J] = &a9_pwm_j.hw,
+ [CLKID_PWM_K_SEL] = &a9_pwm_k_sel.hw,
+ [CLKID_PWM_K_DIV] = &a9_pwm_k_div.hw,
+ [CLKID_PWM_K] = &a9_pwm_k.hw,
+ [CLKID_PWM_L_SEL] = &a9_pwm_l_sel.hw,
+ [CLKID_PWM_L_DIV] = &a9_pwm_l_div.hw,
+ [CLKID_PWM_L] = &a9_pwm_l.hw,
+ [CLKID_PWM_M_SEL] = &a9_pwm_m_sel.hw,
+ [CLKID_PWM_M_DIV] = &a9_pwm_m_div.hw,
+ [CLKID_PWM_M] = &a9_pwm_m.hw,
+ [CLKID_PWM_N_SEL] = &a9_pwm_n_sel.hw,
+ [CLKID_PWM_N_DIV] = &a9_pwm_n_div.hw,
+ [CLKID_PWM_N] = &a9_pwm_n.hw,
+ [CLKID_SPISG0_SEL] = &a9_spisg0_sel.hw,
+ [CLKID_SPISG0_DIV] = &a9_spisg0_div.hw,
+ [CLKID_SPISG0] = &a9_spisg0.hw,
+ [CLKID_SPISG1_SEL] = &a9_spisg1_sel.hw,
+ [CLKID_SPISG1_DIV] = &a9_spisg1_div.hw,
+ [CLKID_SPISG1] = &a9_spisg1.hw,
+ [CLKID_SPISG2_SEL] = &a9_spisg2_sel.hw,
+ [CLKID_SPISG2_DIV] = &a9_spisg2_div.hw,
+ [CLKID_SPISG2] = &a9_spisg2.hw,
+ [CLKID_SARADC_SEL] = &a9_saradc_sel.hw,
+ [CLKID_SARADC_DIV] = &a9_saradc_div.hw,
+ [CLKID_SARADC] = &a9_saradc.hw,
+ [CLKID_AMFC_SEL] = &a9_amfc_sel.hw,
+ [CLKID_AMFC_DIV] = &a9_amfc_div.hw,
+ [CLKID_AMFC] = &a9_amfc.hw,
+ [CLKID_NNA_SEL] = &a9_nna_sel.hw,
+ [CLKID_NNA_DIV] = &a9_nna_div.hw,
+ [CLKID_NNA] = &a9_nna.hw,
+ [CLKID_USB_250M_SEL] = &a9_usb_250m_sel.hw,
+ [CLKID_USB_250M_DIV] = &a9_usb_250m_div.hw,
+ [CLKID_USB_250M] = &a9_usb_250m.hw,
+ [CLKID_USB_48M_PRE_SEL] = &a9_usb_48m_pre_sel.hw,
+ [CLKID_USB_48M_PRE_DIV] = &a9_usb_48m_pre_div.hw,
+ [CLKID_USB_48M_PRE] = &a9_usb_48m_pre.hw,
+ [CLKID_PCIE0_TL_SEL] = &a9_pcie0_tl_sel.hw,
+ [CLKID_PCIE0_TL_DIV] = &a9_pcie0_tl_div.hw,
+ [CLKID_PCIE0_TL] = &a9_pcie0_tl.hw,
+ [CLKID_PCIE1_TL_SEL] = &a9_pcie1_tl_sel.hw,
+ [CLKID_PCIE1_TL_DIV] = &a9_pcie1_tl_div.hw,
+ [CLKID_PCIE1_TL] = &a9_pcie1_tl.hw,
+ [CLKID_CMPR_SEL] = &a9_cmpr_sel.hw,
+ [CLKID_CMPR_DIV] = &a9_cmpr_div.hw,
+ [CLKID_CMPR] = &a9_cmpr.hw,
+ [CLKID_DEWARPA_SEL] = &a9_dewarpa_sel.hw,
+ [CLKID_DEWARPA_DIV] = &a9_dewarpa_div.hw,
+ [CLKID_DEWARPA] = &a9_dewarpa.hw,
+ [CLKID_SC_PRE_SEL] = &a9_sc_pre_sel.hw,
+ [CLKID_SC_PRE_DIV] = &a9_sc_pre_div.hw,
+ [CLKID_SC_PRE] = &a9_sc_pre.hw,
+ [CLKID_SC] = &a9_sc.hw,
+ [CLKID_DPTX_APB2_SEL] = &a9_dptx_apb2_sel.hw,
+ [CLKID_DPTX_APB2_DIV] = &a9_dptx_apb2_div.hw,
+ [CLKID_DPTX_APB2] = &a9_dptx_apb2.hw,
+ [CLKID_DPTX_AUD_SEL] = &a9_dptx_aud_sel.hw,
+ [CLKID_DPTX_AUD_DIV] = &a9_dptx_aud_div.hw,
+ [CLKID_DPTX_AUD] = &a9_dptx_aud.hw,
+ [CLKID_ISP_SEL] = &a9_isp_sel.hw,
+ [CLKID_ISP_DIV] = &a9_isp_div.hw,
+ [CLKID_ISP] = &a9_isp.hw,
+ [CLKID_CVE_SEL] = &a9_cve_sel.hw,
+ [CLKID_CVE_DIV] = &a9_cve_div.hw,
+ [CLKID_CVE] = &a9_cve.hw,
+ [CLKID_VGE_SEL] = &a9_vge_sel.hw,
+ [CLKID_VGE_DIV] = &a9_vge_div.hw,
+ [CLKID_VGE] = &a9_vge.hw,
+ [CLKID_PP_SEL] = &a9_pp_sel.hw,
+ [CLKID_PP_DIV] = &a9_pp_div.hw,
+ [CLKID_PP] = &a9_pp.hw,
+ [CLKID_GLB_SEL] = &a9_glb_sel.hw,
+ [CLKID_GLB_DIV] = &a9_glb_div.hw,
+ [CLKID_GLB] = &a9_glb.hw,
+ [CLKID_USB_48M_DUALDIV_IN] = &a9_usb_48m_dualdiv_in.hw,
+ [CLKID_USB_48M_DUALDIV_DIV] = &a9_usb_48m_dualdiv_div.hw,
+ [CLKID_USB_48M_DUALDIV_SEL] = &a9_usb_48m_dualdiv_sel.hw,
+ [CLKID_USB_48M_DUALDIV] = &a9_usb_48m_dualdiv.hw,
+ [CLKID_USB_48M] = &a9_usb_48m.hw,
+ [CLKID_CAN0_PE_SEL] = &a9_can0_pe_sel.hw,
+ [CLKID_CAN0_PE_DIV] = &a9_can0_pe_div.hw,
+ [CLKID_CAN0_PE] = &a9_can0_pe.hw,
+ [CLKID_CAN1_PE_SEL] = &a9_can1_pe_sel.hw,
+ [CLKID_CAN1_PE_DIV] = &a9_can1_pe_div.hw,
+ [CLKID_CAN1_PE] = &a9_can1_pe.hw,
+ [CLKID_CAN0_FILTER_SEL] = &a9_can0_filter_sel.hw,
+ [CLKID_CAN0_FILTER_DIV] = &a9_can0_filter_div.hw,
+ [CLKID_CAN0_FILTER] = &a9_can0_filter.hw,
+ [CLKID_CAN1_FILTER_SEL] = &a9_can1_filter_sel.hw,
+ [CLKID_CAN1_FILTER_DIV] = &a9_can1_filter_div.hw,
+ [CLKID_CAN1_FILTER] = &a9_can1_filter.hw,
+ [CLKID_I3C_SEL] = &a9_i3c_sel.hw,
+ [CLKID_I3C_DIV] = &a9_i3c_div.hw,
+ [CLKID_I3C] = &a9_i3c.hw,
+ [CLKID_TS_DIV] = &a9_ts_div.hw,
+ [CLKID_TS] = &a9_ts.hw,
+ [CLKID_ETH_125M_DIV] = &a9_eth_125m_div.hw,
+ [CLKID_ETH_125M] = &a9_eth_125m.hw,
+ [CLKID_ETH_RMII_SEL] = &a9_eth_rmii_sel.hw,
+ [CLKID_ETH_RMII_DIV] = &a9_eth_rmii_div.hw,
+ [CLKID_ETH_RMII] = &a9_eth_rmii.hw,
+ [CLKID_GEN_SEL] = &a9_gen_sel.hw,
+ [CLKID_GEN_DIV] = &a9_gen_div.hw,
+ [CLKID_GEN] = &a9_gen.hw,
+ [CLKID_CLK24M_IN] = &a9_24m_in.hw,
+ [CLKID_CLK12_24M] = &a9_12_24m.hw,
+ [CLKID_MALI_0_SEL] = &a9_mali_0_sel.hw,
+ [CLKID_MALI_0_DIV] = &a9_mali_0_div.hw,
+ [CLKID_MALI_0] = &a9_mali_0.hw,
+ [CLKID_MALI_1_SEL] = &a9_mali_1_sel.hw,
+ [CLKID_MALI_1_DIV] = &a9_mali_1_div.hw,
+ [CLKID_MALI_1] = &a9_mali_1.hw,
+ [CLKID_MALI] = &a9_mali.hw,
+ [CLKID_MALI_STACK_0_SEL] = &a9_mali_stack_0_sel.hw,
+ [CLKID_MALI_STACK_0_DIV] = &a9_mali_stack_0_div.hw,
+ [CLKID_MALI_STACK_0] = &a9_mali_stack_0.hw,
+ [CLKID_MALI_STACK_1_SEL] = &a9_mali_stack_1_sel.hw,
+ [CLKID_MALI_STACK_1_DIV] = &a9_mali_stack_1_div.hw,
+ [CLKID_MALI_STACK_1] = &a9_mali_stack_1.hw,
+ [CLKID_MALI_STACK] = &a9_mali_stack.hw,
+ [CLKID_DSPA_0_SEL] = &a9_dspa_0_sel.hw,
+ [CLKID_DSPA_0_DIV] = &a9_dspa_0_div.hw,
+ [CLKID_DSPA_0] = &a9_dspa_0.hw,
+ [CLKID_DSPA_1_SEL] = &a9_dspa_1_sel.hw,
+ [CLKID_DSPA_1_DIV] = &a9_dspa_1_div.hw,
+ [CLKID_DSPA_1] = &a9_dspa_1.hw,
+ [CLKID_DSPA] = &a9_dspa.hw,
+ [CLKID_HEVCF_0_SEL] = &a9_hevcf_0_sel.hw,
+ [CLKID_HEVCF_0_DIV] = &a9_hevcf_0_div.hw,
+ [CLKID_HEVCF_0] = &a9_hevcf_0.hw,
+ [CLKID_HEVCF_1_SEL] = &a9_hevcf_1_sel.hw,
+ [CLKID_HEVCF_1_DIV] = &a9_hevcf_1_div.hw,
+ [CLKID_HEVCF_1] = &a9_hevcf_1.hw,
+ [CLKID_HEVCF] = &a9_hevcf.hw,
+ [CLKID_HCODEC_0_SEL] = &a9_hcodec_0_sel.hw,
+ [CLKID_HCODEC_0_DIV] = &a9_hcodec_0_div.hw,
+ [CLKID_HCODEC_0] = &a9_hcodec_0.hw,
+ [CLKID_HCODEC_1_SEL] = &a9_hcodec_1_sel.hw,
+ [CLKID_HCODEC_1_DIV] = &a9_hcodec_1_div.hw,
+ [CLKID_HCODEC_1] = &a9_hcodec_1.hw,
+ [CLKID_HCODEC] = &a9_hcodec.hw,
+ [CLKID_VPU_0_SEL] = &a9_vpu_0_sel.hw,
+ [CLKID_VPU_0_DIV] = &a9_vpu_0_div.hw,
+ [CLKID_VPU_0] = &a9_vpu_0.hw,
+ [CLKID_VPU_1_SEL] = &a9_vpu_1_sel.hw,
+ [CLKID_VPU_1_DIV] = &a9_vpu_1_div.hw,
+ [CLKID_VPU_1] = &a9_vpu_1.hw,
+ [CLKID_VPU] = &a9_vpu.hw,
+ [CLKID_VAPB_0_SEL] = &a9_vapb_0_sel.hw,
+ [CLKID_VAPB_0_DIV] = &a9_vapb_0_div.hw,
+ [CLKID_VAPB_0] = &a9_vapb_0.hw,
+ [CLKID_VAPB_1_SEL] = &a9_vapb_1_sel.hw,
+ [CLKID_VAPB_1_DIV] = &a9_vapb_1_div.hw,
+ [CLKID_VAPB_1] = &a9_vapb_1.hw,
+ [CLKID_VAPB] = &a9_vapb.hw,
+ [CLKID_GE2D] = &a9_ge2d.hw,
+ [CLKID_VPU_CLKB_TMP_SEL] = &a9_vpu_clkb_tmp_sel.hw,
+ [CLKID_VPU_CLKB_TMP_DIV] = &a9_vpu_clkb_tmp_div.hw,
+ [CLKID_VPU_CLKB_TMP] = &a9_vpu_clkb_tmp.hw,
+ [CLKID_VPU_CLKB_DIV] = &a9_vpu_clkb_div.hw,
+ [CLKID_VPU_CLKB] = &a9_vpu_clkb.hw,
+ [CLKID_HDMITX_SYS_SEL] = &a9_hdmitx_sys_sel.hw,
+ [CLKID_HDMITX_SYS_DIV] = &a9_hdmitx_sys_div.hw,
+ [CLKID_HDMITX_SYS] = &a9_hdmitx_sys.hw,
+ [CLKID_HDMITX_PRIF_SEL] = &a9_hdmitx_prif_sel.hw,
+ [CLKID_HDMITX_PRIF_DIV] = &a9_hdmitx_prif_div.hw,
+ [CLKID_HDMITX_PRIF] = &a9_hdmitx_prif.hw,
+ [CLKID_HDMITX_200M_SEL] = &a9_hdmitx_200m_sel.hw,
+ [CLKID_HDMITX_200M_DIV] = &a9_hdmitx_200m_div.hw,
+ [CLKID_HDMITX_200M] = &a9_hdmitx_200m.hw,
+ [CLKID_HDMITX_AUD_SEL] = &a9_hdmitx_aud_sel.hw,
+ [CLKID_HDMITX_AUD_DIV] = &a9_hdmitx_aud_div.hw,
+ [CLKID_HDMITX_AUD] = &a9_hdmitx_aud.hw,
+ [CLKID_HDMIRX_5M_SEL] = &a9_hdmirx_5m_sel.hw,
+ [CLKID_HDMIRX_5M_DIV] = &a9_hdmirx_5m_div.hw,
+ [CLKID_HDMIRX_5M] = &a9_hdmirx_5m.hw,
+ [CLKID_HDMIRX_2M_SEL] = &a9_hdmirx_2m_sel.hw,
+ [CLKID_HDMIRX_2M_DIV] = &a9_hdmirx_2m_div.hw,
+ [CLKID_HDMIRX_2M] = &a9_hdmirx_2m.hw,
+ [CLKID_HDMIRX_CFG_SEL] = &a9_hdmirx_cfg_sel.hw,
+ [CLKID_HDMIRX_CFG_DIV] = &a9_hdmirx_cfg_div.hw,
+ [CLKID_HDMIRX_CFG] = &a9_hdmirx_cfg.hw,
+ [CLKID_HDMIRX_HDCP2X_SEL] = &a9_hdmirx_hdcp2x_sel.hw,
+ [CLKID_HDMIRX_HDCP2X_DIV] = &a9_hdmirx_hdcp2x_div.hw,
+ [CLKID_HDMIRX_HDCP2X] = &a9_hdmirx_hdcp2x.hw,
+ [CLKID_HDMIRX_ACR_REF_SEL] = &a9_hdmirx_acr_ref_sel.hw,
+ [CLKID_HDMIRX_ACR_REF_DIV] = &a9_hdmirx_acr_ref_div.hw,
+ [CLKID_HDMIRX_ACR_REF] = &a9_hdmirx_acr_ref.hw,
+ [CLKID_HDMIRX_METER_SEL] = &a9_hdmirx_meter_sel.hw,
+ [CLKID_HDMIRX_METER_DIV] = &a9_hdmirx_meter_div.hw,
+ [CLKID_HDMIRX_METER] = &a9_hdmirx_meter.hw,
+ [CLKID_VID_LOCK_SEL] = &a9_vid_lock_sel.hw,
+ [CLKID_VID_LOCK_DIV] = &a9_vid_lock_div.hw,
+ [CLKID_VID_LOCK] = &a9_vid_lock.hw,
+ [CLKID_VDIN_MEAS_SEL] = &a9_vdin_meas_sel.hw,
+ [CLKID_VDIN_MEAS_DIV] = &a9_vdin_meas_div.hw,
+ [CLKID_VDIN_MEAS] = &a9_vdin_meas.hw,
+ [CLKID_VID_PLL_DIV] = &a9_vid_pll_div.hw,
+ [CLKID_VID_PLL_SEL] = &a9_vid_pll_sel.hw,
+ [CLKID_VID_PLL] = &a9_vid_pll.hw,
+ [CLKID_VID_PLL_VCLK] = &a9_vid_pll_vclk.hw,
+ [CLKID_VCLK0_SEL] = &a9_vclk0_sel.hw,
+ [CLKID_VCLK0_IN] = &a9_vclk0_in.hw,
+ [CLKID_VCLK0_DIV] = &a9_vclk0_div.hw,
+ [CLKID_VCLK0] = &a9_vclk0.hw,
+ [CLKID_VCLK0_DIV1_EN] = &a9_vclk0_div1_en.hw,
+ [CLKID_VCLK0_DIV2_EN] = &a9_vclk0_div2_en.hw,
+ [CLKID_VCLK0_DIV2] = &a9_vclk0_div2.hw,
+ [CLKID_VCLK0_DIV4_EN] = &a9_vclk0_div4_en.hw,
+ [CLKID_VCLK0_DIV4] = &a9_vclk0_div4.hw,
+ [CLKID_VCLK0_DIV6_EN] = &a9_vclk0_div6_en.hw,
+ [CLKID_VCLK0_DIV6] = &a9_vclk0_div6.hw,
+ [CLKID_VCLK0_DIV12_EN] = &a9_vclk0_div12_en.hw,
+ [CLKID_VCLK0_DIV12] = &a9_vclk0_div12.hw,
+ [CLKID_VCLK1_SEL] = &a9_vclk1_sel.hw,
+ [CLKID_VCLK1_IN] = &a9_vclk1_in.hw,
+ [CLKID_VCLK1_DIV] = &a9_vclk1_div.hw,
+ [CLKID_VCLK1] = &a9_vclk1.hw,
+ [CLKID_VCLK1_DIV1_EN] = &a9_vclk1_div1_en.hw,
+ [CLKID_VCLK1_DIV2_EN] = &a9_vclk1_div2_en.hw,
+ [CLKID_VCLK1_DIV2] = &a9_vclk1_div2.hw,
+ [CLKID_VCLK1_DIV4_EN] = &a9_vclk1_div4_en.hw,
+ [CLKID_VCLK1_DIV4] = &a9_vclk1_div4.hw,
+ [CLKID_VCLK1_DIV6_EN] = &a9_vclk1_div6_en.hw,
+ [CLKID_VCLK1_DIV6] = &a9_vclk1_div6.hw,
+ [CLKID_VCLK1_DIV12_EN] = &a9_vclk1_div12_en.hw,
+ [CLKID_VCLK1_DIV12] = &a9_vclk1_div12.hw,
+ [CLKID_VDAC_SEL] = &a9_vdac_sel.hw,
+ [CLKID_VDAC] = &a9_vdac.hw,
+ [CLKID_ENCODER0_SEL] = &a9_encoder0_sel.hw,
+ [CLKID_ENCODER0] = &a9_encoder0.hw,
+ [CLKID_ENCODER1_SEL] = &a9_encoder1_sel.hw,
+ [CLKID_ENCODER1] = &a9_encoder1.hw,
+ [CLKID_HDMITX_PIXEL_SEL] = &a9_hdmitx_pixel_sel.hw,
+ [CLKID_HDMITX_PIXEL] = &a9_hdmitx_pixel.hw,
+ [CLKID_HDMITX_FE_SEL] = &a9_hdmitx_fe_sel.hw,
+ [CLKID_HDMITX_FE] = &a9_hdmitx_fe.hw,
+ [CLKID_HDMITX1_PIXEL_SEL] = &a9_hdmitx1_pixel_sel.hw,
+ [CLKID_HDMITX1_PIXEL] = &a9_hdmitx1_pixel.hw,
+ [CLKID_HDMITX1_FE_SEL] = &a9_hdmitx1_fe_sel.hw,
+ [CLKID_HDMITX1_FE] = &a9_hdmitx1_fe.hw,
+ [CLKID_CSI_PHY_SEL] = &a9_csi_phy_sel.hw,
+ [CLKID_CSI_PHY_DIV] = &a9_csi_phy_div.hw,
+ [CLKID_CSI_PHY] = &a9_csi_phy.hw,
+ [CLKID_DSI_MEAS_SEL] = &a9_dsi_meas_sel.hw,
+ [CLKID_DSI_MEAS_DIV] = &a9_dsi_meas_div.hw,
+ [CLKID_DSI_MEAS] = &a9_dsi_meas.hw,
+ [CLKID_DSI_B_MEAS_SEL] = &a9_dsi_b_meas_sel.hw,
+ [CLKID_DSI_B_MEAS_DIV] = &a9_dsi_b_meas_div.hw,
+ [CLKID_DSI_B_MEAS] = &a9_dsi_b_meas.hw,
+};
+
+static const struct meson_clkc_data a9_peripherals_clkc_data = {
+ .hw_clks = {
+ .hws = a9_peripherals_hw_clks,
+ .num = ARRAY_SIZE(a9_peripherals_hw_clks),
+ },
+};
+
+static const struct of_device_id a9_peripherals_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,a9-peripherals-clkc",
+ .data = &a9_peripherals_clkc_data,
+ },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, a9_peripherals_clkc_match_table);
+
+static struct platform_driver a9_peripherals_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "a9-peripherals-clkc",
+ .of_match_table = a9_peripherals_clkc_match_table,
+ },
+};
+module_platform_driver(a9_peripherals_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic A9 Peripherals Clock Controller driver");
+MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
--
2.47.1
^ permalink raw reply related
* [PATCH v4 0/2] clk: amlogic: Add A9 peripherals clock controller
From: Jian Hu via B4 Relay @ 2026-06-18 9:51 UTC (permalink / raw)
To: Neil Armstrong, Jerome Brunet, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao,
Kevin Hilman, Martin Blumenstingl
Cc: linux-amlogic, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Jian Hu, Conor Dooley
This series adds Amlogic A9 peripherals clock support,
including dt-binding and peripherals clock driver.
Note that this driver depends on the A9 PLL driver, which is not yet
upstream. The Meson PLL framework is currently undergoing refactoring,
and the A9 PLL driver will be submitted as a follow-up series after
that work has been completed.
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
Changes in v4:
- Expose channel 3 for gen_clk.
- Add suffix 0 for some clocks.
- Use helper macro for vclk clocks definitions.
- Drop forward declaration.
- Drop CLK_HW_INIT* and revert to explicit clock declarations.
- Fix can clock channel number comment.
- Rename enc clocks to encoder0/encoder1.
- Link to v3: https://lore.kernel.org/r/20260610-a9_peripherals-v3-0-d07a78085f71@amlogic.com
Changes in v3:
- Add "depend on A9 pll" instructions in the cover-letter.
- Move COMPILE_TEST after 'depends on ARM64' reported by sashiko-bot.
- Fix usb_48m_pre_sel's parent reported by sashiko-bot.
- Fix gen_div width reported by sashiko-bot.
- Fix hdmitx1_pixel's parent reported by sashiko-bot.
- Link to v2: https://lore.kernel.org/r/20260603-a9_peripherals-v2-0-ee1b8c0a1e6c@amlogic.com
Changes in v2:
- Split the A9 clock driver and send the peripherals clock separately.
- Add COMPILE_TEST in Kconfig.
- Drop the 'optional'.
- Rename apb4 to soc.
- Sort the header file.
- Rename hifi to hifi0.
- Use CLK_HW_INIT_PARENTS_DATA to describe clk_init_data.
- Use CLK_HW_INIT_HW to describe clk_init_data.
- Use CLK_HW_INIT_PARENTS_HW to describe clk_init_data.
- Link to v1: https://lore.kernel.org/all/20260511-b4-a9_clk-v1-0-41cb4071b7c9@amlogic.com/
---
Jian Hu (2):
dt-bindings: clock: Add Amlogic A9 peripherals clock controller
clk: amlogic: Add A9 peripherals clock controller driver
.../clock/amlogic,a9-peripherals-clkc.yaml | 164 ++
drivers/clk/meson/Kconfig | 15 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/a9-peripherals.c | 2096 ++++++++++++++++++++
.../clock/amlogic,a9-peripherals-clkc.h | 352 ++++
5 files changed, 2628 insertions(+)
---
base-commit: ca89c88bcf69daca829044c638a8163d5ce47af0
change-id: 20260603-a9_peripherals-4214e79705dc
Best regards,
--
Jian Hu <jian.hu@amlogic.com>
^ permalink raw reply
* [PATCH v4 1/2] dt-bindings: clock: Add Amlogic A9 peripherals clock controller
From: Jian Hu via B4 Relay @ 2026-06-18 9:51 UTC (permalink / raw)
To: Neil Armstrong, Jerome Brunet, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao,
Kevin Hilman, Martin Blumenstingl
Cc: linux-amlogic, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Jian Hu, Conor Dooley
In-Reply-To: <20260618-a9_peripherals-v4-0-fe120de44e77@amlogic.com>
From: Jian Hu <jian.hu@amlogic.com>
Add the peripherals clock controller dt-bindings for the Amlogic A9
SoC family.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
.../clock/amlogic,a9-peripherals-clkc.yaml | 164 ++++++++++
.../clock/amlogic,a9-peripherals-clkc.h | 352 +++++++++++++++++++++
2 files changed, 516 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/amlogic,a9-peripherals-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,a9-peripherals-clkc.yaml
new file mode 100644
index 000000000000..f0aef005ecf3
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/amlogic,a9-peripherals-clkc.yaml
@@ -0,0 +1,164 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2026 Amlogic, Inc. All rights reserved
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/amlogic,a9-peripherals-clkc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic A9 Series Peripherals Clock Controller
+
+maintainers:
+ - Neil Armstrong <neil.armstrong@linaro.org>
+ - Jerome Brunet <jbrunet@baylibre.com>
+ - Jian Hu <jian.hu@amlogic.com>
+ - Xianwei Zhao <xianwei.zhao@amlogic.com>
+
+properties:
+ compatible:
+ const: amlogic,a9-peripherals-clkc
+
+ reg:
+ maxItems: 1
+
+ '#clock-cells':
+ const: 1
+
+ clocks:
+ minItems: 27
+ items:
+ - description: input oscillator
+ - description: input fclk div 2
+ - description: input fclk div 3
+ - description: input fclk div 4
+ - description: input fclk div 5
+ - description: input fclk div 7
+ - description: input fclk div 2p5
+ - description: input sys clk
+ - description: input gp1 pll
+ - description: input gp2 pll
+ - description: input sys pll div 16
+ - description: input cpu clk div 16
+ - description: input a78 clk div 16
+ - description: input dsu clk div 16
+ - description: input rtc clk
+ - description: input gp0 pll
+ - description: input hifi0 pll
+ - description: input hifi1 pll
+ - description: input mclk0 pll
+ - description: input mclk1 pll
+ - description: input video1 pll
+ - description: input video2 pll
+ - description: input hdmi out2 clk
+ - description: input hdmi pixel clk
+ - description: input pixel0 pll
+ - description: input pixel1 pll
+ - description: input ddr pll test clk
+ - description: external input rmii oscillator (optional)
+
+ clock-names:
+ minItems: 27
+ items:
+ - const: xtal
+ - const: fdiv2
+ - const: fdiv3
+ - const: fdiv4
+ - const: fdiv5
+ - const: fdiv7
+ - const: fdiv2p5
+ - const: sys
+ - const: gp1
+ - const: gp2
+ - const: sysplldiv16
+ - const: cpudiv16
+ - const: a78div16
+ - const: dsudiv16
+ - const: rtc
+ - const: gp0
+ - const: hifi0
+ - const: hifi1
+ - const: mclk0
+ - const: mclk1
+ - const: vid1
+ - const: vid2
+ - const: hdmiout2
+ - const: hdmipix
+ - const: pix0
+ - const: pix1
+ - const: ddr_test
+ - const: ext_rmii
+
+required:
+ - compatible
+ - reg
+ - '#clock-cells'
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ clock-controller@200 {
+ compatible = "amlogic,a9-peripherals-clkc";
+ reg = <0x0 0x200 0x0 0x2f8>;
+ #clock-cells = <1>;
+ clocks = <&xtal>,
+ <&scmi_clk 10>,
+ <&scmi_clk 12>,
+ <&scmi_clk 14>,
+ <&scmi_clk 16>,
+ <&scmi_clk 18>,
+ <&scmi_clk 20>,
+ <&scmi_clk 21>,
+ <&scmi_clk 33>,
+ <&scmi_clk 34>,
+ <&scmi_clk 35>,
+ <&scmi_clk 36>,
+ <&scmi_clk 37>,
+ <&scmi_clk 38>,
+ <&scmi_clk 40>,
+ <&gp0 3>,
+ <&hifi0 3>,
+ <&hifi1 3>,
+ <&mclk0 3>,
+ <&mclk1 3>,
+ <&vid1>,
+ <&vid2>,
+ <&hdmitx 10>,
+ <&hdmitx 11>,
+ <&pix0>,
+ <&pix1>,
+ <&ddr 3>;
+ clock-names = "xtal",
+ "fdiv2",
+ "fdiv3",
+ "fdiv4",
+ "fdiv5",
+ "fdiv7",
+ "fdiv2p5",
+ "sys",
+ "gp1",
+ "gp2",
+ "sysplldiv16",
+ "cpudiv16",
+ "a78div16",
+ "dsudiv16",
+ "rtc",
+ "gp0",
+ "hifi0",
+ "hifi1",
+ "mclk0",
+ "mclk1",
+ "vid1",
+ "vid2",
+ "hdmiout2",
+ "hdmipix",
+ "pix0",
+ "pix1",
+ "ddr_test";
+ };
+ };
diff --git a/include/dt-bindings/clock/amlogic,a9-peripherals-clkc.h b/include/dt-bindings/clock/amlogic,a9-peripherals-clkc.h
new file mode 100644
index 000000000000..50b8f96b435e
--- /dev/null
+++ b/include/dt-bindings/clock/amlogic,a9-peripherals-clkc.h
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) 2026 Amlogic, Inc. All rights reserved.
+ */
+
+#ifndef __AMLOGIC_A9_PERIPHERALS_CLKC_H
+#define __AMLOGIC_A9_PERIPHERALS_CLKC_H
+
+#define CLKID_SYS_AM_AXI 0
+#define CLKID_SYS_DOS 1
+#define CLKID_SYS_MIPI_DSI 2
+#define CLKID_SYS_ETH_PHY 3
+#define CLKID_SYS_AMFC 4
+#define CLKID_SYS_MALI 5
+#define CLKID_SYS_NNA 6
+#define CLKID_SYS_ETH_AXI 7
+#define CLKID_SYS_DP_APB 8
+#define CLKID_SYS_EDPTX_APB 9
+#define CLKID_SYS_U3HSG 10
+#define CLKID_SYS_AUCPU 11
+#define CLKID_SYS_GLB 12
+#define CLKID_SYS_COMBO_DPHY_APB 13
+#define CLKID_SYS_HDMIRX_APB 14
+#define CLKID_SYS_HDMIRX_PCLK 15
+#define CLKID_SYS_MIPI_DSI_PHY 16
+#define CLKID_SYS_CAN0 17
+#define CLKID_SYS_CAN1 18
+#define CLKID_SYS_SD_EMMC_A 19
+#define CLKID_SYS_SD_EMMC_B 20
+#define CLKID_SYS_SD_EMMC_C 21
+#define CLKID_SYS_SC 22
+#define CLKID_SYS_ACODEC 23
+#define CLKID_SYS_MIPI_ISP 24
+#define CLKID_SYS_MSR 25
+#define CLKID_SYS_AUDIO 26
+#define CLKID_SYS_MIPI_DSI_B 27
+#define CLKID_SYS_MIPI_DSI1_PHY 28
+#define CLKID_SYS_ETH 29
+#define CLKID_SYS_ETH_1G_MAC 30
+#define CLKID_SYS_UART_A 31
+#define CLKID_SYS_UART_F 32
+#define CLKID_SYS_TS_A55 33
+#define CLKID_SYS_ETH_1G_AXI 34
+#define CLKID_SYS_TS_DOS 35
+#define CLKID_SYS_U3DRD_B 36
+#define CLKID_SYS_TS_CORE 37
+#define CLKID_SYS_TS_PLL 38
+#define CLKID_SYS_CSI_DIG_CLKIN 39
+#define CLKID_SYS_CVE 40
+#define CLKID_SYS_GE2D 41
+#define CLKID_SYS_SPISG 42
+#define CLKID_SYS_U3DRD_1 43
+#define CLKID_SYS_U2H 44
+#define CLKID_SYS_PCIE_MAC_A 45
+#define CLKID_SYS_U3DRD_A 46
+#define CLKID_SYS_U2DRD 47
+#define CLKID_SYS_PCIE_PHY 48
+#define CLKID_SYS_PCIE_MAC_B 49
+#define CLKID_SYS_PERIPH 50
+#define CLKID_SYS_PIO 51
+#define CLKID_SYS_I3C 52
+#define CLKID_SYS_I2C_M_E 53
+#define CLKID_SYS_I2C_M_F 54
+#define CLKID_SYS_HDMITX_APB 55
+#define CLKID_SYS_I2C_M_I 56
+#define CLKID_SYS_I2C_M_G 57
+#define CLKID_SYS_I2C_M_H 58
+#define CLKID_SYS_HDMI20_AES 59
+#define CLKID_SYS_CSI2_HOST 60
+#define CLKID_SYS_CSI2_ADAPT 61
+#define CLKID_SYS_DSPA 62
+#define CLKID_SYS_PP_DMA 63
+#define CLKID_SYS_PP_WRAPPER 64
+#define CLKID_SYS_VPU_INTR 65
+#define CLKID_SYS_CSI2_PHY 66
+#define CLKID_SYS_SARADC 67
+#define CLKID_SYS_PWM_J 68
+#define CLKID_SYS_PWM_I 69
+#define CLKID_SYS_PWM_H 70
+#define CLKID_SYS_PWM_N 71
+#define CLKID_SYS_PWM_M 72
+#define CLKID_SYS_PWM_L 73
+#define CLKID_SYS_PWM_K 74
+#define CLKID_SD_EMMC_A_SEL 75
+#define CLKID_SD_EMMC_A_DIV 76
+#define CLKID_SD_EMMC_A 77
+#define CLKID_SD_EMMC_B_SEL 78
+#define CLKID_SD_EMMC_B_DIV 79
+#define CLKID_SD_EMMC_B 80
+#define CLKID_SD_EMMC_C_SEL 81
+#define CLKID_SD_EMMC_C_DIV 82
+#define CLKID_SD_EMMC_C 83
+#define CLKID_PWM_H_SEL 84
+#define CLKID_PWM_H_DIV 85
+#define CLKID_PWM_H 86
+#define CLKID_PWM_I_SEL 87
+#define CLKID_PWM_I_DIV 88
+#define CLKID_PWM_I 89
+#define CLKID_PWM_J_SEL 90
+#define CLKID_PWM_J_DIV 91
+#define CLKID_PWM_J 92
+#define CLKID_PWM_K_SEL 93
+#define CLKID_PWM_K_DIV 94
+#define CLKID_PWM_K 95
+#define CLKID_PWM_L_SEL 96
+#define CLKID_PWM_L_DIV 97
+#define CLKID_PWM_L 98
+#define CLKID_PWM_M_SEL 99
+#define CLKID_PWM_M_DIV 100
+#define CLKID_PWM_M 101
+#define CLKID_PWM_N_SEL 102
+#define CLKID_PWM_N_DIV 103
+#define CLKID_PWM_N 104
+#define CLKID_SPISG0_SEL 105
+#define CLKID_SPISG0_DIV 106
+#define CLKID_SPISG0 107
+#define CLKID_SPISG1_SEL 108
+#define CLKID_SPISG1_DIV 109
+#define CLKID_SPISG1 110
+#define CLKID_SPISG2_SEL 111
+#define CLKID_SPISG2_DIV 112
+#define CLKID_SPISG2 113
+#define CLKID_SARADC_SEL 114
+#define CLKID_SARADC_DIV 115
+#define CLKID_SARADC 116
+#define CLKID_AMFC_SEL 117
+#define CLKID_AMFC_DIV 118
+#define CLKID_AMFC 119
+#define CLKID_NNA_SEL 120
+#define CLKID_NNA_DIV 121
+#define CLKID_NNA 122
+#define CLKID_USB_250M_SEL 123
+#define CLKID_USB_250M_DIV 124
+#define CLKID_USB_250M 125
+#define CLKID_USB_48M_PRE_SEL 126
+#define CLKID_USB_48M_PRE_DIV 127
+#define CLKID_USB_48M_PRE 128
+#define CLKID_PCIE0_TL_SEL 129
+#define CLKID_PCIE0_TL_DIV 130
+#define CLKID_PCIE0_TL 131
+#define CLKID_PCIE1_TL_SEL 132
+#define CLKID_PCIE1_TL_DIV 133
+#define CLKID_PCIE1_TL 134
+#define CLKID_CMPR_SEL 135
+#define CLKID_CMPR_DIV 136
+#define CLKID_CMPR 137
+#define CLKID_DEWARPA_SEL 138
+#define CLKID_DEWARPA_DIV 139
+#define CLKID_DEWARPA 140
+#define CLKID_SC_PRE_SEL 141
+#define CLKID_SC_PRE_DIV 142
+#define CLKID_SC_PRE 143
+#define CLKID_SC 144
+#define CLKID_DPTX_APB2_SEL 145
+#define CLKID_DPTX_APB2_DIV 146
+#define CLKID_DPTX_APB2 147
+#define CLKID_DPTX_AUD_SEL 148
+#define CLKID_DPTX_AUD_DIV 149
+#define CLKID_DPTX_AUD 150
+#define CLKID_ISP_SEL 151
+#define CLKID_ISP_DIV 152
+#define CLKID_ISP 153
+#define CLKID_CVE_SEL 154
+#define CLKID_CVE_DIV 155
+#define CLKID_CVE 156
+#define CLKID_VGE_SEL 157
+#define CLKID_VGE_DIV 158
+#define CLKID_VGE 159
+#define CLKID_PP_SEL 160
+#define CLKID_PP_DIV 161
+#define CLKID_PP 162
+#define CLKID_GLB_SEL 163
+#define CLKID_GLB_DIV 164
+#define CLKID_GLB 165
+#define CLKID_USB_48M_DUALDIV_IN 166
+#define CLKID_USB_48M_DUALDIV_DIV 167
+#define CLKID_USB_48M_DUALDIV_SEL 168
+#define CLKID_USB_48M_DUALDIV 169
+#define CLKID_USB_48M 170
+#define CLKID_CAN0_PE_SEL 171
+#define CLKID_CAN0_PE_DIV 172
+#define CLKID_CAN0_PE 173
+#define CLKID_CAN1_PE_SEL 174
+#define CLKID_CAN1_PE_DIV 175
+#define CLKID_CAN1_PE 176
+#define CLKID_CAN0_FILTER_SEL 177
+#define CLKID_CAN0_FILTER_DIV 178
+#define CLKID_CAN0_FILTER 179
+#define CLKID_CAN1_FILTER_SEL 180
+#define CLKID_CAN1_FILTER_DIV 181
+#define CLKID_CAN1_FILTER 182
+#define CLKID_I3C_SEL 183
+#define CLKID_I3C_DIV 184
+#define CLKID_I3C 185
+#define CLKID_TS_DIV 186
+#define CLKID_TS 187
+#define CLKID_ETH_125M_DIV 188
+#define CLKID_ETH_125M 189
+#define CLKID_ETH_RMII_SEL 190
+#define CLKID_ETH_RMII_DIV 191
+#define CLKID_ETH_RMII 192
+#define CLKID_GEN_SEL 193
+#define CLKID_GEN_DIV 194
+#define CLKID_GEN 195
+#define CLKID_CLK24M_IN 196
+#define CLKID_CLK12_24M 197
+#define CLKID_MALI_0_SEL 198
+#define CLKID_MALI_0_DIV 199
+#define CLKID_MALI_0 200
+#define CLKID_MALI_1_SEL 201
+#define CLKID_MALI_1_DIV 202
+#define CLKID_MALI_1 203
+#define CLKID_MALI 204
+#define CLKID_MALI_STACK_0_SEL 205
+#define CLKID_MALI_STACK_0_DIV 206
+#define CLKID_MALI_STACK_0 207
+#define CLKID_MALI_STACK_1_SEL 208
+#define CLKID_MALI_STACK_1_DIV 209
+#define CLKID_MALI_STACK_1 210
+#define CLKID_MALI_STACK 211
+#define CLKID_DSPA_0_SEL 212
+#define CLKID_DSPA_0_DIV 213
+#define CLKID_DSPA_0 214
+#define CLKID_DSPA_1_SEL 215
+#define CLKID_DSPA_1_DIV 216
+#define CLKID_DSPA_1 217
+#define CLKID_DSPA 218
+#define CLKID_HEVCF_0_SEL 219
+#define CLKID_HEVCF_0_DIV 220
+#define CLKID_HEVCF_0 221
+#define CLKID_HEVCF_1_SEL 222
+#define CLKID_HEVCF_1_DIV 223
+#define CLKID_HEVCF_1 224
+#define CLKID_HEVCF 225
+#define CLKID_HCODEC_0_SEL 226
+#define CLKID_HCODEC_0_DIV 227
+#define CLKID_HCODEC_0 228
+#define CLKID_HCODEC_1_SEL 229
+#define CLKID_HCODEC_1_DIV 230
+#define CLKID_HCODEC_1 231
+#define CLKID_HCODEC 232
+#define CLKID_VPU_0_SEL 233
+#define CLKID_VPU_0_DIV 234
+#define CLKID_VPU_0 235
+#define CLKID_VPU_1_SEL 236
+#define CLKID_VPU_1_DIV 237
+#define CLKID_VPU_1 238
+#define CLKID_VPU 239
+#define CLKID_VAPB_0_SEL 240
+#define CLKID_VAPB_0_DIV 241
+#define CLKID_VAPB_0 242
+#define CLKID_VAPB_1_SEL 243
+#define CLKID_VAPB_1_DIV 244
+#define CLKID_VAPB_1 245
+#define CLKID_VAPB 246
+#define CLKID_GE2D 247
+#define CLKID_VPU_CLKB_TMP_SEL 248
+#define CLKID_VPU_CLKB_TMP_DIV 249
+#define CLKID_VPU_CLKB_TMP 250
+#define CLKID_VPU_CLKB_DIV 251
+#define CLKID_VPU_CLKB 252
+#define CLKID_HDMITX_SYS_SEL 253
+#define CLKID_HDMITX_SYS_DIV 254
+#define CLKID_HDMITX_SYS 255
+#define CLKID_HDMITX_PRIF_SEL 256
+#define CLKID_HDMITX_PRIF_DIV 257
+#define CLKID_HDMITX_PRIF 258
+#define CLKID_HDMITX_200M_SEL 259
+#define CLKID_HDMITX_200M_DIV 260
+#define CLKID_HDMITX_200M 261
+#define CLKID_HDMITX_AUD_SEL 262
+#define CLKID_HDMITX_AUD_DIV 263
+#define CLKID_HDMITX_AUD 264
+#define CLKID_HDMIRX_5M_SEL 265
+#define CLKID_HDMIRX_5M_DIV 266
+#define CLKID_HDMIRX_5M 267
+#define CLKID_HDMIRX_2M_SEL 268
+#define CLKID_HDMIRX_2M_DIV 269
+#define CLKID_HDMIRX_2M 270
+#define CLKID_HDMIRX_CFG_SEL 271
+#define CLKID_HDMIRX_CFG_DIV 272
+#define CLKID_HDMIRX_CFG 273
+#define CLKID_HDMIRX_HDCP2X_SEL 274
+#define CLKID_HDMIRX_HDCP2X_DIV 275
+#define CLKID_HDMIRX_HDCP2X 276
+#define CLKID_HDMIRX_ACR_REF_SEL 277
+#define CLKID_HDMIRX_ACR_REF_DIV 278
+#define CLKID_HDMIRX_ACR_REF 279
+#define CLKID_HDMIRX_METER_SEL 280
+#define CLKID_HDMIRX_METER_DIV 281
+#define CLKID_HDMIRX_METER 282
+#define CLKID_VID_LOCK_SEL 283
+#define CLKID_VID_LOCK_DIV 284
+#define CLKID_VID_LOCK 285
+#define CLKID_VDIN_MEAS_SEL 286
+#define CLKID_VDIN_MEAS_DIV 287
+#define CLKID_VDIN_MEAS 288
+#define CLKID_VID_PLL_DIV 289
+#define CLKID_VID_PLL_SEL 290
+#define CLKID_VID_PLL 291
+#define CLKID_VID_PLL_VCLK 292
+#define CLKID_VCLK0_SEL 293
+#define CLKID_VCLK0_IN 294
+#define CLKID_VCLK0_DIV 295
+#define CLKID_VCLK0 296
+#define CLKID_VCLK0_DIV1_EN 297
+#define CLKID_VCLK0_DIV2_EN 298
+#define CLKID_VCLK0_DIV2 299
+#define CLKID_VCLK0_DIV4_EN 300
+#define CLKID_VCLK0_DIV4 301
+#define CLKID_VCLK0_DIV6_EN 302
+#define CLKID_VCLK0_DIV6 303
+#define CLKID_VCLK0_DIV12_EN 304
+#define CLKID_VCLK0_DIV12 305
+#define CLKID_VCLK1_SEL 306
+#define CLKID_VCLK1_IN 307
+#define CLKID_VCLK1_DIV 308
+#define CLKID_VCLK1 309
+#define CLKID_VCLK1_DIV1_EN 310
+#define CLKID_VCLK1_DIV2_EN 311
+#define CLKID_VCLK1_DIV2 312
+#define CLKID_VCLK1_DIV4_EN 313
+#define CLKID_VCLK1_DIV4 314
+#define CLKID_VCLK1_DIV6_EN 315
+#define CLKID_VCLK1_DIV6 316
+#define CLKID_VCLK1_DIV12_EN 317
+#define CLKID_VCLK1_DIV12 318
+#define CLKID_VDAC_SEL 319
+#define CLKID_VDAC 320
+#define CLKID_ENCODER0_SEL 321
+#define CLKID_ENCODER0 322
+#define CLKID_ENCODER1_SEL 323
+#define CLKID_ENCODER1 324
+#define CLKID_HDMITX_PIXEL_SEL 325
+#define CLKID_HDMITX_PIXEL 326
+#define CLKID_HDMITX_FE_SEL 327
+#define CLKID_HDMITX_FE 328
+#define CLKID_HDMITX1_PIXEL_SEL 329
+#define CLKID_HDMITX1_PIXEL 330
+#define CLKID_HDMITX1_FE_SEL 331
+#define CLKID_HDMITX1_FE 332
+#define CLKID_CSI_PHY_SEL 333
+#define CLKID_CSI_PHY_DIV 334
+#define CLKID_CSI_PHY 335
+#define CLKID_DSI_MEAS_SEL 336
+#define CLKID_DSI_MEAS_DIV 337
+#define CLKID_DSI_MEAS 338
+#define CLKID_DSI_B_MEAS_SEL 339
+#define CLKID_DSI_B_MEAS_DIV 340
+#define CLKID_DSI_B_MEAS 341
+
+#endif /* __AMLOGIC_A9_PERIPHERALS_CLKC_H */
--
2.47.1
^ permalink raw reply related
* [PATCH v4 2/2] clk: amlogic: Add A9 AO clock controller driver
From: Jian Hu via B4 Relay @ 2026-06-18 9:49 UTC (permalink / raw)
To: Neil Armstrong, Jerome Brunet, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao,
Kevin Hilman, Martin Blumenstingl
Cc: linux-amlogic, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Jian Hu
In-Reply-To: <20260618-a9_aoclk-v4-0-569d0425e50c@amlogic.com>
From: Jian Hu <jian.hu@amlogic.com>
Add the Always-on clock controller driver for the Amlogic A9 SoC family.
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
drivers/clk/meson/Kconfig | 13 ++
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/a9-aoclk.c | 488 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 502 insertions(+)
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index cf8cf3f9e4ee..b71299898197 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -132,6 +132,19 @@ config COMMON_CLK_A1_PERIPHERALS
device, A1 SoC Family. Say Y if you want A1 Peripherals clock
controller to work.
+config COMMON_CLK_A9_AO
+ tristate "Amlogic A9 SoC AO clock controller support"
+ depends on ARM64 || COMPILE_TEST
+ default ARCH_MESON
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_DUALDIV
+ imply COMMON_CLK_SCMI
+ help
+ Support for the AO clock controller on Amlogic A311Y3 based
+ device, AKA A9.
+ Say Y if you want A9 AO clock controller to work.
+
config COMMON_CLK_C3_PLL
tristate "Amlogic C3 PLL clock controller"
depends on ARM64
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index c6719694a242..f89d027c282c 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
+obj-$(CONFIG_COMMON_CLK_A9_AO) += a9-aoclk.o
obj-$(CONFIG_COMMON_CLK_C3_PLL) += c3-pll.o
obj-$(CONFIG_COMMON_CLK_C3_PERIPHERALS) += c3-peripherals.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
diff --git a/drivers/clk/meson/a9-aoclk.c b/drivers/clk/meson/a9-aoclk.c
new file mode 100644
index 000000000000..88aa8cf3f5d0
--- /dev/null
+++ b/drivers/clk/meson/a9-aoclk.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2026 Amlogic, Inc. All rights reserved
+ */
+
+#include <dt-bindings/clock/amlogic,a9-aoclkc.h>
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include "clk-regmap.h"
+#include "clk-dualdiv.h"
+#include "meson-clkc-utils.h"
+
+#define AO_OSCIN_CTRL 0x00
+#define AO_SYS_CLK0 0x04
+#define AO_PWM_CLK_A_CTRL 0x1c
+#define AO_PWM_CLK_B_CTRL 0x20
+#define AO_PWM_CLK_C_CTRL 0x24
+#define AO_PWM_CLK_D_CTRL 0x28
+#define AO_PWM_CLK_E_CTRL 0x2c
+#define AO_PWM_CLK_F_CTRL 0x30
+#define AO_PWM_CLK_G_CTRL 0x34
+#define AO_CEC_CTRL0 0x38
+#define AO_CEC_CTRL1 0x3c
+#define AO_RTC_BY_OSCIN_CTRL0 0x50
+#define AO_RTC_BY_OSCIN_CTRL1 0x54
+
+#define A9_COMP_SEL(_name, _reg, _shift, _mask, _pdata) \
+ MESON_COMP_SEL(a9_ao_, _name, _reg, _shift, _mask, _pdata, NULL, 0, 0)
+
+#define A9_COMP_DIV(_name, _reg, _shift, _width) \
+ MESON_COMP_DIV(a9_ao_, _name, _reg, _shift, _width, 0, CLK_SET_RATE_PARENT)
+
+#define A9_COMP_GATE(_name, _reg, _bit) \
+ MESON_COMP_GATE(a9_ao_, _name, _reg, _bit, CLK_SET_RATE_PARENT)
+
+static struct clk_regmap a9_ao_xtal_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_OSCIN_CTRL,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ao_xtal_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xtal",
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_ao_xtal = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_OSCIN_CTRL,
+ .mask = 0x1,
+ .shift = 0,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_xtal",
+ .ops = &clk_regmap_mux_ops,
+ /* ext_32k is from external PAD, do not automatically reparent */
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a9_ao_xtal_in.hw },
+ { .fw_name = "ext_32k", },
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_NO_REPARENT,
+ },
+};
+
+static struct clk_regmap a9_ao_sys = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_OSCIN_CTRL,
+ .mask = 0x1,
+ .shift = 1,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_sys",
+ .ops = &clk_regmap_mux_ops,
+ .parent_data = (const struct clk_parent_data []) {
+ { .hw = &a9_ao_xtal.hw },
+ { .fw_name = "sys", },
+ },
+ .num_parents = 2,
+ },
+};
+
+static const struct clk_parent_data a9_ao_pclk_parents = { .hw = &a9_ao_sys.hw };
+
+#define A9_AO_PCLK(_name, _bit, _flags) \
+ MESON_PCLK(a9_ao_sys_##_name, AO_SYS_CLK0, _bit, \
+ &a9_ao_pclk_parents, _flags)
+
+/*
+ * A9 integrates a low-power microprocessor (Always-on CPU: AOCPU). Some AO sys
+ * clocks control the AOCPU modules. Mark the AOCPU-related clocks with
+ * CLK_IS_CRITICAL to avoid them being disabled and impacting AOCPU functionality.
+ * AOCPU-related clocks list:
+ * - clktree
+ * - rst_ctrl
+ * - pad
+ * - irq
+ * - pwrctrl
+ * - aocpu
+ * - sram
+ */
+static A9_AO_PCLK(i3c, 0, 0);
+static A9_AO_PCLK(rtc_reg, 1, 0);
+static A9_AO_PCLK(clktree, 2, CLK_IS_CRITICAL);
+static A9_AO_PCLK(rst_ctrl, 3, CLK_IS_CRITICAL);
+static A9_AO_PCLK(pad, 4, CLK_IS_CRITICAL);
+static A9_AO_PCLK(rtc_dig, 5, 0);
+static A9_AO_PCLK(irq, 6, CLK_IS_CRITICAL);
+static A9_AO_PCLK(pwrctrl, 7, CLK_IS_CRITICAL);
+static A9_AO_PCLK(pwm_a, 8, 0);
+static A9_AO_PCLK(pwm_b, 9, 0);
+static A9_AO_PCLK(pwm_c, 10, 0);
+static A9_AO_PCLK(pwm_d, 11, 0);
+static A9_AO_PCLK(pwm_e, 12, 0);
+static A9_AO_PCLK(pwm_f, 13, 0);
+static A9_AO_PCLK(pwm_g, 14, 0);
+static A9_AO_PCLK(i2c_a, 15, 0);
+static A9_AO_PCLK(i2c_b, 16, 0);
+static A9_AO_PCLK(i2c_c, 17, 0);
+static A9_AO_PCLK(i2c_d, 18, 0);
+static A9_AO_PCLK(sed, 19, 0);
+static A9_AO_PCLK(ir_ctrl, 20, 0);
+static A9_AO_PCLK(uart_b, 21, 0);
+static A9_AO_PCLK(uart_c, 22, 0);
+static A9_AO_PCLK(uart_d, 23, 0);
+static A9_AO_PCLK(uart_e, 24, 0);
+static A9_AO_PCLK(spisg_0, 25, 0);
+static A9_AO_PCLK(rtc_secure, 26, 0);
+static A9_AO_PCLK(cec, 27, 0);
+static A9_AO_PCLK(aocpu, 28, CLK_IS_CRITICAL);
+static A9_AO_PCLK(sram, 29, CLK_IS_CRITICAL);
+static A9_AO_PCLK(spisg_1, 30, 0);
+static A9_AO_PCLK(spisg_2, 31, 0);
+
+static const struct clk_parent_data a9_ao_pwm_parents[] = {
+ { .hw = &a9_ao_xtal.hw },
+ { .fw_name = "fdiv5", },
+ { .fw_name = "fdiv4", },
+ { .fw_name = "fdiv3", }
+};
+
+static A9_COMP_SEL(pwm_a, AO_PWM_CLK_A_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_a, AO_PWM_CLK_A_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_a, AO_PWM_CLK_A_CTRL, 8);
+
+static A9_COMP_SEL(pwm_b, AO_PWM_CLK_B_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_b, AO_PWM_CLK_B_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_b, AO_PWM_CLK_B_CTRL, 8);
+
+static A9_COMP_SEL(pwm_c, AO_PWM_CLK_C_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_c, AO_PWM_CLK_C_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_c, AO_PWM_CLK_C_CTRL, 8);
+
+static A9_COMP_SEL(pwm_d, AO_PWM_CLK_D_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_d, AO_PWM_CLK_D_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_d, AO_PWM_CLK_D_CTRL, 8);
+
+static A9_COMP_SEL(pwm_e, AO_PWM_CLK_E_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_e, AO_PWM_CLK_E_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_e, AO_PWM_CLK_E_CTRL, 8);
+
+static A9_COMP_SEL(pwm_f, AO_PWM_CLK_F_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_f, AO_PWM_CLK_F_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_f, AO_PWM_CLK_F_CTRL, 8);
+
+static A9_COMP_SEL(pwm_g, AO_PWM_CLK_G_CTRL, 9, 0x7, a9_ao_pwm_parents);
+static A9_COMP_DIV(pwm_g, AO_PWM_CLK_G_CTRL, 0, 8);
+static A9_COMP_GATE(pwm_g, AO_PWM_CLK_G_CTRL, 8);
+
+static struct clk_regmap a9_ao_rtc_dualdiv_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ao_rtc_duandiv_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_xtal.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static const struct meson_clk_dualdiv_param a9_ao_dualdiv_table[] = {
+ { 733, 732, 8, 11, 1 },
+ { /* sentinel */ }
+};
+
+static struct clk_regmap a9_ao_rtc_dualdiv_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = AO_RTC_BY_OSCIN_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = AO_RTC_BY_OSCIN_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = AO_RTC_BY_OSCIN_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = AO_RTC_BY_OSCIN_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = AO_RTC_BY_OSCIN_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = a9_ao_dualdiv_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "a9_ao_rtc_dualdiv_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_rtc_dualdiv_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_ao_rtc_dualdiv_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTC_BY_OSCIN_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_rtc_dualdiv_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_rtc_dualdiv_div.hw,
+ &a9_ao_rtc_dualdiv_in.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_ao_rtc_dualdiv = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_RTC_BY_OSCIN_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ao_rtc_dualdiv",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_rtc_dualdiv_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_ao_rtc = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_RTC_BY_OSCIN_CTRL1,
+ .mask = 0x1,
+ .shift = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_rtc",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_xtal.hw,
+ &a9_ao_rtc_dualdiv.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_ao_cec_dualdiv_in = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_CEC_CTRL0,
+ .bit_idx = 31,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "ao_cec_dualdiv_in",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_xtal.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_ao_cec_dualdiv_div = {
+ .data = &(struct meson_clk_dualdiv_data){
+ .n1 = {
+ .reg_off = AO_CEC_CTRL0,
+ .shift = 0,
+ .width = 12,
+ },
+ .n2 = {
+ .reg_off = AO_CEC_CTRL0,
+ .shift = 12,
+ .width = 12,
+ },
+ .m1 = {
+ .reg_off = AO_CEC_CTRL1,
+ .shift = 0,
+ .width = 12,
+ },
+ .m2 = {
+ .reg_off = AO_CEC_CTRL1,
+ .shift = 12,
+ .width = 12,
+ },
+ .dual = {
+ .reg_off = AO_CEC_CTRL0,
+ .shift = 28,
+ .width = 1,
+ },
+ .table = a9_ao_dualdiv_table,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cec_dualdiv_div",
+ .ops = &meson_clk_dualdiv_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_cec_dualdiv_in.hw
+ },
+ .num_parents = 1,
+ },
+};
+
+static struct clk_regmap a9_ao_cec_dualdiv_sel = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_CEC_CTRL1,
+ .mask = 0x1,
+ .shift = 24,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cec_dualdiv_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_cec_dualdiv_div.hw,
+ &a9_ao_cec_dualdiv_in.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_ao_cec_dualdiv = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AO_CEC_CTRL0,
+ .bit_idx = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cec_dualdiv",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_cec_dualdiv_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap a9_ao_cec = {
+ .data = &(struct clk_regmap_mux_data) {
+ .offset = AO_CEC_CTRL1,
+ .mask = 0x1,
+ .shift = 30,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "ao_cec",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &a9_ao_cec_dualdiv.hw,
+ &a9_ao_rtc.hw,
+ },
+ .num_parents = 2,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_hw *a9_ao_hw_clks[] = {
+ [CLKID_AO_XTAL_IN] = &a9_ao_xtal_in.hw,
+ [CLKID_AO_XTAL] = &a9_ao_xtal.hw,
+ [CLKID_AO_SYS] = &a9_ao_sys.hw,
+ [CLKID_AO_SYS_I3C] = &a9_ao_sys_i3c.hw,
+ [CLKID_AO_SYS_RTC_REG] = &a9_ao_sys_rtc_reg.hw,
+ [CLKID_AO_SYS_CLKTREE] = &a9_ao_sys_clktree.hw,
+ [CLKID_AO_SYS_RST_CTRL] = &a9_ao_sys_rst_ctrl.hw,
+ [CLKID_AO_SYS_PAD] = &a9_ao_sys_pad.hw,
+ [CLKID_AO_SYS_RTC_DIG] = &a9_ao_sys_rtc_dig.hw,
+ [CLKID_AO_SYS_IRQ] = &a9_ao_sys_irq.hw,
+ [CLKID_AO_SYS_PWRCTRL] = &a9_ao_sys_pwrctrl.hw,
+ [CLKID_AO_SYS_PWM_A] = &a9_ao_sys_pwm_a.hw,
+ [CLKID_AO_SYS_PWM_B] = &a9_ao_sys_pwm_b.hw,
+ [CLKID_AO_SYS_PWM_C] = &a9_ao_sys_pwm_c.hw,
+ [CLKID_AO_SYS_PWM_D] = &a9_ao_sys_pwm_d.hw,
+ [CLKID_AO_SYS_PWM_E] = &a9_ao_sys_pwm_e.hw,
+ [CLKID_AO_SYS_PWM_F] = &a9_ao_sys_pwm_f.hw,
+ [CLKID_AO_SYS_PWM_G] = &a9_ao_sys_pwm_g.hw,
+ [CLKID_AO_SYS_I2C_A] = &a9_ao_sys_i2c_a.hw,
+ [CLKID_AO_SYS_I2C_B] = &a9_ao_sys_i2c_b.hw,
+ [CLKID_AO_SYS_I2C_C] = &a9_ao_sys_i2c_c.hw,
+ [CLKID_AO_SYS_I2C_D] = &a9_ao_sys_i2c_d.hw,
+ [CLKID_AO_SYS_SED] = &a9_ao_sys_sed.hw,
+ [CLKID_AO_SYS_IR_CTRL] = &a9_ao_sys_ir_ctrl.hw,
+ [CLKID_AO_SYS_UART_B] = &a9_ao_sys_uart_b.hw,
+ [CLKID_AO_SYS_UART_C] = &a9_ao_sys_uart_c.hw,
+ [CLKID_AO_SYS_UART_D] = &a9_ao_sys_uart_d.hw,
+ [CLKID_AO_SYS_UART_E] = &a9_ao_sys_uart_e.hw,
+ [CLKID_AO_SYS_SPISG_0] = &a9_ao_sys_spisg_0.hw,
+ [CLKID_AO_SYS_RTC_SECURE] = &a9_ao_sys_rtc_secure.hw,
+ [CLKID_AO_SYS_CEC] = &a9_ao_sys_cec.hw,
+ [CLKID_AO_SYS_AOCPU] = &a9_ao_sys_aocpu.hw,
+ [CLKID_AO_SYS_SRAM] = &a9_ao_sys_sram.hw,
+ [CLKID_AO_SYS_SPISG_1] = &a9_ao_sys_spisg_1.hw,
+ [CLKID_AO_SYS_SPISG_2] = &a9_ao_sys_spisg_2.hw,
+ [CLKID_AO_PWM_A_SEL] = &a9_ao_pwm_a_sel.hw,
+ [CLKID_AO_PWM_A_DIV] = &a9_ao_pwm_a_div.hw,
+ [CLKID_AO_PWM_A] = &a9_ao_pwm_a.hw,
+ [CLKID_AO_PWM_B_SEL] = &a9_ao_pwm_b_sel.hw,
+ [CLKID_AO_PWM_B_DIV] = &a9_ao_pwm_b_div.hw,
+ [CLKID_AO_PWM_B] = &a9_ao_pwm_b.hw,
+ [CLKID_AO_PWM_C_SEL] = &a9_ao_pwm_c_sel.hw,
+ [CLKID_AO_PWM_C_DIV] = &a9_ao_pwm_c_div.hw,
+ [CLKID_AO_PWM_C] = &a9_ao_pwm_c.hw,
+ [CLKID_AO_PWM_D_SEL] = &a9_ao_pwm_d_sel.hw,
+ [CLKID_AO_PWM_D_DIV] = &a9_ao_pwm_d_div.hw,
+ [CLKID_AO_PWM_D] = &a9_ao_pwm_d.hw,
+ [CLKID_AO_PWM_E_SEL] = &a9_ao_pwm_e_sel.hw,
+ [CLKID_AO_PWM_E_DIV] = &a9_ao_pwm_e_div.hw,
+ [CLKID_AO_PWM_E] = &a9_ao_pwm_e.hw,
+ [CLKID_AO_PWM_F_SEL] = &a9_ao_pwm_f_sel.hw,
+ [CLKID_AO_PWM_F_DIV] = &a9_ao_pwm_f_div.hw,
+ [CLKID_AO_PWM_F] = &a9_ao_pwm_f.hw,
+ [CLKID_AO_PWM_G_SEL] = &a9_ao_pwm_g_sel.hw,
+ [CLKID_AO_PWM_G_DIV] = &a9_ao_pwm_g_div.hw,
+ [CLKID_AO_PWM_G] = &a9_ao_pwm_g.hw,
+ [CLKID_AO_RTC_DUALDIV_IN] = &a9_ao_rtc_dualdiv_in.hw,
+ [CLKID_AO_RTC_DUALDIV_DIV] = &a9_ao_rtc_dualdiv_div.hw,
+ [CLKID_AO_RTC_DUALDIV_SEL] = &a9_ao_rtc_dualdiv_sel.hw,
+ [CLKID_AO_RTC_DUALDIV] = &a9_ao_rtc_dualdiv.hw,
+ [CLKID_AO_RTC] = &a9_ao_rtc.hw,
+ [CLKID_AO_CEC_DUALDIV_IN] = &a9_ao_cec_dualdiv_in.hw,
+ [CLKID_AO_CEC_DUALDIV_DIV] = &a9_ao_cec_dualdiv_div.hw,
+ [CLKID_AO_CEC_DUALDIV_SEL] = &a9_ao_cec_dualdiv_sel.hw,
+ [CLKID_AO_CEC_DUALDIV] = &a9_ao_cec_dualdiv.hw,
+ [CLKID_AO_CEC] = &a9_ao_cec.hw,
+};
+
+static const struct meson_clkc_data a9_ao_clkc_data = {
+ .hw_clks = {
+ .hws = a9_ao_hw_clks,
+ .num = ARRAY_SIZE(a9_ao_hw_clks),
+ },
+};
+
+static const struct of_device_id a9_ao_clkc_match_table[] = {
+ {
+ .compatible = "amlogic,a9-aoclkc",
+ .data = &a9_ao_clkc_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, a9_ao_clkc_match_table);
+
+static struct platform_driver a9_ao_clkc_driver = {
+ .probe = meson_clkc_mmio_probe,
+ .driver = {
+ .name = "a9-aoclkc",
+ .of_match_table = a9_ao_clkc_match_table,
+ },
+};
+module_platform_driver(a9_ao_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic A9 Always-ON Clock Controller driver");
+MODULE_AUTHOR("Jian Hu <jian.hu@amlogic.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CLK_MESON");
--
2.47.1
^ permalink raw reply related
* [PATCH v4 0/2] clk: amlogic: Add A9 AO clock controller
From: Jian Hu via B4 Relay @ 2026-06-18 9:49 UTC (permalink / raw)
To: Neil Armstrong, Jerome Brunet, Michael Turquette, Stephen Boyd,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao,
Kevin Hilman, Martin Blumenstingl
Cc: linux-amlogic, linux-clk, devicetree, linux-kernel,
linux-arm-kernel, Jian Hu, Conor Dooley
This series adds Amlogic A9 AO clock support, including dt-binding and AO clock driver.
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
---
Changes in v4:
- Drop CLK_IS_CRITICAL for ao_xtal_in clock.
- Drop CLK_HW_INIT* and revert to explicit clock declarations.
- Link to v3: https://lore.kernel.org/r/20260610-a9_aoclk-v3-0-b7592d6c31e2@amlogic.com
Changes in v3:
- Move COMPILE_TEST after 'depends on ARM64' reported by sashiko-bot.
- Rename i2c3 to i3c reported by sashiko-bot.
- Reword the comment describing ao_xtal_in's flags.
- Use struct clk_init_data to describe ao_xtal_in's hw.init.
- Link to v2: https://lore.kernel.org/r/20260603-a9_aoclk-v2-0-f47ea616ee78@amlogic.com
Changes in v2:
- Split the A9 clock driver and send the AO clock separately.
- Rename aobus to soc.
- Use CLK_HW_INIT_FW_NAME to describe clk_init_data.
- Use CLK_HW_INIT_PARENTS_DATA to describe clk_init_data.
- Use a9_ao prefix for MESON_COMP_SEL.
- Correct duandiv name.
- Fix pwm b reg.
- Link to v1: https://lore.kernel.org/all/20260511-b4-a9_clk-v1-0-41cb4071b7c9@amlogic.com/
---
Jian Hu (2):
dt-bindings: clock: Add Amlogic A9 AO clock controller
clk: amlogic: Add A9 AO clock controller driver
.../bindings/clock/amlogic,a9-aoclkc.yaml | 76 ++++
drivers/clk/meson/Kconfig | 13 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/a9-aoclk.c | 488 +++++++++++++++++++++
include/dt-bindings/clock/amlogic,a9-aoclkc.h | 76 ++++
5 files changed, 654 insertions(+)
---
base-commit: ca89c88bcf69daca829044c638a8163d5ce47af0
change-id: 20260603-a9_aoclk-bbf531badc63
Best regards,
--
Jian Hu <jian.hu@amlogic.com>
^ permalink raw reply
* Re: [PATCH v1 03/11] KVM: arm64: Use guard()/scoped_guard() in arm64 KVM EL1 code
From: Fuad Tabba @ 2026-06-18 9:46 UTC (permalink / raw)
To: Marc Zyngier
Cc: Oliver Upton, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <86ik7grxrg.wl-maz@kernel.org>
On Thu, 18 Jun 2026 at 10:41, Marc Zyngier <maz@kernel.org> wrote:
>
> On Thu, 18 Jun 2026 10:24:55 +0100,
> Fuad Tabba <tabba@google.com> wrote:
> >
> > On Thu, 18 Jun 2026 at 10:23, Marc Zyngier <maz@kernel.org> wrote:
> > >
> > > On Fri, 12 Jun 2026 07:59:17 +0100,
> > > tabba@google.com wrote:
> > > >
> > > > Convert the manual mutex_lock()/spin_lock() pairs in
> > > > arch/arm64/kvm/{pkvm,arm,mmu,reset,psci}.c to guard(mutex),
> > > > guard(spinlock) and scoped_guard(), dropping unlock-only goto labels in
> > > > favour of direct returns. Centralised cleanup gotos that still serve
> > > > other resources are preserved.
> > > >
> > > > reset.c uses scoped_guard() rather than guard() so the lock covers only
> > > > the small read/update window inside kvm_reset_vcpu(), leaving the rest
> > > > of the function outside the critical section.
> > >
> > > To be brutally honest, I don't think this sort of widespread changes
> > > bring us anything. This is just churn.
> > >
> > > Sure, if you are reworking a particular bit of code that is goto-heavy
> > > for the purpose of error handling, this has the potential to cleanup
> > > the code *while you are changing it*.
> > >
> > > But doing it for the sake of doing it? I think we have bigger fish to
> > > fry right now.
> >
> > I understand what you mean. Would you like me to drop all of the guard
> > patches, or only those that go beyond the code changed in this series?
>
> The latter. I'm fine with conversion that happens while the code is
> being reworked.
Sure, I'll do that on the respin.
Cheers,
/fuad
>
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v4 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: Philipp Zabel @ 2026-06-18 9:45 UTC (permalink / raw)
To: Joakim Zhang, Conor Dooley
Cc: mturquette@baylibre.com, sboyd@kernel.org, bmasney@redhat.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Gary Yang, cix-kernel-upstream, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <SEYPR06MB6226B25BA7652287C752E4C782E32@SEYPR06MB6226.apcprd06.prod.outlook.com>
On Do, 2026-06-18 at 09:27 +0000, Joakim Zhang wrote:
> Hello,
>
>
> > -----Original Message-----
> > From: Philipp Zabel <p.zabel@pengutronix.de>
> > Sent: Thursday, June 18, 2026 4:30 PM
> > To: Joakim Zhang <joakim.zhang@cixtech.com>; Conor Dooley
> > <conor@kernel.org>
> > Cc: mturquette@baylibre.com; sboyd@kernel.org; bmasney@redhat.com;
> > robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org; Gary Yang
> > <Gary.Yang@cixtech.com>; cix-kernel-upstream <cix-kernel-
> > upstream@cixtech.com>; linux-clk@vger.kernel.org;
> > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-
> > kernel@lists.infradead.org
> > Subject: Re: [PATCH v4 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss
> > clock controller
> >
> > EXTERNAL EMAIL
> >
> > CAUTION: Suspicious Email from unusual domain.
> >
> > On Do, 2026-06-18 at 01:43 +0000, Joakim Zhang wrote:
> > > Hello,
> > >
> > >
> > > > -----Original Message-----
> > > > From: Conor Dooley <conor@kernel.org>
> > > > Sent: Wednesday, June 17, 2026 11:56 PM
> > > > To: Joakim Zhang <joakim.zhang@cixtech.com>
> > > > Cc: mturquette@baylibre.com; sboyd@kernel.org; bmasney@redhat.com;
> > > > robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org;
> > > > p.zabel@pengutronix.de; Gary Yang <gary.yang@cixtech.com>;
> > > > cix-kernel- upstream <cix-kernel-upstream@cixtech.com>;
> > > > linux-clk@vger.kernel.org; devicetree@vger.kernel.org;
> > > > linux-kernel@vger.kernel.org; linux-arm- kernel@lists.infradead.org
> > > > Subject: Re: [PATCH v4 3/5] dt-bindings: clock:
> > > > cix,sky1-audss-clock: add audss clock controller
> > > >
> > > > On Wed, Jun 17, 2026 at 02:04:35PM +0800, joakim.zhang@cixtech.com
> > wrote:
> > > > > From: Joakim Zhang <joakim.zhang@cixtech.com>
> > > > >
> > > > > The AUDSS CRU contains an internal clock tree of muxes, dividers
> > > > > and gates for DSP, I2S, HDA, DMAC and related blocks. The clock
> > > > > provider is a child node of the cix,sky1-audss-system-control
> > > > > syscon and accesses registers through the parent MMIO region.
> > > >
> > > > Why can this not just be part of the parent syscon node?
> > >
> > > The clock and reset blocks are handled by different subsystems and
> > maintainers (clk vs reset). Putting the clock provider on the parent syscon node
> > would mean a single driver has to register both the reset controller and the
> > clock provider on one device, which doesn't fit well.
> >
> > There are many examples of clock and reset drivers sharing the same node, by
> > using platform_driver for one (usually clk) and auxiliary_driver for the other
> > (usually reset).
>
> OK, I will have a look. If you are also prefer to this, I will refactor the patch.
The hardware should dictate this, not the driver/subsystem/maintainer
boundaries. Looking at the register numbers used by the drivers, and
assuming that the audss_cru label in the dts patch means the TRM also
calls this a single Clock-Reset-Unit, I'd say Conor has a point.
regards
Philipp
^ permalink raw reply
* Re: [PATCH v1 03/11] KVM: arm64: Use guard()/scoped_guard() in arm64 KVM EL1 code
From: Marc Zyngier @ 2026-06-18 9:41 UTC (permalink / raw)
To: Fuad Tabba
Cc: Oliver Upton, Will Deacon, Catalin Marinas, Quentin Perret,
Vincent Donnefort, Sebastian Ene, Per Larsen, Suzuki K Poulose,
Zenghui Yu, Joey Gouly, Steffen Eiden, Mark Rutland,
Jonathan Cameron, Hyunwoo Kim, linux-arm-kernel, kvmarm,
linux-kernel
In-Reply-To: <CA+EHjTz-+8M5oSh+7A2vkQ-zPmDkaKptYMoBhLz=rReufKsAHg@mail.gmail.com>
On Thu, 18 Jun 2026 10:24:55 +0100,
Fuad Tabba <tabba@google.com> wrote:
>
> On Thu, 18 Jun 2026 at 10:23, Marc Zyngier <maz@kernel.org> wrote:
> >
> > On Fri, 12 Jun 2026 07:59:17 +0100,
> > tabba@google.com wrote:
> > >
> > > Convert the manual mutex_lock()/spin_lock() pairs in
> > > arch/arm64/kvm/{pkvm,arm,mmu,reset,psci}.c to guard(mutex),
> > > guard(spinlock) and scoped_guard(), dropping unlock-only goto labels in
> > > favour of direct returns. Centralised cleanup gotos that still serve
> > > other resources are preserved.
> > >
> > > reset.c uses scoped_guard() rather than guard() so the lock covers only
> > > the small read/update window inside kvm_reset_vcpu(), leaving the rest
> > > of the function outside the critical section.
> >
> > To be brutally honest, I don't think this sort of widespread changes
> > bring us anything. This is just churn.
> >
> > Sure, if you are reworking a particular bit of code that is goto-heavy
> > for the purpose of error handling, this has the potential to cleanup
> > the code *while you are changing it*.
> >
> > But doing it for the sake of doing it? I think we have bigger fish to
> > fry right now.
>
> I understand what you mean. Would you like me to drop all of the guard
> patches, or only those that go beyond the code changed in this series?
The latter. I'm fine with conversion that happens while the code is
being reworked.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH 2/9] drm/rockchip: vop2: Reset AXI and DCLK to improve robustness
From: Philipp Zabel @ 2026-06-18 9:39 UTC (permalink / raw)
To: Cristian Ciocaltea, Sandy Huang, Heiko Stübner, Andy Yan,
David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli
Cc: kernel, Andy Yan, dri-devel, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260617-dw-hdmi-qp-yuv-v1-2-a665cfd06d7d@collabora.com>
On Mi, 2026-06-17 at 21:51 +0300, Cristian Ciocaltea wrote:
> Assert the AXI reset in the CRTC disable path, and the VP DCLK reset in
> the enable path.
>
> These resets are intended to leave the hardware in a clean state for the
> next use, helping recover from exceptions such as IOMMU page faults, as
> well as to prevent random display output glitches, such as a blank
> image, observed when switching modes that also change the color format,
> e.g. from RGB to YUV420 and vice versa.
>
> For now this seems to affect only the RK3588, hence the resets are
> optional and will be provided in the device tree for this SoC only.
>
> Co-developed-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
> ---
> drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 35 ++++++++++++++++++++++++++++
> drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 4 ++++
> 2 files changed, 39 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> index 4cce3e336f5b..2833fb49ad81 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
> @@ -17,6 +17,7 @@
> #include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> #include <linux/regmap.h>
> +#include <linux/reset.h>
> #include <linux/swab.h>
>
> #include <drm/drm.h>
> @@ -860,6 +861,26 @@ static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
> return ret;
> }
>
> +static void vop2_clk_reset(struct vop2 *vop2, struct reset_control *rstc)
The _clk part of the function name is misleading ...
[...]
> @@ -938,6 +959,8 @@ static void vop2_disable(struct vop2 *vop2)
> {
> rockchip_drm_dma_detach_device(vop2->drm, vop2->dev);
>
> + vop2_clk_reset(vop2, vop2->axi_rst);
... because this function is also called with the AXI reset control.
> +
> pm_runtime_put_sync(vop2->dev);
>
> regcache_drop_region(vop2->map, 0, vop2_regmap_config.max_register);
> @@ -1948,6 +1971,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
>
> vop2_crtc_atomic_try_set_gamma(vop2, vp, crtc, crtc_state);
>
> + vop2_clk_reset(vop2, vp->dclk_rst);
> +
> drm_crtc_vblank_on(crtc);
>
> vop2_unlock(vop2);
> @@ -2531,6 +2556,11 @@ static int vop2_create_crtcs(struct vop2 *vop2)
> return dev_err_probe(drm->dev, PTR_ERR(vp->dclk),
> "failed to get %s\n", dclk_name);
>
> + vp->dclk_rst = devm_reset_control_get_optional(vop2->dev, dclk_name);
Please use devm_reset_control_get_optional_exclusive() directly.
> + if (IS_ERR(vp->dclk_rst))
> + return dev_err_probe(drm->dev, PTR_ERR(vp->dclk_rst),
> + "failed to get %s reset\n", dclk_name);
> +
> np = of_graph_get_remote_node(dev->of_node, i, -1);
> if (!np) {
> drm_dbg(vop2->drm, "%s: No remote for vp%d\n", __func__, i);
> @@ -2890,6 +2920,11 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
> return dev_err_probe(drm->dev, PTR_ERR(vop2->pll_hdmiphy1),
> "failed to get pll_hdmiphy1\n");
>
> + vop2->axi_rst = devm_reset_control_get_optional(vop2->dev, "axi");
Same as above, devm_reset_control_get_optional_exclusive().
regards
Philipp
^ permalink raw reply
* [PATCH v10 4/4] media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
From: guoniu.zhou @ 2026-06-18 9:41 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Abel Vesa, Peng Fan,
Michael Turquette, Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou, Frank Li
In-Reply-To: <20260618-csi_formatter-v10-0-f23830312ba5@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
The CSI pixel formatter is a module found on i.MX95 used to reformat
packet info, pixel and non-pixel data from CSI-2 host controller to
match Pixel Link(PL) definition.
Add data formatting support.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
---
Changes in v10:
- Use u8 for vc in csi_formatter_get_vc() and drop vc < 0 check
- Add MFD_SYSCON dependency to Kconfig
- Fix stream/VC mapping potential mismatch in start/stop_stream functions
Changes in v8:
- Remove fmt field and look up format from subdev state instead
- Unify function and structure naming to use csi_formatter_ prefix
- Remove misleading alignment comment from set_fmt function
- Optimize get_frame_desc to call once per start_stream
- Replace V4L2_FRAME_DESC_ENTRY_MAX with CSI_FORMATTER_VC_NUM in loops
- Remove redundant debug message in enable_streams
- Use MEDIA_PAD_FL_MUST_CONNECT flag instead of manual link check
- Fix typo: Formater -> Formatter in Kconfig help text
- Improve grammar in data type index mapping comment
Changes in v7:
- Update references from imx9 to imx95 for consistency with dt-bindings
- Enable PM runtime before async registration
Changes in v6:
- Remove unused header includes
- Unify macro naming: VCx/VCX -> VC and parameter x -> vc
- Remove unused format field from csi_formatter struct
- Use compact initialization for formats array
- Make find_csi_format() return NULL instead of default format
- Use unsigned int for array index in find_csi_format()
- Add err_ prefix to error handling labels
- Add v4l2_subdev_cleanup() and reorder cleanup sequence
- Update enable_streams debug output format
- Rename VC_MAX to VC_NUM and fix boundary check
- Update CSI formatter Kconfig description
- Use v4l2_subdev_get_frame_desc_passthrough() helper
- Fix error paths in async registration and probe
- Add mutex to protect enabled_streams
- Switch to devm_pm_runtime_enable()
- Remove redundant num_routes check in set_routing
- Optimize get_index_by_dt() and add warning for unsupported type
- csi_formatter_start/stop_stream: Process all streams in mask
---
MAINTAINERS | 8 +
drivers/media/platform/nxp/Kconfig | 15 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx95-csi-formatter.c | 775 +++++++++++++++++++++++
4 files changed, 799 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index efbf808063e5..05009228b162 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19275,6 +19275,14 @@ S: Maintained
F: Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
F: drivers/media/platform/nxp/imx-jpeg
+NXP i.MX 95 CSI PIXEL FORMATTER V4L2 DRIVER
+M: Guoniu Zhou <guoniu.zhou@nxp.com>
+L: imx@lists.linux.dev
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
+F: drivers/media/platform/nxp/imx95-csi-formatter.c
+
NXP i.MX CLOCK DRIVERS
M: Abel Vesa <abelvesa@kernel.org>
R: Peng Fan <peng.fan@nxp.com>
diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig
index 40e3436669e2..8f49908b0022 100644
--- a/drivers/media/platform/nxp/Kconfig
+++ b/drivers/media/platform/nxp/Kconfig
@@ -28,6 +28,21 @@ config VIDEO_IMX8MQ_MIPI_CSI2
Video4Linux2 driver for the MIPI CSI-2 receiver found on the i.MX8MQ
SoC.
+config VIDEO_IMX95_CSI_FORMATTER
+ tristate "NXP i.MX95 CSI Pixel Formatter driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on MFD_SYSCON
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This driver provides support for the CSI Pixel Formatter found on
+ i.MX95 series SoCs. This module unpacks the pixels received from the
+ CSI-2 interface and reformats them to meet pixel link requirements.
+
+ Say Y here to enable CSI Pixel Formatter module for i.MX95 SoC.
+
config VIDEO_IMX_MIPI_CSIS
tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models"
depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile
index 4d90eb713652..6410115d870e 100644
--- a/drivers/media/platform/nxp/Makefile
+++ b/drivers/media/platform/nxp/Makefile
@@ -6,6 +6,7 @@ obj-y += imx8-isi/
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o
+obj-$(CONFIG_VIDEO_IMX95_CSI_FORMATTER) += imx95-csi-formatter.o
obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o
obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
diff --git a/drivers/media/platform/nxp/imx95-csi-formatter.c b/drivers/media/platform/nxp/imx95-csi-formatter.c
new file mode 100644
index 000000000000..cfe448fedd37
--- /dev/null
+++ b/drivers/media/platform/nxp/imx95-csi-formatter.c
@@ -0,0 +1,775 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/* CSI Pixel Formatter registers map */
+
+#define CSI_VC_INTERLACED_LINE_CNT(vc) (0x00 + (vc) * 0x04)
+#define INTERLACED_ODD_LINE_CNT_SET(x) FIELD_PREP(GENMASK(13, 0), (x))
+#define INTERLACED_EVEN_LINE_CNT_SET(x) FIELD_PREP(GENMASK(29, 16), (x))
+
+#define CSI_VC_INTERLACED_CTRL 0x20
+
+#define CSI_VC_INTERLACED_ERR 0x24
+#define CSI_VC_ERR_MASK GENMASK(7, 0)
+#define CSI_VC_ERR(vc) BIT((vc))
+
+#define CSI_VC_YUV420_FIRST_LINE_EVEN 0x28
+#define YUV420_FIRST_LINE_EVEN(vc) BIT((vc))
+
+#define CSI_RAW32_CTRL 0x30
+#define CSI_VC_RAW32_MODE(vc) BIT((vc))
+#define CSI_VC_RAW32_SWAP_MODE(vc) BIT((vc) + 8)
+
+#define CSI_STREAM_FENCING_CTRL 0x34
+#define CSI_VC_STREAM_FENCING(vc) BIT((vc))
+#define CSI_VC_STREAM_FENCING_RST(vc) BIT((vc) + 8)
+
+#define CSI_STREAM_FENCING_STS 0x38
+#define CSI_STREAM_FENCING_STS_MASK GENMASK(7, 0)
+
+#define CSI_VC_NON_PIXEL_DATA_TYPE(vc) (0x40 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_CTRL(vc) (0x60 + (vc) * 0x04)
+#define NEW_VC(vc) FIELD_PREP(GENMASK(3, 1), vc)
+#define REROUTE_VC_ENABLE BIT(0)
+
+#define CSI_VC_ROUTE_PIXEL_DATA_TYPE(vc) (0x80 + (vc) * 0x04)
+
+#define CSI_VC_NON_PIXEL_DATA_CTRL(vc) (0xa0 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_TYPE(vc) (0xc0 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_TYPE_ERR(vc) (0xe0 + (vc) * 0x04)
+
+#define CSI_FORMATTER_PAD_SINK 0
+#define CSI_FORMATTER_PAD_SOURCE 1
+#define CSI_FORMATTER_PAD_NUM 2
+
+#define CSI_FORMATTER_VC_NUM 8 /* Number of virtual channels */
+
+struct csi_formatter_pix_format {
+ u32 code;
+ u32 data_type;
+};
+
+struct csi_formatter {
+ struct device *dev;
+ struct regmap *regs;
+ struct clk *clk;
+
+ struct v4l2_subdev sd;
+ struct v4l2_subdev *csi_sd;
+ struct v4l2_async_notifier notifier;
+ struct media_pad pads[CSI_FORMATTER_PAD_NUM];
+
+ u32 remote_pad;
+ u32 reg_offset;
+
+ /* Protects enabled_streams */
+ struct mutex lock;
+ u64 enabled_streams;
+
+ u8 stream_to_vc[CSI_FORMATTER_VC_NUM];
+};
+
+struct csi_formatter_dt_index {
+ u8 dtype;
+ u8 index;
+};
+
+/*
+ * The index corresponds to the bit index in the register that enables
+ * the data type of pixel data transported by the Formatter.
+ */
+static const struct csi_formatter_dt_index formatter_dt_to_index_map[] = {
+ { .dtype = MIPI_CSI2_DT_YUV420_8B, .index = 0 },
+ { .dtype = MIPI_CSI2_DT_YUV420_8B_LEGACY, .index = 2 },
+ { .dtype = MIPI_CSI2_DT_YUV422_8B, .index = 6 },
+ { .dtype = MIPI_CSI2_DT_RGB444, .index = 8 },
+ { .dtype = MIPI_CSI2_DT_RGB555, .index = 9 },
+ { .dtype = MIPI_CSI2_DT_RGB565, .index = 10 },
+ { .dtype = MIPI_CSI2_DT_RGB666, .index = 11 },
+ { .dtype = MIPI_CSI2_DT_RGB888, .index = 12 },
+ { .dtype = MIPI_CSI2_DT_RAW6, .index = 16 },
+ { .dtype = MIPI_CSI2_DT_RAW7, .index = 17 },
+ { .dtype = MIPI_CSI2_DT_RAW8, .index = 18 },
+ { .dtype = MIPI_CSI2_DT_RAW10, .index = 19 },
+ { .dtype = MIPI_CSI2_DT_RAW12, .index = 20 },
+ { .dtype = MIPI_CSI2_DT_RAW14, .index = 21 },
+ { .dtype = MIPI_CSI2_DT_RAW16, .index = 22 },
+};
+
+static const struct csi_formatter_pix_format formats[] = {
+ /* YUV formats */
+ { MEDIA_BUS_FMT_UYVY8_1X16, MIPI_CSI2_DT_YUV422_8B },
+ /* RGB formats */
+ { MEDIA_BUS_FMT_RGB565_1X16, MIPI_CSI2_DT_RGB565 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888 },
+ /* RAW (Bayer and greyscale) formats */
+ { MEDIA_BUS_FMT_SBGGR8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_Y8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_Y10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_Y12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SBGGR16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SGBRG16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SGRBG16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SRGGB16_1X16, MIPI_CSI2_DT_RAW16 },
+};
+
+static const struct v4l2_mbus_framefmt formatter_default_fmt = {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .width = 1920U,
+ .height = 1080U,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
+ .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+};
+
+static const struct csi_formatter_pix_format *csi_formatter_find_format(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (code == formats[i].code)
+ return &formats[i];
+
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static inline struct csi_formatter *sd_to_formatter(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_formatter, sd);
+}
+
+static int __csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &formatter_default_fmt);
+}
+
+static int csi_formatter_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = CSI_FORMATTER_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = CSI_FORMATTER_PAD_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return __csi_formatter_subdev_set_routing(sd, sd_state, &routing);
+}
+
+static int csi_formatter_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == CSI_FORMATTER_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, code->pad,
+ code->stream);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ code->code = formats[code->index].code;
+
+ return 0;
+}
+
+static int csi_formatter_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_formatter_pix_format const *format;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (sdformat->pad == CSI_FORMATTER_PAD_SOURCE)
+ return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
+
+ format = csi_formatter_find_format(sdformat->format.code);
+ if (!format)
+ format = &formats[0];
+
+ v4l_bound_align_image(&sdformat->format.width, 1, 0xffff, 2,
+ &sdformat->format.height, 1, 0xffff, 0, 0);
+
+ fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad,
+ sdformat->stream);
+ *fmt = sdformat->format;
+
+ /* Set default code if user set an invalid value */
+ fmt->code = format->code;
+
+ /* Propagate the format from sink stream to source stream */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(sd_state, sdformat->pad,
+ sdformat->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = sdformat->format;
+
+ return 0;
+}
+
+static int csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ media_entity_is_streaming(&sd->entity))
+ return -EBUSY;
+
+ return __csi_formatter_subdev_set_routing(sd, state, routing);
+}
+
+static inline void csi_formatter_write(struct csi_formatter *formatter,
+ unsigned int reg, unsigned int value)
+{
+ u32 offset = formatter->reg_offset;
+
+ regmap_write(formatter->regs, reg + offset, value);
+}
+
+static u8 csi_formatter_get_index_by_dt(u8 data_type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formatter_dt_to_index_map); ++i) {
+ const struct csi_formatter_dt_index *entry =
+ &formatter_dt_to_index_map[i];
+
+ if (data_type == entry->dtype)
+ return entry->index;
+ }
+
+ pr_warn_once("Unsupported data type 0x%x, using default\n", data_type);
+
+ return formatter_dt_to_index_map[0].index;
+}
+
+static int csi_formatter_get_vc(struct csi_formatter *formatter,
+ struct v4l2_mbus_frame_desc *fd,
+ unsigned int stream)
+{
+ struct v4l2_mbus_frame_desc_entry *entry = NULL;
+ unsigned int i;
+ u8 vc;
+
+ for (i = 0; i < fd->num_entries; ++i) {
+ if (fd->entry[i].stream == stream) {
+ entry = &fd->entry[i];
+ break;
+ }
+ }
+
+ if (!entry) {
+ dev_err(formatter->dev,
+ "No frame desc entry for stream %u\n", stream);
+ return -EPIPE;
+ }
+
+ vc = entry->bus.csi2.vc;
+
+ if (vc >= CSI_FORMATTER_VC_NUM) {
+ dev_err(formatter->dev, "Invalid virtual channel %u\n", vc);
+ return -EINVAL;
+ }
+
+ return vc;
+}
+
+static void csi_formatter_stop_stream(struct csi_formatter *formatter,
+ u64 stream_mask)
+{
+ unsigned int i;
+ u8 vc;
+
+ for (i = 0; i < CSI_FORMATTER_VC_NUM; ++i) {
+ if (!(stream_mask & BIT(i)))
+ continue;
+
+ /* Use the VC that was configured in start_stream */
+ vc = formatter->stream_to_vc[i];
+ if (vc >= CSI_FORMATTER_VC_NUM)
+ continue;
+
+ csi_formatter_write(formatter, CSI_VC_PIXEL_DATA_TYPE(vc), 0);
+
+ /* Clear after use */
+ formatter->stream_to_vc[i] = 0xff;
+ }
+}
+
+static int csi_formatter_start_stream(struct csi_formatter *formatter,
+ struct v4l2_subdev_state *state,
+ u64 stream_mask)
+{
+ const struct csi_formatter_pix_format *pix_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_mbus_frame_desc fd = {};
+ u64 configured_streams = 0;
+ unsigned int i;
+ u32 val;
+ int vc;
+ int ret;
+
+ ret = v4l2_subdev_call(formatter->csi_sd, pad, get_frame_desc,
+ formatter->remote_pad, &fd);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < CSI_FORMATTER_VC_NUM; ++i) {
+ if (!(stream_mask & BIT(i)))
+ continue;
+
+ fmt = v4l2_subdev_state_get_format(state,
+ CSI_FORMATTER_PAD_SINK, i);
+
+ pix_fmt = csi_formatter_find_format(fmt->code);
+
+ val = BIT(csi_formatter_get_index_by_dt(pix_fmt->data_type));
+
+ if (ret == -ENOIOCTLCMD) {
+ /*
+ * Source doesn't implement get_frame_desc, use
+ * default VC 0
+ */
+ vc = 0;
+ } else {
+ vc = csi_formatter_get_vc(formatter, &fd, i);
+ if (vc < 0) {
+ ret = vc;
+ goto err_cleanup;
+ }
+ }
+
+ /* Store the stream to VC mapping for stop_stream */
+ formatter->stream_to_vc[i] = vc;
+
+ csi_formatter_write(formatter, CSI_VC_PIXEL_DATA_TYPE(vc), val);
+ configured_streams |= BIT(i);
+ }
+
+ return 0;
+
+err_cleanup:
+ csi_formatter_stop_stream(formatter, configured_streams);
+ return ret;
+}
+
+static int csi_formatter_subdev_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+ struct device *dev = formatter->dev;
+ u64 sink_streams;
+ int ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ CSI_FORMATTER_PAD_SOURCE,
+ CSI_FORMATTER_PAD_SINK,
+ &streams_mask);
+ if (!sink_streams || !streams_mask)
+ return -EINVAL;
+
+ guard(mutex)(&formatter->lock);
+
+ if (!formatter->enabled_streams) {
+ ret = pm_runtime_resume_and_get(formatter->dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to resume runtime PM: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = csi_formatter_start_stream(formatter, state, streams_mask);
+ if (ret)
+ goto err_runtime_put;
+
+ ret = v4l2_subdev_enable_streams(formatter->csi_sd,
+ formatter->remote_pad,
+ sink_streams);
+ if (ret)
+ goto err_stop_stream;
+
+ formatter->enabled_streams |= streams_mask;
+
+ return 0;
+
+err_stop_stream:
+ csi_formatter_stop_stream(formatter, streams_mask);
+err_runtime_put:
+ if (!formatter->enabled_streams)
+ pm_runtime_put(formatter->dev);
+ return ret;
+}
+
+static int csi_formatter_subdev_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+ u64 sink_streams;
+ int ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ CSI_FORMATTER_PAD_SOURCE,
+ CSI_FORMATTER_PAD_SINK,
+ &streams_mask);
+ if (!sink_streams || !streams_mask)
+ return -EINVAL;
+
+ guard(mutex)(&formatter->lock);
+
+ ret = v4l2_subdev_disable_streams(formatter->csi_sd, formatter->remote_pad,
+ sink_streams);
+ if (ret)
+ dev_err(formatter->dev, "Failed to disable streams: %d\n", ret);
+
+ csi_formatter_stop_stream(formatter, streams_mask);
+
+ formatter->enabled_streams &= ~streams_mask;
+
+ if (!formatter->enabled_streams)
+ pm_runtime_put(formatter->dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops formatter_subdev_pad_ops = {
+ .enum_mbus_code = csi_formatter_subdev_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi_formatter_subdev_set_fmt,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
+ .set_routing = csi_formatter_subdev_set_routing,
+ .enable_streams = csi_formatter_subdev_enable_streams,
+ .disable_streams = csi_formatter_subdev_disable_streams,
+};
+
+static const struct v4l2_subdev_ops formatter_subdev_ops = {
+ .pad = &formatter_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops formatter_internal_ops = {
+ .init_state = csi_formatter_subdev_init_state,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static const struct media_entity_operations formatter_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+};
+
+static int csi_formatter_subdev_init(struct csi_formatter *formatter)
+{
+ struct v4l2_subdev *sd = &formatter->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &formatter_subdev_ops);
+
+ snprintf(sd->name, sizeof(sd->name), "%s", dev_name(formatter->dev));
+ sd->internal_ops = &formatter_internal_ops;
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS |
+ V4L2_SUBDEV_FL_STREAMS;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->entity.ops = &formatter_entity_ops;
+ sd->dev = formatter->dev;
+
+ formatter->pads[CSI_FORMATTER_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ formatter->pads[CSI_FORMATTER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, CSI_FORMATTER_PAD_NUM,
+ formatter->pads);
+ if (ret) {
+ dev_err(formatter->dev, "Failed to init pads\n");
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ media_entity_cleanup(&sd->entity);
+
+ return ret;
+}
+
+static inline struct csi_formatter *
+notifier_to_csi_formatter(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_formatter, notifier);
+}
+
+static int csi_formatter_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ struct csi_formatter *formatter = notifier_to_csi_formatter(notifier);
+ struct v4l2_subdev *sdev = &formatter->sd;
+ struct media_pad *sink = &sdev->entity.pads[CSI_FORMATTER_PAD_SINK];
+ struct media_pad *remote_pad;
+ int ret;
+
+ formatter->csi_sd = sd;
+
+ dev_dbg(formatter->dev, "Bound subdev: %s pad\n", sd->name);
+
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink, link_flags);
+ if (ret < 0)
+ return ret;
+
+ remote_pad = media_pad_remote_pad_first(sink);
+ if (!remote_pad) {
+ dev_err(formatter->dev, "Pipe not setup correctly\n");
+ return -EPIPE;
+ }
+ formatter->remote_pad = remote_pad->index;
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations formatter_notify_ops = {
+ .bound = csi_formatter_notify_bound,
+};
+
+static int csi_formatter_async_register(struct csi_formatter *formatter)
+{
+ struct device *dev = formatter->dev;
+ struct v4l2_async_connection *asc;
+ int ret;
+
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ v4l2_async_subdev_nf_init(&formatter->notifier, &formatter->sd);
+
+ asc = v4l2_async_nf_add_fwnode_remote(&formatter->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_cleanup_notifier;
+ }
+
+ formatter->notifier.ops = &formatter_notify_ops;
+
+ ret = v4l2_async_nf_register(&formatter->notifier);
+ if (ret)
+ goto err_cleanup_notifier;
+
+ ret = v4l2_async_register_subdev(&formatter->sd);
+ if (ret)
+ goto err_unregister_notifier;
+
+ return 0;
+
+err_unregister_notifier:
+ v4l2_async_nf_unregister(&formatter->notifier);
+err_cleanup_notifier:
+ v4l2_async_nf_cleanup(&formatter->notifier);
+ return ret;
+}
+
+static void csi_formatter_async_unregister(struct csi_formatter *formatter)
+{
+ v4l2_async_unregister_subdev(&formatter->sd);
+ v4l2_async_nf_unregister(&formatter->notifier);
+ v4l2_async_nf_cleanup(&formatter->notifier);
+}
+
+/* -----------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+static int csi_formatter_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+
+ clk_disable_unprepare(formatter->clk);
+
+ return 0;
+}
+
+static int csi_formatter_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+
+ return clk_prepare_enable(formatter->clk);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(csi_formatter_pm_ops,
+ csi_formatter_runtime_suspend,
+ csi_formatter_runtime_resume, NULL);
+
+static int csi_formatter_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct csi_formatter *formatter;
+ u32 val;
+ int ret;
+
+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
+ if (!formatter)
+ return -ENOMEM;
+
+ /* Initialize stream to VC mapping to invalid */
+ memset(formatter->stream_to_vc, 0xff, sizeof(formatter->stream_to_vc));
+
+ formatter->dev = dev;
+
+ ret = devm_mutex_init(dev, &formatter->lock);
+ if (ret)
+ return ret;
+
+ formatter->regs = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(formatter->regs))
+ return dev_err_probe(dev, PTR_ERR(formatter->regs),
+ "Failed to get csi formatter regmap\n");
+
+ ret = of_property_read_u32(dev->of_node, "reg", &val);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get csi formatter reg property\n");
+
+ formatter->reg_offset = val;
+
+ formatter->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(formatter->clk))
+ return dev_err_probe(dev, PTR_ERR(formatter->clk),
+ "Failed to get pixel clock\n");
+
+ ret = csi_formatter_subdev_init(formatter);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to initialize formatter subdev\n");
+
+ platform_set_drvdata(pdev, &formatter->sd);
+
+ /* Enable runtime PM. */
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto err_cleanup_subdev;
+
+ ret = csi_formatter_async_register(formatter);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "Failed to register async subdevice\n");
+ goto err_cleanup_subdev;
+ }
+
+ return 0;
+
+err_cleanup_subdev:
+ v4l2_subdev_cleanup(&formatter->sd);
+ media_entity_cleanup(&formatter->sd.entity);
+ return ret;
+}
+
+static void csi_formatter_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+
+ csi_formatter_async_unregister(formatter);
+
+ v4l2_subdev_cleanup(&formatter->sd);
+ media_entity_cleanup(&formatter->sd.entity);
+}
+
+static const struct of_device_id csi_formatter_of_match[] = {
+ { .compatible = "fsl,imx95-csi-formatter" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, csi_formatter_of_match);
+
+static struct platform_driver csi_formatter_device_driver = {
+ .driver = {
+ .name = "csi-pixel-formatter",
+ .of_match_table = csi_formatter_of_match,
+ .pm = pm_ptr(&csi_formatter_pm_ops),
+ },
+ .probe = csi_formatter_probe,
+ .remove = csi_formatter_remove,
+};
+
+module_platform_driver(csi_formatter_device_driver);
+
+MODULE_AUTHOR("NXP Semiconductor, Inc.");
+MODULE_DESCRIPTION("NXP i.MX95 CSI Pixel Formatter driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH v10 3/4] dt-bindings: clock: imx95-blk-ctl: Define formatter child node schema
From: guoniu.zhou @ 2026-06-18 9:41 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Abel Vesa, Peng Fan,
Michael Turquette, Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou
In-Reply-To: <20260618-csi_formatter-v10-0-f23830312ba5@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
The Camera CSR contains control registers for multiple CSI formatter IPs
at different register offsets. Each formatter is an independent hardware
block with its own clock input and media pipeline connection.
Define schema to allow formatter child nodes under nxp,imx95-camera-csr,
with 'reg' property specifying the formatter's register offset within the
CSR address space.
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
---
Changes in v10:
- Use single quotes for regex pattern to be consistent (Krzysztof Kozlowski)
- Add formatter subnode binding and camera-csr syscon example
- Update commit title and message
Changes in v9:
- New patch to address the issue of formatter acting as a child node of syscon
---
.../bindings/clock/nxp,imx95-blk-ctl.yaml | 64 +++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
index 534fa219d9f9..b4d0a7670fac 100644
--- a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
+++ b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
@@ -46,7 +46,27 @@ required:
- power-domains
- clocks
-additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nxp,imx95-camera-csr
+ then:
+ properties:
+ '#address-cells':
+ const: 1
+ '#size-cells':
+ const: 1
+ required:
+ - '#address-cells'
+ - '#size-cells'
+ patternProperties:
+ '^formatter@[0-9a-f]+$':
+ type: object
+ $ref: /schemas/media/fsl,imx95-csi-formatter.yaml#
+
+unevaluatedProperties: false
examples:
- |
@@ -57,4 +77,46 @@ examples:
clocks = <&scmi_clk 114>;
power-domains = <&scmi_devpd 21>;
};
+
+ - |
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ syscon@4ac10000 {
+ compatible = "nxp,imx95-camera-csr", "syscon";
+ reg = <0x4ac10000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #clock-cells = <1>;
+ clocks = <&scmi_clk 62>;
+ power-domains = <&scmi_devpd 3>;
+
+ formatter@20 {
+ compatible = "fsl,imx95-csi-formatter";
+ reg = <0x20 0x100>;
+ clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
+ power-domains = <&scmi_devpd 3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mipi_csi_0_out>;
+ };
+
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&isi_in_2>;
+ };
+ };
+ };
+ };
+ };
...
--
2.34.1
^ permalink raw reply related
* [PATCH v10 2/4] media: dt-bindings: Add CSI Pixel Formatter DT bindings
From: guoniu.zhou @ 2026-06-18 9:41 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Abel Vesa, Peng Fan,
Michael Turquette, Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou
In-Reply-To: <20260618-csi_formatter-v10-0-f23830312ba5@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
The i.MX95 CSI pixel formatting module uses packet info, pixel and
non-pixel data from the CSI-2 host controller and reformat them to
match Pixel Link(PL) definition.
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
---
Changes in v10:
- Drop syscon parent node from example
- Drop Reviewed-by tags from Frank and Krzysztof due to binding changes
- Add description for reg property
- Add space after formatter@20 before opening brace in example
- Enhance the port description with more detailed information
- Delete the blank line immediately following the endpoint in example
Changes in v9:
- Use direct node instead of syscon wrapper in example
Changes in v8:
- Use standard port reference instead of video-interfaces.yaml
- Add parent syscon node in example to show device integration
- Add required constraints for port@0 and port@1 in ports node
Changes in v7:
- Change compatible to imx95-csi-formatter as IP is i.MX95 specific per Marco's suggestion
Link: https://lore.kernel.org/linux-media/20260511-csi_formatter-v6-0-01028e312e2b@oss.nxp.com/T/#mcd135b3de179b3cb69daa1fd6e0e8e27c85b3332
---
.../bindings/media/fsl,imx95-csi-formatter.yaml | 88 ++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml b/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
new file mode 100644
index 000000000000..58c4e1cc056b
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/fsl,imx95-csi-formatter.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: i.MX95 CSI Pixel Formatter
+
+maintainers:
+ - Guoniu Zhou <guoniu.zhou@nxp.com>
+
+description:
+ The CSI pixel formatting module found on i.MX95 uses packet info, pixel
+ and non-pixel data from the CSI-2 host controller and reformat them to
+ match Pixel Link(PL) definition.
+
+properties:
+ compatible:
+ const: fsl,imx95-csi-formatter
+
+ reg:
+ maxItems: 1
+ description: Register offset and size within the parent syscon
+
+ clocks:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Input port, connects to MIPI CSI-2 receiver output (IDI interface)
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output port, connects to ISI input via Pixel Link (PL)
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ formatter@20 {
+ compatible = "fsl,imx95-csi-formatter";
+ reg = <0x20 0x100>;
+ clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
+ power-domains = <&scmi_devpd 3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mipi_csi_0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&isi_in_2>;
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v10 1/4] dt-bindings: clock: imx95-blk-ctl: Use single quotes consistently
From: guoniu.zhou @ 2026-06-18 9:41 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Abel Vesa, Peng Fan,
Michael Turquette, Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou
In-Reply-To: <20260618-csi_formatter-v10-0-f23830312ba5@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
Change "clocks" to 'clocks' in the description to match the quote style
used for property names like '#clock-cells' throughout the file.
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
---
Changes in v10:
- New patch to fix inconsistent quote usage (Krzysztof Kozlowski)
---
Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
index 27403b4c52d6..534fa219d9f9 100644
--- a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
+++ b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
@@ -36,7 +36,7 @@ properties:
const: 1
description:
The clock consumer should specify the desired clock by having the clock
- ID in its "clocks" phandle cell. See
+ ID in its 'clocks' phandle cell. See
include/dt-bindings/clock/nxp,imx95-clock.h
required:
--
2.34.1
^ permalink raw reply related
* [PATCH v10 0/4] media: nxp: Add CSI Pixel Formatter support
From: guoniu.zhou @ 2026-06-18 9:41 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Abel Vesa, Peng Fan,
Michael Turquette, Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou, Frank Li
CSI Pixel Formatter is a module found on i.MX95. It could unpack the
pixels received by the formatter and reformat them to meet the pixel
link format requirement.
This patch series adds a new V4L2 driver for CSI Pixel Formatter.
Background
----------
The Camera CSR binding was upstreamed during the pre-silicon phase when
the hardware integration details were not fully finalized. At that time,
the syscon binding focused on the CSR's primary role as a shared register
provider for clock gating, QoS, and error handling across Camera domain IPs.
However, the Camera CSR hardware is actually a mixed-function register
container with varying control levels over different IP blocks:
- ISP/CSI: Clock gating only
- ISI: QoS configuration
- CSI: Buffer overflow handling
- LPCAC: Error handling only
- ISP: Pixel link selection
- Formatter: Complete IP control (full register set)
Why Child Nodes Now
-------------------
The CSI formatter is unique among these IPs - it's an independent hardware
block whose complete control registers reside within the CSR address
space, not just a few configuration bits. This architectural detail was
not reflected in the original binding because:
1. The pre-silicon binding focused on simple register-level controls
2. The syscon pattern for complete IP control was not initially considered
Discussion with original CSR author(Peng Fan) confirmed this reflects the
intended hardware design that wasn't fully captured during pre-silicon
upstreaming.
v4l2-compliance 1.28.1-5233, 64 bits, 64-bit time_t
v4l2-compliance SHA: fc15e229d9d3 2024-07-23 19:22:15
Compliance test for device /dev/v4l-subdev9:
Driver Info:
Driver version : 7.1.0
Capabilities : 0x00000002
Streams Support
Client Capabilities: 0x0000000000000003
streams interval-uses-which
Required ioctls:
test VIDIOC_SUDBEV_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/v4l-subdev9 open: OK
test VIDIOC_SUBDEV_QUERYCAP: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 0 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Sub-Device routing ioctls:
test Try VIDIOC_SUBDEV_G_ROUTING/VIDIOC_SUBDEV_S_ROUTING: OK
test Active VIDIOC_SUBDEV_G_ROUTING/VIDIOC_SUBDEV_S_ROUTING: OK
Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK (Not Supported)
test VIDIOC_TRY_FMT: OK (Not Supported)
test VIDIOC_S_FMT: OK (Not Supported)
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
test CREATE_BUFS maximum buffers: OK
test VIDIOC_REMOVE_BUFS: OK
test VIDIOC_EXPBUF: OK (Not Supported)
test Requests: OK (Not Supported)
Total for device /dev/v4l-subdev9: 47, Succeeded: 47, Failed: 0, Warnings: 0
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
---
Changes in v10:
- Rebase to latest media/next
- [NEW PATCH] Use single quotes consistently (Krzysztof Kozlowski)
- Drop syscon parent node from example
- Drop Reviewed-by tags from Frank and Krzysztof due to binding changes,
requesting re-review
- Add description for reg property
- Add space after formatter@20 before opening brace in example
- Enhance the port description with more detailed information
- Delete the blank line immediately following the endpoint in example
- Use single quotes for regex pattern to be consistent (Krzysztof Kozlowski)
- Add formatter subnode binding and camera-csr syscon example
- Update commit title and message
- Use u8 for vc in csi_formatter_get_vc() and drop vc < 0 check
- Add MFD_SYSCON dependency to Kconfig
- Fix stream/VC mapping potential mismatch in start/stop_stream functions
- Link to v9: https://lore.kernel.org/r/20260526-csi_formatter-v9-0-ca3d8c334c39@oss.nxp.com
Changes in v9:
- [NEW PATCH] Fix formatter as syscon child node issue
- Link to v8: https://lore.kernel.org/r/20260525-csi_formatter-v8-0-6b646231224b@oss.nxp.com
Changes in v8:
- Rebase to latest media/next
- Use standard port reference instead of video-interfaces.yaml
- Add parent syscon node in example to show device integration
- Remove fmt field and look up format from subdev state instead
- Unify function and structure naming to use csi_formatter_ prefix
- Remove misleading alignment comment from set_fmt function
- Optimize get_frame_desc to call once per start_stream
- Replace V4L2_FRAME_DESC_ENTRY_MAX with CSI_FORMATTER_VC_NUM in loops
- Remove redundant debug message in enable_streams
- Use MEDIA_PAD_FL_MUST_CONNECT flag instead of manual link check
- Link to v7: https://lore.kernel.org/r/20260518-csi_formatter-v7-0-562b750557e3@oss.nxp.com
Changes in v7:
- Change compatible to imx95-csi-formatter as IP is i.MX95 specific per Marco's suggestion
Link: https://lore.kernel.org/linux-media/20260511-csi_formatter-v6-0-01028e312e2b@oss.nxp.com/T/#mcd135b3de179b3cb69daa1fd6e0e8e27c85b3332
- Update references from imx9 to imx95 for consistency with dt-bindings
- Enable PM runtime before async registration
- Link to v6: https://lore.kernel.org/r/20260511-csi_formatter-v6-0-01028e312e2b@oss.nxp.com
Changes in v6:
- Rebase to latest media/next
- Update v4l2-compliace test
- Remove unused header includes
- Unify macro naming: VCx/VCX -> VC and parameter x -> vc
- Remove unused format field from csi_formatter struct
- Use compact initialization for formats array
- Make find_csi_format() return NULL instead of default format
- Use unsigned int for array index in find_csi_format()
- Add err_ prefix to error handling labels
- Add v4l2_subdev_cleanup() and reorder cleanup sequence
- Update enable_streams debug output format
- Rename VC_MAX to VC_NUM and fix boundary check
- Update CSI formatter Kconfig description
- Use v4l2_subdev_get_frame_desc_passthrough() helper
- Fix error paths in async registration and probe
- Add mutex to protect enabled_streams
- Switch to devm_pm_runtime_enable()
- Remove redundant num_routes check in set_routing
- Optimize get_index_by_dt() and add warning for unsupported type
- csi_formatter_start/stop_stream: Process all streams in mask
- Link to v5: https://lore.kernel.org/r/20260123-csi_formatter-v5-0-d5b803f867bf@nxp.com
Changes in v5:
- Remove CSI_FORMATTER_DRV_NAME macro since only use once.
- Remove sd->owner = THIS_MODULE;
- Simplify code by using DEFINE_RUNTIME_DEV_PM_OPS macro.
- Link to v4: https://lore.kernel.org/r/20260122-csi_formatter-v4-0-6f6fcad1c33a@nxp.com
Changes in v4:
- Rebase to latest media/next.
- Add comments to describe the index field in formatter_dt_to_index_map array.
- Link to v3: https://lore.kernel.org/r/20251219-csi_formatter-v3-0-8680d6d87091@nxp.com
Changes in v3:
- Rename nxp,imx9-csi-formatter.yaml to fsl,imx9-csi-formatter.yaml.
- Drop clock-names property.
- Drop macro IMX95_PD_CAMERA definition and use a constant directly.
[PATCH 1/2] media: dt-bindings: Add CSI Pixel Formatter DT bindings
- Remove the assignment driver.owner = THIS_MODULE.
- Assign struct fwnode_handle *ep __free(fwnode_handle) when definition.
- Update yaml file name for csi formatter in MAINTAINERS.
[PATCH 2/2] media: nxp: Add i.MX9 CSI pixel formatter v4l2 driver
- Link to v2: https://lore.kernel.org/r/20251217-csi_formatter-v2-0-62168af80210@nxp.com
Changes in v2:
- Delete "|" for description key.
- Add empty line between child node and property.
- Delete labels for endpoint of child nodes.
[PATCH 1/2] media: dt-bindings: Add CSI Pixel Formatter DT bindings
- Update commit message.
- Use the value defined by bellow macros directly since they are used only once.
#define CSI_FORMATTER_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_1X16
#define CSI_FORMATTER_DEF_PIX_WIDTH 1920U
#define CSI_FORMATTER_DEF_PIX_HEIGHT 1080U
#define CSI_FORMATTER_MAX_PIX_WIDTH 0xffff
#define CSI_FORMATTER_MAX_PIX_HEIGHT 0xffff
- Use macro pm_ptr() to fix build warning when CONFIG_PM is disabled.
- Finish route loop by break statement, instead of goto.
- Return dev_err_probe() when meet errors in probe() function instead of dev_err().
- Remove MODULE_ALIAS().
- Refine .enable(.dsable)_stream callback implementation, include bellow changes:
Add stream checking.
Fix potential pm runtime count unbalance issue.
Add stop stream error handling when enabling remote subdev stream.
- Use __free(fwnode_handle) to drop reference to a device node automatically.
[PATCH 2/2] media: nxp: Add i.MX9 CSI pixel formatter v4l2 driver
- Link to v1: https://lore.kernel.org/r/20251203-csi_formatter-v1-0-eb9e1147b49e@nxp.com
---
Guoniu Zhou (4):
dt-bindings: clock: imx95-blk-ctl: Use single quotes consistently
media: dt-bindings: Add CSI Pixel Formatter DT bindings
dt-bindings: clock: imx95-blk-ctl: Define formatter child node schema
media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
.../bindings/clock/nxp,imx95-blk-ctl.yaml | 66 +-
.../bindings/media/fsl,imx95-csi-formatter.yaml | 88 +++
MAINTAINERS | 8 +
drivers/media/platform/nxp/Kconfig | 15 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx95-csi-formatter.c | 775 +++++++++++++++++++++
6 files changed, 951 insertions(+), 2 deletions(-)
---
base-commit: 06cb687a5132fcffe624c0070576ab852ac6b568
change-id: 20251125-csi_formatter-e6d29316dce6
Best regards,
--
Guoniu Zhou <guoniu.zhou@oss.nxp.com>
^ permalink raw reply
* [PATCH net v2] net: ethernet: ti: icssg: guard PA stat lookups
From: Philippe Schenker @ 2026-06-18 9:30 UTC (permalink / raw)
To: netdev
Cc: Philippe Schenker, Simon Horman, danishanwar, rogerq,
linux-arm-kernel, stable, Andrew Lunn, David Carlier,
David S. Miller, Eric Dumazet, Jacob Keller, Jakub Kicinski,
Kevin Hao, Meghana Malladi, Paolo Abeni, Vadim Fedorenko,
linux-kernel
From: Philippe Schenker <philippe.schenker@impulsing.ch>
icssg_ndo_get_stats64() unconditionally calls emac_get_stat_by_name()
with FW PA stat names regardless of whether the PA stats block is
present on the hardware. emac_get_stat_by_name() already guards the
PA stats lookup with `if (emac->prueth->pa_stats)`; when that pointer
is NULL the lookup falls through to netdev_err() and returns -EINVAL.
Because ndo_get_stats64 is polled regularly by the networking stack
this produces thousands of log entries of the form:
icssg-prueth icssg1-eth end0: Invalid stats FW_RX_ERROR
A secondary consequence is that the int(-EINVAL) return value is
implicitly widened to a near-ULLONG_MAX unsigned value when accumulated
into the __u64 fields of rtnl_link_stats64, silently corrupting the
rx_errors, rx_dropped and tx_dropped counters reported by `ip -s link`.
Every other PA-aware code path in the driver is already guarded with
the same `if (emac->prueth->pa_stats)` check. Apply the same guard
here.
Fixes: 0d15a26b247d ("net: ti: icssg-prueth: Add ICSSG FW Stats")
Signed-off-by: Philippe Schenker <philippe.schenker@impulsing.ch>
Reviewed-by: Simon Horman <horms@kernel.org>
Cc: danishanwar@ti.com
Cc: rogerq@kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: stable@vger.kernel.org
---
Changes in v2:
- Removed newline between Fixes tag and Signed-off-by
- Use return in if statement to guard so we get rid
of the 80 char warnings.
- Added Simon's Reviewed-by. Thanks!
drivers/net/ethernet/ti/icssg/icssg_common.c | 49 +++++++++++---------
1 file changed, 28 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index a28a608f9bf4..d9af6419e032 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -1628,28 +1628,35 @@ void icssg_ndo_get_stats64(struct net_device *ndev,
stats->rx_over_errors = emac_get_stat_by_name(emac, "rx_over_errors");
stats->multicast = emac_get_stat_by_name(emac, "rx_multicast_frames");
- stats->rx_errors = ndev->stats.rx_errors +
- emac_get_stat_by_name(emac, "FW_RX_ERROR") +
- emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") +
- emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") +
- emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") +
- emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN");
- stats->rx_dropped = ndev->stats.rx_dropped +
- emac_get_stat_by_name(emac, "FW_DROPPED_PKT") +
- emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") +
- emac_get_stat_by_name(emac, "FW_INF_SAV") +
- emac_get_stat_by_name(emac, "FW_INF_SA_DL") +
- emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") +
- emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") +
- emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") +
- emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") +
- emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER");
+ stats->rx_errors = ndev->stats.rx_errors;
+ stats->rx_dropped = ndev->stats.rx_dropped;
stats->tx_errors = ndev->stats.tx_errors;
- stats->tx_dropped = ndev->stats.tx_dropped +
- emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") +
- emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") +
- emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") +
- emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF");
+ stats->tx_dropped = ndev->stats.tx_dropped;
+
+ if (!emac->prueth->pa_stats)
+ return;
+
+ stats->rx_errors +=
+ emac_get_stat_by_name(emac, "FW_RX_ERROR") +
+ emac_get_stat_by_name(emac, "FW_RX_EOF_SHORT_FRMERR") +
+ emac_get_stat_by_name(emac, "FW_RX_B0_DROP_EARLY_EOF") +
+ emac_get_stat_by_name(emac, "FW_RX_EXP_FRAG_Q_DROP") +
+ emac_get_stat_by_name(emac, "FW_RX_FIFO_OVERRUN");
+ stats->rx_dropped +=
+ emac_get_stat_by_name(emac, "FW_DROPPED_PKT") +
+ emac_get_stat_by_name(emac, "FW_INF_PORT_DISABLED") +
+ emac_get_stat_by_name(emac, "FW_INF_SAV") +
+ emac_get_stat_by_name(emac, "FW_INF_SA_DL") +
+ emac_get_stat_by_name(emac, "FW_INF_PORT_BLOCKED") +
+ emac_get_stat_by_name(emac, "FW_INF_DROP_TAGGED") +
+ emac_get_stat_by_name(emac, "FW_INF_DROP_PRIOTAGGED") +
+ emac_get_stat_by_name(emac, "FW_INF_DROP_NOTAG") +
+ emac_get_stat_by_name(emac, "FW_INF_DROP_NOTMEMBER");
+ stats->tx_dropped +=
+ emac_get_stat_by_name(emac, "FW_RTU_PKT_DROP") +
+ emac_get_stat_by_name(emac, "FW_TX_DROPPED_PACKET") +
+ emac_get_stat_by_name(emac, "FW_TX_TS_DROPPED_PACKET") +
+ emac_get_stat_by_name(emac, "FW_TX_JUMBO_FRM_CUTOFF");
}
EXPORT_SYMBOL_GPL(icssg_ndo_get_stats64);
--
2.54.0
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
branch: fix-icssg_common-pa-stats-errors__master-7-1
^ permalink raw reply related
* Re: [PATCH net] net: ethernet: ti: icssg: guard PA stat lookups
From: Philippe Schenker @ 2026-06-18 9:29 UTC (permalink / raw)
To: Simon Horman
Cc: netdev, danishanwar, rogerq, linux-arm-kernel, stable,
Andrew Lunn, David Carlier, David S. Miller, Eric Dumazet,
Jacob Keller, Jakub Kicinski, Kevin Hao, Meghana Malladi,
Paolo Abeni, Vadim Fedorenko, linux-kernel
In-Reply-To: <20260618091004.GG827683@horms.kernel.org>
[-- Attachment #1: Type: text/plain, Size: 1794 bytes --]
Hi Simon
Thanks for the review and I'll send a v2 with that blank line removed.
Saw it right after sending the patch.
Philippe
On Thu, 2026-06-18 at 10:10 +0100, Simon Horman wrote:
> On Tue, Jun 16, 2026 at 04:35:34PM +0200, Philippe Schenker wrote:
> > From: Philippe Schenker <philippe.schenker@impulsing.ch>
> >
> > icssg_ndo_get_stats64() unconditionally calls
> > emac_get_stat_by_name()
> > with FW PA stat names regardless of whether the PA stats block is
> > present on the hardware. emac_get_stat_by_name() already guards
> > the
> > PA stats lookup with `if (emac->prueth->pa_stats)`; when that
> > pointer
> > is NULL the lookup falls through to netdev_err() and returns -
> > EINVAL.
> > Because ndo_get_stats64 is polled regularly by the networking stack
> > this produces thousands of log entries of the form:
> >
> > icssg-prueth icssg1-eth end0: Invalid stats FW_RX_ERROR
> >
> > A secondary consequence is that the int(-EINVAL) return value is
> > implicitly widened to a near-ULLONG_MAX unsigned value when
> > accumulated
> > into the __u64 fields of rtnl_link_stats64, silently corrupting the
> > rx_errors, rx_dropped and tx_dropped counters reported by `ip -s
> > link`.
> >
> > Every other PA-aware code path in the driver is already guarded
> > with
> > the same `if (emac->prueth->pa_stats)` check. Apply the same guard
> > here.
> >
> > Fixes: 0d15a26b247d ("net: ti: icssg-prueth: Add ICSSG FW Stats")
>
> nit: no blank line between tags
>
> >
> > Signed-off-by: Philippe Schenker <philippe.schenker@impulsing.ch>
> >
> > Cc: danishanwar@ti.com
> > Cc: rogerq@kernel.org
> > Cc: linux-arm-kernel@lists.infradead.org
> > Cc: stable@vger.kernel.org
>
> Reviewed-by: Simon Horman <horms@kernel.org>
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 659 bytes --]
^ permalink raw reply
* Re: [PATCH v2 2/6] iommu/arm-smmu: Add interconnect bandwidth voting support
From: Konrad Dybcio @ 2026-06-18 9:28 UTC (permalink / raw)
To: Bibek Kumar Patro, Dmitry Baryshkov
Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
linux-arm-kernel, iommu, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <ea345832-711d-4e32-af0f-bc74d8646aef@oss.qualcomm.com>
On 6/17/26 4:26 PM, Bibek Kumar Patro wrote:
>
>
> On 6/16/2026 5:51 AM, Dmitry Baryshkov wrote:
>> On Mon, Jun 15, 2026 at 06:36:51PM +0530, Bibek Kumar Patro wrote:
>>>
>>>
>>> On 6/8/2026 7:25 PM, Dmitry Baryshkov wrote:
>>>> On Tue, May 26, 2026 at 08:12:03PM +0530, Bibek Kumar Patro wrote:
>>>>> On some SoCs the SMMU registers require an active interconnect
>>>>> bandwidth vote to be accessible. While other clients typically
>>>>> satisfy this requirement implicitly, certain corner cases (e.g.
>>>>> during sleep/wakeup transitions) can leave the SMMU without a
>>>>> vote, causing intermittent register access failures.
>>>>>
>>>>> Add support for an optional interconnect path to the arm-smmu
>>>>> driver and vote for bandwidth while the SMMU is active. The path
>>>>> is acquired from DT if present and ignored otherwise.
>>>>>
>>>>> The bandwidth vote is enabled before accessing SMMU registers
>>>>> during probe and runtime resume, and released during runtime
>>>>> suspend and on error paths.
>>>>>
>>>>> Generally, from an architectural perspective, GEM_NOC and DDR are
>>>>> expected to have an active vote whenever the adreno_smmu block is
>>>>> powered on. In most common use cases, this requirement is implicitly
>>>>> satisfied because other GPU-related clients (for example, the GMU
>>>>> device) already hold a GEM_NOC vote when adreno_smmu is enabled.
>>>>>
>>>>> However, there are certain corner cases, such as during sleep/wakeup
>>>>> transitions, where the GEM_NOC vote can be removed before adreno_smmu
>>>>> is powered down. If adreno_smmu is then accessed while the interconnect
>>>>> vote is missing, it can lead to the observed failures. Because of the
>>>>> precise ordering involved, this scenario is difficult to reproduce
>>>>> consistently.
>>>>> (also GDSC is involved in adreno usecases can have an independent vote)
>>>>>
>>>>> Signed-off-by: Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
>>>>> ---
>>>>> drivers/iommu/arm/arm-smmu/arm-smmu.c | 57 +++++++++++++++++++++++++++++++++--
>>>>> drivers/iommu/arm/arm-smmu/arm-smmu.h | 2 ++
>>>>> 2 files changed, 57 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
>>>>> index 0bd21d206eb3e75c3b9fb1364cdc92e82c5aa499..07c7e44ec6a5bd1488f00f87d859a20495e46601 100644
>>>>> --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
>>>>> +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
>>>>> @@ -53,6 +53,11 @@
>>>>> #define MSI_IOVA_BASE 0x8000000
>>>>> #define MSI_IOVA_LENGTH 0x100000
>>>>> +/* Interconnect bandwidth vote values for the SMMU register access path */
>>>>> +#define ARM_SMMU_ICC_AVG_BW 0
>>>>> +#define ARM_SMMU_ICC_PEAK_BW_HIGH 1000
>>>>
>>>> totally random numbers, which might be different for non-Qualcomm platform.
>>>>
>>>
>>> Ideally, any non-zero value would be enough to keep the path active.
>>
>> This is true for Qualcomm devices. However, you are adding this to a
>> generic code.
>>
>>> Here 1 Would be enough to keep the path active, but might be too small to
>>> reliably keep the bus active.
>>> Other is UINT_MAX, which will reliably keep the bus active but might cause a
>>> power penalty.
>>>
>>> #define ARM_SMMU_ICC_PEAK_BW_HIGH UINT_MAX
>>>
>>> seems to be suitable here to reliably keep the bus active by BCM
>>> for both Qualcomm and non-Qualcomm platforms (with some power penalty).
>>>
>>> LMK, if you feel otherwise.
>>
>> Shift it to the qcom instance or provide platform-specific values? (My
>> preference would be towards the first solution).
>>
>
>
> To support platform-specific values, we may need to introduce a LUT-based approach in the driver. (Bandwidth voting values cannot be placed in device-tree property IIRC ?)
>
> Currently, all Qualcomm platforms use 0x1000 for SMMU ICC voting. I
(you used decimal 1000)
> can evaluate if this could be moved to a Qualcomm-specific
> implementation.
Add a vendor hook to arm_smmu_runtime_suspend/resume and handle it within
the QC driver
Konrad
^ permalink raw reply
* RE: [PATCH v4 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: Joakim Zhang @ 2026-06-18 9:27 UTC (permalink / raw)
To: Philipp Zabel, Conor Dooley
Cc: mturquette@baylibre.com, sboyd@kernel.org, bmasney@redhat.com,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Gary Yang, cix-kernel-upstream, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <cd63c1ab9831fdfd39aad8d1a40c4702a9cbb158.camel@pengutronix.de>
Hello,
> -----Original Message-----
> From: Philipp Zabel <p.zabel@pengutronix.de>
> Sent: Thursday, June 18, 2026 4:30 PM
> To: Joakim Zhang <joakim.zhang@cixtech.com>; Conor Dooley
> <conor@kernel.org>
> Cc: mturquette@baylibre.com; sboyd@kernel.org; bmasney@redhat.com;
> robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org; Gary Yang
> <Gary.Yang@cixtech.com>; cix-kernel-upstream <cix-kernel-
> upstream@cixtech.com>; linux-clk@vger.kernel.org;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org
> Subject: Re: [PATCH v4 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss
> clock controller
>
> EXTERNAL EMAIL
>
> CAUTION: Suspicious Email from unusual domain.
>
> On Do, 2026-06-18 at 01:43 +0000, Joakim Zhang wrote:
> > Hello,
> >
> >
> > > -----Original Message-----
> > > From: Conor Dooley <conor@kernel.org>
> > > Sent: Wednesday, June 17, 2026 11:56 PM
> > > To: Joakim Zhang <joakim.zhang@cixtech.com>
> > > Cc: mturquette@baylibre.com; sboyd@kernel.org; bmasney@redhat.com;
> > > robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org;
> > > p.zabel@pengutronix.de; Gary Yang <gary.yang@cixtech.com>;
> > > cix-kernel- upstream <cix-kernel-upstream@cixtech.com>;
> > > linux-clk@vger.kernel.org; devicetree@vger.kernel.org;
> > > linux-kernel@vger.kernel.org; linux-arm- kernel@lists.infradead.org
> > > Subject: Re: [PATCH v4 3/5] dt-bindings: clock:
> > > cix,sky1-audss-clock: add audss clock controller
> > >
> > > On Wed, Jun 17, 2026 at 02:04:35PM +0800, joakim.zhang@cixtech.com
> wrote:
> > > > From: Joakim Zhang <joakim.zhang@cixtech.com>
> > > >
> > > > The AUDSS CRU contains an internal clock tree of muxes, dividers
> > > > and gates for DSP, I2S, HDA, DMAC and related blocks. The clock
> > > > provider is a child node of the cix,sky1-audss-system-control
> > > > syscon and accesses registers through the parent MMIO region.
> > >
> > > Why can this not just be part of the parent syscon node?
> >
> > The clock and reset blocks are handled by different subsystems and
> maintainers (clk vs reset). Putting the clock provider on the parent syscon node
> would mean a single driver has to register both the reset controller and the
> clock provider on one device, which doesn't fit well.
>
> There are many examples of clock and reset drivers sharing the same node, by
> using platform_driver for one (usually clk) and auxiliary_driver for the other
> (usually reset).
OK, I will have a look. If you are also prefer to this, I will refactor the patch.
Thanks,
Joakim
^ permalink raw reply
* Re: [PATCH v2 3/6] arm64: dts: qcom: kodiak: Add GEM_NOC interconnect for adreno SMMU
From: Konrad Dybcio @ 2026-06-18 9:26 UTC (permalink / raw)
To: Bibek Kumar Patro, Dmitry Baryshkov
Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
linux-arm-kernel, iommu, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <26d51cbb-2d87-4564-b3c6-cc61ab900e19@oss.qualcomm.com>
On 6/8/26 4:37 PM, Bibek Kumar Patro wrote:
>
>
> On 6/8/2026 7:27 PM, Dmitry Baryshkov wrote:
>> On Tue, May 26, 2026 at 08:12:04PM +0530, Bibek Kumar Patro wrote:
>>> On Kodiak platforms, the Adreno SMMU requires a bandwidth vote on
>>> the GEM_NOC path (MASTER_GPU_TCU -> SLAVE_EBI1) before its registers
>>> are accessible. Without this vote, the SMMU may become unreachable,
>>> leading to intermittent probe failures and runtime issues.
>>>
>>> Add the required interconnect to ensure reliable register access.
>>
>> Does it only concern the GPU SMMU? What about the APPS SMMU? Should it
>> be voting on other interconnects too? I guess so, because currently I
>> see that TBUs vote for various interconnects. BTW: should apps_smmu also
>> vote on the power domains?
>>
>
> This race mainly occurs in GPU SMMU, where the GDSC can have an
Mainly or exclusively?
Konrad
> independent vote on the Adreno SMMU. However, the GEM_NOC vote may
> already have been removed by the GPU (or any consumer of adreno_smmu,
> e.g gmu), unless it is explicitly voted by the GPU SMMU (which acts as a
> supplier for the GPU). This mismatch can lead to SHUB timeouts or NoC
> errors.
>
> Mostly this race reported in suspend/resume cycle (when gpu/gmu devices moves to slumber/suspend state before adreno_smmu powers down
> and the later doesn't have explicit interconnect voting).
>
> In the case of APPS SMMU, such a race is not expected for any known
> use case. APPS SMMU is part of a shared infrastructure block, and its
> power is typically kept enabled as long as attached master devices are
> active. Therefore, explicit power-domain voting from APPS SMMU may not
> be required.
>
> Thanks,
> Bibek
>
>
>>>
>>> Signed-off-by: Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
>>> ---
>>> arch/arm64/boot/dts/qcom/kodiak.dtsi | 2 ++
>>> 1 file changed, 2 insertions(+)
>>>
>>> diff --git a/arch/arm64/boot/dts/qcom/kodiak.dtsi b/arch/arm64/boot/dts/qcom/kodiak.dtsi
>>> index fa540d8c2615dc02d941eb16bc7253204c2750bd..eefa4b836a81374ff437ab4bbcbc3fecc1590ab6 100644
>>> --- a/arch/arm64/boot/dts/qcom/kodiak.dtsi
>>> +++ b/arch/arm64/boot/dts/qcom/kodiak.dtsi
>>> @@ -3386,6 +3386,8 @@ adreno_smmu: iommu@3da0000 {
>>> power-domains = <&gpucc GPU_CC_CX_GDSC>;
>>> dma-coherent;
>>> + interconnects = <&gem_noc MASTER_GPU_TCU QCOM_ICC_TAG_ALWAYS
>>> + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
>>> };
>>> gfx_0_tbu: tbu@3dd9000 {
>>>
>>> --
>>> 2.34.1
>>>
>>
>
>
^ permalink raw reply
* [PATCH v2 3/4] arm64: smp: Defer RCU reporting until after local CPU capability checks
From: Jinjie Ruan @ 2026-06-18 9:24 UTC (permalink / raw)
To: catalin.marinas, will, tsbogend, pjw, palmer, aou, alex, tglx,
mingo, bp, dave.hansen, hpa, peterz, kees, nathan, linusw,
jpoimboe, lukas.bulwahn, ryan.roberts, ojeda, maz, timothy.hayes,
lpieralisi, thuth, menglong8.dong, oupton, yeoreum.yun,
miko.lenczewski, broonie, kevin.brodsky, james.clark, tabba,
mrigendra.chaubey, arnd, anshuman.khandual, x86, linux-kernel,
linux-arm-kernel, linux-mips, linux-riscv, apatel, mhklinux
Cc: ruanjinjie
In-Reply-To: <20260618092444.1316336-1-ruanjinjie@huawei.com>
To support HOTPLUG_PARALLEL on arm64, and to prevent a potential deadlock
on the control CPU, check_local_cpu_capabilities() must be executed
before cpuhp_ap_sync_alive(). This ensures that if an early capability
mismatch occurs and the AP invokes cpu_die_early(), the control CPU
can detect the boot timeout and proceed, rather than hanging
indefinitely.
Furthermore, under parallel bringup, cpuhp_ap_sync_alive() must be called
before rcutree_report_cpu_starting(). This sequence prevents a false
RCU CPU Stall Warning caused by the prolonged spin-waiting/busy-waiting
required during the AP synchronization process.
GICv3: CPU1: using allocated LPI pending table @0x0000000104160000
CPU1: Booted secondary processor 0x0000000001 [0x410fd082]
rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu: 2-O..!: (2 GPs behind) idle=0004/1/0x4000000000000000 softirq=0/0 fqs=2625
rcu: 3-O..!: (2 GPs behind) idle=0004/1/0x4000000000000000 softirq=0/0 fqs=2625
rcu: (detected by 0, t=5252 jiffies, g=-1187, q=1 ncpus=16)
rcu: Offline CPU 2 blocking current GP.
rcu: Offline CPU 3 blocking current GP.
To avoid suspicious RCU usage, commit ce3d31ad3cac ("arm64/smp: Move
rcu_cpu_starting() earlier") move rcutree_report_cpu_starting() earlier
which is before check_local_cpu_capabilities().
But For parallel bringup, the order should be as following:
secondary_start_kernel()
-> check_local_cpu_capabilities()
-> cpu_die_early()
-> cpuhp_ap_sync_alive()
-> rcutree_report_cpu_starting()
And this required order forces standard printk/pr_* statements inside
check_local_cpu_capabilities() to execute while the secondary CPU is still
marked as offline to RCU, triggering a lockdep "suspicious RCU usage" splat
due to console semaphore operations.
So converting early capability logging and failure paths to printk_deferred().
This pushes the logs into the lockless ringbuffer without triggering console
locks or RCU validation on the offline CPU, resolving the lockdep splat while
preserving critical error messages and the strictly required initialization
order.
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
arch/arm64/kernel/cpufeature.c | 20 ++++++++++----------
arch/arm64/kernel/smp.c | 4 ++--
arch/arm64/mm/context.c | 2 +-
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 0552202702bf..a5e0bc4d383b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3546,7 +3546,7 @@ static void update_cpu_capabilities(u16 scope_mask)
* system capabilities are finalised.
*/
if (!match_all && caps->desc && !caps->cpus)
- pr_info("detected: %s\n", caps->desc);
+ printk_deferred(KERN_INFO "detected: %s\n", caps->desc);
__set_bit(caps->capability, system_cpucaps);
@@ -3669,7 +3669,7 @@ static void verify_local_cpu_caps(u16 scope_mask)
}
if (i < ARM64_NCAPS) {
- pr_crit("CPU%d: Detected conflict for capability %d (%s), System: %d, CPU: %d\n",
+ printk_deferred(KERN_CRIT "CPU%d: Detected conflict for capability %d (%s), System: %d, CPU: %d\n",
smp_processor_id(), caps->capability,
caps->desc, system_has_cap, cpu_has_cap);
@@ -3697,7 +3697,7 @@ __verify_local_elf_hwcaps(const struct arm64_cpu_capabilities *caps)
for (; caps->matches; caps++)
if (cpus_have_elf_hwcap(caps) && !caps->matches(caps, SCOPE_LOCAL_CPU)) {
- pr_crit("CPU%d: missing HWCAP: %s\n",
+ printk_deferred(KERN_CRIT "CPU%d: missing HWCAP: %s\n",
smp_processor_id(), caps->desc);
cpu_die_early();
}
@@ -3716,7 +3716,7 @@ static void verify_sve_features(void)
unsigned long cpacr = cpacr_save_enable_kernel_sve();
if (vec_verify_vq_map(ARM64_VEC_SVE)) {
- pr_crit("CPU%d: SVE: vector length support mismatch\n",
+ printk_deferred(KERN_CRIT "CPU%d: SVE: vector length support mismatch\n",
smp_processor_id());
cpu_die_early();
}
@@ -3729,7 +3729,7 @@ static void verify_sme_features(void)
unsigned long cpacr = cpacr_save_enable_kernel_sme();
if (vec_verify_vq_map(ARM64_VEC_SME)) {
- pr_crit("CPU%d: SME: vector length support mismatch\n",
+ printk_deferred(KERN_CRIT "CPU%d: SME: vector length support mismatch\n",
smp_processor_id());
cpu_die_early();
}
@@ -3754,7 +3754,7 @@ static void verify_hyp_capabilities(void)
safe_vmid_bits = get_vmid_bits(safe_mmfr1);
vmid_bits = get_vmid_bits(mmfr1);
if (vmid_bits < safe_vmid_bits) {
- pr_crit("CPU%d: VMID width mismatch\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: VMID width mismatch\n", smp_processor_id());
cpu_die_early();
}
@@ -3763,7 +3763,7 @@ static void verify_hyp_capabilities(void)
ID_AA64MMFR0_EL1_PARANGE_SHIFT);
ipa_max = id_aa64mmfr0_parange_to_phys_shift(parange);
if (ipa_max < get_kvm_ipa_limit()) {
- pr_crit("CPU%d: IPA range mismatch\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: IPA range mismatch\n", smp_processor_id());
cpu_die_early();
}
}
@@ -3776,7 +3776,7 @@ static void verify_mpam_capabilities(void)
if (FIELD_GET(ID_AA64PFR0_EL1_MPAM_MASK, cpu_idr) !=
FIELD_GET(ID_AA64PFR0_EL1_MPAM_MASK, sys_idr)) {
- pr_crit("CPU%d: MPAM version mismatch\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: MPAM version mismatch\n", smp_processor_id());
cpu_die_early();
}
@@ -3784,7 +3784,7 @@ static void verify_mpam_capabilities(void)
sys_idr = read_sanitised_ftr_reg(SYS_MPAMIDR_EL1);
if (FIELD_GET(MPAMIDR_EL1_HAS_HCR, cpu_idr) !=
FIELD_GET(MPAMIDR_EL1_HAS_HCR, sys_idr)) {
- pr_crit("CPU%d: Missing MPAM HCR\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: Missing MPAM HCR\n", smp_processor_id());
cpu_die_early();
}
@@ -3793,7 +3793,7 @@ static void verify_mpam_capabilities(void)
sys_partid_max = FIELD_GET(MPAMIDR_EL1_PARTID_MAX, sys_idr);
sys_pmg_max = FIELD_GET(MPAMIDR_EL1_PMG_MAX, sys_idr);
if (cpu_partid_max < sys_partid_max || cpu_pmg_max < sys_pmg_max) {
- pr_crit("CPU%d: MPAM PARTID/PMG max values are mismatched\n", smp_processor_id());
+ printk_deferred(KERN_CRIT "CPU%d: MPAM PARTID/PMG max values are mismatched\n", smp_processor_id());
cpu_die_early();
}
}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 6bc90ee4820a..52edabc13d51 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -215,7 +215,6 @@ asmlinkage notrace void secondary_start_kernel(void)
if (system_uses_irq_prio_masking())
init_gic_priority_masking();
- rcutree_report_cpu_starting(cpu);
trace_hardirqs_off();
/*
@@ -224,6 +223,7 @@ asmlinkage notrace void secondary_start_kernel(void)
* fail to come online.
*/
check_local_cpu_capabilities();
+ rcutree_report_cpu_starting(cpu);
ops = get_cpu_ops(cpu);
if (ops->cpu_postboot)
@@ -404,7 +404,7 @@ void __noreturn cpu_die_early(void)
{
int cpu = smp_processor_id();
- pr_crit("CPU%d: will not boot\n", cpu);
+ printk_deferred(KERN_CRIT "CPU%d: will not boot\n", cpu);
/* Mark this CPU absent */
set_cpu_present(cpu, 0);
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 6b8a3245f393..9b5ab56aad5a 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -70,7 +70,7 @@ void verify_cpu_asid_bits(void)
* We cannot decrease the ASID size at runtime, so panic if we support
* fewer ASID bits than the boot CPU.
*/
- pr_crit("CPU%d: smaller ASID size(%u) than boot CPU (%u)\n",
+ printk_deferred(KERN_CRIT "CPU%d: smaller ASID size(%u) than boot CPU (%u)\n",
smp_processor_id(), asid, asid_bits);
cpu_panic_kernel(smp_processor_id());
}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 4/4] arm64: Add HOTPLUG_PARALLEL support for secondary CPUs
From: Jinjie Ruan @ 2026-06-18 9:24 UTC (permalink / raw)
To: catalin.marinas, will, tsbogend, pjw, palmer, aou, alex, tglx,
mingo, bp, dave.hansen, hpa, peterz, kees, nathan, linusw,
jpoimboe, lukas.bulwahn, ryan.roberts, ojeda, maz, timothy.hayes,
lpieralisi, thuth, menglong8.dong, oupton, yeoreum.yun,
miko.lenczewski, broonie, kevin.brodsky, james.clark, tabba,
mrigendra.chaubey, arnd, anshuman.khandual, x86, linux-kernel,
linux-arm-kernel, linux-mips, linux-riscv, apatel, mhklinux
Cc: ruanjinjie
In-Reply-To: <20260618092444.1316336-1-ruanjinjie@huawei.com>
Support for parallel secondary CPU bringup is already utilized by x86,
MIPS, and RISC-V. This patch brings this capability to the arm64
architecture.
Rework the global `secondary_data` accessed during early boot into
a per-CPU array `cpu_boot_data` to allow secondary CPUs to boot
in parallel.
And reuse `__cpu_logical_map` array in the early boot code in head.S
to resolve each secondary CPU's logical ID concurrently.
To fully enable HOTPLUG_PARALLEL, this patch implements:
1) An arm64-specific arch_cpuhp_init_parallel_bringup() handler.
2) An arm64-specific arch_cpuhp_kick_ap_alive() handler.
3) Callbacks to cpuhp_ap_sync_alive() inside secondary_start_kernel().
Tested natively with ATF on QEMU arm64 virt machine with 64 cores
and also tested with KVM arm64 guest with 128 vCPUs.
Bringup Time Comparison (ms, lower is better):
| Platform | Baseline| P=0 | P=1 | Delta(%)|
| -------------------------------- | ------- | ------- | ------ | ------- |
| 128 vCPUs KVM (256-core HIP09) | 1921.5 | 1895.9 | 2776.9 | -44.52% |
| 48 vCPUs KVM (64-core Cortex-A72)| 3644.7 | 3883.6 | 4406.3 | -20.9% |
| 64-core ATF QEMU | 2075.8 | 2080.7 | 1653.4 | 20.34% |
| 192-core server(HIP12) | 14619.2 | 14619.1 | 8589.4 | 41.21% |
| 32-core board | 2776.5 | 2881.0 | 1045.0 | 62.36% |
Tested-by: Michael Kelley <mhklinux@outlook.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/smp.h | 11 ++++++++++
arch/arm64/kernel/asm-offsets.c | 4 ++++
arch/arm64/kernel/head.S | 36 +++++++++++++++++++++++++++++++++
arch/arm64/kernel/smp.c | 33 ++++++++++++++++++++++++++++++
5 files changed, 85 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 9091c67e1cc2..8735e9d8ed13 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -113,6 +113,7 @@ config ARM64
select CPUMASK_OFFSTACK if NR_CPUS > 256
select DCACHE_WORD_ACCESS
select HAVE_EXTRA_IPI_TRACEPOINTS
+ select HOTPLUG_PARALLEL if SMP && HOTPLUG_CPU
select DYNAMIC_FTRACE if FUNCTION_TRACER
select DMA_BOUNCE_UNALIGNED_KMALLOC
select DMA_DIRECT_REMAP
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index e2151a01731f..30025030489c 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -92,7 +92,14 @@ struct secondary_data {
long status;
};
+#ifdef CONFIG_HOTPLUG_PARALLEL
+static_assert((sizeof(struct secondary_data) & (sizeof(struct secondary_data) - 1)) == 0,
+ "secondary_data size must be a power of 2 for assembly lsl assembly!");
+
+extern struct secondary_data cpu_boot_data[NR_CPUS];
+#else
extern struct secondary_data secondary_data;
+#endif
extern long __early_cpu_boot_status;
extern void secondary_entry(void);
@@ -124,7 +131,11 @@ static inline void __noreturn cpu_park_loop(void)
static inline void update_cpu_boot_status(unsigned int cpu, int val)
{
+#ifdef CONFIG_HOTPLUG_PARALLEL
+ WRITE_ONCE(cpu_boot_data[cpu].status, val);
+#else
WRITE_ONCE(secondary_data.status, val);
+#endif
/* Ensure the visibility of the status update */
dsb(ishst);
}
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index b6367ff3a49c..0a0aa965dbb4 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -11,6 +11,7 @@
#include <linux/arm_sdei.h>
#include <linux/sched.h>
#include <linux/ftrace.h>
+#include <linux/log2.h>
#include <linux/kexec.h>
#include <linux/mm.h>
#include <linux/kvm_host.h>
@@ -97,6 +98,9 @@ int main(void)
BLANK();
#endif
DEFINE(CPU_BOOT_TASK, offsetof(struct secondary_data, task));
+#ifdef CONFIG_HOTPLUG_PARALLEL
+ DEFINE(SECONDARY_DATA_SHIFT, ilog2(sizeof(struct secondary_data)));
+#endif
BLANK();
DEFINE(FTR_OVR_VAL_OFFSET, offsetof(struct arm64_ftr_override, val));
DEFINE(FTR_OVR_MASK_OFFSET, offsetof(struct arm64_ftr_override, mask));
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 87a822e5c4ca..000ab1acf7c5 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -12,6 +12,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
#include <linux/pgtable.h>
+#include <linux/threads.h>
#include <asm/asm_pointer_auth.h>
#include <asm/assembler.h>
@@ -378,6 +379,33 @@ alternative_else_nop_endif
br x8
SYM_FUNC_END(secondary_startup)
+#ifdef CONFIG_HOTPLUG_PARALLEL
+ /*
+ * Convert the physical MPIDR of the current secondary CPU
+ * to its logical CPUID by traversing __cpu_logical_map
+ * in parallel.
+ */
+ .macro mpidr_to_cpuid, mpidr, cpuid, tmp1, tmp2
+ mov_q \tmp1, MPIDR_HWID_BITMASK
+ and \mpidr, \mpidr, \tmp1
+
+ adr_l \tmp1, __cpu_logical_map
+ mov \cpuid, #0
+.Lfind_cpuid\@:
+ ldr \tmp2, [\tmp1, \cpuid, lsl #3]
+ cmp \tmp2, #-1
+ b.eq .Lnext_cpu\@
+ cmp \tmp2, \mpidr
+ b.eq .Lfound_cpuid\@
+.Lnext_cpu\@:
+ add \cpuid, \cpuid, #1
+ cmp \cpuid, #NR_CPUS
+ b.ne .Lfind_cpuid\@
+ b __secondary_too_slow
+.Lfound_cpuid\@:
+ .endm
+#endif
+
.text
SYM_FUNC_START_LOCAL(__secondary_switched)
mov x0, x20
@@ -391,7 +419,15 @@ SYM_FUNC_START_LOCAL(__secondary_switched)
msr vbar_el1, x5
isb
+#ifdef CONFIG_HOTPLUG_PARALLEL
+ mrs x0, mpidr_el1
+ mpidr_to_cpuid mpidr=x0, cpuid=x2, tmp1=x1, tmp2=x3
+
+ adr_l x0, cpu_boot_data
+ add x0, x0, x2, lsl #SECONDARY_DATA_SHIFT
+#else
adr_l x0, secondary_data
+#endif
ldr x2, [x0, #CPU_BOOT_TASK]
cbz x2, __secondary_too_slow
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 52edabc13d51..f7562c38d724 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -61,6 +61,11 @@
* where to place its SVC stack
*/
struct secondary_data secondary_data;
+
+#ifdef CONFIG_HOTPLUG_PARALLEL
+struct secondary_data cpu_boot_data[NR_CPUS] ____cacheline_aligned;
+#endif
+
/* Number of CPUs which aren't online, but looping in kernel text. */
static int cpus_stuck_in_kernel;
@@ -106,8 +111,30 @@ static int boot_secondary(unsigned int cpu, struct task_struct *idle)
return -EOPNOTSUPP;
}
+#ifndef CONFIG_HOTPLUG_PARALLEL
static DECLARE_COMPLETION(cpu_running);
+#endif
+
+#ifdef CONFIG_HOTPLUG_PARALLEL
+extern const struct cpu_operations smp_spin_table_ops;
+
+/* Establish whether parallel bringup can be supported. */
+bool __init arch_cpuhp_init_parallel_bringup(void)
+{
+ int cpu = smp_processor_id();
+ const struct cpu_operations *ops = get_cpu_ops(cpu);
+ return ops && ops != &smp_spin_table_ops;
+}
+
+int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle)
+{
+ cpu_boot_data[cpu].task = tidle;
+ update_cpu_boot_status(cpu, CPU_MMU_OFF);
+
+ return boot_secondary(cpu, tidle);
+}
+#else
int __cpu_up(unsigned int cpu, struct task_struct *idle)
{
int ret;
@@ -172,6 +199,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
return -EIO;
}
+#endif /* CONFIG_HOTPLUG_PARALLEL */
static void init_gic_priority_masking(void)
{
@@ -223,6 +251,9 @@ asmlinkage notrace void secondary_start_kernel(void)
* fail to come online.
*/
check_local_cpu_capabilities();
+#ifdef CONFIG_HOTPLUG_PARALLEL
+ cpuhp_ap_sync_alive();
+#endif
rcutree_report_cpu_starting(cpu);
ops = get_cpu_ops(cpu);
@@ -254,7 +285,9 @@ asmlinkage notrace void secondary_start_kernel(void)
read_cpuid_id());
update_cpu_boot_status(cpu, CPU_BOOT_SUCCESS);
set_cpu_online(cpu, true);
+#ifndef CONFIG_HOTPLUG_PARALLEL
complete(&cpu_running);
+#endif
/*
* Secondary CPUs enter the kernel with all DAIF exceptions masked.
--
2.34.1
^ permalink raw reply related
* [PATCH v2 2/4] arm64: smp: Pass CPU ID to update_cpu_boot_status()
From: Jinjie Ruan @ 2026-06-18 9:24 UTC (permalink / raw)
To: catalin.marinas, will, tsbogend, pjw, palmer, aou, alex, tglx,
mingo, bp, dave.hansen, hpa, peterz, kees, nathan, linusw,
jpoimboe, lukas.bulwahn, ryan.roberts, ojeda, maz, timothy.hayes,
lpieralisi, thuth, menglong8.dong, oupton, yeoreum.yun,
miko.lenczewski, broonie, kevin.brodsky, james.clark, tabba,
mrigendra.chaubey, arnd, anshuman.khandual, x86, linux-kernel,
linux-arm-kernel, linux-mips, linux-riscv, apatel, mhklinux
Cc: ruanjinjie
In-Reply-To: <20260618092444.1316336-1-ruanjinjie@huawei.com>
To support CONFIG_HOTPLUG_PARALLEL, the CPU boot status tracking must
be refactored from a single global variable (secondary_data.status)
to a per-CPU tracking structure to prevent multi-core race conditions.
Add a 'cpu' parameter to update_cpu_boot_status() and update all its
callsites to pass the corresponding CPU ID. This allows updating the
boot status at a per-CPU granularity during parallel bringup.
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
arch/arm64/include/asm/smp.h | 6 +++---
arch/arm64/kernel/cpufeature.c | 2 +-
arch/arm64/kernel/smp.c | 8 ++++----
arch/arm64/mm/context.c | 2 +-
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 10ea4f543069..e2151a01731f 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -122,7 +122,7 @@ static inline void __noreturn cpu_park_loop(void)
}
}
-static inline void update_cpu_boot_status(int val)
+static inline void update_cpu_boot_status(unsigned int cpu, int val)
{
WRITE_ONCE(secondary_data.status, val);
/* Ensure the visibility of the status update */
@@ -134,9 +134,9 @@ static inline void update_cpu_boot_status(int val)
* which calls for a kernel panic. Update the boot status and park the calling
* CPU.
*/
-static inline void __noreturn cpu_panic_kernel(void)
+static inline void __noreturn cpu_panic_kernel(unsigned int cpu)
{
- update_cpu_boot_status(CPU_PANIC_KERNEL);
+ update_cpu_boot_status(cpu, CPU_PANIC_KERNEL);
cpu_park_loop();
}
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6d53bb15cf7b..0552202702bf 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -3674,7 +3674,7 @@ static void verify_local_cpu_caps(u16 scope_mask)
caps->desc, system_has_cap, cpu_has_cap);
if (cpucap_panic_on_conflict(caps))
- cpu_panic_kernel();
+ cpu_panic_kernel(smp_processor_id());
else
cpu_die_early();
}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 24f8448e1fbb..6bc90ee4820a 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -118,7 +118,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
* page tables.
*/
secondary_data.task = idle;
- update_cpu_boot_status(CPU_MMU_OFF);
+ update_cpu_boot_status(cpu, CPU_MMU_OFF);
/* Now bring the CPU into our world */
ret = boot_secondary(cpu, idle);
@@ -252,7 +252,7 @@ asmlinkage notrace void secondary_start_kernel(void)
pr_info("CPU%u: Booted secondary processor 0x%010lx [0x%08x]\n",
cpu, (unsigned long)mpidr,
read_cpuid_id());
- update_cpu_boot_status(CPU_BOOT_SUCCESS);
+ update_cpu_boot_status(cpu, CPU_BOOT_SUCCESS);
set_cpu_online(cpu, true);
complete(&cpu_running);
@@ -411,11 +411,11 @@ void __noreturn cpu_die_early(void)
rcutree_report_cpu_dead();
if (IS_ENABLED(CONFIG_HOTPLUG_CPU)) {
- update_cpu_boot_status(CPU_KILL_ME);
+ update_cpu_boot_status(cpu, CPU_KILL_ME);
__cpu_try_die(cpu);
}
- update_cpu_boot_status(CPU_STUCK_IN_KERNEL);
+ update_cpu_boot_status(cpu, CPU_STUCK_IN_KERNEL);
cpu_park_loop();
}
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 0f4a28b87469..6b8a3245f393 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -72,7 +72,7 @@ void verify_cpu_asid_bits(void)
*/
pr_crit("CPU%d: smaller ASID size(%u) than boot CPU (%u)\n",
smp_processor_id(), asid, asid_bits);
- cpu_panic_kernel();
+ cpu_panic_kernel(smp_processor_id());
}
}
--
2.34.1
^ permalink raw reply related
* [PATCH v2 0/4] arm64: Add HOTPLUG_PARALLEL support for secondary CPUs
From: Jinjie Ruan @ 2026-06-18 9:24 UTC (permalink / raw)
To: catalin.marinas, will, tsbogend, pjw, palmer, aou, alex, tglx,
mingo, bp, dave.hansen, hpa, peterz, kees, nathan, linusw,
jpoimboe, lukas.bulwahn, ryan.roberts, ojeda, maz, timothy.hayes,
lpieralisi, thuth, menglong8.dong, oupton, yeoreum.yun,
miko.lenczewski, broonie, kevin.brodsky, james.clark, tabba,
mrigendra.chaubey, arnd, anshuman.khandual, x86, linux-kernel,
linux-arm-kernel, linux-mips, linux-riscv, apatel, mhklinux
Cc: ruanjinjie
Support for parallel secondary CPU bringup is already utilized by x86,
MIPS, and RISC-V. This patch brings this capability to the arm64
architecture.
Introduce CONFIG_PARALLEL_SMT_PRIMARY_FIRST to avoid primary SMT threads
to boot first constraint.
And Add a 'cpu' parameter to update_cpu_boot_status() to allow updating the
boot status at a per-CPU granularity during parallel bringup.
Rework the global `secondary_data` accessed during early boot into
a per-CPU array `cpu_boot_data` to allow secondary CPUs to boot
in parallel.
And reuse `__cpu_logical_map` array in the early boot code in head.S
to resolve each secondary CPU's logical ID concurrently.
Changes in v2:
- Remove RFC.
- Add Tested-by.
- Fix AI review issues in [1].
- Add arch_cpuhp_init_parallel_bringup() to check psci boot.
- Reuse `__cpu_logical_map` instead of a new aray.
- Defer rcutree_report_cpu_starting() until after
check_local_cpu_capabilities() to prevent a potential control CPU
deadlock if an early capability check fails.
- Move the assembly in head.S to a macro called `mpidr_to_cpuid`.
- Add `SECONDARY_DATA_SHIFT` for `lsl` to access `cpu_boot_data`.
- Add sizeof(struct secondary_data) power of 2 assert check.
- Expand testing with more data collected from real hardware.
[1] https://sashiko.dev/#/patchset/20260611133809.3854977-1-ruanjinjie%40huawei.com
Jinjie Ruan (4):
cpu/hotplug: Introduce CONFIG_PARALLEL_SMT_PRIMARY_FIRST
arm64: smp: Pass CPU ID to update_cpu_boot_status()
arm64: smp: Defer RCU reporting until after local CPU capability
checks
arm64: Add HOTPLUG_PARALLEL support for secondary CPUs
arch/Kconfig | 4 +++
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/smp.h | 17 ++++++++++---
arch/arm64/kernel/asm-offsets.c | 4 +++
arch/arm64/kernel/cpufeature.c | 22 ++++++++--------
arch/arm64/kernel/head.S | 36 ++++++++++++++++++++++++++
arch/arm64/kernel/smp.c | 45 ++++++++++++++++++++++++++++-----
arch/arm64/mm/context.c | 4 +--
arch/mips/Kconfig | 1 +
arch/riscv/Kconfig | 1 +
arch/x86/Kconfig | 1 +
kernel/cpu.c | 6 ++++-
12 files changed, 119 insertions(+), 23 deletions(-)
--
2.34.1
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox