diff mbox series

[v6,4/4] clk: qcom: add clock controller driver for qca8386/qca8084

Message ID 20230901091823.30242-5-quic_luoj@quicinc.com (mailing list archive)
State Changes Requested, archived
Headers show
Series add clock controller of qca8386/qca8084 | expand

Commit Message

Jie Luo Sept. 1, 2023, 9:18 a.m. UTC
The clock controller driver of qca8386/qca8084 is registered
as the MDIO device, the hardware register is accessed by MDIO bus
that is normally used to access general PHY device, which is
different from the current existed qcom clock controller drivers
using ioremap to access hardware clock registers.

MDIO bus is common utilized by both qca8386/qca8084 and other
PHY devices, so the mutex lock mdio_bus->mdio_lock should be
used instead of using the mutex lock of remap.

To access the hardware clock registers of qca8386/qca8084, there
is special MDIO frame sequence(three MDIO read/write operations)
need to be sent to device.

Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
---
 drivers/clk/qcom/Kconfig       |    8 +
 drivers/clk/qcom/Makefile      |    1 +
 drivers/clk/qcom/nsscc-qca8k.c | 2179 ++++++++++++++++++++++++++++++++
 3 files changed, 2188 insertions(+)
 create mode 100644 drivers/clk/qcom/nsscc-qca8k.c

Comments

Stephen Boyd Sept. 5, 2023, 9:36 p.m. UTC | #1
Quoting Luo Jie (2023-09-01 02:18:23)
> diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
> index 263e55d75e3f..785cb6eb514f 100644
> --- a/drivers/clk/qcom/Kconfig
> +++ b/drivers/clk/qcom/Kconfig
> @@ -195,6 +195,14 @@ config IPQ_GCC_9574
>           i2c, USB, SD/eMMC, etc. Select this for the root clock
>           of ipq9574.
>  
> +config IPQ_NSSCC_QCA8K
> +       tristate "QCA8K(QCA8386 or QCA8084) NSS Clock Controller"

This needs to be limited by a depends.

	depends on MDIO_BUS || COMPILE_TEST

perhaps?

> +       help
> +         Support for NSS(Network SubSystem) clock controller on
> +         qca8386/qca8084 chip.
> +         Say Y or M if you want to use network features of switch or
> +         PHY device. Select this for the root clock of qca8k.
> +
>  config MSM_GCC_8660
>         tristate "MSM8660 Global Clock Controller"
>         depends on ARM || COMPILE_TEST
> diff --git a/drivers/clk/qcom/nsscc-qca8k.c b/drivers/clk/qcom/nsscc-qca8k.c
> new file mode 100644
> index 000000000000..f9312735daf3
> --- /dev/null
> +++ b/drivers/clk/qcom/nsscc-qca8k.c
> @@ -0,0 +1,2179 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>

Is platform_device include used?

> +#include <linux/regmap.h>
> +#include <linux/phy.h>

Is the phy include used? Where is the mdio.h include?

> +
> +#include <dt-bindings/clock/qcom,qca8k-nsscc.h>
> +#include <dt-bindings/reset/qcom,qca8k-nsscc.h>
> +
> +#include "clk-branch.h"
> +#include "clk-rcg.h"
> +#include "clk-regmap.h"
> +#include "clk-regmap-divider.h"
> +#include "clk-regmap-mux.h"
[...]
> +
> +static const struct freq_tbl ftbl_nss_cc_mac5_rx_clk_src[] = {
> +       F(50000000, P_XO, 1, 0, 0),
> +       F(125000000, P_UNIPHY0_RX, 1, 0, 0),
> +       F(125000000, P_UNIPHY0_TX, 1, 0, 0),
> +       F(312500000, P_UNIPHY0_RX, 1, 0, 0),
> +       F(312500000, P_UNIPHY0_TX, 1, 0, 0),

This frequency table looks like the parent should change rate...

> +       { }
> +};
> +
> +static struct clk_rcg2 nss_cc_mac5_rx_clk_src = {
> +       .cmd_rcgr = 0x154,
> +       .freq_tbl = ftbl_nss_cc_mac5_rx_clk_src,
> +       .hid_width = 5,
> +       .parent_map = nss_cc_uniphy0_rx_tx_map,
> +       .clkr.hw.init = &(const struct clk_init_data) {
> +               .name = "nss_cc_mac5_rx_clk_src",
> +               .parent_data = nss_cc_uniphy0_rx_tx_data,
> +               .num_parents = ARRAY_SIZE(nss_cc_uniphy0_rx_tx_data),
> +               .ops = &clk_rcg2_ops,

... but this doesn't have any CLK_SET_RATE_PARENT flag set. How does it
work?

> +       },
> +};
> +
> +static struct clk_regmap_div nss_cc_mac5_rx_div_clk_src = {
> +       .reg = 0x15c,
> +       .shift = 0,
> +       .width = 4,
> +       .clkr = {
> +               .hw.init = &(const struct clk_init_data) {
> +                       .name = "nss_cc_mac5_rx_div_clk_src",
[...]
> +
> +static struct clk_branch nss_cc_mdio_master_ahb_clk = {
> +       .halt_reg = 0x19c,
> +       .halt_check = BRANCH_HALT,
> +       .clkr = {
> +               .enable_reg = 0x19c,
> +               .enable_mask = BIT(0),
> +               .hw.init = &(const struct clk_init_data) {
> +                       .name = "nss_cc_mdio_master_ahb_clk",
> +                       .parent_hws = (const struct clk_hw *[]) {
> +                               &nss_cc_ahb_clk_src.clkr.hw,
> +                       },
> +                       .num_parents = 1,
> +                       .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,

Why can't we simply enable clks in probe that are critical? The regmap
operations are complicated?

> +                       .ops = &clk_branch2_prepare_ops,
> +               },
> +       },
> +};
> +
> +static const struct clk_parent_data nss_cc_xo_data[] = {
> +       { .index = DT_XO },
> +};
> +
> +static const struct parent_map nss_cc_xo_map[] = {
> +       { P_XO, 0 },
> +};
> +
> +static const struct freq_tbl ftbl_nss_cc_sys_clk_src[] = {
> +       F(25000000, P_XO, 2, 0, 0),
> +       { }
> +};
[...]
> +
> +static const struct qcom_reset_map nss_cc_qca8k_resets[] = {
[...]
> +       [NSS_CC_GEPHY1_ARES] = { 0x304, 1 },
> +       [NSS_CC_GEPHY2_ARES] = { 0x304, 2 },
> +       [NSS_CC_GEPHY3_ARES] = { 0x304, 3 },
> +       [NSS_CC_DSP_ARES] = { 0x304, 4 },
> +       [NSS_CC_GLOBAL_ARES] = { 0x308, 0 },
> +       [NSS_CC_XPCS_ARES] = { 0x30C, 0 },

Lowercase hex please.

> +};
> +
> +/* For each read/write operation of clock register, there are three MDIO frames
> + * sent to the device.
> + *
> + * 1. The high address part[31:8] of register is packaged into the first MDIO frame.
> + * 2. The low address part[7:0] of register is packaged into the second MDIO frame
> + *    with the low 16bit data to read/write.
> + * 3. The low address part[7:0] of register is packaged into the last MDIO frame
> + *    with the high 16bit data to read/write.
> + *
> + * The clause22 MDIO frame format used by device is as below.
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * | ST| OP|   ADDR  |   REG   | TA|             DATA              |
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + */
> +static inline void split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)

split_addr() is too generic of a name. Please namespace this function to
something else.

> +{
> +       *r1 = regaddr & 0x1c;
> +
> +       regaddr >>= 5;
> +       *r2 = regaddr & 0x7;
> +
> +       regaddr >>= 3;
> +       *page = regaddr & 0xffff;

Instead of this can you use FIELD_GET and have some macros for the part
of the address? Something like

#define QCA8K_CLK_REG_MASK		GENMASK(4, 2)
#define QCA8K_CLK_PHY_ADDR_MASK		GENMASK(7, 5)
#define QCA8K_CLK_PAGE_MASK		GENMASK(24, 8)

and then rename 'r1' and 'r2' to something else?

	*reg = FIELD_GET(QCA8K_CLK_REG_MASK, regaddr);
	*phy_addr = FIELD_GET(QCA8K_CLK_PHY_ADDR_MASK, regaddr) | QCA8K_LOW_ADDR_PREFIX;
	*page = FIELD_GET(QCA8K_CLK_PAGE_MASK);

> +}
> +
> +int qca8k_mii_read(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 *val)
> +{
> +       int ret;
> +
> +       ret = bus->read(bus, switch_phy_id, reg);

Why can't we use __mdiobus_read()?

> +       if (ret >= 0) {
> +               *val = ret;
> +               ret = bus->read(bus, switch_phy_id, (reg | BIT(1)));

What is BIT(1)? Can it have a #define? What if ret is negative? We
shouldn't treat that as data, right?

> +               *val |= ret << 16;
> +       }
> +
> +       if (ret < 0)
> +               dev_err_ratelimited(&bus->dev, "fail to read qca8k mii register\n");
> +
> +       return ret < 0 ? ret : 0;
> +}
> +
> +void qca8k_mii_write(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 val)
> +{
> +       int ret;
> +
> +       ret = bus->write(bus, switch_phy_id, reg, lower_16_bits(val));
> +       if (ret >= 0)
> +               ret = bus->write(bus, switch_phy_id, (reg | BIT(1)), upper_16_bits(val));
> +
> +       if (ret < 0)
> +               dev_err_ratelimited(&bus->dev, "fail to write qca8k mii register\n");
> +}
> +
> +int qca8k_mii_page_set(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u16 page)

Regmap core has support for picking pages. Can that be used here?

> +{
> +       int ret;
> +
> +       ret = bus->write(bus, switch_phy_id, reg, page);
> +       if (ret < 0)
> +               dev_err_ratelimited(&bus->dev, "fail to set page\n");
> +
> +       return ret;
> +}
> +
> +int qca8k_regmap_read(void *context, unsigned int reg, unsigned int *val)
> +{
> +       struct mii_bus *bus = context;
> +       u16 r1, r2, page;
> +       int ret;
> +
> +       reg += QCA8K_CLK_REG_BASE;
> +       split_addr(reg, &r1, &r2, &page);
> +
> +       mutex_lock(&bus->mdio_lock);
> +       ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
> +       if (ret < 0)
> +               goto qca8k_read_exit;
> +
> +       ret = qca8k_mii_read(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
> +
> +qca8k_read_exit:
> +       mutex_unlock(&bus->mdio_lock);
> +       return ret;
> +};
> +
> +int qca8k_regmap_write(void *context, unsigned int reg, unsigned int val)

These wrappers should be static. Please run sparse.

> +{
> +       struct mii_bus *bus = context;
> +       u16 r1, r2, page;
> +       int ret;
> +
> +       reg += QCA8K_CLK_REG_BASE;
> +       split_addr(reg, &r1, &r2, &page);
> +
> +       mutex_lock(&bus->mdio_lock);
> +       ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
> +       if (ret < 0)
> +               goto qca8k_write_exit;
> +
> +       qca8k_mii_write(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
> +
> +qca8k_write_exit:
> +       mutex_unlock(&bus->mdio_lock);
> +       return ret;
> +};
> +
> +int qca8k_regmap_update_bits(void *context, unsigned int reg, unsigned int mask, unsigned int value)
> +{
> +       struct mii_bus *bus = context;
> +       u16 r1, r2, page;
> +       int ret;
> +       u32 val;
> +
> +       reg += QCA8K_CLK_REG_BASE;
> +       split_addr(reg, &r1, &r2, &page);
> +
> +       mutex_lock(&bus->mdio_lock);
> +       ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
> +       if (ret < 0)
> +               goto qca8k_update_exit;
> +
> +       ret = qca8k_mii_read(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, &val);
> +       if (ret < 0)
> +               goto qca8k_update_exit;
> +
> +       val &= ~mask;
> +       val |= value;
> +       qca8k_mii_write(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
> +
> +qca8k_update_exit:
> +       mutex_unlock(&bus->mdio_lock);
> +       return ret;
> +}
> +
> +static const struct regmap_config nss_cc_qca8k_regmap_config = {
> +       .reg_bits = 12,
> +       .reg_stride = 4,
> +       .val_bits = 32,
> +       .max_register = 0x30C,

Lowercase hex please.

> +       .reg_read = qca8k_regmap_read,
> +       .reg_write = qca8k_regmap_write,
> +       .reg_update_bits = qca8k_regmap_update_bits,
> +       .disable_locking = true,
> +       .cache_type = REGCACHE_NONE,

Isn't this the default?

> +};
> +
> +static const struct qcom_cc_desc nss_cc_qca8k_desc = {
> +       .config = &nss_cc_qca8k_regmap_config,
> +       .clks = nss_cc_qca8k_clocks,
> +       .num_clks = ARRAY_SIZE(nss_cc_qca8k_clocks),
> +       .resets = nss_cc_qca8k_resets,
> +       .num_resets = ARRAY_SIZE(nss_cc_qca8k_resets),
> +};
> +
> +static int nss_cc_qca8k_probe(struct mdio_device *mdiodev)
> +{
> +       struct regmap *regmap;
> +
> +       regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev->bus, nss_cc_qca8k_desc.config);

Why can't we use devm_regmap_init_mdio() here? Is it because the device
needs special data marshaling per split_addr()?

> +       if (IS_ERR(regmap))
> +               return dev_err_probe(&mdiodev->dev, PTR_ERR(regmap), "Failed to init regmap\n");
> +
> +       return qcom_cc_really_probe(&mdiodev->dev, &nss_cc_qca8k_desc, regmap);
> +}
> +
Jie Luo Sept. 7, 2023, 8:36 a.m. UTC | #2
On 9/6/2023 5:36 AM, Stephen Boyd wrote:
> Quoting Luo Jie (2023-09-01 02:18:23)
>> diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
>> index 263e55d75e3f..785cb6eb514f 100644
>> --- a/drivers/clk/qcom/Kconfig
>> +++ b/drivers/clk/qcom/Kconfig
>> @@ -195,6 +195,14 @@ config IPQ_GCC_9574
>>            i2c, USB, SD/eMMC, etc. Select this for the root clock
>>            of ipq9574.
>>   
>> +config IPQ_NSSCC_QCA8K
>> +       tristate "QCA8K(QCA8386 or QCA8084) NSS Clock Controller"
> 
> This needs to be limited by a depends.
> 
> 	depends on MDIO_BUS || COMPILE_TEST
> 
> perhaps?
> 
Yes, the driver depends on MDIO BUS, will add this depends in the next 
patchset.

>> +       help
>> +         Support for NSS(Network SubSystem) clock controller on
>> +         qca8386/qca8084 chip.
>> +         Say Y or M if you want to use network features of switch or
>> +         PHY device. Select this for the root clock of qca8k.
>> +
>>   config MSM_GCC_8660
>>          tristate "MSM8660 Global Clock Controller"
>>          depends on ARM || COMPILE_TEST
>> diff --git a/drivers/clk/qcom/nsscc-qca8k.c b/drivers/clk/qcom/nsscc-qca8k.c
>> new file mode 100644
>> index 000000000000..f9312735daf3
>> --- /dev/null
>> +++ b/drivers/clk/qcom/nsscc-qca8k.c
>> @@ -0,0 +1,2179 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
> 
> Is platform_device include used?
> 
will remove this.

>> +#include <linux/regmap.h>
>> +#include <linux/phy.h>
> 
> Is the phy include used? Where is the mdio.h include?

there is no PHY include, just the mdio_device is included, however the 
mii_bus->mdio_lock is needed by this clock controller.

so "struct mii_bus" is needed and included by the header file phy.h,
the mdio.h is included by phy.h.

> 
>> +
>> +#include <dt-bindings/clock/qcom,qca8k-nsscc.h>
>> +#include <dt-bindings/reset/qcom,qca8k-nsscc.h>
>> +
>> +#include "clk-branch.h"
>> +#include "clk-rcg.h"
>> +#include "clk-regmap.h"
>> +#include "clk-regmap-divider.h"
>> +#include "clk-regmap-mux.h"
> [...]
>> +
>> +static const struct freq_tbl ftbl_nss_cc_mac5_rx_clk_src[] = {
>> +       F(50000000, P_XO, 1, 0, 0),
>> +       F(125000000, P_UNIPHY0_RX, 1, 0, 0),
>> +       F(125000000, P_UNIPHY0_TX, 1, 0, 0),
>> +       F(312500000, P_UNIPHY0_RX, 1, 0, 0),
>> +       F(312500000, P_UNIPHY0_TX, 1, 0, 0),
> 
> This frequency table looks like the parent should change rate...

Yes, the parent need to change the rate for the different interface 
mode, PHY_INTERFACE_MODE_2500BASEX use 312.5M, PHY_INTERFACE_MODE_SGMII 
use 125M.

> 
>> +       { }
>> +};
>> +
>> +static struct clk_rcg2 nss_cc_mac5_rx_clk_src = {
>> +       .cmd_rcgr = 0x154,
>> +       .freq_tbl = ftbl_nss_cc_mac5_rx_clk_src,
>> +       .hid_width = 5,
>> +       .parent_map = nss_cc_uniphy0_rx_tx_map,
>> +       .clkr.hw.init = &(const struct clk_init_data) {
>> +               .name = "nss_cc_mac5_rx_clk_src",
>> +               .parent_data = nss_cc_uniphy0_rx_tx_data,
>> +               .num_parents = ARRAY_SIZE(nss_cc_uniphy0_rx_tx_data),
>> +               .ops = &clk_rcg2_ops,
> 
> ... but this doesn't have any CLK_SET_RATE_PARENT flag set. How does it
> work?

since it has the different parent clock rate 312.5M and 125M for the 
deffernet interface mode used. If the flag CLK_SET_RATE_PARENT is set, 
when we require to configure 25M clock rate for example, it may lead to 
the parent rate changed(312.5M/12.5 or 125M/5), which is not expected, 
the parent rate(312.5M or 125M) can't be changed, since the parent rate 
is decided by interface mode(PHY_INTERFACE_MODE_2500BASEX or 
PHY_INTERFACE_MODE_SGMII).

the work flow:
the parent of nss_cc_mac5_rx_clk_src is selected as 312.5M or 125M 
firstly, then configure the required clock rate of clk_branch.

uniphy(312.5M or 125M) ---> RCG(nss_cc_mac5_rx_clk_src) ---> clk_branch.

> 
>> +       },
>> +};
>> +
>> +static struct clk_regmap_div nss_cc_mac5_rx_div_clk_src = {
>> +       .reg = 0x15c,
>> +       .shift = 0,
>> +       .width = 4,
>> +       .clkr = {
>> +               .hw.init = &(const struct clk_init_data) {
>> +                       .name = "nss_cc_mac5_rx_div_clk_src",
> [...]
>> +
>> +static struct clk_branch nss_cc_mdio_master_ahb_clk = {
>> +       .halt_reg = 0x19c,
>> +       .halt_check = BRANCH_HALT,
>> +       .clkr = {
>> +               .enable_reg = 0x19c,
>> +               .enable_mask = BIT(0),
>> +               .hw.init = &(const struct clk_init_data) {
>> +                       .name = "nss_cc_mdio_master_ahb_clk",
>> +                       .parent_hws = (const struct clk_hw *[]) {
>> +                               &nss_cc_ahb_clk_src.clkr.hw,
>> +                       },
>> +                       .num_parents = 1,
>> +                       .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> 
> Why can't we simply enable clks in probe that are critical? The regmap
> operations are complicated?

since these clocks with the flag CLK_IS_CRITICAL are the common clocks 
needed to be enabled for all devices no matter what work mode(qca8084 or 
qca8386) used, which is base clock to enable to use the clock driver, to 
enable these clocks by using flag CLK_IS_CRITICAL is simplier way and 
can simply the device probe driver and device tree definations.

> 
>> +                       .ops = &clk_branch2_prepare_ops,
>> +               },
>> +       },
>> +};
>> +
>> +static const struct clk_parent_data nss_cc_xo_data[] = {
>> +       { .index = DT_XO },
>> +};
>> +
>> +static const struct parent_map nss_cc_xo_map[] = {
>> +       { P_XO, 0 },
>> +};
>> +
>> +static const struct freq_tbl ftbl_nss_cc_sys_clk_src[] = {
>> +       F(25000000, P_XO, 2, 0, 0),
>> +       { }
>> +};
> [...]
>> +
>> +static const struct qcom_reset_map nss_cc_qca8k_resets[] = {
> [...]
>> +       [NSS_CC_GEPHY1_ARES] = { 0x304, 1 },
>> +       [NSS_CC_GEPHY2_ARES] = { 0x304, 2 },
>> +       [NSS_CC_GEPHY3_ARES] = { 0x304, 3 },
>> +       [NSS_CC_DSP_ARES] = { 0x304, 4 },
>> +       [NSS_CC_GLOBAL_ARES] = { 0x308, 0 },
>> +       [NSS_CC_XPCS_ARES] = { 0x30C, 0 },
> 
> Lowercase hex please.

will correct it.

> 
>> +};
>> +
>> +/* For each read/write operation of clock register, there are three MDIO frames
>> + * sent to the device.
>> + *
>> + * 1. The high address part[31:8] of register is packaged into the first MDIO frame.
>> + * 2. The low address part[7:0] of register is packaged into the second MDIO frame
>> + *    with the low 16bit data to read/write.
>> + * 3. The low address part[7:0] of register is packaged into the last MDIO frame
>> + *    with the high 16bit data to read/write.
>> + *
>> + * The clause22 MDIO frame format used by device is as below.
>> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> + * | ST| OP|   ADDR  |   REG   | TA|             DATA              |
>> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>> + */
>> +static inline void split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
> 
> split_addr() is too generic of a name. Please namespace this function to
> something else.

okay, maybe convert_reg_to_mii_addr?

> 
>> +{
>> +       *r1 = regaddr & 0x1c;
>> +
>> +       regaddr >>= 5;
>> +       *r2 = regaddr & 0x7;
>> +
>> +       regaddr >>= 3;
>> +       *page = regaddr & 0xffff;
> 
> Instead of this can you use FIELD_GET and have some macros for the part
> of the address? Something like
> 
> #define QCA8K_CLK_REG_MASK		GENMASK(4, 2)
> #define QCA8K_CLK_PHY_ADDR_MASK		GENMASK(7, 5)
> #define QCA8K_CLK_PAGE_MASK		GENMASK(24, 8)
> 
> and then rename 'r1' and 'r2' to something else?
> 
> 	*reg = FIELD_GET(QCA8K_CLK_REG_MASK, regaddr);
> 	*phy_addr = FIELD_GET(QCA8K_CLK_PHY_ADDR_MASK, regaddr) | QCA8K_LOW_ADDR_PREFIX;
> 	*page = FIELD_GET(QCA8K_CLK_PAGE_MASK);
> 
okay, thanks Stephen for the suggestion, will take this in the next 
patch set.

>> +}
>> +
>> +int qca8k_mii_read(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 *val)
>> +{
>> +       int ret;
>> +
>> +       ret = bus->read(bus, switch_phy_id, reg);
> 
> Why can't we use __mdiobus_read()?
> 

yes, we can use __mdiobus_read and will verify it, thanks.

>> +       if (ret >= 0) {
>> +               *val = ret;
>> +               ret = bus->read(bus, switch_phy_id, (reg | BIT(1)));
> 
> What is BIT(1)? Can it have a #define? What if ret is negative? We
> shouldn't treat that as data, right?

okay, will add the "#define DATA_UPPER_16_BITS BIT(1)"? which is for 
writing or reading upper 16 bit data when BIT(1) is set.

when the ret is negative, the ret should be not treated as data, will 
take this case into account in the next updated patch.

> 
>> +               *val |= ret << 16;
>> +       }
>> +
>> +       if (ret < 0)
>> +               dev_err_ratelimited(&bus->dev, "fail to read qca8k mii register\n");
>> +
>> +       return ret < 0 ? ret : 0;
>> +}
>> +
>> +void qca8k_mii_write(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 val)
>> +{
>> +       int ret;
>> +
>> +       ret = bus->write(bus, switch_phy_id, reg, lower_16_bits(val));
>> +       if (ret >= 0)
>> +               ret = bus->write(bus, switch_phy_id, (reg | BIT(1)), upper_16_bits(val));
>> +
>> +       if (ret < 0)
>> +               dev_err_ratelimited(&bus->dev, "fail to write qca8k mii register\n");
>> +}
>> +
>> +int qca8k_mii_page_set(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u16 page)
> 
> Regmap core has support for picking pages. Can that be used here?

Hi Stephen,
No, we can't depend on regmap to pick the page, since the MDIO bus is 
shared by qca8k device and PHY device, if there is a PHY device access, 
even if the page is not changed, we still need to configure the page 
again, so the page is alwasy configured for each register access, the 
sequence can't be interrupted.

> 
>> +{
>> +       int ret;
>> +
>> +       ret = bus->write(bus, switch_phy_id, reg, page);
>> +       if (ret < 0)
>> +               dev_err_ratelimited(&bus->dev, "fail to set page\n");
>> +
>> +       return ret;
>> +}
>> +
>> +int qca8k_regmap_read(void *context, unsigned int reg, unsigned int *val)
>> +{
>> +       struct mii_bus *bus = context;
>> +       u16 r1, r2, page;
>> +       int ret;
>> +
>> +       reg += QCA8K_CLK_REG_BASE;
>> +       split_addr(reg, &r1, &r2, &page);
>> +
>> +       mutex_lock(&bus->mdio_lock);
>> +       ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
>> +       if (ret < 0)
>> +               goto qca8k_read_exit;
>> +
>> +       ret = qca8k_mii_read(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
>> +
>> +qca8k_read_exit:
>> +       mutex_unlock(&bus->mdio_lock);
>> +       return ret;
>> +};
>> +
>> +int qca8k_regmap_write(void *context, unsigned int reg, unsigned int val)
> 
> These wrappers should be static. Please run sparse.

okay, will update this.

> 
>> +{
>> +       struct mii_bus *bus = context;
>> +       u16 r1, r2, page;
>> +       int ret;
>> +
>> +       reg += QCA8K_CLK_REG_BASE;
>> +       split_addr(reg, &r1, &r2, &page);
>> +
>> +       mutex_lock(&bus->mdio_lock);
>> +       ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
>> +       if (ret < 0)
>> +               goto qca8k_write_exit;
>> +
>> +       qca8k_mii_write(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
>> +
>> +qca8k_write_exit:
>> +       mutex_unlock(&bus->mdio_lock);
>> +       return ret;
>> +};
>> +
>> +int qca8k_regmap_update_bits(void *context, unsigned int reg, unsigned int mask, unsigned int value)
>> +{
>> +       struct mii_bus *bus = context;
>> +       u16 r1, r2, page;
>> +       int ret;
>> +       u32 val;
>> +
>> +       reg += QCA8K_CLK_REG_BASE;
>> +       split_addr(reg, &r1, &r2, &page);
>> +
>> +       mutex_lock(&bus->mdio_lock);
>> +       ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
>> +       if (ret < 0)
>> +               goto qca8k_update_exit;
>> +
>> +       ret = qca8k_mii_read(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, &val);
>> +       if (ret < 0)
>> +               goto qca8k_update_exit;
>> +
>> +       val &= ~mask;
>> +       val |= value;
>> +       qca8k_mii_write(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
>> +
>> +qca8k_update_exit:
>> +       mutex_unlock(&bus->mdio_lock);
>> +       return ret;
>> +}
>> +
>> +static const struct regmap_config nss_cc_qca8k_regmap_config = {
>> +       .reg_bits = 12,
>> +       .reg_stride = 4,
>> +       .val_bits = 32,
>> +       .max_register = 0x30C,
> 
> Lowercase hex please.

okay, will correct this.

> 
>> +       .reg_read = qca8k_regmap_read,
>> +       .reg_write = qca8k_regmap_write,
>> +       .reg_update_bits = qca8k_regmap_update_bits,
>> +       .disable_locking = true,
>> +       .cache_type = REGCACHE_NONE,
> 
> Isn't this the default?

yes, REGCACHE_NONE should be the default value, we can remove this line 
config.

> 
>> +};
>> +
>> +static const struct qcom_cc_desc nss_cc_qca8k_desc = {
>> +       .config = &nss_cc_qca8k_regmap_config,
>> +       .clks = nss_cc_qca8k_clocks,
>> +       .num_clks = ARRAY_SIZE(nss_cc_qca8k_clocks),
>> +       .resets = nss_cc_qca8k_resets,
>> +       .num_resets = ARRAY_SIZE(nss_cc_qca8k_resets),
>> +};
>> +
>> +static int nss_cc_qca8k_probe(struct mdio_device *mdiodev)
>> +{
>> +       struct regmap *regmap;
>> +
>> +       regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev->bus, nss_cc_qca8k_desc.config);
> 
> Why can't we use devm_regmap_init_mdio() here? Is it because the device
> needs special data marshaling per split_addr()?

Hi Stephen,
No, we can't use devm_regmap_init_mdio, which is for the standard PHY 
device access(clause22 and clause 45), but the clock device needs the 
special MDIO sequences for the register access.

Thanks Stephen for your previous time and the detail review comments.

> 
>> +       if (IS_ERR(regmap))
>> +               return dev_err_probe(&mdiodev->dev, PTR_ERR(regmap), "Failed to init regmap\n");
>> +
>> +       return qcom_cc_really_probe(&mdiodev->dev, &nss_cc_qca8k_desc, regmap);
>> +}
>> +
Stephen Boyd Sept. 7, 2023, 10:45 p.m. UTC | #3
Quoting Jie Luo (2023-09-07 01:36:50)
> 
> On 9/6/2023 5:36 AM, Stephen Boyd wrote:
> > Quoting Luo Jie (2023-09-01 02:18:23)
> >> diff --git a/drivers/clk/qcom/nsscc-qca8k.c b/drivers/clk/qcom/nsscc-qca8k.c
> >> new file mode 100644
> >> index 000000000000..f9312735daf3
> >> --- /dev/null
> >> +++ b/drivers/clk/qcom/nsscc-qca8k.c
> >> @@ -0,0 +1,2179 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
> >> + */
> >> +
> >> +#include <linux/clk-provider.h>
> >> +#include <linux/kernel.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.h>
> >> +#include <linux/platform_device.h>
> > 
> > Is platform_device include used?
> > 
> will remove this.
> 
> >> +#include <linux/regmap.h>
> >> +#include <linux/phy.h>
> > 
> > Is the phy include used? Where is the mdio.h include?
> 
> there is no PHY include, just the mdio_device is included, however the 
> mii_bus->mdio_lock is needed by this clock controller.
> 
> so "struct mii_bus" is needed and included by the header file phy.h,
> the mdio.h is included by phy.h.

Don't rely on implicit includes. It leads to compile errors if headers
are ever split/moved around. Just include mdio.h as you use it.

> 
> > 
> >> +
> >> +#include <dt-bindings/clock/qcom,qca8k-nsscc.h>
> >> +#include <dt-bindings/reset/qcom,qca8k-nsscc.h>
> >> +
> >> +#include "clk-branch.h"
> >> +#include "clk-rcg.h"
> >> +#include "clk-regmap.h"
> >> +#include "clk-regmap-divider.h"
> >> +#include "clk-regmap-mux.h"
> > [...]
> >> +
> >> +static const struct freq_tbl ftbl_nss_cc_mac5_rx_clk_src[] = {
> >> +       F(50000000, P_XO, 1, 0, 0),
> >> +       F(125000000, P_UNIPHY0_RX, 1, 0, 0),
> >> +       F(125000000, P_UNIPHY0_TX, 1, 0, 0),
> >> +       F(312500000, P_UNIPHY0_RX, 1, 0, 0),
> >> +       F(312500000, P_UNIPHY0_TX, 1, 0, 0),
> > 
> > This frequency table looks like the parent should change rate...
> 
> Yes, the parent need to change the rate for the different interface 
> mode, PHY_INTERFACE_MODE_2500BASEX use 312.5M, PHY_INTERFACE_MODE_SGMII 
> use 125M.
> 
> > 
> >> +       { }
> >> +};
> >> +
> >> +static struct clk_rcg2 nss_cc_mac5_rx_clk_src = {
> >> +       .cmd_rcgr = 0x154,
> >> +       .freq_tbl = ftbl_nss_cc_mac5_rx_clk_src,
> >> +       .hid_width = 5,
> >> +       .parent_map = nss_cc_uniphy0_rx_tx_map,
> >> +       .clkr.hw.init = &(const struct clk_init_data) {
> >> +               .name = "nss_cc_mac5_rx_clk_src",
> >> +               .parent_data = nss_cc_uniphy0_rx_tx_data,
> >> +               .num_parents = ARRAY_SIZE(nss_cc_uniphy0_rx_tx_data),
> >> +               .ops = &clk_rcg2_ops,
> > 
> > ... but this doesn't have any CLK_SET_RATE_PARENT flag set. How does it
> > work?
> 
> since it has the different parent clock rate 312.5M and 125M for the 
> deffernet interface mode used. If the flag CLK_SET_RATE_PARENT is set, 
> when we require to configure 25M clock rate for example, it may lead to 
> the parent rate changed(312.5M/12.5 or 125M/5), which is not expected, 
> the parent rate(312.5M or 125M) can't be changed, since the parent rate 
> is decided by interface mode(PHY_INTERFACE_MODE_2500BASEX or 
> PHY_INTERFACE_MODE_SGMII).
> 
> the work flow:
> the parent of nss_cc_mac5_rx_clk_src is selected as 312.5M or 125M 
> firstly, then configure the required clock rate of clk_branch.
> 
> uniphy(312.5M or 125M) ---> RCG(nss_cc_mac5_rx_clk_src) ---> clk_branch.

Ok. So you're saying that the uniphy rate changes outside of the clk
framework? That is potentially troublesome because the clk framework
aggressively caches things to the point that if the parent of the RCG
changes rates the branch rate won't reflect the new rate. It looks like
none of that really matters in practice because the divider is always 1
here, but this will be confusing if a driver calls clk_get_rate() when
the uniphy rate has changed.

Why can't that be driven from the clk framework? Or why can't the uniphy
implement a clk provider that supports changing rates? If that was done,
then a driver could change the uniphy rate and the clk framework would
propagate the frequency down to all the children, recalculating the
rates along the way. It may even mean that there's nothing to do when
changing these clks, besides perhaps changing the parent?

> 
> > 
> >> +       },
> >> +};
> >> +
> >> +static struct clk_regmap_div nss_cc_mac5_rx_div_clk_src = {
> >> +       .reg = 0x15c,
> >> +       .shift = 0,
> >> +       .width = 4,
> >> +       .clkr = {
> >> +               .hw.init = &(const struct clk_init_data) {
> >> +                       .name = "nss_cc_mac5_rx_div_clk_src",
> > [...]
> >> +
> >> +static struct clk_branch nss_cc_mdio_master_ahb_clk = {
> >> +       .halt_reg = 0x19c,
> >> +       .halt_check = BRANCH_HALT,
> >> +       .clkr = {
> >> +               .enable_reg = 0x19c,
> >> +               .enable_mask = BIT(0),
> >> +               .hw.init = &(const struct clk_init_data) {
> >> +                       .name = "nss_cc_mdio_master_ahb_clk",
> >> +                       .parent_hws = (const struct clk_hw *[]) {
> >> +                               &nss_cc_ahb_clk_src.clkr.hw,
> >> +                       },
> >> +                       .num_parents = 1,
> >> +                       .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
> > 
> > Why can't we simply enable clks in probe that are critical? The regmap
> > operations are complicated?
> 
> since these clocks with the flag CLK_IS_CRITICAL are the common clocks 
> needed to be enabled for all devices no matter what work mode(qca8084 or 
> qca8386) used, which is base clock to enable to use the clock driver, to 
> enable these clocks by using flag CLK_IS_CRITICAL is simplier way and 
> can simply the device probe driver and device tree definations.

Sure, but it also means you use the despised CLK_IS_CRITICAL flag when
it could simply be some code in probe that sets some bits for "boot
configuration". The benefit is that we don't register clks that do
practically nothing besides use resources in the kernel for a one time
operation at probe.

> 
> > 
> >> +};
> >> +
> >> +/* For each read/write operation of clock register, there are three MDIO frames
> >> + * sent to the device.
> >> + *
> >> + * 1. The high address part[31:8] of register is packaged into the first MDIO frame.
> >> + * 2. The low address part[7:0] of register is packaged into the second MDIO frame
> >> + *    with the low 16bit data to read/write.
> >> + * 3. The low address part[7:0] of register is packaged into the last MDIO frame
> >> + *    with the high 16bit data to read/write.
> >> + *
> >> + * The clause22 MDIO frame format used by device is as below.
> >> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> >> + * | ST| OP|   ADDR  |   REG   | TA|             DATA              |
> >> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> >> + */
> >> +static inline void split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
> > 
> > split_addr() is too generic of a name. Please namespace this function to
> > something else.
> 
> okay, maybe convert_reg_to_mii_addr?

Sure!

> > 
> >> +               *val |= ret << 16;
> >> +       }
> >> +
> >> +       if (ret < 0)
> >> +               dev_err_ratelimited(&bus->dev, "fail to read qca8k mii register\n");
> >> +
> >> +       return ret < 0 ? ret : 0;
> >> +}
> >> +
> >> +void qca8k_mii_write(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 val)
> >> +{
> >> +       int ret;
> >> +
> >> +       ret = bus->write(bus, switch_phy_id, reg, lower_16_bits(val));
> >> +       if (ret >= 0)
> >> +               ret = bus->write(bus, switch_phy_id, (reg | BIT(1)), upper_16_bits(val));
> >> +
> >> +       if (ret < 0)
> >> +               dev_err_ratelimited(&bus->dev, "fail to write qca8k mii register\n");
> >> +}
> >> +
> >> +int qca8k_mii_page_set(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u16 page)
> > 
> > Regmap core has support for picking pages. Can that be used here?
> 
> Hi Stephen,
> No, we can't depend on regmap to pick the page, since the MDIO bus is 
> shared by qca8k device and PHY device, if there is a PHY device access, 
> even if the page is not changed, we still need to configure the page 
> again, so the page is alwasy configured for each register access, the 
> sequence can't be interrupted.

Ok.

> > 
> >> +};
> >> +
> >> +static const struct qcom_cc_desc nss_cc_qca8k_desc = {
> >> +       .config = &nss_cc_qca8k_regmap_config,
> >> +       .clks = nss_cc_qca8k_clocks,
> >> +       .num_clks = ARRAY_SIZE(nss_cc_qca8k_clocks),
> >> +       .resets = nss_cc_qca8k_resets,
> >> +       .num_resets = ARRAY_SIZE(nss_cc_qca8k_resets),
> >> +};
> >> +
> >> +static int nss_cc_qca8k_probe(struct mdio_device *mdiodev)
> >> +{
> >> +       struct regmap *regmap;
> >> +
> >> +       regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev->bus, nss_cc_qca8k_desc.config);
> > 
> > Why can't we use devm_regmap_init_mdio() here? Is it because the device
> > needs special data marshaling per split_addr()?
> 
> Hi Stephen,
> No, we can't use devm_regmap_init_mdio, which is for the standard PHY 
> device access(clause22 and clause 45), but the clock device needs the 
> special MDIO sequences for the register access.

Ok.
Jie Luo Sept. 8, 2023, 11:10 a.m. UTC | #4
On 9/8/2023 6:45 AM, Stephen Boyd wrote:
> Quoting Jie Luo (2023-09-07 01:36:50)
>>
>> On 9/6/2023 5:36 AM, Stephen Boyd wrote:
>>> Quoting Luo Jie (2023-09-01 02:18:23)
>>>> diff --git a/drivers/clk/qcom/nsscc-qca8k.c b/drivers/clk/qcom/nsscc-qca8k.c
>>>> new file mode 100644
>>>> index 000000000000..f9312735daf3
>>>> --- /dev/null
>>>> +++ b/drivers/clk/qcom/nsscc-qca8k.c
>>>> @@ -0,0 +1,2179 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>> +/*
>>>> + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
>>>> + */
>>>> +
>>>> +#include <linux/clk-provider.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>
>>> Is platform_device include used?
>>>
>> will remove this.
>>
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/phy.h>
>>>
>>> Is the phy include used? Where is the mdio.h include?
>>
>> there is no PHY include, just the mdio_device is included, however the
>> mii_bus->mdio_lock is needed by this clock controller.
>>
>> so "struct mii_bus" is needed and included by the header file phy.h,
>> the mdio.h is included by phy.h.
> 
> Don't rely on implicit includes. It leads to compile errors if headers
> are ever split/moved around. Just include mdio.h as you use it.
> 
okay, will include mdio.h

>>
>>>
>>>> +
>>>> +#include <dt-bindings/clock/qcom,qca8k-nsscc.h>
>>>> +#include <dt-bindings/reset/qcom,qca8k-nsscc.h>
>>>> +
>>>> +#include "clk-branch.h"
>>>> +#include "clk-rcg.h"
>>>> +#include "clk-regmap.h"
>>>> +#include "clk-regmap-divider.h"
>>>> +#include "clk-regmap-mux.h"
>>> [...]
>>>> +
>>>> +static const struct freq_tbl ftbl_nss_cc_mac5_rx_clk_src[] = {
>>>> +       F(50000000, P_XO, 1, 0, 0),
>>>> +       F(125000000, P_UNIPHY0_RX, 1, 0, 0),
>>>> +       F(125000000, P_UNIPHY0_TX, 1, 0, 0),
>>>> +       F(312500000, P_UNIPHY0_RX, 1, 0, 0),
>>>> +       F(312500000, P_UNIPHY0_TX, 1, 0, 0),
>>>
>>> This frequency table looks like the parent should change rate...
>>
>> Yes, the parent need to change the rate for the different interface
>> mode, PHY_INTERFACE_MODE_2500BASEX use 312.5M, PHY_INTERFACE_MODE_SGMII
>> use 125M.
>>
>>>
>>>> +       { }
>>>> +};
>>>> +
>>>> +static struct clk_rcg2 nss_cc_mac5_rx_clk_src = {
>>>> +       .cmd_rcgr = 0x154,
>>>> +       .freq_tbl = ftbl_nss_cc_mac5_rx_clk_src,
>>>> +       .hid_width = 5,
>>>> +       .parent_map = nss_cc_uniphy0_rx_tx_map,
>>>> +       .clkr.hw.init = &(const struct clk_init_data) {
>>>> +               .name = "nss_cc_mac5_rx_clk_src",
>>>> +               .parent_data = nss_cc_uniphy0_rx_tx_data,
>>>> +               .num_parents = ARRAY_SIZE(nss_cc_uniphy0_rx_tx_data),
>>>> +               .ops = &clk_rcg2_ops,
>>>
>>> ... but this doesn't have any CLK_SET_RATE_PARENT flag set. How does it
>>> work?
>>
>> since it has the different parent clock rate 312.5M and 125M for the
>> deffernet interface mode used. If the flag CLK_SET_RATE_PARENT is set,
>> when we require to configure 25M clock rate for example, it may lead to
>> the parent rate changed(312.5M/12.5 or 125M/5), which is not expected,
>> the parent rate(312.5M or 125M) can't be changed, since the parent rate
>> is decided by interface mode(PHY_INTERFACE_MODE_2500BASEX or
>> PHY_INTERFACE_MODE_SGMII).
>>
>> the work flow:
>> the parent of nss_cc_mac5_rx_clk_src is selected as 312.5M or 125M
>> firstly, then configure the required clock rate of clk_branch.
>>
>> uniphy(312.5M or 125M) ---> RCG(nss_cc_mac5_rx_clk_src) ---> clk_branch.
> 
> Ok. So you're saying that the uniphy rate changes outside of the clk
> framework? That is potentially troublesome because the clk framework
> aggressively caches things to the point that if the parent of the RCG
> changes rates the branch rate won't reflect the new rate. It looks like
> none of that really matters in practice because the divider is always 1
> here, but this will be confusing if a driver calls clk_get_rate() when
> the uniphy rate has changed.
> 
> Why can't that be driven from the clk framework? Or why can't the uniphy
> implement a clk provider that supports changing rates? If that was done,
> then a driver could change the uniphy rate and the clk framework would
> propagate the frequency down to all the children, recalculating the
> rates along the way. It may even mean that there's nothing to do when
> changing these clks, besides perhaps changing the parent?
> 

Hi Stephen,
Yes, the uniphy implements the clock provider that supports changing 
rate, which will be upstream later, and nss_cc_mac5_rx_clk_src is the 
special case, which is only used in the switch device qca8386.

For the phy device qca8084(uniphy has only 312.5M fix clock which is 
registered by device tree), this clock nss_cc_mac5_rx_clk_src is not used.

The issue for the switch device(qca8386) here is the clock rate of 
parent uniphy can't be changed because of the clock rate requirement of 
branch clock, since the uniphy clock rate is decided by the current 
working interface mode(PHY_INTERFACE_MODE_2500BASEX with 312.5M or 
PHY_INTERFACE_MODE_SGMII with 125M).

For example, when the uniphy works on PHY_INTERFACE_MODE_2500BASEX, then 
the parent uniphy clock rate is 312.5M, which is decided by hardware and 
can't be changed. when a branch clock requires a 25M clock, the parent 
uniphy clock maybe updated to 125M by clock framework if the flag 
CLK_SET_RATE_PARENT is set here, but the actual hardware clock rate of 
uniphy is still 315.5M since the uniphy still works in the interface 
mode PHY_INTERFACE_MODE_2500BASEX.


>>
>>>
>>>> +       },
>>>> +};
>>>> +
>>>> +static struct clk_regmap_div nss_cc_mac5_rx_div_clk_src = {
>>>> +       .reg = 0x15c,
>>>> +       .shift = 0,
>>>> +       .width = 4,
>>>> +       .clkr = {
>>>> +               .hw.init = &(const struct clk_init_data) {
>>>> +                       .name = "nss_cc_mac5_rx_div_clk_src",
>>> [...]
>>>> +
>>>> +static struct clk_branch nss_cc_mdio_master_ahb_clk = {
>>>> +       .halt_reg = 0x19c,
>>>> +       .halt_check = BRANCH_HALT,
>>>> +       .clkr = {
>>>> +               .enable_reg = 0x19c,
>>>> +               .enable_mask = BIT(0),
>>>> +               .hw.init = &(const struct clk_init_data) {
>>>> +                       .name = "nss_cc_mdio_master_ahb_clk",
>>>> +                       .parent_hws = (const struct clk_hw *[]) {
>>>> +                               &nss_cc_ahb_clk_src.clkr.hw,
>>>> +                       },
>>>> +                       .num_parents = 1,
>>>> +                       .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
>>>
>>> Why can't we simply enable clks in probe that are critical? The regmap
>>> operations are complicated?
>>
>> since these clocks with the flag CLK_IS_CRITICAL are the common clocks
>> needed to be enabled for all devices no matter what work mode(qca8084 or
>> qca8386) used, which is base clock to enable to use the clock driver, to
>> enable these clocks by using flag CLK_IS_CRITICAL is simplier way and
>> can simply the device probe driver and device tree definations.
> 
> Sure, but it also means you use the despised CLK_IS_CRITICAL flag when
> it could simply be some code in probe that sets some bits for "boot
> configuration". The benefit is that we don't register clks that do
> practically nothing besides use resources in the kernel for a one time
> operation at probe.
> 

Okay, that makes sense, i will remove this flag CLK_IS_CRITICAL, and 
enable these clocks in the probe function of the consumer driver.

Thanks Stephen for the review and suggestions!
>>
>>>
>>>> +};
>>>> +
>>>> +/* For each read/write operation of clock register, there are three MDIO frames
>>>> + * sent to the device.
>>>> + *
>>>> + * 1. The high address part[31:8] of register is packaged into the first MDIO frame.
>>>> + * 2. The low address part[7:0] of register is packaged into the second MDIO frame
>>>> + *    with the low 16bit data to read/write.
>>>> + * 3. The low address part[7:0] of register is packaged into the last MDIO frame
>>>> + *    with the high 16bit data to read/write.
>>>> + *
>>>> + * The clause22 MDIO frame format used by device is as below.
>>>> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>>>> + * | ST| OP|   ADDR  |   REG   | TA|             DATA              |
>>>> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>>>> + */
>>>> +static inline void split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
>>>
>>> split_addr() is too generic of a name. Please namespace this function to
>>> something else.
>>
>> okay, maybe convert_reg_to_mii_addr?
> 
> Sure!
> 
>>>
>>>> +               *val |= ret << 16;
>>>> +       }
>>>> +
>>>> +       if (ret < 0)
>>>> +               dev_err_ratelimited(&bus->dev, "fail to read qca8k mii register\n");
>>>> +
>>>> +       return ret < 0 ? ret : 0;
>>>> +}
>>>> +
>>>> +void qca8k_mii_write(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 val)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       ret = bus->write(bus, switch_phy_id, reg, lower_16_bits(val));
>>>> +       if (ret >= 0)
>>>> +               ret = bus->write(bus, switch_phy_id, (reg | BIT(1)), upper_16_bits(val));
>>>> +
>>>> +       if (ret < 0)
>>>> +               dev_err_ratelimited(&bus->dev, "fail to write qca8k mii register\n");
>>>> +}
>>>> +
>>>> +int qca8k_mii_page_set(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u16 page)
>>>
>>> Regmap core has support for picking pages. Can that be used here?
>>
>> Hi Stephen,
>> No, we can't depend on regmap to pick the page, since the MDIO bus is
>> shared by qca8k device and PHY device, if there is a PHY device access,
>> even if the page is not changed, we still need to configure the page
>> again, so the page is alwasy configured for each register access, the
>> sequence can't be interrupted.
> 
> Ok.
> 
>>>
>>>> +};
>>>> +
>>>> +static const struct qcom_cc_desc nss_cc_qca8k_desc = {
>>>> +       .config = &nss_cc_qca8k_regmap_config,
>>>> +       .clks = nss_cc_qca8k_clocks,
>>>> +       .num_clks = ARRAY_SIZE(nss_cc_qca8k_clocks),
>>>> +       .resets = nss_cc_qca8k_resets,
>>>> +       .num_resets = ARRAY_SIZE(nss_cc_qca8k_resets),
>>>> +};
>>>> +
>>>> +static int nss_cc_qca8k_probe(struct mdio_device *mdiodev)
>>>> +{
>>>> +       struct regmap *regmap;
>>>> +
>>>> +       regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev->bus, nss_cc_qca8k_desc.config);
>>>
>>> Why can't we use devm_regmap_init_mdio() here? Is it because the device
>>> needs special data marshaling per split_addr()?
>>
>> Hi Stephen,
>> No, we can't use devm_regmap_init_mdio, which is for the standard PHY
>> device access(clause22 and clause 45), but the clock device needs the
>> special MDIO sequences for the register access.
> 
> Ok.
Stephen Boyd Sept. 11, 2023, 8:11 p.m. UTC | #5
Quoting Jie Luo (2023-09-08 04:10:35)
> 
> 
> Yes, the uniphy implements the clock provider that supports changing 
> rate, which will be upstream later, and nss_cc_mac5_rx_clk_src is the 
> special case, which is only used in the switch device qca8386.

Ok great.

> 
> For the phy device qca8084(uniphy has only 312.5M fix clock which is 
> registered by device tree), this clock nss_cc_mac5_rx_clk_src is not used.
> 
> The issue for the switch device(qca8386) here is the clock rate of 
> parent uniphy can't be changed because of the clock rate requirement of 
> branch clock, since the uniphy clock rate is decided by the current 
> working interface mode(PHY_INTERFACE_MODE_2500BASEX with 312.5M or 
> PHY_INTERFACE_MODE_SGMII with 125M).

Got it.

> 
> For example, when the uniphy works on PHY_INTERFACE_MODE_2500BASEX, then 
> the parent uniphy clock rate is 312.5M, which is decided by hardware and 
> can't be changed. when a branch clock requires a 25M clock, the parent 
> uniphy clock maybe updated to 125M by clock framework if the flag 
> CLK_SET_RATE_PARENT is set here, but the actual hardware clock rate of 
> uniphy is still 315.5M since the uniphy still works in the interface 
> mode PHY_INTERFACE_MODE_2500BASEX.
> 

If the parent rate can't change because CLK_SET_RATE_PARENT is missing
and the hardware doesn't allow it, then perhaps instead of having a
frequency table we should have rcg clk ops for determine_rate that
simply looks at the parent rates and finds the rate closest to what is
desired. And for the set_rate clk_op we can have it be simple and just
program a fixed divider. The benefit is less frequency tables that don't
do anything and less hard-coding of the frequency. I thought we already
had those rcg clk_ops but I couldn't find them with a quick glance.
Jie Luo Sept. 12, 2023, 12:07 p.m. UTC | #6
On 9/12/2023 4:11 AM, Stephen Boyd wrote:
> Quoting Jie Luo (2023-09-08 04:10:35)
>>
>>
>> Yes, the uniphy implements the clock provider that supports changing
>> rate, which will be upstream later, and nss_cc_mac5_rx_clk_src is the
>> special case, which is only used in the switch device qca8386.
> 
> Ok great.
> 
>>
>> For the phy device qca8084(uniphy has only 312.5M fix clock which is
>> registered by device tree), this clock nss_cc_mac5_rx_clk_src is not used.
>>
>> The issue for the switch device(qca8386) here is the clock rate of
>> parent uniphy can't be changed because of the clock rate requirement of
>> branch clock, since the uniphy clock rate is decided by the current
>> working interface mode(PHY_INTERFACE_MODE_2500BASEX with 312.5M or
>> PHY_INTERFACE_MODE_SGMII with 125M).
> 
> Got it.
> 
>>
>> For example, when the uniphy works on PHY_INTERFACE_MODE_2500BASEX, then
>> the parent uniphy clock rate is 312.5M, which is decided by hardware and
>> can't be changed. when a branch clock requires a 25M clock, the parent
>> uniphy clock maybe updated to 125M by clock framework if the flag
>> CLK_SET_RATE_PARENT is set here, but the actual hardware clock rate of
>> uniphy is still 315.5M since the uniphy still works in the interface
>> mode PHY_INTERFACE_MODE_2500BASEX.
>>
> 
> If the parent rate can't change because CLK_SET_RATE_PARENT is missing
> and the hardware doesn't allow it, then perhaps instead of having a
> frequency table we should have rcg clk ops for determine_rate that
> simply looks at the parent rates and finds the rate closest to what is
> desired. And for the set_rate clk_op we can have it be simple and just
> program a fixed divider. The benefit is less frequency tables that don't
> do anything and less hard-coding of the frequency. I thought we already
> had those rcg clk_ops but I couldn't find them with a quick glance.

Thanks Stephen for the suggestion.
looks you are saying the clk ops clk_dp_ops for the fix parent rate? 
which seems not meet the clock requirement of this clock.

For the device qca8k, it is also possible to switch the interface modes 
between PHY_INTERFACE_MODE_2500BASEX(312.5M) and 
PHY_INTERFACE_MODE_SGMII(125M) during the running time, and there are 
multiple parent clock source(P_UNIPHY0_RX or P_UNIPHY0_TX) for the RCG 
clocks to select according to the current work mode. so the parent_map 
and freq_tbl are necessary to this clock.

such as the following clock table, same parent clock rate has the 
different parent source.

+static const struct parent_map nss_cc_uniphy0_rx_tx_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY0_RX, 1 },
+	{ P_UNIPHY0_TX, 2 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac5_rx_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY0_RX, 1, 0, 0),
+	F(125000000, P_UNIPHY0_TX, 1, 0, 0),
+	F(312500000, P_UNIPHY0_RX, 1, 0, 0),
+	F(312500000, P_UNIPHY0_TX, 1, 0, 0),
+	{ }
+};
Stephen Boyd Sept. 12, 2023, 5:18 p.m. UTC | #7
Quoting Jie Luo (2023-09-12 05:07:02)
> 
> 
> On 9/12/2023 4:11 AM, Stephen Boyd wrote:
> > Quoting Jie Luo (2023-09-08 04:10:35)
> >>
> >> For example, when the uniphy works on PHY_INTERFACE_MODE_2500BASEX, then
> >> the parent uniphy clock rate is 312.5M, which is decided by hardware and
> >> can't be changed. when a branch clock requires a 25M clock, the parent
> >> uniphy clock maybe updated to 125M by clock framework if the flag
> >> CLK_SET_RATE_PARENT is set here, but the actual hardware clock rate of
> >> uniphy is still 315.5M since the uniphy still works in the interface
> >> mode PHY_INTERFACE_MODE_2500BASEX.
> >>
> > 
> > If the parent rate can't change because CLK_SET_RATE_PARENT is missing
> > and the hardware doesn't allow it, then perhaps instead of having a
> > frequency table we should have rcg clk ops for determine_rate that
> > simply looks at the parent rates and finds the rate closest to what is
> > desired. And for the set_rate clk_op we can have it be simple and just
> > program a fixed divider. The benefit is less frequency tables that don't
> > do anything and less hard-coding of the frequency. I thought we already
> > had those rcg clk_ops but I couldn't find them with a quick glance.
> 
> Thanks Stephen for the suggestion.
> looks you are saying the clk ops clk_dp_ops for the fix parent rate? 
> which seems not meet the clock requirement of this clock.

Yeah that is close, but the determine_rate clk_op needs to look at all
possible parents. With the dp clk_ops we assume that only one parent is
possible.

> 
> For the device qca8k, it is also possible to switch the interface modes 
> between PHY_INTERFACE_MODE_2500BASEX(312.5M) and 
> PHY_INTERFACE_MODE_SGMII(125M) during the running time, and there are 
> multiple parent clock source(P_UNIPHY0_RX or P_UNIPHY0_TX) for the RCG 
> clocks to select according to the current work mode. so the parent_map 
> and freq_tbl are necessary to this clock.

I still don't see why the freq_tbl is necessary.
Jie Luo Sept. 13, 2023, 3:27 a.m. UTC | #8
On 9/13/2023 1:18 AM, Stephen Boyd wrote:
> Quoting Jie Luo (2023-09-12 05:07:02)
>>
>>
>> On 9/12/2023 4:11 AM, Stephen Boyd wrote:
>>> Quoting Jie Luo (2023-09-08 04:10:35)
>>>>
>>>> For example, when the uniphy works on PHY_INTERFACE_MODE_2500BASEX, then
>>>> the parent uniphy clock rate is 312.5M, which is decided by hardware and
>>>> can't be changed. when a branch clock requires a 25M clock, the parent
>>>> uniphy clock maybe updated to 125M by clock framework if the flag
>>>> CLK_SET_RATE_PARENT is set here, but the actual hardware clock rate of
>>>> uniphy is still 315.5M since the uniphy still works in the interface
>>>> mode PHY_INTERFACE_MODE_2500BASEX.
>>>>
>>>
>>> If the parent rate can't change because CLK_SET_RATE_PARENT is missing
>>> and the hardware doesn't allow it, then perhaps instead of having a
>>> frequency table we should have rcg clk ops for determine_rate that
>>> simply looks at the parent rates and finds the rate closest to what is
>>> desired. And for the set_rate clk_op we can have it be simple and just
>>> program a fixed divider. The benefit is less frequency tables that don't
>>> do anything and less hard-coding of the frequency. I thought we already
>>> had those rcg clk_ops but I couldn't find them with a quick glance.
>>
>> Thanks Stephen for the suggestion.
>> looks you are saying the clk ops clk_dp_ops for the fix parent rate?
>> which seems not meet the clock requirement of this clock.
> 
> Yeah that is close, but the determine_rate clk_op needs to look at all
> possible parents. With the dp clk_ops we assume that only one parent is
> possible.
> 
>>
>> For the device qca8k, it is also possible to switch the interface modes
>> between PHY_INTERFACE_MODE_2500BASEX(312.5M) and
>> PHY_INTERFACE_MODE_SGMII(125M) during the running time, and there are
>> multiple parent clock source(P_UNIPHY0_RX or P_UNIPHY0_TX) for the RCG
>> clocks to select according to the current work mode. so the parent_map
>> and freq_tbl are necessary to this clock.
> 
> I still don't see why the freq_tbl is necessary.

Hi Stephen,
For clk_rcg2_ops, freq_tbl is used to find the closest rate to decided 
the parent clock, the configuration of clock source and clock divider 
are saved in the freq_tbl to configure the RCG hardware register, the 
mapping of parent clock and hardware register value is decided by the 
freq_tbl for the RCG clock.
Stephen Boyd Sept. 14, 2023, 4:30 p.m. UTC | #9
Quoting Jie Luo (2023-09-12 20:27:25)
> 
> 
> On 9/13/2023 1:18 AM, Stephen Boyd wrote:
> > Quoting Jie Luo (2023-09-12 05:07:02)
> >>
> >> and freq_tbl are necessary to this clock.
> > 
> > I still don't see why the freq_tbl is necessary.
> 
> Hi Stephen,
> For clk_rcg2_ops, freq_tbl is used to find the closest rate to decided 
> the parent clock, the configuration of clock source and clock divider 
> are saved in the freq_tbl to configure the RCG hardware register, the 
> mapping of parent clock and hardware register value is decided by the 
> freq_tbl for the RCG clock.

The divider is always 1. The frequency is the frequency of the parent.
The two pieces of information are already known without the frequency
table. Why is it needed?
Jie Luo Sept. 15, 2023, 9:57 a.m. UTC | #10
On 9/15/2023 12:30 AM, Stephen Boyd wrote:
> Quoting Jie Luo (2023-09-12 20:27:25)
>>
>>
>> On 9/13/2023 1:18 AM, Stephen Boyd wrote:
>>> Quoting Jie Luo (2023-09-12 05:07:02)
>>>>
>>>> and freq_tbl are necessary to this clock.
>>>
>>> I still don't see why the freq_tbl is necessary.
>>
>> Hi Stephen,
>> For clk_rcg2_ops, freq_tbl is used to find the closest rate to decided
>> the parent clock, the configuration of clock source and clock divider
>> are saved in the freq_tbl to configure the RCG hardware register, the
>> mapping of parent clock and hardware register value is decided by the
>> freq_tbl for the RCG clock.
> 
> The divider is always 1. The frequency is the frequency of the parent.
> The two pieces of information are already known without the frequency
> table. Why is it needed?

Hi Stephen,
For mac0 and mac5 RCG clock, it is true with divider 1, since these two 
MACs are connected with CPU port, which is always the fix link speed, 
the clock rate is always 312.5M or 125M, in this case with multiple 
parent clocks and divider 1, it seems there is no special RCG clock ops 
for it currently, so we leverage the clock ops clk_rcg2_ops.

For other MACs(1-4), which are connected with physical port, the link 
speed is dynamically changed, and the divider is different for the 
different link speed, such as the mac1 clock freq table as below.

static const struct freq_tbl ftbl_nss_cc_mac1_tx_clk_src[] = { 

         F(25000000, P_UNIPHY1_TX312P5M, 12.5, 0, 0), 

         F(25000000, P_UNIPHY1_RX312P5M, 12.5, 0, 0), 

         F(50000000, P_XO, 1, 0, 0), 

         F(125000000, P_UNIPHY1_TX312P5M, 2.5, 0, 0), 

         F(125000000, P_UNIPHY1_RX312P5M, 2.5, 0, 0), 

         F(312500000, P_UNIPHY1_TX312P5M, 1, 0, 0), 

         F(312500000, P_UNIPHY1_RX312P5M, 1, 0, 0), 

         { } 

};

Thanks,
Jie.
Jie Luo Sept. 23, 2023, 11:26 a.m. UTC | #11
On 9/15/2023 12:30 AM, Stephen Boyd wrote:
> Quoting Jie Luo (2023-09-12 20:27:25)
>>
>>
>> On 9/13/2023 1:18 AM, Stephen Boyd wrote:
>>> Quoting Jie Luo (2023-09-12 05:07:02)
>>>>
>>>> and freq_tbl are necessary to this clock.
>>>
>>> I still don't see why the freq_tbl is necessary.
>>
>> Hi Stephen,
>> For clk_rcg2_ops, freq_tbl is used to find the closest rate to decided
>> the parent clock, the configuration of clock source and clock divider
>> are saved in the freq_tbl to configure the RCG hardware register, the
>> mapping of parent clock and hardware register value is decided by the
>> freq_tbl for the RCG clock.
> 
> The divider is always 1. The frequency is the frequency of the parent.
> The two pieces of information are already known without the frequency
> table. Why is it needed?

Hi Stephen,
i uploaded the new patchset V9 to remove these redundant freq_tbl by 
using the clk_ops clk_ops clk_rcg2_mux_closest_ops, thanks for this
suggestion for the code improvement.

Best Regards,
Jie
diff mbox series

Patch

diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 263e55d75e3f..785cb6eb514f 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -195,6 +195,14 @@  config IPQ_GCC_9574
 	  i2c, USB, SD/eMMC, etc. Select this for the root clock
 	  of ipq9574.
 
+config IPQ_NSSCC_QCA8K
+	tristate "QCA8K(QCA8386 or QCA8084) NSS Clock Controller"
+	help
+	  Support for NSS(Network SubSystem) clock controller on
+	  qca8386/qca8084 chip.
+	  Say Y or M if you want to use network features of switch or
+	  PHY device. Select this for the root clock of qca8k.
+
 config MSM_GCC_8660
 	tristate "MSM8660 Global Clock Controller"
 	depends on ARM || COMPILE_TEST
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index e6e294274c35..2279d15ced61 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -30,6 +30,7 @@  obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
 obj-$(CONFIG_IPQ_GCC_8074) += gcc-ipq8074.o
 obj-$(CONFIG_IPQ_GCC_9574) += gcc-ipq9574.o
 obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o
+obj-$(CONFIG_IPQ_NSSCC_QCA8K) += nsscc-qca8k.o
 obj-$(CONFIG_MDM_GCC_9607) += gcc-mdm9607.o
 obj-$(CONFIG_MDM_GCC_9615) += gcc-mdm9615.o
 obj-$(CONFIG_MDM_LCC_9615) += lcc-mdm9615.o
diff --git a/drivers/clk/qcom/nsscc-qca8k.c b/drivers/clk/qcom/nsscc-qca8k.c
new file mode 100644
index 000000000000..f9312735daf3
--- /dev/null
+++ b/drivers/clk/qcom/nsscc-qca8k.c
@@ -0,0 +1,2179 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/phy.h>
+
+#include <dt-bindings/clock/qcom,qca8k-nsscc.h>
+#include <dt-bindings/reset/qcom,qca8k-nsscc.h>
+
+#include "clk-branch.h"
+#include "clk-rcg.h"
+#include "clk-regmap.h"
+#include "clk-regmap-divider.h"
+#include "clk-regmap-mux.h"
+#include "common.h"
+#include "reset.h"
+
+#define QCA8K_HIGH_ADDR_PREFIX	0x18
+#define QCA8K_LOW_ADDR_PREFIX	0x10
+#define QCA8K_CFG_PAGE_REG	0xc
+#define QCA8K_CLK_REG_BASE	0x800000
+
+enum {
+	DT_XO,
+	DT_UNIPHY0_RX_CLK,
+	DT_UNIPHY0_TX_CLK,
+	DT_UNIPHY1_RX_CLK,
+	DT_UNIPHY1_TX_CLK,
+	DT_UNIPHY1_RX312P5M_CLK,
+	DT_UNIPHY1_TX312P5M_CLK,
+};
+
+enum {
+	P_XO,
+	P_UNIPHY0_RX,
+	P_UNIPHY0_TX,
+	P_UNIPHY1_RX,
+	P_UNIPHY1_TX,
+	P_UNIPHY1_RX312P5M,
+	P_UNIPHY1_TX312P5M,
+	P_MAC4_RX_DIV,
+	P_MAC4_TX_DIV,
+	P_MAC5_RX_DIV,
+	P_MAC5_TX_DIV,
+};
+
+static const struct clk_parent_data nss_cc_uniphy1_tx312p5m_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_TX312P5M_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy1_tx312p5m_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX312P5M, 1 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_switch_core_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(312500000, P_UNIPHY1_TX312P5M, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_switch_core_clk_src = {
+	.cmd_rcgr = 0x0,
+	.freq_tbl = ftbl_nss_cc_switch_core_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_tx312p5m_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_switch_core_clk_src",
+		.parent_data = nss_cc_uniphy1_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_branch nss_cc_switch_core_clk = {
+	.halt_reg = 0x8,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x8,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_switch_core_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_switch_core_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_apb_bridge_clk = {
+	.halt_reg = 0x10,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x10,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_apb_bridge_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_switch_core_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy1_tx_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_TX_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy1_tx_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX, 2 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac0_tx_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY1_TX, 1, 0, 0),
+	F(312500000, P_UNIPHY1_TX, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac0_tx_clk_src = {
+	.cmd_rcgr = 0x14,
+	.freq_tbl = ftbl_nss_cc_mac0_tx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_tx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac0_tx_clk_src",
+		.parent_data = nss_cc_uniphy1_tx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_tx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac0_tx_div_clk_src = {
+	.reg = 0x1c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac0_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac0_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac0_tx_clk = {
+	.halt_reg = 0x20,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x20,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac0_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac0_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac0_tx_srds1_clk = {
+	.halt_reg = 0x24,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x24,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac0_tx_srds1_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac0_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy1_rx_tx_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_RX_CLK },
+	{ .index = DT_UNIPHY1_TX_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy1_rx_tx_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_RX, 1 },
+	{ P_UNIPHY1_TX, 2 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac0_rx_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY1_RX, 1, 0, 0),
+	F(125000000, P_UNIPHY1_TX, 1, 0, 0),
+	F(312500000, P_UNIPHY1_RX, 1, 0, 0),
+	F(312500000, P_UNIPHY1_TX, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac0_rx_clk_src = {
+	.cmd_rcgr = 0x28,
+	.freq_tbl = ftbl_nss_cc_mac0_rx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_rx_tx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac0_rx_clk_src",
+		.parent_data = nss_cc_uniphy1_rx_tx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_rx_tx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac0_rx_div_clk_src = {
+	.reg = 0x30,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac0_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac0_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac0_rx_clk = {
+	.halt_reg = 0x34,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x34,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac0_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac0_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac0_rx_srds1_clk = {
+	.halt_reg = 0x3c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x3c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac0_rx_srds1_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac0_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy1_rx_tx312p5m_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_TX312P5M_CLK },
+	{ .index = DT_UNIPHY1_RX312P5M_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy1_rx_tx312p5m_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX312P5M, 6 },
+	{ P_UNIPHY1_RX312P5M, 7 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac1_tx_clk_src[] = {
+	F(25000000, P_UNIPHY1_TX312P5M, 12.5, 0, 0),
+	F(25000000, P_UNIPHY1_RX312P5M, 12.5, 0, 0),
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY1_TX312P5M, 2.5, 0, 0),
+	F(125000000, P_UNIPHY1_RX312P5M, 2.5, 0, 0),
+	F(312500000, P_UNIPHY1_TX312P5M, 1, 0, 0),
+	F(312500000, P_UNIPHY1_RX312P5M, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac1_tx_clk_src = {
+	.cmd_rcgr = 0x40,
+	.freq_tbl = ftbl_nss_cc_mac1_tx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_rx_tx312p5m_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac1_tx_clk_src",
+		.parent_data = nss_cc_uniphy1_rx_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_rx_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac1_tx_div_clk_src = {
+	.reg = 0x48,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac1_srds1_ch0_xgmii_rx_div_clk_src = {
+	.reg = 0x4c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_srds1_ch0_xgmii_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_srds1_ch0_rx_clk = {
+	.halt_reg = 0x50,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x50,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_srds1_ch0_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_tx_clk = {
+	.halt_reg = 0x54,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x54,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_gephy0_tx_clk = {
+	.halt_reg = 0x58,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x58,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_gephy0_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_srds1_ch0_xgmii_rx_clk = {
+	.halt_reg = 0x5c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x5c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_srds1_ch0_xgmii_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_srds1_ch0_xgmii_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy1_tx312p5m_prx_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_TX312P5M_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy1_tx312p5m_prx_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX312P5M, 6 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac1_rx_clk_src[] = {
+	F(25000000, P_UNIPHY1_TX312P5M, 12.5, 0, 0),
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY1_TX312P5M, 2.5, 0, 0),
+	F(312500000, P_UNIPHY1_TX312P5M, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac1_rx_clk_src = {
+	.cmd_rcgr = 0x60,
+	.freq_tbl = ftbl_nss_cc_mac1_rx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_tx312p5m_prx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac1_rx_clk_src",
+		.parent_data = nss_cc_uniphy1_tx312p5m_prx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_tx312p5m_prx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac1_rx_div_clk_src = {
+	.reg = 0x68,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac1_srds1_ch0_xgmii_tx_div_clk_src = {
+	.reg = 0x6c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_srds1_ch0_xgmii_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_srds1_ch0_tx_clk = {
+	.halt_reg = 0x70,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x70,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_srds1_ch0_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_rx_clk = {
+	.halt_reg = 0x74,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x74,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_gephy0_rx_clk = {
+	.halt_reg = 0x78,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x78,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_gephy0_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac1_srds1_ch0_xgmii_tx_clk = {
+	.halt_reg = 0x7c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x7c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac1_srds1_ch0_xgmii_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac1_srds1_ch0_xgmii_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_rcg2 nss_cc_mac2_tx_clk_src = {
+	.cmd_rcgr = 0x80,
+	.freq_tbl = ftbl_nss_cc_mac1_tx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_rx_tx312p5m_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac2_tx_clk_src",
+		.parent_data = nss_cc_uniphy1_rx_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_rx_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac2_tx_div_clk_src = {
+	.reg = 0x88,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac2_srds1_ch1_xgmii_rx_div_clk_src = {
+	.reg = 0x8c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_srds1_ch1_xgmii_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_srds1_ch1_rx_clk = {
+	.halt_reg = 0x90,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x90,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_srds1_ch1_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_tx_clk = {
+	.halt_reg = 0x94,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x94,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_gephy1_tx_clk = {
+	.halt_reg = 0x98,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x98,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_gephy1_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_srds1_ch1_xgmii_rx_clk = {
+	.halt_reg = 0x9c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x9c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_srds1_ch1_xgmii_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_srds1_ch1_xgmii_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_rcg2 nss_cc_mac2_rx_clk_src = {
+	.cmd_rcgr = 0xa0,
+	.freq_tbl = ftbl_nss_cc_mac1_rx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_tx312p5m_prx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac2_rx_clk_src",
+		.parent_data = nss_cc_uniphy1_tx312p5m_prx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_tx312p5m_prx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac2_rx_div_clk_src = {
+	.reg = 0xa8,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac2_srds1_ch1_xgmii_tx_div_clk_src = {
+	.reg = 0xac,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_srds1_ch1_xgmii_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_srds1_ch1_tx_clk = {
+	.halt_reg = 0xb0,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb0,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_srds1_ch1_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_rx_clk = {
+	.halt_reg = 0xb4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_gephy1_rx_clk = {
+	.halt_reg = 0xb8,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb8,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_gephy1_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac2_srds1_ch1_xgmii_tx_clk = {
+	.halt_reg = 0xbc,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xbc,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac2_srds1_ch1_xgmii_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac2_srds1_ch1_xgmii_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_rcg2 nss_cc_mac3_tx_clk_src = {
+	.cmd_rcgr = 0xc0,
+	.freq_tbl = ftbl_nss_cc_mac1_tx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_rx_tx312p5m_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac3_tx_clk_src",
+		.parent_data = nss_cc_uniphy1_rx_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_rx_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac3_tx_div_clk_src = {
+	.reg = 0xc8,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac3_srds1_ch2_xgmii_rx_div_clk_src = {
+	.reg = 0xcc,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_srds1_ch2_xgmii_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_srds1_ch2_rx_clk = {
+	.halt_reg = 0xd0,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xd0,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_srds1_ch2_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_tx_clk = {
+	.halt_reg = 0xd4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xd4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_gephy2_tx_clk = {
+	.halt_reg = 0xd8,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xd8,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_gephy2_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_srds1_ch2_xgmii_rx_clk = {
+	.halt_reg = 0xdc,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xdc,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_srds1_ch2_xgmii_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_srds1_ch2_xgmii_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_rcg2 nss_cc_mac3_rx_clk_src = {
+	.cmd_rcgr = 0xe0,
+	.freq_tbl = ftbl_nss_cc_mac1_rx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_tx312p5m_prx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac3_rx_clk_src",
+		.parent_data = nss_cc_uniphy1_tx312p5m_prx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_tx312p5m_prx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac3_rx_div_clk_src = {
+	.reg = 0xe8,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac3_srds1_ch2_xgmii_tx_div_clk_src = {
+	.reg = 0xec,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_srds1_ch2_xgmii_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_srds1_ch2_tx_clk = {
+	.halt_reg = 0xf0,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xf0,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_srds1_ch2_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_rx_clk = {
+	.halt_reg = 0xf4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xf4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_gephy2_rx_clk = {
+	.halt_reg = 0xf8,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xf8,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_gephy2_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac3_srds1_ch2_xgmii_tx_clk = {
+	.halt_reg = 0xfc,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xfc,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac3_srds1_ch2_xgmii_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac3_srds1_ch2_xgmii_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy0_rx_uniphy1_rx_tx312p5m_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_TX312P5M_CLK },
+	{ .index = DT_UNIPHY1_RX312P5M_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy0_rx_uniphy1_rx_tx312p5m_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX312P5M, 3 },
+	{ P_UNIPHY1_RX312P5M, 7 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac4_tx_clk_src[] = {
+	F(25000000, P_UNIPHY1_TX312P5M, 12.5, 0, 0),
+	F(25000000, P_UNIPHY1_RX312P5M, 12.5, 0, 0),
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY1_TX312P5M, 2.5, 0, 0),
+	F(125000000, P_UNIPHY1_RX312P5M, 2.5, 0, 0),
+	F(312500000, P_UNIPHY1_TX312P5M, 1, 0, 0),
+	F(312500000, P_UNIPHY1_RX312P5M, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac4_tx_clk_src = {
+	.cmd_rcgr = 0x100,
+	.freq_tbl = ftbl_nss_cc_mac4_tx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy0_rx_uniphy1_rx_tx312p5m_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac4_tx_clk_src",
+		.parent_data = nss_cc_uniphy0_rx_uniphy1_rx_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy0_rx_uniphy1_rx_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac4_tx_div_clk_src = {
+	.reg = 0x108,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac4_srds1_ch3_xgmii_rx_div_clk_src = {
+	.reg = 0x10c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_srds1_ch3_xgmii_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_srds1_ch3_rx_clk = {
+	.halt_reg = 0x110,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x110,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_srds1_ch3_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_tx_clk = {
+	.halt_reg = 0x114,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x114,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_gephy3_tx_clk = {
+	.halt_reg = 0x118,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x118,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_gephy3_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_srds1_ch3_xgmii_rx_clk = {
+	.halt_reg = 0x11c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x11c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_srds1_ch3_xgmii_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_srds1_ch3_xgmii_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy0_tx_uniphy1_tx312p5m_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY1_TX312P5M_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy0_tx_uniphy1_tx312p5m_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX312P5M, 3 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac4_rx_clk_src[] = {
+	F(25000000, P_UNIPHY1_TX312P5M, 12.5, 0, 0),
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY1_TX312P5M, 2.5, 0, 0),
+	F(312500000, P_UNIPHY1_TX312P5M, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac4_rx_clk_src = {
+	.cmd_rcgr = 0x120,
+	.freq_tbl = ftbl_nss_cc_mac4_rx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy0_tx_uniphy1_tx312p5m_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac4_rx_clk_src",
+		.parent_data = nss_cc_uniphy0_tx_uniphy1_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy0_tx_uniphy1_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac4_rx_div_clk_src = {
+	.reg = 0x128,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac4_srds1_ch3_xgmii_tx_div_clk_src = {
+	.reg = 0x12c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_srds1_ch3_xgmii_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_srds1_ch3_tx_clk = {
+	.halt_reg = 0x130,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x130,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_srds1_ch3_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_rx_clk = {
+	.halt_reg = 0x134,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x134,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_gephy3_rx_clk = {
+	.halt_reg = 0x138,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x138,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_gephy3_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac4_srds1_ch3_xgmii_tx_clk = {
+	.halt_reg = 0x13c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x13c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac4_srds1_ch3_xgmii_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_srds1_ch3_xgmii_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy0_tx_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY0_TX_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy0_tx_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY0_TX, 2 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac5_tx_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY0_TX, 1, 0, 0),
+	F(312500000, P_UNIPHY0_TX, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac5_tx_clk_src = {
+	.cmd_rcgr = 0x140,
+	.freq_tbl = ftbl_nss_cc_mac5_tx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy0_tx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac5_tx_clk_src",
+		.parent_data = nss_cc_uniphy0_tx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy0_tx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac5_tx_div_clk_src = {
+	.reg = 0x148,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_tx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac5_tx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac5_tx_clk = {
+	.halt_reg = 0x14c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x14c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_tx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac5_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_uniphy0_rx_tx_data[] = {
+	{ .index = DT_XO },
+	{ .index = DT_UNIPHY0_RX_CLK },
+	{ .index = DT_UNIPHY0_TX_CLK },
+};
+
+static const struct parent_map nss_cc_uniphy0_rx_tx_map[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY0_RX, 1 },
+	{ P_UNIPHY0_TX, 2 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_mac5_rx_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(125000000, P_UNIPHY0_RX, 1, 0, 0),
+	F(125000000, P_UNIPHY0_TX, 1, 0, 0),
+	F(312500000, P_UNIPHY0_RX, 1, 0, 0),
+	F(312500000, P_UNIPHY0_TX, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_mac5_rx_clk_src = {
+	.cmd_rcgr = 0x154,
+	.freq_tbl = ftbl_nss_cc_mac5_rx_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy0_rx_tx_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_mac5_rx_clk_src",
+		.parent_data = nss_cc_uniphy0_rx_tx_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy0_rx_tx_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_regmap_div nss_cc_mac5_rx_div_clk_src = {
+	.reg = 0x15c,
+	.shift = 0,
+	.width = 4,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_rx_div_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac5_rx_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_div_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac5_rx_clk = {
+	.halt_reg = 0x160,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x160,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_rx_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac5_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct parent_map nss_cc_mac4_rx_div_mac5_tx_div_map[] = {
+	{ P_MAC4_RX_DIV, 0 },
+	{ P_MAC5_TX_DIV, 1 },
+};
+
+static struct clk_regmap_mux nss_cc_mac5_tx_srds0_clk_src = {
+	.reg = 0x300,
+	.shift = 0,
+	.width = 1,
+	.parent_map = nss_cc_mac4_rx_div_mac5_tx_div_map,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_tx_srds0_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_rx_div_clk_src.clkr.hw,
+				&nss_cc_mac5_tx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 2,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_mux_closest_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac5_tx_srds0_clk = {
+	.halt_reg = 0x150,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x150,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_tx_srds0_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac5_tx_srds0_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct parent_map nss_cc_mac4_tx_div_mac5_rx_div_map[] = {
+	{ P_MAC4_TX_DIV, 0 },
+	{ P_MAC5_RX_DIV, 1 },
+};
+
+static struct clk_regmap_mux nss_cc_mac5_rx_srds0_clk_src = {
+	.reg = 0x300,
+	.shift = 1,
+	.width = 1,
+	.parent_map = nss_cc_mac4_tx_div_mac5_rx_div_map,
+	.clkr = {
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_rx_srds0_clk_src",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac4_tx_div_clk_src.clkr.hw,
+				&nss_cc_mac5_rx_div_clk_src.clkr.hw,
+			},
+			.num_parents = 2,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_regmap_mux_closest_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mac5_rx_srds0_clk = {
+	.halt_reg = 0x164,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x164,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mac5_rx_srds0_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_mac5_rx_srds0_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct parent_map nss_cc_uniphy1_tx312p5m_map2[] = {
+	{ P_XO, 0 },
+	{ P_UNIPHY1_TX312P5M, 2 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_ahb_clk_src[] = {
+	F(50000000, P_XO, 1, 0, 0),
+	F(104170000, P_UNIPHY1_TX312P5M, 3, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_ahb_clk_src = {
+	.cmd_rcgr = 0x168,
+	.freq_tbl = ftbl_nss_cc_ahb_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_uniphy1_tx312p5m_map2,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_ahb_clk_src",
+		.parent_data = nss_cc_uniphy1_tx312p5m_data,
+		.num_parents = ARRAY_SIZE(nss_cc_uniphy1_tx312p5m_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_branch nss_cc_ahb_clk = {
+	.halt_reg = 0x170,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x170,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_ahb_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_sec_ctrl_ahb_clk = {
+	.halt_reg = 0x174,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x174,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_sec_ctrl_ahb_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_tlmm_clk = {
+	.halt_reg = 0x178,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x178,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_tlmm_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_tlmm_ahb_clk = {
+	.halt_reg = 0x190,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x190,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_tlmm_ahb_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_cnoc_ahb_clk = {
+	.halt_reg = 0x194,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x194,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_cnoc_ahb_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mdio_ahb_clk = {
+	.halt_reg = 0x198,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x198,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mdio_ahb_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_mdio_master_ahb_clk = {
+	.halt_reg = 0x19c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x19c,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_mdio_master_ahb_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_ahb_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static const struct clk_parent_data nss_cc_xo_data[] = {
+	{ .index = DT_XO },
+};
+
+static const struct parent_map nss_cc_xo_map[] = {
+	{ P_XO, 0 },
+};
+
+static const struct freq_tbl ftbl_nss_cc_sys_clk_src[] = {
+	F(25000000, P_XO, 2, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 nss_cc_sys_clk_src = {
+	.cmd_rcgr = 0x1a0,
+	.freq_tbl = ftbl_nss_cc_sys_clk_src,
+	.hid_width = 5,
+	.parent_map = nss_cc_xo_map,
+	.clkr.hw.init = &(const struct clk_init_data) {
+		.name = "nss_cc_sys_clk_src",
+		.parent_data = nss_cc_xo_data,
+		.num_parents = ARRAY_SIZE(nss_cc_xo_data),
+		.ops = &clk_rcg2_ops,
+	},
+};
+
+static struct clk_branch nss_cc_srds0_sys_clk = {
+	.halt_reg = 0x1a8,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1a8,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_srds0_sys_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_sys_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_srds1_sys_clk = {
+	.halt_reg = 0x1ac,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1ac,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_srds1_sys_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_sys_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_gephy0_sys_clk = {
+	.halt_reg = 0x1b0,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1b0,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_gephy0_sys_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_sys_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_gephy1_sys_clk = {
+	.halt_reg = 0x1b4,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1b4,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_gephy1_sys_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_sys_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_gephy2_sys_clk = {
+	.halt_reg = 0x1b8,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1b8,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_gephy2_sys_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_sys_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_branch nss_cc_gephy3_sys_clk = {
+	.halt_reg = 0x1bc,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1bc,
+		.enable_mask = BIT(0),
+		.hw.init = &(const struct clk_init_data) {
+			.name = "nss_cc_gephy3_sys_clk",
+			.parent_hws = (const struct clk_hw *[]) {
+				&nss_cc_sys_clk_src.clkr.hw,
+			},
+			.num_parents = 1,
+			.flags = CLK_IS_CRITICAL,
+			.ops = &clk_branch2_prepare_ops,
+		},
+	},
+};
+
+static struct clk_regmap *nss_cc_qca8k_clocks[] = {
+	[NSS_CC_SWITCH_CORE_CLK_SRC] = &nss_cc_switch_core_clk_src.clkr,
+	[NSS_CC_SWITCH_CORE_CLK] = &nss_cc_switch_core_clk.clkr,
+	[NSS_CC_APB_BRIDGE_CLK] = &nss_cc_apb_bridge_clk.clkr,
+	[NSS_CC_MAC0_TX_CLK_SRC] = &nss_cc_mac0_tx_clk_src.clkr,
+	[NSS_CC_MAC0_TX_DIV_CLK_SRC] = &nss_cc_mac0_tx_div_clk_src.clkr,
+	[NSS_CC_MAC0_TX_CLK] = &nss_cc_mac0_tx_clk.clkr,
+	[NSS_CC_MAC0_TX_SRDS1_CLK] = &nss_cc_mac0_tx_srds1_clk.clkr,
+	[NSS_CC_MAC0_RX_CLK_SRC] = &nss_cc_mac0_rx_clk_src.clkr,
+	[NSS_CC_MAC0_RX_DIV_CLK_SRC] = &nss_cc_mac0_rx_div_clk_src.clkr,
+	[NSS_CC_MAC0_RX_CLK] = &nss_cc_mac0_rx_clk.clkr,
+	[NSS_CC_MAC0_RX_SRDS1_CLK] = &nss_cc_mac0_rx_srds1_clk.clkr,
+	[NSS_CC_MAC1_TX_CLK_SRC] = &nss_cc_mac1_tx_clk_src.clkr,
+	[NSS_CC_MAC1_TX_DIV_CLK_SRC] = &nss_cc_mac1_tx_div_clk_src.clkr,
+	[NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_DIV_CLK_SRC] =
+		&nss_cc_mac1_srds1_ch0_xgmii_rx_div_clk_src.clkr,
+	[NSS_CC_MAC1_SRDS1_CH0_RX_CLK] = &nss_cc_mac1_srds1_ch0_rx_clk.clkr,
+	[NSS_CC_MAC1_TX_CLK] = &nss_cc_mac1_tx_clk.clkr,
+	[NSS_CC_MAC1_GEPHY0_TX_CLK] = &nss_cc_mac1_gephy0_tx_clk.clkr,
+	[NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_CLK] = &nss_cc_mac1_srds1_ch0_xgmii_rx_clk.clkr,
+	[NSS_CC_MAC1_RX_CLK_SRC] = &nss_cc_mac1_rx_clk_src.clkr,
+	[NSS_CC_MAC1_RX_DIV_CLK_SRC] = &nss_cc_mac1_rx_div_clk_src.clkr,
+	[NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_DIV_CLK_SRC] =
+		&nss_cc_mac1_srds1_ch0_xgmii_tx_div_clk_src.clkr,
+	[NSS_CC_MAC1_SRDS1_CH0_TX_CLK] = &nss_cc_mac1_srds1_ch0_tx_clk.clkr,
+	[NSS_CC_MAC1_RX_CLK] = &nss_cc_mac1_rx_clk.clkr,
+	[NSS_CC_MAC1_GEPHY0_RX_CLK] = &nss_cc_mac1_gephy0_rx_clk.clkr,
+	[NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_CLK] = &nss_cc_mac1_srds1_ch0_xgmii_tx_clk.clkr,
+	[NSS_CC_MAC2_TX_CLK_SRC] = &nss_cc_mac2_tx_clk_src.clkr,
+	[NSS_CC_MAC2_TX_DIV_CLK_SRC] = &nss_cc_mac2_tx_div_clk_src.clkr,
+	[NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_DIV_CLK_SRC] =
+		&nss_cc_mac2_srds1_ch1_xgmii_rx_div_clk_src.clkr,
+	[NSS_CC_MAC2_SRDS1_CH1_RX_CLK] = &nss_cc_mac2_srds1_ch1_rx_clk.clkr,
+	[NSS_CC_MAC2_TX_CLK] = &nss_cc_mac2_tx_clk.clkr,
+	[NSS_CC_MAC2_GEPHY1_TX_CLK] = &nss_cc_mac2_gephy1_tx_clk.clkr,
+	[NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_CLK] = &nss_cc_mac2_srds1_ch1_xgmii_rx_clk.clkr,
+	[NSS_CC_MAC2_RX_CLK_SRC] = &nss_cc_mac2_rx_clk_src.clkr,
+	[NSS_CC_MAC2_RX_DIV_CLK_SRC] = &nss_cc_mac2_rx_div_clk_src.clkr,
+	[NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_DIV_CLK_SRC] =
+		&nss_cc_mac2_srds1_ch1_xgmii_tx_div_clk_src.clkr,
+	[NSS_CC_MAC2_SRDS1_CH1_TX_CLK] = &nss_cc_mac2_srds1_ch1_tx_clk.clkr,
+	[NSS_CC_MAC2_RX_CLK] = &nss_cc_mac2_rx_clk.clkr,
+	[NSS_CC_MAC2_GEPHY1_RX_CLK] = &nss_cc_mac2_gephy1_rx_clk.clkr,
+	[NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_CLK] = &nss_cc_mac2_srds1_ch1_xgmii_tx_clk.clkr,
+	[NSS_CC_MAC3_TX_CLK_SRC] = &nss_cc_mac3_tx_clk_src.clkr,
+	[NSS_CC_MAC3_TX_DIV_CLK_SRC] = &nss_cc_mac3_tx_div_clk_src.clkr,
+	[NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_DIV_CLK_SRC] =
+		&nss_cc_mac3_srds1_ch2_xgmii_rx_div_clk_src.clkr,
+	[NSS_CC_MAC3_SRDS1_CH2_RX_CLK] = &nss_cc_mac3_srds1_ch2_rx_clk.clkr,
+	[NSS_CC_MAC3_TX_CLK] = &nss_cc_mac3_tx_clk.clkr,
+	[NSS_CC_MAC3_GEPHY2_TX_CLK] = &nss_cc_mac3_gephy2_tx_clk.clkr,
+	[NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_CLK] = &nss_cc_mac3_srds1_ch2_xgmii_rx_clk.clkr,
+	[NSS_CC_MAC3_RX_CLK_SRC] = &nss_cc_mac3_rx_clk_src.clkr,
+	[NSS_CC_MAC3_RX_DIV_CLK_SRC] = &nss_cc_mac3_rx_div_clk_src.clkr,
+	[NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_DIV_CLK_SRC] =
+		&nss_cc_mac3_srds1_ch2_xgmii_tx_div_clk_src.clkr,
+	[NSS_CC_MAC3_SRDS1_CH2_TX_CLK] = &nss_cc_mac3_srds1_ch2_tx_clk.clkr,
+	[NSS_CC_MAC3_RX_CLK] = &nss_cc_mac3_rx_clk.clkr,
+	[NSS_CC_MAC3_GEPHY2_RX_CLK] = &nss_cc_mac3_gephy2_rx_clk.clkr,
+	[NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_CLK] = &nss_cc_mac3_srds1_ch2_xgmii_tx_clk.clkr,
+	[NSS_CC_MAC4_TX_CLK_SRC] = &nss_cc_mac4_tx_clk_src.clkr,
+	[NSS_CC_MAC4_TX_DIV_CLK_SRC] = &nss_cc_mac4_tx_div_clk_src.clkr,
+	[NSS_CC_MAC4_SRDS1_CH2_XGMII_RX_DIV_CLK_SRC] =
+		&nss_cc_mac4_srds1_ch3_xgmii_rx_div_clk_src.clkr,
+	[NSS_CC_MAC4_SRDS1_CH3_RX_CLK] = &nss_cc_mac4_srds1_ch3_rx_clk.clkr,
+	[NSS_CC_MAC4_TX_CLK] = &nss_cc_mac4_tx_clk.clkr,
+	[NSS_CC_MAC4_GEPHY3_TX_CLK] = &nss_cc_mac4_gephy3_tx_clk.clkr,
+	[NSS_CC_MAC4_SRDS1_CH3_XGMII_RX_CLK] = &nss_cc_mac4_srds1_ch3_xgmii_rx_clk.clkr,
+	[NSS_CC_MAC4_RX_CLK_SRC] = &nss_cc_mac4_rx_clk_src.clkr,
+	[NSS_CC_MAC4_RX_DIV_CLK_SRC] = &nss_cc_mac4_rx_div_clk_src.clkr,
+	[NSS_CC_MAC4_SRDS1_CH2_XGMII_TX_DIV_CLK_SRC] =
+		&nss_cc_mac4_srds1_ch3_xgmii_tx_div_clk_src.clkr,
+	[NSS_CC_MAC4_SRDS1_CH3_TX_CLK] = &nss_cc_mac4_srds1_ch3_tx_clk.clkr,
+	[NSS_CC_MAC4_RX_CLK] = &nss_cc_mac4_rx_clk.clkr,
+	[NSS_CC_MAC4_GEPHY3_RX_CLK] = &nss_cc_mac4_gephy3_rx_clk.clkr,
+	[NSS_CC_MAC4_SRDS1_CH3_XGMII_TX_CLK] = &nss_cc_mac4_srds1_ch3_xgmii_tx_clk.clkr,
+	[NSS_CC_MAC5_TX_CLK_SRC] = &nss_cc_mac5_tx_clk_src.clkr,
+	[NSS_CC_MAC5_TX_DIV_CLK_SRC] = &nss_cc_mac5_tx_div_clk_src.clkr,
+	[NSS_CC_MAC5_TX_SRDS0_CLK] = &nss_cc_mac5_tx_srds0_clk.clkr,
+	[NSS_CC_MAC5_TX_CLK] = &nss_cc_mac5_tx_clk.clkr,
+	[NSS_CC_MAC5_RX_CLK_SRC] = &nss_cc_mac5_rx_clk_src.clkr,
+	[NSS_CC_MAC5_RX_DIV_CLK_SRC] = &nss_cc_mac5_rx_div_clk_src.clkr,
+	[NSS_CC_MAC5_RX_SRDS0_CLK] = &nss_cc_mac5_rx_srds0_clk.clkr,
+	[NSS_CC_MAC5_RX_CLK] = &nss_cc_mac5_rx_clk.clkr,
+	[NSS_CC_MAC5_TX_SRDS0_CLK_SRC] = &nss_cc_mac5_tx_srds0_clk_src.clkr,
+	[NSS_CC_MAC5_RX_SRDS0_CLK_SRC] = &nss_cc_mac5_rx_srds0_clk_src.clkr,
+	[NSS_CC_AHB_CLK_SRC] = &nss_cc_ahb_clk_src.clkr,
+	[NSS_CC_AHB_CLK] = &nss_cc_ahb_clk.clkr,
+	[NSS_CC_SEC_CTRL_AHB_CLK] = &nss_cc_sec_ctrl_ahb_clk.clkr,
+	[NSS_CC_TLMM_CLK] = &nss_cc_tlmm_clk.clkr,
+	[NSS_CC_TLMM_AHB_CLK] = &nss_cc_tlmm_ahb_clk.clkr,
+	[NSS_CC_CNOC_AHB_CLK] = &nss_cc_cnoc_ahb_clk.clkr,
+	[NSS_CC_MDIO_AHB_CLK] = &nss_cc_mdio_ahb_clk.clkr,
+	[NSS_CC_MDIO_MASTER_AHB_CLK] = &nss_cc_mdio_master_ahb_clk.clkr,
+	[NSS_CC_SYS_CLK_SRC] = &nss_cc_sys_clk_src.clkr,
+	[NSS_CC_SRDS0_SYS_CLK] = &nss_cc_srds0_sys_clk.clkr,
+	[NSS_CC_SRDS1_SYS_CLK] = &nss_cc_srds1_sys_clk.clkr,
+	[NSS_CC_GEPHY0_SYS_CLK] = &nss_cc_gephy0_sys_clk.clkr,
+	[NSS_CC_GEPHY1_SYS_CLK] = &nss_cc_gephy1_sys_clk.clkr,
+	[NSS_CC_GEPHY2_SYS_CLK] = &nss_cc_gephy2_sys_clk.clkr,
+	[NSS_CC_GEPHY3_SYS_CLK] = &nss_cc_gephy3_sys_clk.clkr,
+};
+
+static const struct qcom_reset_map nss_cc_qca8k_resets[] = {
+	[NSS_CC_SWITCH_CORE_ARES] = { 0xC, 2 },
+	[NSS_CC_APB_BRIDGE_ARES] = { 0x10, 2 },
+	[NSS_CC_MAC0_TX_ARES] = { 0x20, 2 },
+	[NSS_CC_MAC0_TX_SRDS1_ARES] = { 0x24, 2 },
+	[NSS_CC_MAC0_RX_ARES] = { 0x34, 2 },
+	[NSS_CC_MAC0_RX_SRDS1_ARES] = { 0x3C, 2 },
+	[NSS_CC_MAC1_SRDS1_CH0_RX_ARES] = { 0x50, 2 },
+	[NSS_CC_MAC1_TX_ARES] = { 0x54, 2 },
+	[NSS_CC_MAC1_GEPHY0_TX_ARES] = { 0x58, 2 },
+	[NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_ARES] = { 0x5C, 2 },
+	[NSS_CC_MAC1_SRDS1_CH0_TX_ARES] = { 0x70, 2 },
+	[NSS_CC_MAC1_RX_ARES] = { 0x74, 2 },
+	[NSS_CC_MAC1_GEPHY0_RX_ARES] = { 0x78, 2 },
+	[NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_ARES] = { 0x7C, 2 },
+	[NSS_CC_MAC2_SRDS1_CH1_RX_ARES] = { 0x90, 2 },
+	[NSS_CC_MAC2_TX_ARES] = { 0x94, 2 },
+	[NSS_CC_MAC2_GEPHY1_TX_ARES] = { 0x98, 2 },
+	[NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_ARES] = { 0x9C, 2 },
+	[NSS_CC_MAC2_SRDS1_CH1_TX_ARES] = { 0xB0, 2 },
+	[NSS_CC_MAC2_RX_ARES] = { 0xB4, 2 },
+	[NSS_CC_MAC2_GEPHY1_RX_ARES] = { 0xB8, 2 },
+	[NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_ARES] = { 0xBC, 2 },
+	[NSS_CC_MAC3_SRDS1_CH2_RX_ARES] = { 0xD0, 2 },
+	[NSS_CC_MAC3_TX_ARES] = { 0xD4, 2 },
+	[NSS_CC_MAC3_GEPHY2_TX_ARES] = { 0xD8, 2 },
+	[NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_ARES] = { 0xDC, 2 },
+	[NSS_CC_MAC3_SRDS1_CH2_TX_ARES] = { 0xF0, 2 },
+	[NSS_CC_MAC3_RX_ARES] = { 0xF4, 2 },
+	[NSS_CC_MAC3_GEPHY2_RX_ARES] = { 0xF8, 2 },
+	[NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_ARES] = { 0xFC, 2 },
+	[NSS_CC_MAC4_SRDS1_CH3_RX_ARES] = { 0x110, 2 },
+	[NSS_CC_MAC4_TX_ARES] = { 0x114, 2 },
+	[NSS_CC_MAC4_GEPHY3_TX_ARES] = { 0x118, 2 },
+	[NSS_CC_MAC4_SRDS1_CH3_XGMII_RX_ARES] = { 0x11C, 2 },
+	[NSS_CC_MAC4_SRDS1_CH3_TX_ARES] = { 0x130, 2 },
+	[NSS_CC_MAC4_RX_ARES] = { 0x134, 2 },
+	[NSS_CC_MAC4_GEPHY3_RX_ARES] = { 0x138, 2 },
+	[NSS_CC_MAC4_SRDS1_CH3_XGMII_TX_ARES] = { 0x13C, 2 },
+	[NSS_CC_MAC5_TX_ARES] = { 0x14C, 2 },
+	[NSS_CC_MAC5_TX_SRDS0_ARES] = { 0x150, 2 },
+	[NSS_CC_MAC5_RX_ARES] = { 0x160, 2 },
+	[NSS_CC_MAC5_RX_SRDS0_ARES] = { 0x164, 2 },
+	[NSS_CC_AHB_ARES] = { 0x170, 2 },
+	[NSS_CC_SEC_CTRL_AHB_ARES] = { 0x174, 2 },
+	[NSS_CC_TLMM_ARES] = { 0x178, 2 },
+	[NSS_CC_TLMM_AHB_ARES] = { 0x190, 2 },
+	[NSS_CC_CNOC_AHB_ARES] = { 0x194, 2 }, /* reset CNOC AHB & APB */
+	[NSS_CC_MDIO_AHB_ARES] = { 0x198, 2 },
+	[NSS_CC_MDIO_MASTER_AHB_ARES] = { 0x19C, 2 },
+	[NSS_CC_SRDS0_SYS_ARES] = { 0x1A8, 2 },
+	[NSS_CC_SRDS1_SYS_ARES] = { 0x1AC, 2 },
+	[NSS_CC_GEPHY0_SYS_ARES] = { 0x1B0, 2 },
+	[NSS_CC_GEPHY1_SYS_ARES] = { 0x1B4, 2 },
+	[NSS_CC_GEPHY2_SYS_ARES] = { 0x1B8, 2 },
+	[NSS_CC_GEPHY3_SYS_ARES] = { 0x1BC, 2 },
+	[NSS_CC_SEC_CTRL_ARES] = { 0x1C8, 2 },
+	[NSS_CC_SEC_CTRL_SENSE_ARES] = { 0x1D0, 2 },
+	[NSS_CC_SLEEP_ARES] = { 0x1E0, 2 },
+	[NSS_CC_DEBUG_ARES] = { 0x1E8, 2 },
+	[NSS_CC_GEPHY0_ARES] = { 0x304, 0 },
+	[NSS_CC_GEPHY1_ARES] = { 0x304, 1 },
+	[NSS_CC_GEPHY2_ARES] = { 0x304, 2 },
+	[NSS_CC_GEPHY3_ARES] = { 0x304, 3 },
+	[NSS_CC_DSP_ARES] = { 0x304, 4 },
+	[NSS_CC_GLOBAL_ARES] = { 0x308, 0 },
+	[NSS_CC_XPCS_ARES] = { 0x30C, 0 },
+};
+
+/* For each read/write operation of clock register, there are three MDIO frames
+ * sent to the device.
+ *
+ * 1. The high address part[31:8] of register is packaged into the first MDIO frame.
+ * 2. The low address part[7:0] of register is packaged into the second MDIO frame
+ *    with the low 16bit data to read/write.
+ * 3. The low address part[7:0] of register is packaged into the last MDIO frame
+ *    with the high 16bit data to read/write.
+ *
+ * The clause22 MDIO frame format used by device is as below.
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ST| OP|   ADDR  |   REG   | TA|             DATA              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static inline void split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+{
+	*r1 = regaddr & 0x1c;
+
+	regaddr >>= 5;
+	*r2 = regaddr & 0x7;
+
+	regaddr >>= 3;
+	*page = regaddr & 0xffff;
+}
+
+int qca8k_mii_read(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 *val)
+{
+	int ret;
+
+	ret = bus->read(bus, switch_phy_id, reg);
+	if (ret >= 0) {
+		*val = ret;
+		ret = bus->read(bus, switch_phy_id, (reg | BIT(1)));
+		*val |= ret << 16;
+	}
+
+	if (ret < 0)
+		dev_err_ratelimited(&bus->dev, "fail to read qca8k mii register\n");
+
+	return ret < 0 ? ret : 0;
+}
+
+void qca8k_mii_write(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u32 val)
+{
+	int ret;
+
+	ret = bus->write(bus, switch_phy_id, reg, lower_16_bits(val));
+	if (ret >= 0)
+		ret = bus->write(bus, switch_phy_id, (reg | BIT(1)), upper_16_bits(val));
+
+	if (ret < 0)
+		dev_err_ratelimited(&bus->dev, "fail to write qca8k mii register\n");
+}
+
+int qca8k_mii_page_set(struct mii_bus *bus, u16 switch_phy_id, u32 reg, u16 page)
+{
+	int ret;
+
+	ret = bus->write(bus, switch_phy_id, reg, page);
+	if (ret < 0)
+		dev_err_ratelimited(&bus->dev, "fail to set page\n");
+
+	return ret;
+}
+
+int qca8k_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct mii_bus *bus = context;
+	u16 r1, r2, page;
+	int ret;
+
+	reg += QCA8K_CLK_REG_BASE;
+	split_addr(reg, &r1, &r2, &page);
+
+	mutex_lock(&bus->mdio_lock);
+	ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
+	if (ret < 0)
+		goto qca8k_read_exit;
+
+	ret = qca8k_mii_read(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
+
+qca8k_read_exit:
+	mutex_unlock(&bus->mdio_lock);
+	return ret;
+};
+
+int qca8k_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct mii_bus *bus = context;
+	u16 r1, r2, page;
+	int ret;
+
+	reg += QCA8K_CLK_REG_BASE;
+	split_addr(reg, &r1, &r2, &page);
+
+	mutex_lock(&bus->mdio_lock);
+	ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
+	if (ret < 0)
+		goto qca8k_write_exit;
+
+	qca8k_mii_write(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
+
+qca8k_write_exit:
+	mutex_unlock(&bus->mdio_lock);
+	return ret;
+};
+
+int qca8k_regmap_update_bits(void *context, unsigned int reg, unsigned int mask, unsigned int value)
+{
+	struct mii_bus *bus = context;
+	u16 r1, r2, page;
+	int ret;
+	u32 val;
+
+	reg += QCA8K_CLK_REG_BASE;
+	split_addr(reg, &r1, &r2, &page);
+
+	mutex_lock(&bus->mdio_lock);
+	ret = qca8k_mii_page_set(bus, QCA8K_HIGH_ADDR_PREFIX, QCA8K_CFG_PAGE_REG, page);
+	if (ret < 0)
+		goto qca8k_update_exit;
+
+	ret = qca8k_mii_read(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, &val);
+	if (ret < 0)
+		goto qca8k_update_exit;
+
+	val &= ~mask;
+	val |= value;
+	qca8k_mii_write(bus, QCA8K_LOW_ADDR_PREFIX | r2, r1, val);
+
+qca8k_update_exit:
+	mutex_unlock(&bus->mdio_lock);
+	return ret;
+}
+
+static const struct regmap_config nss_cc_qca8k_regmap_config = {
+	.reg_bits = 12,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = 0x30C,
+	.reg_read = qca8k_regmap_read,
+	.reg_write = qca8k_regmap_write,
+	.reg_update_bits = qca8k_regmap_update_bits,
+	.disable_locking = true,
+	.cache_type = REGCACHE_NONE,
+};
+
+static const struct qcom_cc_desc nss_cc_qca8k_desc = {
+	.config = &nss_cc_qca8k_regmap_config,
+	.clks = nss_cc_qca8k_clocks,
+	.num_clks = ARRAY_SIZE(nss_cc_qca8k_clocks),
+	.resets = nss_cc_qca8k_resets,
+	.num_resets = ARRAY_SIZE(nss_cc_qca8k_resets),
+};
+
+static int nss_cc_qca8k_probe(struct mdio_device *mdiodev)
+{
+	struct regmap *regmap;
+
+	regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev->bus, nss_cc_qca8k_desc.config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(&mdiodev->dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+	return qcom_cc_really_probe(&mdiodev->dev, &nss_cc_qca8k_desc, regmap);
+}
+
+static const struct of_device_id nss_cc_qca8k_match_table[] = {
+	{ .compatible = "qcom,qca8084-nsscc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, nss_cc_qca8k_match_table);
+
+static struct mdio_driver nss_cc_qca8k_driver = {
+	.mdiodrv.driver = {
+		.name = "qcom,qca8k-nsscc",
+		.of_match_table	= nss_cc_qca8k_match_table,
+	},
+	.probe = nss_cc_qca8k_probe,
+};
+
+mdio_module_driver(nss_cc_qca8k_driver);
+
+MODULE_DESCRIPTION("QCOM NSS_CC QCA8K Driver");
+MODULE_LICENSE("GPL");