Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v12 3/4] net: mana: force full-page RX buffers via ethtool private flag
From: Dipayaan Roy @ 2026-07-11  4:10 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
	kuba, pabeni, leon, longli, kotaranov, horms, shradhagupta,
	ssengar, ernis, shirazsaleem, linux-hyperv, netdev, linux-kernel,
	linux-rdma, stephen, jacob.e.keller, dipayanroy, leitao, kees,
	john.fastabend, hawk, bpf, daniel, ast, sdf, yury.norov,
	pavan.chebbi, schakrabarti, gargaditya
In-Reply-To: <20260711041415.3008868-1-dipayanroy@linux.microsoft.com>

On some ARM64 platforms with 4K PAGE_SIZE, page_pool fragment
allocation in the RX refill path can cause 15-20% throughput
regression under high connection counts (>16 TCP streams).

Add an ethtool private flag "full-page-rx" that allows the user to
force one RX buffer per page, bypassing the page_pool fragment path.
This restores line-rate (180+ Gbps) performance on affected platforms.

Usage:
  ethtool --set-priv-flags eth0 full-page-rx on

There is no behavioral change by default. The flag must be explicitly
enabled by the user or udev rule.

The existing single-buffer-per-page logic for XDP and jumbo frames is
consolidated into a new helper mana_use_single_rxbuf_per_page() which
is now the single decision point for both the automatic and
user-controlled paths.

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
---
 drivers/net/ethernet/microsoft/mana/mana_en.c |  22 +++-
 .../ethernet/microsoft/mana/mana_ethtool.c    | 100 ++++++++++++++++++
 include/net/mana/mana.h                       |   8 ++
 3 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 5e3c7a2a2b49..3e5c52e4886b 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -755,6 +755,25 @@ static void *mana_get_rxbuf_pre(struct mana_rxq *rxq, dma_addr_t *da)
 	return va;
 }
 
+static bool
+mana_use_single_rxbuf_per_page(struct mana_port_context *apc, u32 mtu)
+{
+	/* On some platforms with 4K PAGE_SIZE, page_pool fragment allocation
+	 * in the RX refill path (~2kB buffer) can cause significant throughput
+	 * regression under high connection counts. Allow user to force one RX
+	 * buffer per page via ethtool private flag to bypass the fragment
+	 * path.
+	 */
+	if (apc->priv_flags & BIT(MANA_PRIV_FLAG_USE_FULL_PAGE_RXBUF))
+		return true;
+
+	/* For xdp and jumbo frames make sure only one packet fits per page. */
+	if (mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 || mana_xdp_get(apc))
+		return true;
+
+	return false;
+}
+
 /* Get RX buffer's data size, alloc size, XDP headroom based on MTU */
 static void mana_get_rxbuf_cfg(struct mana_port_context *apc,
 			       int mtu, u32 *datasize, u32 *alloc_size,
@@ -765,8 +784,7 @@ static void mana_get_rxbuf_cfg(struct mana_port_context *apc,
 	/* Calculate datasize first (consistent across all cases) */
 	*datasize = mtu + ETH_HLEN;
 
-	/* For xdp and jumbo frames make sure only one packet fits per page */
-	if (mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 || mana_xdp_get(apc)) {
+	if (mana_use_single_rxbuf_per_page(apc, mtu)) {
 		if (mana_xdp_get(apc)) {
 			*headroom = XDP_PACKET_HEADROOM;
 			*alloc_size = PAGE_SIZE;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index 482cd16009ab..f77509818d07 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -133,6 +133,10 @@ static const struct mana_stats_desc mana_phy_stats[] = {
 	{ "hc_tc7_tx_pause_phy", offsetof(struct mana_ethtool_phy_stats, tx_pause_tc7_phy) },
 };
 
+static const char mana_priv_flags[MANA_PRIV_FLAG_MAX][ETH_GSTRING_LEN] = {
+	[MANA_PRIV_FLAG_USE_FULL_PAGE_RXBUF] = "full-page-rx"
+};
+
 static int mana_get_sset_count(struct net_device *ndev, int stringset)
 {
 	struct mana_port_context *apc = netdev_priv(ndev);
@@ -144,6 +148,10 @@ static int mana_get_sset_count(struct net_device *ndev, int stringset)
 		       ARRAY_SIZE(mana_phy_stats) +
 		       ARRAY_SIZE(mana_hc_stats)  +
 		       num_queues * (MANA_STATS_RX_COUNT + MANA_STATS_TX_COUNT);
+
+	case ETH_SS_PRIV_FLAGS:
+		return MANA_PRIV_FLAG_MAX;
+
 	default:
 		return -EINVAL;
 	}
@@ -192,6 +200,14 @@ static void mana_get_strings_stats(struct mana_port_context *apc, u8 **data)
 	}
 }
 
+static void mana_get_strings_priv_flags(u8 **data)
+{
+	int i;
+
+	for (i = 0; i < MANA_PRIV_FLAG_MAX; i++)
+		ethtool_puts(data, mana_priv_flags[i]);
+}
+
 static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
 {
 	struct mana_port_context *apc = netdev_priv(ndev);
@@ -200,6 +216,9 @@ static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
 	case ETH_SS_STATS:
 		mana_get_strings_stats(apc, &data);
 		break;
+	case ETH_SS_PRIV_FLAGS:
+		mana_get_strings_priv_flags(&data);
+		break;
 	default:
 		break;
 	}
@@ -756,6 +775,84 @@ static int mana_get_link_ksettings(struct net_device *ndev,
 	return 0;
 }
 
+static u32 mana_get_priv_flags(struct net_device *ndev)
+{
+	struct mana_port_context *apc = netdev_priv(ndev);
+
+	return apc->priv_flags;
+}
+
+static int mana_set_priv_flags(struct net_device *ndev, u32 priv_flags)
+{
+	struct mana_port_context *apc = netdev_priv(ndev);
+	u32 changed = apc->priv_flags ^ priv_flags;
+	u32 old_priv_flags = apc->priv_flags;
+	bool schedule_port_reset = false;
+	int err = 0;
+
+	if (!changed)
+		return 0;
+
+	/* Reject unknown bits */
+	if (priv_flags & ~GENMASK(MANA_PRIV_FLAG_MAX - 1, 0))
+		return -EINVAL;
+
+	apc->priv_flags = priv_flags;
+
+	if (changed & BIT(MANA_PRIV_FLAG_USE_FULL_PAGE_RXBUF)) {
+		if (!apc->port_is_up)
+			return 0;
+
+		/* If XDP is attached or MTU is jumbo, single-buffer-per-page
+		 * is already forced regardless of this flag. Skip the
+		 * expensive detach/attach cycle since nothing changes.
+		 */
+		if (ndev->mtu + MANA_RXBUF_PAD > PAGE_SIZE / 2 ||
+		    mana_xdp_get(apc))
+			return 0;
+
+		/* Block RDMA from grabbing the vport during detach/attach */
+		mutex_lock(&apc->vport_mutex);
+		apc->channel_changing = true;
+		mutex_unlock(&apc->vport_mutex);
+
+		err = mana_pre_alloc_rxbufs(apc, ndev->mtu, apc->num_queues);
+		if (err) {
+			netdev_err(ndev,
+				   "Insufficient memory for new allocations\n");
+			apc->priv_flags = old_priv_flags;
+			goto clear_flag;
+		}
+
+		err = mana_detach(ndev, false);
+		if (err) {
+			netdev_err(ndev, "mana_detach failed: %d\n", err);
+			apc->priv_flags = old_priv_flags;
+			goto out;
+		}
+
+		err = mana_attach(ndev);
+		if (err) {
+			netdev_err(ndev, "mana_attach failed: %d\n", err);
+			apc->priv_flags = old_priv_flags;
+			schedule_port_reset = true;
+		}
+	}
+
+out:
+	mana_pre_dealloc_rxbufs(apc);
+clear_flag:
+	mutex_lock(&apc->vport_mutex);
+	apc->channel_changing = false;
+	mutex_unlock(&apc->vport_mutex);
+
+	if (schedule_port_reset)
+		queue_work(apc->ac->per_port_queue_reset_wq,
+			   &apc->queue_reset_work);
+
+	return err;
+}
+
 const struct ethtool_ops mana_ethtool_ops = {
 	.supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES |
 				     ETHTOOL_COALESCE_RX_USECS |
@@ -766,6 +863,7 @@ const struct ethtool_ops mana_ethtool_ops = {
 				     ETHTOOL_COALESCE_USE_ADAPTIVE_TX,
 	.op_needs_rtnl		= ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
 				  ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
+				  ETHTOOL_OP_NEEDS_RTNL_SPFLAGS |
 				  ETHTOOL_OP_NEEDS_RTNL_GLINK,
 	.get_ethtool_stats	= mana_get_ethtool_stats,
 	.get_sset_count		= mana_get_sset_count,
@@ -783,4 +881,6 @@ const struct ethtool_ops mana_ethtool_ops = {
 	.set_ringparam          = mana_set_ringparam,
 	.get_link_ksettings	= mana_get_link_ksettings,
 	.get_link		= ethtool_op_get_link,
+	.get_priv_flags		= mana_get_priv_flags,
+	.set_priv_flags		= mana_set_priv_flags,
 };
diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h
index 226b61504596..768d9f9bf167 100644
--- a/include/net/mana/mana.h
+++ b/include/net/mana/mana.h
@@ -31,6 +31,12 @@ enum TRI_STATE {
 	TRI_STATE_TRUE = 1
 };
 
+/* MANA ethtool private flag bit positions */
+enum mana_priv_flag_bits {
+	MANA_PRIV_FLAG_USE_FULL_PAGE_RXBUF = 0,
+	MANA_PRIV_FLAG_MAX,
+};
+
 /* Number of entries for hardware indirection table must be in power of 2 */
 #define MANA_INDIRECT_TABLE_MAX_SIZE 512
 #define MANA_INDIRECT_TABLE_DEF_SIZE 64
@@ -565,6 +571,8 @@ struct mana_port_context {
 	u32 rxbpre_headroom;
 	u32 rxbpre_frag_count;
 
+	u32 priv_flags;
+
 	struct bpf_prog *bpf_prog;
 
 	/* Create num_queues EQs, SQs, SQ-CQs, RQs and RQ-CQs, respectively. */
-- 
2.43.0


^ permalink raw reply related

* [PATCH net-next v12 4/4] net: mana: recover port on attach failure in ethtool operations
From: Dipayaan Roy @ 2026-07-11  4:10 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
	kuba, pabeni, leon, longli, kotaranov, horms, shradhagupta,
	ssengar, ernis, shirazsaleem, linux-hyperv, netdev, linux-kernel,
	linux-rdma, stephen, jacob.e.keller, dipayanroy, leitao, kees,
	john.fastabend, hawk, bpf, daniel, ast, sdf, yury.norov,
	pavan.chebbi, schakrabarti, gargaditya
In-Reply-To: <20260711041415.3008868-1-dipayanroy@linux.microsoft.com>

When mana_attach() fails during ethtool ring size or channel count
changes, the port is left in a broken state with no recovery
mechanism, requiring manual intervention to bring the port back up.

On VM SKUs without a netvsc fallback interface, this results in
complete loss of network connectivity to the VM.

Fix by scheduling queue_reset_work when mana_attach() fails. The
preceding patch ensures mana_detach() always completes its full
teardown (netif_device_detach + cleanup), so the reset handler's
mana_detach() takes the "already detached" early return, preserving
port_st_save for a successful mana_attach() recovery.

When mana_attach() fails, choose retry values that maximize recovery
chances: if the operation was an increase, fall back to the previous
working values; if it was a decrease but still above default, fall
back to defaults; otherwise use the minimum supported values.

Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
Tested-by: Aditya Garg <gargaditya@linux.microsoft.com>
Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
---
 .../ethernet/microsoft/mana/mana_ethtool.c    | 48 +++++++++++++++++--
 1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index f77509818d07..71e69d5a9a04 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -646,6 +646,7 @@ static int mana_set_channels(struct net_device *ndev,
 	struct mana_port_context *apc = netdev_priv(ndev);
 	unsigned int new_count = channels->combined_count;
 	unsigned int old_count = apc->num_queues;
+	bool schedule_port_reset = false;
 	int err;
 
 	/* Set channel_changing to block RDMA from grabbing the vport
@@ -675,8 +676,19 @@ static int mana_set_channels(struct net_device *ndev,
 	apc->num_queues = new_count;
 	err = mana_attach(ndev);
 	if (err) {
-		apc->num_queues = old_count;
 		netdev_err(ndev, "mana_attach failed: %d\n", err);
+
+		/* Choose a retry queue count that maximizes recovery
+		 * chances in the reset work handler.
+		 */
+		if (old_count < new_count)
+			apc->num_queues = old_count;
+		else if (new_count > MANA_DEF_NUM_QUEUES)
+			apc->num_queues = MANA_DEF_NUM_QUEUES;
+		else
+			apc->num_queues = 1;
+
+		schedule_port_reset = true;
 	}
 
 out:
@@ -685,6 +697,11 @@ static int mana_set_channels(struct net_device *ndev,
 	mutex_lock(&apc->vport_mutex);
 	apc->channel_changing = false;
 	mutex_unlock(&apc->vport_mutex);
+
+	if (schedule_port_reset)
+		queue_work(apc->ac->per_port_queue_reset_wq,
+			   &apc->queue_reset_work);
+
 	return err;
 }
 
@@ -707,6 +724,7 @@ static int mana_set_ringparam(struct net_device *ndev,
 			      struct netlink_ext_ack *extack)
 {
 	struct mana_port_context *apc = netdev_priv(ndev);
+	bool schedule_port_reset = false;
 	u32 new_tx, new_rx;
 	u32 old_tx, old_rx;
 	int err;
@@ -752,11 +770,35 @@ static int mana_set_ringparam(struct net_device *ndev,
 	err = mana_attach(ndev);
 	if (err) {
 		netdev_err(ndev, "mana_attach failed: %d\n", err);
-		apc->tx_queue_size = old_tx;
-		apc->rx_queue_size = old_rx;
+		NL_SET_ERR_MSG_FMT(extack, "failed to change ring params: %d",
+				   err);
+
+		/* Choose retry ring sizes that maximize recovery
+		 * chances in the reset work handler. Handle RX and
+		 * TX independently.
+		 */
+		if (old_rx < new_rx)
+			apc->rx_queue_size = old_rx;
+		else if (new_rx > DEF_RX_BUFFERS_PER_QUEUE)
+			apc->rx_queue_size = DEF_RX_BUFFERS_PER_QUEUE;
+		else
+			apc->rx_queue_size = MIN_RX_BUFFERS_PER_QUEUE;
+
+		if (old_tx < new_tx)
+			apc->tx_queue_size = old_tx;
+		else if (new_tx > DEF_TX_BUFFERS_PER_QUEUE)
+			apc->tx_queue_size = DEF_TX_BUFFERS_PER_QUEUE;
+		else
+			apc->tx_queue_size = MIN_TX_BUFFERS_PER_QUEUE;
+
+		schedule_port_reset = true;
 	}
 out:
 	mana_pre_dealloc_rxbufs(apc);
+
+	if (schedule_port_reset)
+		queue_work(apc->ac->per_port_queue_reset_wq,
+			   &apc->queue_reset_work);
 	return err;
 }
 
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH net v2] bnge/bng_re: fix ring ID widths
From: Przemek Kitszel @ 2026-07-11  4:16 UTC (permalink / raw)
  To: Vikas Gupta
  Cc: netdev, linux-kernel, linux-rdma, leonro, jgg, bhargava.marreddy,
	rahul-rg.gupta, vsrama-krishna.nemani, rajashekar.hudumula,
	ajit.khaparde, Siva Reddy Kallam, Dharmender Garg,
	Yendapally Reddy Dhananjaya Reddy, davem, edumazet, kuba, pabeni,
	andrew+netdev, horms
In-Reply-To: <CAHLZf_tTVipQZ_MX-gud5GhqHE7KDV=44N=17mZgsrWoJfa_Gw@mail.gmail.com>

On 7/10/26 16:25, Vikas Gupta wrote:
> On Fri, Jul 10, 2026 at 3:36 PM Przemek Kitszel
> <przemyslaw.kitszel@intel.com> wrote:
>>
>> On 7/4/26 18:47, Vikas Gupta wrote:
>>> Firmware requires more than 16 bits to address TX ring IDs for its
>>> internal QP management. Widen the associated HSI ring ID fields to
>>> 32 bits. The values firmware assigns remain within 24 bits, bounded
>>> by the hardware doorbell XID field.
>>>
>>> RX, completion, and NQ ring IDs are unaffected and remain 16-bit.
>>
>> Here you mention Rx is unaffected. But you touch multiple places that
>> are Rx specific (some comments below).
> 
> The fw_ring_id field belongs to bnge_ring_struct, a common struct
> shared by all ring types. Widening it to u32 applies uniformly across
> TX, RX, CP, and NQ rings at the struct level.
> I believe the commit message is incomplete but the intent was that
> firmware assigns values within 16-bit range for all ring types
> except TX, which requires the wider field.
> Please let me know if this clarifies.

Thank you, it does clarify.

Would be nice to extend commit message to include that and also the
info about HW not yet being released. Not sure if it is worth v2 though.

Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>

^ permalink raw reply

* [PATCH net v2 1/2] sctp: avoid auth_enable sysctl UAF during netns teardown
From: Ren Wei @ 2026-07-11  4:22 UTC (permalink / raw)
  To: linux-sctp, netdev
  Cc: marcelo.leitner, lucien.xin, davem, edumazet, pabeni, horms,
	matttbe, yuantan098, yifanwucs, tomapufckgml, bird, tpluszz77,
	roxy520tt, n05ec, sashiko-bot
In-Reply-To: <cover.1782745545.git.roxy520tt@gmail.com>

From: Zhiling Zou <roxy520tt@gmail.com>

proc_sctp_do_auth() updates the SCTP control socket after changing
net.sctp.auth_enable. The handler gets the per-net SCTP state from
ctl->data, so an already opened sysctl file can still target a network
namespace while that namespace is being torn down.

SCTP previously registered its per-net sysctls from sctp_defaults_init(),
while the control socket is created later from sctp_ctrlsock_init(). This
exposed a window during initialization where auth_enable was writable
before net->sctp.ctl_sock existed, and a teardown window where auth_enable
stayed writable after inet_ctl_sock_destroy() had released the control
socket.

Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after
sctp_ctl_sock_init() succeeds, and unregister the sysctl table before
destroying the control socket in sctp_ctrlsock_exit(). If sysctl
registration fails after the control socket was created, destroy the
control socket in the same init path.

Make sctp_sysctl_net_unregister() tolerate a missing header and clear the
saved pointer so init-error and exit paths can safely share the unregister
helper.

Fixes: 15649fd5415e ("sctp: sysctl: auth_enable: avoid using current->nsproxy")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Co-developed-by: Qi Tang <tpluszz77@gmail.com>
Signed-off-by: Qi Tang <tpluszz77@gmail.com>
Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
 net/sctp/protocol.c | 17 ++++++++++-------
 net/sctp/sysctl.c   |  9 +++++++--
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 587b0017a67d..f5fe6ddf0d7d 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1382,10 +1382,6 @@ static int __net_init sctp_defaults_init(struct net *net)
 	net->sctp.l3mdev_accept = 1;
 #endif
 
-	status = sctp_sysctl_net_register(net);
-	if (status)
-		goto err_sysctl_register;
-
 	/* Allocate and initialise sctp mibs.  */
 	status = init_sctp_mibs(net);
 	if (status)
@@ -1419,8 +1415,6 @@ static int __net_init sctp_defaults_init(struct net *net)
 	cleanup_sctp_mibs(net);
 #endif
 err_init_mibs:
-	sctp_sysctl_net_unregister(net);
-err_sysctl_register:
 	return status;
 }
 
@@ -1435,7 +1429,6 @@ static void __net_exit sctp_defaults_exit(struct net *net)
 	net->sctp.proc_net_sctp = NULL;
 #endif
 	cleanup_sctp_mibs(net);
-	sctp_sysctl_net_unregister(net);
 }
 
 static struct pernet_operations sctp_defaults_ops = {
@@ -1451,14 +1444,24 @@ static int __net_init sctp_ctrlsock_init(struct net *net)
 	status = sctp_ctl_sock_init(net);
 	if (status)
 		pr_err("Failed to initialize the SCTP control sock\n");
+	else
+		status = sctp_sysctl_net_register(net);
+
+	if (status && net->sctp.ctl_sock) {
+		inet_ctl_sock_destroy(net->sctp.ctl_sock);
+		net->sctp.ctl_sock = NULL;
+	}
 
 	return status;
 }
 
 static void __net_exit sctp_ctrlsock_exit(struct net *net)
 {
+	sctp_sysctl_net_unregister(net);
+
 	/* Free the control endpoint.  */
 	inet_ctl_sock_destroy(net->sctp.ctl_sock);
+	net->sctp.ctl_sock = NULL;
 }
 
 static struct pernet_operations sctp_ctrlsock_ops = {
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 15e7db9a3ab2..fca840484ebf 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -615,11 +615,16 @@ int sctp_sysctl_net_register(struct net *net)
 
 void sctp_sysctl_net_unregister(struct net *net)
 {
+	struct ctl_table_header *header = net->sctp.sysctl_header;
 	const struct ctl_table *table;
 
-	table = net->sctp.sysctl_header->ctl_table_arg;
-	unregister_net_sysctl_table(net->sctp.sysctl_header);
+	if (!header)
+		return;
+
+	table = header->ctl_table_arg;
+	unregister_net_sysctl_table(header);
 	kfree(table);
+	net->sctp.sysctl_header = NULL;
 }
 
 static struct ctl_table_header *sctp_sysctl_header;
-- 
2.43.0


^ permalink raw reply related

* [PATCH net v2 2/2] sctp: close UDP tunnel sockets during netns teardown
From: Ren Wei @ 2026-07-11  4:22 UTC (permalink / raw)
  To: linux-sctp, netdev
  Cc: marcelo.leitner, lucien.xin, davem, edumazet, pabeni, horms,
	matttbe, yuantan098, yifanwucs, tomapufckgml, bird, tpluszz77,
	roxy520tt, n05ec, sashiko-bot
In-Reply-To: <cover.1782745545.git.roxy520tt@gmail.com>

From: Zhiling Zou <roxy520tt@gmail.com>

proc_sctp_do_udp_port() starts per-net SCTP UDP tunneling sockets when
net.sctp.udp_port is set, and stops/restarts them when the sysctl value
changes. The netns exit path does not stop these sockets, so a namespace
can be torn down while its SCTP UDP tunnel sockets are still installed.

Close the UDP tunnel sockets from sctp_ctrlsock_exit() after unregistering
the per-net sysctl table. This prevents new sysctl writes from racing in
while the sockets are being released, and closes the sockets before the
control socket is destroyed.

Fixes: 046c052b475e ("sctp: enable udp tunneling socks")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/b9f1f02b0780ad6a719e2413f5f0bb8eb7702d94.1782585631.git.roxy520tt%40gmail.com
Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
 net/sctp/protocol.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index f5fe6ddf0d7d..5dbc7e9e8f06 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1458,6 +1458,7 @@ static int __net_init sctp_ctrlsock_init(struct net *net)
 static void __net_exit sctp_ctrlsock_exit(struct net *net)
 {
 	sctp_sysctl_net_unregister(net);
+	sctp_udp_sock_stop(net);
 
 	/* Free the control endpoint.  */
 	inet_ctl_sock_destroy(net->sctp.ctl_sock);
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] wifi: brcmfmac: drain bus_reset work on device removal
From: Eddie Phillips @ 2026-07-11  5:23 UTC (permalink / raw)
  To: Arend van Spriel
  Cc: Fan Wu, Arend van Spriel, Kalle Valo, Franky Lin, Hante Meuleman,
	Chi-Hsien Lin, Wright Feng, Chung-Hsien Hsu, David S . Miller,
	Jakub Kicinski, linux-wireless, brcm80211-dev-list.pdl,
	SHA-cyfmac-dev-list, netdev, linux-kernel, stable
In-Reply-To: <caae46b1-50c6-495d-94fe-c95229d489ce@broadcom.com>

On Fri, Jul 10, 2026 at 12:18 PM Arend van Spriel
<arend.vanspriel@broadcom.com> wrote:
>
> On 10/07/2026 02:23, Eddie Phillips wrote:
> > On Thu,  9 Jul 2026 10:16:35 +0000 Fan Wu <fanwu01@zju.edu.cn> wrote:
> >
> >> brcmf_fw_crashed() and the debugfs "reset" entry both schedule
> >> drvr->bus_reset, whose callback recovers drvr through container_of()
> >> and dereferences it.  The teardown paths free drvr (brcmf_free ->
> >> wiphy_free) without draining the work, so a bus_reset callback pending
> >> or running during removal can outlive drvr.
> >>
>
> [...]
>
> >>
> >> This issue was found by an in-house static analysis tool.
> >>
> >> Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash")
> >> Cc: stable@vger.kernel.org
> >> Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
> >> Assisted-by: Codex:gpt-5.5
> >> ---
> >>   .../broadcom/brcm80211/brcmfmac/bcmsdh.c      | 13 ++++++++
> >>   .../broadcom/brcm80211/brcmfmac/bus.h         |  6 ++++
> >>   .../broadcom/brcm80211/brcmfmac/core.c        | 33 +++++++++++++++++--
> >>   .../broadcom/brcm80211/brcmfmac/pcie.c        |  6 ++++
> >>   .../broadcom/brcm80211/brcmfmac/sdio.c        |  6 ++++
> >>   .../broadcom/brcm80211/brcmfmac/sdio.h        |  1 +
> >>   .../broadcom/brcm80211/brcmfmac/usb.c         |  3 ++
> >>   7 files changed, 66 insertions(+), 2 deletions(-)
>
> [...]
>
> >> diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
> >> index fed9cd5f2..b934feb9b 100644
> >> --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
> >> +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
> >> @@ -1164,6 +1164,35 @@ static int brcmf_revinfo_read(struct seq_file *s, void *data)
> >>      return 0;
> >>   }
> >>
> >> +/* Serialize bus_reset arming (debugfs reset write, brcmf_fw_crashed) against the
> >> + * teardown drain: the remove path takes bus_reset_lock, sets ->removing and cancels
> >> + * the work under it, so a racing armer either schedules before the cancel (and is
> >> + * drained) or observes ->removing and desists.
> >> + */
> >> +static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if)
> >> +{
> >> +    mutex_lock(&bus_if->bus_reset_lock);
> >> +    if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing)
> >> +            schedule_work(&bus_if->drvr->bus_reset);
> >> +    mutex_unlock(&bus_if->bus_reset_lock);
> >> +}
> >
> > Is this safe in a softIRQ context?
> > mutex_lock() sleeps until it can get the lock.
>
> What softIRQ context? brcmf_fw_crashed() is called by PCIe (thread) and
> SDIO (worker).

Yes, you're right. Since it's thread/worker context, sleeping is fine here.

> >> +
> >> +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if)
> >> +{
> >> +    mutex_lock(&bus_if->bus_reset_lock);
> >> +    bus_if->removing = true;
> >> +    if (bus_if->drvr)
> >> +            cancel_work_sync(&bus_if->drvr->bus_reset);
> >> +    mutex_unlock(&bus_if->bus_reset_lock);
> >> +}
> >
> > How about if brcmf_pcie_remove() calls brcmf_bus_cancel_reset_work(),
> > takes the lock and calls cancel_work_sync(), sleeps. If debugfs
> > path is already running, it can invoke the worker thread. Is there
> > potential that both try to reset?
>
> What is "both" here?

It may be possible that the worker thread is running and then the device is
unplugged, causing a deadlock.

Another possibility here would be to just lock the state change, but both
implementations should are fine.

Best, Eddie

> Regards,
> Arend

^ permalink raw reply

* [PATCH net v3] net: hip04: fix tx coalesce timer and IRQ teardown races
From: Fan Wu @ 2026-07-11  5:29 UTC (permalink / raw)
  To: netdev
  Cc: shenjian15, salil.mehta, dingtianhong, przemyslaw.kitszel, horms,
	andrew+netdev, davem, edumazet, kuba, pabeni, linux-kernel,
	stable, Fan Wu

The hip04 remove path frees the TX/RX rings before unregistering the
netdev. If the interface is still up, unregister_netdev() then runs
.ndo_stop, whose TX reclaim and NAPI poll touch the already-freed DMA
ring memory. The TX coalesce timer and the platform IRQ also outlive the
netdev private data they dereference.

Reorder hip04_remove() so the netdev is unregistered (which runs .ndo_stop
synchronously, stopping NAPI and the TX queue) before the rings are freed.
Free the devm-managed IRQ explicitly before free_netdev(), so
hip04_mac_interrupt() (whose dev_id is the netdev) cannot fire against
freed memory: devm would otherwise release it only after .remove returns.

hip04_mac_stop() must quiesce both arming sites of the coalesce timer.
The NAPI poll arms it, and napi_disable() returns once the poll calls
napi_complete_done(), not when the poll function returns, so move that
arm before napi_complete_done().  The early-exit paths in hip04_rx_poll()
do not call napi_complete_done(); NAPI stays scheduled and will be polled
again, so there is no need to arm the coalesce timer on that pass.  The
Tx path also arms it, and mac_stop() is reached directly from
hip04_tx_timeout_task() as well as via .ndo_stop, so use netif_tx_disable()
rather than netif_stop_queue() to wait for an in-flight
hip04_mac_start_xmit() to finish.  The timer is then drained with
hrtimer_cancel().  A "closing" flag, checked at the single arming site,
guards against a later arm.

hip04_tx_timeout_task() restarts the device with mac_stop() + mac_open().
Serialize that restart against .ndo_open/.ndo_stop with a driver-private
state_lock instead of rtnl_lock(): mac_open()/mac_stop() are split into
internal __hip04_mac_open()/__hip04_mac_stop() helpers, and the
.ndo_open/.ndo_stop wrappers as well as the timeout worker take the lock.
This avoids extending RTNL usage; netdev_lock() is not sufficient here, as
hip04 does not opt into netdev-ops locking.  If timeout recovery is in
progress when a close begins, .ndo_stop waits on the lock and performs the
final MAC stop; if .ndo_stop acquires the lock first, the worker sees the
device is no longer running and returns.  Either way, recovery cannot
re-enable the MAC
during teardown.

This issue was found by an in-house static analysis tool.

Fixes: a41ea46a9a12 ("net: hisilicon: new hip04 ethernet driver")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.5
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---

v3: address review from Przemek Kitszel.

- Drop the rtnl_lock()/rtnl_unlock() and the #include <linux/rtnetlink.h>
  added to hip04_tx_timeout_task() in v2. Serialize the tx-timeout restart
  against .ndo_open/.ndo_stop with a driver-private state_lock instead:
  mac_open()/mac_stop() are split into __hip04_mac_open()/__hip04_mac_stop()
  helpers, and the .ndo_open/.ndo_stop wrappers and the timeout worker all
  take the lock. This avoids new RTNL usage. netdev_lock() was considered
  but does not help: hip04 does not opt into netdev-ops locking, so its
  .ndo_open/.ndo_stop run under RTNL and the netdev instance lock does not
  protect them.

- Rephrase the sentence about napi_complete_done() / early-exit paths that
  was hard to read.

- s/Tx xmit/Tx/.

v2: Address review comments from Simon Horman. Use netif_tx_disable() rather
   than netif_stop_queue() in hip04_mac_stop() so that an in-flight
   hip04_mac_start_xmit() finishes before the TX ring is reclaimed;
   mac_stop() is also called directly from the tx-timeout work, not only
   via .ndo_stop. Arm the coalesce timer in hip04_rx_poll() before
   napi_complete_done(), so that napi_disable() observes the final arm and
   the subsequent cancel cannot miss it. Free the devm-managed IRQ
   explicitly in hip04_remove() before free_netdev() so that
   hip04_mac_interrupt() cannot run against freed memory, placing it after
   unregister_netdev() so that the device is stopped first. Reorder
   hip04_remove() so that the netdev is unregistered (running .ndo_stop)
   before the PHY is disconnected and the TX/RX rings are freed. Check the
   return value of hip04_mac_open() in the tx-timeout restart path.

v2: https://lore.kernel.org/netdev/20260710015730.630775-1-fanwu01@zju.edu.cn/
v1: https://lore.kernel.org/netdev/20260703050133.2445155-1-fanwu01@zju.edu.cn/

 drivers/net/ethernet/hisilicon/hip04_eth.c | 107 ++++++++++++++++++++++++++----
 1 file changed, 96 insertions(+), 11 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
index 18376bcc7..4bd192094 100644
--- a/drivers/net/ethernet/hisilicon/hip04_eth.c
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
@@ -15,6 +15,7 @@
 #include <linux/of_net.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
+#include <linux/mutex.h>
 
 #define SC_PPE_RESET_DREQ		0x026C
 
@@ -232,6 +233,8 @@ struct hip04_priv {
 	int tx_coalesce_frames;
 	int tx_coalesce_usecs;
 	struct hrtimer tx_coalesce_timer;
+	bool closing;
+	struct mutex state_lock; /* Serializes MAC open/stop and timeout recovery. */
 
 	unsigned char *rx_buf[RX_DESC_NUM];
 	dma_addr_t rx_phys[RX_DESC_NUM];
@@ -497,6 +500,12 @@ static void hip04_start_tx_timer(struct hip04_priv *priv)
 {
 	unsigned long ns = priv->tx_coalesce_usecs * NSEC_PER_USEC / 2;
 
+	/* Do not (re-)arm the Tx coalesce timer once teardown has begun.
+	 * Both arming sites (Tx and NAPI Rx poll) go through here.
+	 */
+	if (smp_load_acquire(&priv->closing))
+		return;
+
 	/* allow timer to fire after half the time at the earliest */
 	hrtimer_start_range_ns(&priv->tx_coalesce_timer, ns_to_ktime(ns),
 			       ns, HRTIMER_MODE_REL);
@@ -649,12 +658,15 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget)
 		priv->reg_inten |= RCV_INT;
 		writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN);
 	}
+	/* Arm the coalesce timer BEFORE napi_complete_done(): napi_disable()
+	 * in hip04_mac_stop() returns once SCHED is cleared here, not when
+	 * the poll function returns, so arming afterwards can slip past the
+	 * stop path's hrtimer_cancel().
+	 */
+	if (tx_remaining)
+		hip04_start_tx_timer(priv);
 	napi_complete_done(napi, rx);
 done:
-	/* start a new timer if necessary */
-	if (rx < budget && tx_remaining)
-		hip04_start_tx_timer(priv);
-
 	return rx;
 }
 
@@ -720,7 +732,7 @@ static void hip04_adjust_link(struct net_device *ndev)
 	}
 }
 
-static int hip04_mac_open(struct net_device *ndev)
+static int __hip04_mac_open(struct net_device *ndev)
 {
 	struct hip04_priv *priv = netdev_priv(ndev);
 	int i;
@@ -743,6 +755,13 @@ static int hip04_mac_open(struct net_device *ndev)
 		hip04_set_recv_desc(priv, phys);
 	}
 
+	/* RX mappings are established; clear the closing flag before
+	 * re-enabling traffic.  The store-release pairs with the load-acquire
+	 * in hip04_start_tx_timer(); state_lock serializes this against
+	 * mac_stop()'s store-release.
+	 */
+	smp_store_release(&priv->closing, false);
+
 	if (priv->phy)
 		phy_start(priv->phy);
 
@@ -754,13 +773,42 @@ static int hip04_mac_open(struct net_device *ndev)
 	return 0;
 }
 
-static int hip04_mac_stop(struct net_device *ndev)
+static int hip04_mac_open(struct net_device *ndev)
+{
+	struct hip04_priv *priv = netdev_priv(ndev);
+	int ret;
+
+	mutex_lock(&priv->state_lock);
+	ret = __hip04_mac_open(ndev);
+	mutex_unlock(&priv->state_lock);
+	return ret;
+}
+
+static int __hip04_mac_stop(struct net_device *ndev)
 {
 	struct hip04_priv *priv = netdev_priv(ndev);
 	int i;
 
+	/* Stop new timer arms before draining: set the closing flag (checked
+	 * at the single arming site), wait for the NAPI poll and any in-flight
+	 * TX to finish, then cancel the timer.
+	 *
+	 * netif_tx_disable() (not netif_stop_queue()) is required because this
+	 * function is also called directly from hip04_tx_timeout_task(), not
+	 * only via .ndo_stop where the core has already deactivated TX;
+	 * netif_tx_disable() waits for an in-flight hip04_mac_start_xmit(),
+	 * which arms the timer, to finish.
+	 *
+	 * Because hip04_rx_poll() arms the timer before napi_complete_done(),
+	 * napi_disable() returning means that arm has happened, so the
+	 * hrtimer_cancel() below cannot miss it.  The store-release pairs
+	 * with the load in hip04_start_tx_timer().
+	 */
+	smp_store_release(&priv->closing, true);
+
 	napi_disable(&priv->napi);
-	netif_stop_queue(ndev);
+	netif_tx_disable(ndev);
+	hrtimer_cancel(&priv->tx_coalesce_timer);
 	hip04_mac_disable(ndev);
 	hip04_tx_reclaim(ndev, true);
 	hip04_reset_ppe(priv);
@@ -779,6 +827,16 @@ static int hip04_mac_stop(struct net_device *ndev)
 	return 0;
 }
 
+static int hip04_mac_stop(struct net_device *ndev)
+{
+	struct hip04_priv *priv = netdev_priv(ndev);
+
+	mutex_lock(&priv->state_lock);
+	__hip04_mac_stop(ndev);
+	mutex_unlock(&priv->state_lock);
+	return 0;
+}
+
 static void hip04_timeout(struct net_device *ndev, unsigned int txqueue)
 {
 	struct hip04_priv *priv = netdev_priv(ndev);
@@ -791,8 +849,23 @@ static void hip04_tx_timeout_task(struct work_struct *work)
 	struct hip04_priv *priv;
 
 	priv = container_of(work, struct hip04_priv, tx_timeout_task);
-	hip04_mac_stop(priv->ndev);
-	hip04_mac_open(priv->ndev);
+
+	/* Serialize the restart with .ndo_open/.ndo_stop, which take the
+	 * same lock.  If a close has completed or is in progress the
+	 * netif_running() check bails; if that check races the core's
+	 * running-state clear, .ndo_stop still runs under state_lock
+	 * afterwards and stops the device, so this worker cannot leave the
+	 * MAC re-enabled against a teardown.
+	 */
+	mutex_lock(&priv->state_lock);
+	if (!netif_running(priv->ndev))
+		goto out;
+
+	__hip04_mac_stop(priv->ndev);
+	if (__hip04_mac_open(priv->ndev))
+		netdev_err(priv->ndev, "restart after tx timeout failed\n");
+out:
+	mutex_unlock(&priv->state_lock);
 }
 
 static int hip04_get_coalesce(struct net_device *netdev,
@@ -983,6 +1056,7 @@ static int hip04_mac_probe(struct platform_device *pdev)
 	}
 
 	INIT_WORK(&priv->tx_timeout_task, hip04_tx_timeout_task);
+	mutex_init(&priv->state_lock);
 
 	ndev->netdev_ops = &hip04_netdev_ops;
 	ndev->ethtool_ops = &hip04_ethtool_ops;
@@ -1026,13 +1100,24 @@ static void hip04_remove(struct platform_device *pdev)
 	struct hip04_priv *priv = netdev_priv(ndev);
 	struct device *d = &pdev->dev;
 
+	unregister_netdev(ndev);
+
+	/* The IRQ is devm-managed and would otherwise be freed only after
+	 * this function returns.  Free it now, after unregister_netdev() has
+	 * run .ndo_stop to stop the device and mask its interrupt source, but
+	 * before the manual free_netdev() below, so that hip04_mac_interrupt()
+	 * (dev_id == ndev) cannot fire against freed memory.  free_irq() also
+	 * drains any in-flight handler.
+	 */
+	devm_free_irq(d, ndev->irq, ndev);
+	cancel_work_sync(&priv->tx_timeout_task);
+	hrtimer_cancel(&priv->tx_coalesce_timer);
+
 	if (priv->phy)
 		phy_disconnect(priv->phy);
 
 	hip04_free_ring(ndev, d);
-	unregister_netdev(ndev);
 	of_node_put(priv->phy_node);
-	cancel_work_sync(&priv->tx_timeout_task);
 	free_netdev(ndev);
 }
 


^ permalink raw reply related

* Re: [RFC] VEGA: a syzbot-like workflow for LLM-found kernel bugs
From: Greg KH @ 2026-07-11  5:33 UTC (permalink / raw)
  To: Yuan Tan
  Cc: andrew, Paolo Abeni, laurent.pinchart, hdanton, linux-kernel,
	workflows, jhs, sven, netdev, netfilter-devel, linux-crypto
In-Reply-To: <CAPuPA7Kdy_wghauOH5pggq+woLmtp4-BEyn8LoBJ2UhRExF+Xw@mail.gmail.com>

On Fri, Jul 10, 2026 at 07:14:36PM -0700, Yuan Tan wrote:
> > But hey, I could be totally wrong.  Maybe some generous company that is
> > involved in unleashing this hell on us would be so kind as to pony up to
> > do the work to create this and help fix the issues that their tools are
> > finding.  Just like Google did in the past, there is precedent, but for
> > some reason people don't like learning from history...
> 
> We have also received some bug bounty rewards from Google, which gives
> us some resources to put back into this effort.

That's good, but I don't want you to have to rely on Google's bug bounty
money, as that is not going to be reliable if past history is any
indication :(

> We are prepared to invest more engineering time in fixing these bugs,
> and we are also considering hiring engineers to help.
> 
> Will you also be attending NetDev in person? If so, perhaps we can chat there :)

Unfortunatly due to passport/visa issues, I can not attend this year,
but will be speaking remotely.  I will be in Prague later this year at
OSS and Plumbers.

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH] net: hip04: quiesce tx coalesce timer before teardown
From: Fan Wu @ 2026-07-11  5:34 UTC (permalink / raw)
  To: Simon Horman
  Cc: Fan Wu, netdev, shenjian15, salil.mehta, andrew+netdev,
	David S . Miller, edumazet, Jakub Kicinski, pabeni, linux-kernel,
	stable
In-Reply-To: <20260709124309.1557255-2-horms@kernel.org>

Hi Simon,

Thanks for the v1 review. The updated patch fixes the PHY teardown ordering
and IRQ lifetime issues: hip04_remove() unregisters the netdev, running
.ndo_stop and phy_stop(), before phy_disconnect(), and frees the devm-managed
IRQ before free_netdev().

The remaining NULL tx_desc cleanup in hip04_free_ring() and RX refill
failure paths are independent pre-existing error-path issues. I will address them 
in separate patches rather than expanding this teardown fix. In particular, the
RX fix must preserve the old descriptor mapping until a replacement buffer is
successfully allocated and mapped.

The latest revision is v3:
https://lore.kernel.org/netdev/20260711052922.1634837-1-fanwu01@zju.edu.cn

Thanks,
Fan

> On Jul 9, 2026, at 20:43, Simon Horman <horms@kernel.org> wrote:
> 
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> net: hip04: quiesce tx coalesce timer before teardown
> 
> This patch fixes race conditions with the tx_coalesce_timer by properly
> canceling it during device stop and remove. It also serializes device
> restarts in the tx timeout handler using rtnl_lock().
> 
>> diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c
>> index 18376bcc718a2..cb9b01c79ee6e 100644
>> --- a/drivers/net/ethernet/hisilicon/hip04_eth.c
>> +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c
> 
> [ ... ]
> 
>> @@ -761,6 +762,13 @@ static int hip04_mac_stop(struct net_device *ndev)
>> 
>> napi_disable(&priv->napi);
>> netif_stop_queue(ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but does netif_stop_queue() adequately protect
> against concurrent transmission here?
> 
> Since netif_stop_queue() only changes the queue state flag without waiting for
> an active ndo_start_xmit callback to finish (unlike netif_tx_disable()), could
> this allow the subsequent hip04_tx_reclaim() call to corrupt the TX ring or
> cause invalid DMA mappings if a transmission is actively running?
> 
>> +
>> + /* Cancel the TX-coalesce timer after the arming paths (xmit via the
>> + * queue, rx poll via NAPI) are disabled, so a pending tx_done()
>> + * (which dereferences priv) is drained before the device is freed.
>> + */
>> + hrtimer_cancel(&priv->tx_coalesce_timer);
> 
> [Severity: High]
> This is a pre-existing issue, but is it possible for the softirq to re-arm
> the timer after hrtimer_cancel() is called?
> 
> In hip04_rx_poll(), the NAPI ownership is released via napi_complete_done()
> before the timer is armed:
> 
> hip04_rx_poll() {
>    ...
>    napi_complete_done(napi, rx);
> done:
>    if (rx < budget && tx_remaining)
>        hip04_start_tx_timer(priv);
> }
> 
> If the teardown thread cancels the timer and frees the private structure
> while the softirq is preempted just after napi_complete_done(), could this
> result in a use-after-free when hip04_start_tx_timer() is eventually executed?
> 
>> +
>> hip04_mac_disable(ndev);
>> hip04_tx_reclaim(ndev, true);
> 
> [Severity: High]
> This is a pre-existing issue, but is there missing hardware synchronization
> between disabling the MAC and reclaiming the TX ring?
> 
> hip04_mac_disable() disables the MAC using a relaxed MMIO write without
> polling for an idle state.
> 
> Since the very next call is hip04_tx_reclaim() which unmaps buffers and
> modifies descriptor memory, could this cause silent data corruption or IOMMU
> page faults if the MAC is still actively processing packets in the background?
> 
> [ ... ]
> 
>> @@ -791,8 +799,15 @@ static void hip04_tx_timeout_task(struct work_struct *work)
>> struct hip04_priv *priv;
>> 
>> priv = container_of(work, struct hip04_priv, tx_timeout_task);
>> +
>> + rtnl_lock();
>> + if (!netif_running(priv->ndev))
>> + goto out;
>> +
>> hip04_mac_stop(priv->ndev);
>> hip04_mac_open(priv->ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but what happens if hip04_mac_open() fails
> during this restart?
> 
> If dma_map_single() fails inside hip04_mac_open(), it returns -EIO without
> enabling NAPI. Because this return value is ignored here, the device remains
> broken and NAPI is left disabled (with the SCHED bit set).
> 
> Will this cause a hard lockup later when hip04_mac_stop() spins forever in
> napi_disable() waiting for the SCHED bit to clear?
> 
>> +out:
>> + rtnl_unlock();
>> }
>> 
> 
> [ ... ]
> 
>> @@ -1029,10 +1044,15 @@ static void hip04_remove(struct platform_device *pdev)
>> if (priv->phy)
>> phy_disconnect(priv->phy);
>> 
>> - hip04_free_ring(ndev, d);
>> unregister_netdev(ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but does calling phy_disconnect() before
> unregister_netdev() cause a use-after-free and WARN?
> 
> phy_disconnect() halts and detaches the PHY, dropping its reference count.
> The subsequent call to unregister_netdev() invokes hip04_mac_stop(), which
> unconditionally calls phy_stop().
> 
> Could calling phy_stop() on a detached and potentially freed PHY device
> pointer cause a crash?
> 
>> - of_node_put(priv->phy_node);
>> cancel_work_sync(&priv->tx_timeout_task);
>> + hrtimer_cancel(&priv->tx_coalesce_timer);
>> + /* Free the rings only after the interface is stopped (.ndo_stop via
>> + * unregister_netdev) and the work/timer are drained; the TX/NAPI
>> + * paths touch them while the device is up.
>> + */
>> + hip04_free_ring(ndev, d);
> 
> [Severity: High]
> This is a pre-existing issue, but does hip04_free_ring() safely handle
> allocation failures?
> 
> During probe, if allocating the TX descriptor ring fails, priv->tx_desc
> remains NULL, and the error path calls hip04_free_ring().
> 
> Since hip04_free_ring() unconditionally calls dma_free_coherent() on
> priv->tx_desc, could this cause a kernel panic on architectures that trap
> virtual-to-physical translations on NULL?
> 
>> + of_node_put(priv->phy_node);
>> free_netdev(ndev);
> 
> [Severity: High]
> This is a pre-existing issue, but does explicitly freeing the netdev here
> create a use-after-free race with the interrupt handler?
> 
> The driver requests its interrupt in hip04_mac_probe() using
> devm_request_irq(), meaning the IRQ is only freed during devres cleanup after
> hip04_remove() completely finishes.
> 
> If an interrupt fires between free_netdev() and the devres cleanup,
> hip04_mac_interrupt() will cast dev_id to the freed ndev pointer, which seems
> like it would lead to a use-after-free crash.
> 
>> }
>> 
> 
> [ ... ]
> 
> Regarding the RX polling path in hip04_rx_poll():
> 
> [Severity: High]
> This is a pre-existing issue, but does a failure in build_skb() cause a memory
> and DMA mapping leak?
> 
> Looking at hip04_rx_poll():
> 
>    if (unlikely(!skb)) {
>        net_dbg_ratelimited("build_skb failed\n");
>        goto refill;
>    }
>    ...
> refill:
>    ...
>    priv->rx_buf[priv->rx_head] = buf;
>    priv->rx_phys[priv->rx_head] = phys;
> 
> If build_skb() fails, the code jumps directly to the refill label, completely
> bypassing the dma_unmap_single() call for the original buffer. It then
> allocates and maps a new buffer, overwriting the array entries.
> 
> Could this permanently leak the original buffer and its DMA mapping,
> eventually leading to an OOM crash under memory pressure?


^ permalink raw reply

* Re: Ethtool is missing C2C link modes
From: David Laight @ 2026-07-11  6:31 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: D H, Siddaraju, Maxime Chevallier, Michal Kubecek,
	netdev@vger.kernel.org, Chintalapalle, Balaji, Das, Shubham,
	Srinivasan, Vijay, Samudrala, Sridhar, Keller, Jacob E,
	Nguyen, Anthony L, singhai.anjali55@gmail.com, Brandeburg, Jesse
In-Reply-To: <e3dd8e06-97d1-4943-b54c-fdd0d0e6ecaa@lunn.ch>

On Sat, 11 Jul 2026 00:54:00 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

> On Fri, Jul 10, 2026 at 09:45:43PM +0000, D H, Siddaraju wrote:
> > Hello Linux Ethernet team, Maxime, Andrew & Michal,
> > 
> > The IEEE AUI chip-to-chip (C2C) is the accepted standard for connecting
> > chips that handle subfunctions within the OSI physical layer. Just to
> > pick, the C2C is widely used when connecting Ethernet SoCs with retimers
> > and PCS SerDes terminated external-phys to offload PHY sublayer functions.  
> 
> It cannot be that widely used if Linux does not support it yet :-)

It also seems like something that is fixed for a physical board.
So while a common MAC driver would need to be told how to configure
its output, the user wouldn't be changing the value so it would
be more of a DT parameter than an ethtool one.

...
> Also, an architecture question...
> 
> It sounds like you use this between the MAC and the PCS. The PCS can
> then be connected to a PHY, and the PHY then has a line side. (I'm
> being a bit loose with the terms here, i should probably be saying
> PMA, PMD etc.)
> 
> Should ethtool be saying:
> 
> Settings for eth0:
> 	Supported ports: [ TP	 MII ]
> 	Supported link modes:   25000baseC2C
> 
> or should it be reporting:
> 
> Settings for eth0:
> 	Supported ports: [ TP	 MII ]
> 	Supported link modes:   25000baseSR
> 
> I _think_ ethtool reports the media, not some intermediary format.

You'd want to use ethtool to set the final link parameters of the
external phy?
So I think you's still want to be able to select (say) 100MHDX
for a TP link.

Remember the history.
The parameter was originally used to select between the the AUI, COAX and TP
connectors on a 10M ethernet card.
Then the internal TP gained extra speeds.
We then get MII for external 10M and 100M PHY, later RGMII for external Ge PHY.
But you rarely get boards (not MAC chips) that have a choice of interfaces
any more.

> Is ETHTOOL_LINK_MODE_25000baseC2C_Full_BIT actually needed? I suppose
> one use case would be when you directly connect two MACs together, PMA
> to PMA. So a 25G NIC directly connected to a switch port, with no
> 'media' in the middle. Then ethtool probably should report
> 25000baseC2C.

That might be similar to using RGMII crossover to connect to MAC together
(Which does get used on-board).
I'm not sure how we selected that, but overloading the media is 'sort of'
ok because there is no media in that case.

	David

> 
> 	Andrew
> 


^ permalink raw reply

* [PATCH net] nfc: st21nfca: validate ATR_REQ length against the received frame
From: Doruk Tan Ozturk @ 2026-07-11  7:13 UTC (permalink / raw)
  To: David Heidelberg; +Cc: oe-linux-nfc, netdev, linux-kernel, stable

st21nfca_tm_recv_atr_req() checks that the received ATR_REQ frame is at
least ST21NFCA_ATR_REQ_MIN_SIZE and that the self-declared atr_req->length
is at least sizeof(struct st21nfca_atr_req), but never checks that
atr_req->length does not exceed the actual received length (skb->len).

st21nfca_tm_send_atr_res() then trusts the declared length:

	gb_len = atr_req->length - sizeof(struct st21nfca_atr_req);
	...
	memcpy(atr_res->gbi, atr_req->gbi, gb_len);

so an RF peer that sends a short frame but sets atr_req->length larger
than the frame makes gb_len exceed the general bytes actually present,
and the memcpy reads out of bounds past the received skb. Those bytes are
placed in the ATR_RES and sent back to the peer (kernel-memory disclosure
to a proximity attacker); a larger declared length is an out-of-bounds
read (DoS).

Reject frames whose declared length exceeds the received length. The
adjacent nfc_tm_activated() path in the same function already derives its
general-bytes length from skb->len rather than the declared field.

Found by 0sec (https://0sec.ai) using automated source analysis; the
missing bound is evident from source. Compile-tested.

Fixes: 1892bf844ea0 ("NFC: st21nfca: Adding P2P support to st21nfca in Initiator & Target mode")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:claude-opus-4-8
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
 drivers/nfc/st21nfca/dep.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c
index 3425b68f0ddc..a5fab4fd5129 100644
--- a/drivers/nfc/st21nfca/dep.c
+++ b/drivers/nfc/st21nfca/dep.c
@@ -205,6 +205,9 @@ static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev,
 	if (atr_req->length < sizeof(struct st21nfca_atr_req))
 		return -EPROTO;
 
+	if (atr_req->length > skb->len)
+		return -EPROTO;
+
 	r = st21nfca_tm_send_atr_res(hdev, atr_req);
 	if (r)
 		return r;
-- 
2.43.0


^ permalink raw reply related

* [PATCH] net: wwan: t7xx: validate control-message data_length against the skb
From: Doruk Tan Ozturk @ 2026-07-11  7:13 UTC (permalink / raw)
  To: Chandrashekar Devegowda, Ricardo Martinez
  Cc: Liu Haijun, Loic Poulain, Sergey Ryazanov, Johannes Berg, netdev,
	linux-kernel, stable

control_msg_handler() handles a CTL_ID_HS2_MSG control message by pulling
the ctrl_msg_header and passing the modem-supplied data_length as the
length of the handshake-2 payload to t7xx_fsm_append_event():

	ret = t7xx_fsm_append_event(ctl, event, skb->data,
				    le32_to_cpu(ctrl_msg_h->data_length));

data_length is a device-controlled __le32 that is never bounded against
the actual received payload (skb->len after the pull).
t7xx_fsm_append_event() then does memcpy(event->data, data, length) with
skb->data as the source, so a data_length larger than the payload reads
out of bounds past the control skb (the destination is sized to length,
so only the source over-reads). A compromised or malfunctioning modem can
trigger it during the bring-up handshake; both the modem and AP control
ports reach the same call site.

Reject a data_length that exceeds the received payload.

Found by 0sec (https://0sec.ai) using automated source analysis; the
missing bound is evident from source. Compile-tested.

Fixes: da45d2566a1d ("net: wwan: t7xx: Add control port")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:claude-opus-4-8
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
 drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
index f869e4ed9ee9..871ed63d3c4d 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
@@ -186,10 +186,15 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
 			int event = port_conf->rx_ch == PORT_CH_CONTROL_RX ?
 				    FSM_EVENT_MD_HS2 : FSM_EVENT_AP_HS2;
 
-			ret = t7xx_fsm_append_event(ctl, event, skb->data,
-						    le32_to_cpu(ctrl_msg_h->data_length));
-			if (ret)
-				dev_err(port->dev, "Failed to append Handshake 2 event");
+			if (le32_to_cpu(ctrl_msg_h->data_length) > skb->len) {
+				dev_err(port->dev, "Invalid Handshake 2 data length\n");
+				ret = -EINVAL;
+			} else {
+				ret = t7xx_fsm_append_event(ctl, event, skb->data,
+							    le32_to_cpu(ctrl_msg_h->data_length));
+				if (ret)
+					dev_err(port->dev, "Failed to append Handshake 2 event");
+			}
 		}
 
 		dev_kfree_skb_any(skb);
-- 
2.43.0


^ permalink raw reply related

* [PATCH net] nfc: llcp: reject PDUs shorter than the LLCP header
From: Doruk Tan Ozturk @ 2026-07-11  7:27 UTC (permalink / raw)
  To: David Heidelberg; +Cc: Simon Horman, oe-linux-nfc, netdev, linux-kernel, stable

nfc_llcp_rx_skb() reads the two-byte LLCP header (DSAP/SSAP/PTYPE) and
dispatches by PDU type; several handlers then derive a TLV-array length as
skb->len - LLCP_HEADER_SIZE. Neither nfc_llcp_rx_skb() nor its callers
guarantee the frame is at least LLCP_HEADER_SIZE bytes, and a sub-header
PDU does reach it: digital_in_recv_dep_res() and digital_tg_recv_dep_req()
strip the DEP header with skb_pull() after only checking the DEP header
size, so a DEP I-PDU carrying a 0- or 1-byte LLCP payload is handed up as
a sub-2-byte skb.

For a CONNECT or CC PDU, nfc_llcp_recv_connect() and nfc_llcp_recv_cc()
then pass skb->len - LLCP_HEADER_SIZE to nfc_llcp_parse_connection_tlv().
For skb->len < 2 that subtraction underflows: truncated into the u16
tlv_array_len parameter it becomes ~0xFFFE, and for a CONNECT to the SDP
SAP, nfc_llcp_connect_sn() uses a size_t and underflows to SIZE_MAX. The
TLV parsers bound their walk relative to that length, so they read far
past the end of the skb.

The aggregated-frame path (nfc_llcp_recv_agf()) already drops sub-PDUs
shorter than the header. Apply the same guard once, in the dispatcher, so
every PDU type is covered.

Found by 0sec (https://0sec.ai) using automated source analysis; the
missing guard is evident from source. Compile-tested.

Fixes: d646960f7986 ("NFC: Initial LLCP support")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:claude-opus-4-8
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
 net/nfc/llcp_core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index aed5fe1afef0..e3b3077e0e83 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1481,6 +1481,9 @@ static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb)
 {
 	u8 dsap, ssap, ptype;
 
+	if (skb->len < LLCP_HEADER_SIZE)
+		return;
+
 	ptype = nfc_llcp_ptype(skb);
 	dsap = nfc_llcp_dsap(skb);
 	ssap = nfc_llcp_ssap(skb);
-- 
2.43.0


^ permalink raw reply related

* [PATCH net v4 1/1] tcp: bound SYN-ACK timers to reqsk timeout range
From: Ren Wei @ 2026-07-11  7:27 UTC (permalink / raw)
  To: netdev
  Cc: edumazet, ncardwell, kuniyu, davem, pabeni, horms, yuantan098,
	yifanwucs, tomapufckgml, bird, roxy520tt, n05ec

From: Zhiling Zou <roxy520tt@gmail.com>

tcp_synack_retries supplies the SYN-ACK retry limit used by request
socket timers. The same effective limit can also come from TCP_SYNCNT
through icsk_syn_retries, while TCP_DEFER_ACCEPT can keep an ACKed
request alive until rskq_defer_accept is reached.

The request socket timeout counter is incremented before it is used to
compute the next timeout. tcp_reqsk_timeout() and the Fast Open SYN-ACK
timer shift req->timeout by req->num_timeout. Excessive retry or
defer-accept limits can therefore drive these timer paths into invalid
shift counts before the request expires.

Clamp the effective retry and defer-accept limits in the regular
request socket timer path, clamp the Fast Open retry limit, and make
the request socket timeout helper saturate before shifting. This keeps
sysctl writes unchanged while bounding the timer calculations.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zhiling Zou <roxy520tt@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
---
Changes in v4:
  - Drop the tcp_synack_retries sysctl maximum to preserve existing
    user-space behavior, and keep the runtime clamps at the timer usage
    sites.
  - v3 Link: https://lore.kernel.org/all/20260702095324.2995243-1-n05ec@lzu.edu.cn/

Changes in v3:
  - Order local variables in tcp_reqsk_timeout_sk() by reverse Christmas
    tree.
  - v2 Link: https://lore.kernel.org/all/20260630035009.55201-1-n05ec@lzu.edu.cn/

Changes in v2:
  - Keep the existing max_retries calculation in
    tcp_fastopen_synack_timer() and only add the clamp, avoiding code
    churn.
  - v1 Link: https://lore.kernel.org/all/02e24eb83639e9d7ecc623f000c60254bb5c40a5.1782643946.git.roxy520tt@gmail.com/

 include/net/tcp.h               | 19 +++++++++++++++----
 net/ipv4/inet_connection_sock.c |  6 +++++-
 net/ipv4/tcp_timer.c            |  3 ++-
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6d376ea4d1c0..ee78436b8964 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -183,6 +183,7 @@ static_assert((1 << ATO_BITS) > TCP_DELACK_MAX);
 #define MAX_TCP_KEEPINTVL	32767
 #define MAX_TCP_KEEPCNT		127
 #define MAX_TCP_SYNCNT		127
+#define MAX_TCP_SYNACK_RETRIES	63
 
 /* Ensure that TCP PAWS checks are relaxed after ~2147 seconds
  * to avoid overflows. This assumes a clock smaller than 1 Mhz.
@@ -882,12 +883,22 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
 	return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us);
 }
 
-static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+static inline unsigned long tcp_reqsk_timeout_sk(const struct sock *sk,
+						 struct request_sock *req)
 {
-	u64 timeout = (u64)req->timeout << req->num_timeout;
+	u32 rto_max = tcp_rto_max(sk);
+	u64 timeout = req->timeout;
+
+	if (req->num_timeout >= BITS_PER_TYPE(u64) ||
+	    timeout > U64_MAX >> req->num_timeout)
+		return rto_max;
+
+	return (unsigned long)min_t(u64, timeout << req->num_timeout, rto_max);
+}
 
-	return (unsigned long)min_t(u64, timeout,
-				    tcp_rto_max(req->rsk_listener));
+static inline unsigned long tcp_reqsk_timeout(struct request_sock *req)
+{
+	return tcp_reqsk_timeout_sk(req->rsk_listener, req);
 }
 
 u32 tcp_delack_max(const struct sock *sk);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 56902bba5483..b74212bae3dd 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1056,6 +1056,8 @@ static void reqsk_timer_handler(struct timer_list *t)
 	net = sock_net(sk_listener);
 	max_syn_ack_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
 		READ_ONCE(net->ipv4.sysctl_tcp_synack_retries);
+	max_syn_ack_retries = min_t(int, max_syn_ack_retries,
+				    MAX_TCP_SYNACK_RETRIES);
 	/* Normally all the openreqs are young and become mature
 	 * (i.e. converted to established socket) for first timeout.
 	 * If synack was not acknowledged for 1 second, it means
@@ -1086,7 +1088,9 @@ static void reqsk_timer_handler(struct timer_list *t)
 		}
 	}
 
-	syn_ack_recalc(req, max_syn_ack_retries, READ_ONCE(queue->rskq_defer_accept),
+	syn_ack_recalc(req, max_syn_ack_retries,
+		       min_t(u8, READ_ONCE(queue->rskq_defer_accept),
+			     MAX_TCP_SYNACK_RETRIES),
 		       &expire, &resend);
 	tcp_syn_ack_timeout(req);
 
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index bf171b5e1eb3..bbedf2b9e1bc 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -467,6 +467,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
 	 */
 	max_retries = READ_ONCE(icsk->icsk_syn_retries) ? :
 		READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_synack_retries) + 1;
+	max_retries = min_t(int, max_retries, MAX_TCP_SYNACK_RETRIES);
 
 	if (req->num_timeout >= max_retries) {
 		tcp_write_err(sk);
@@ -488,7 +489,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
 	if (!tp->retrans_stamp)
 		tp->retrans_stamp = tcp_time_stamp_ts(tp);
 	tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
-			  req->timeout << req->num_timeout, false);
+			     tcp_reqsk_timeout_sk(sk, req), false);
 }
 
 static bool tcp_rtx_probe0_timed_out(const struct sock *sk,
-- 
2.43.0


^ permalink raw reply related

* [PATCH net] nfc: llcp: guard against short PDUs in nfc_llcp_rx_skb()
From: Doruk Tan Ozturk @ 2026-07-11  7:30 UTC (permalink / raw)
  To: david, davem, edumazet, kuba, pabeni
  Cc: horms, oe-linux-nfc, netdev, linux-kernel, stable,
	Doruk Tan Ozturk

nfc_llcp_recv_connect() and nfc_llcp_recv_cc() pass
skb->len - LLCP_HEADER_SIZE to nfc_llcp_parse_connection_tlv() as a
size_t. When skb->len < LLCP_HEADER_SIZE the subtraction wraps around
to a huge value and the TLV walk runs past the skb.

nfc_llcp_rx_skb() applied no minimum-length check before dispatching,
so a PDU shorter than the 2-byte LLCP header reached these call sites.
Drop such PDUs up front; every LLCP PDU carries the header, so no valid
frame is shorter (a SYMM PDU is exactly LLCP_HEADER_SIZE bytes).

Found by 0sec (https://0sec.ai) using automated source analysis.

Fixes: d646960f7986 ("NFC: Initial LLCP support")
Cc: stable@vger.kernel.org
Assisted-by: 0sec:claude-opus-4-8
Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
---
 net/nfc/llcp_core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index aed5fe1afef0..e3b3077e0e83 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1481,6 +1481,9 @@ static void nfc_llcp_rx_skb(struct nfc_llcp_local *local, struct sk_buff *skb)
 {
 	u8 dsap, ssap, ptype;
 
+	if (skb->len < LLCP_HEADER_SIZE)
+		return;
+
 	ptype = nfc_llcp_ptype(skb);
 	dsap = nfc_llcp_dsap(skb);
 	ssap = nfc_llcp_ssap(skb);
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH net] nfc: llcp: guard against short PDUs in nfc_llcp_rx_skb()
From: Doruk Tan Ozturk @ 2026-07-11  7:39 UTC (permalink / raw)
  To: david; +Cc: Doruk Tan Ozturk, horms, oe-linux-nfc, netdev, linux-kernel
In-Reply-To: <20260711073012.71066-1-doruk@0sec.ai>

Hi David,

Sorry, two copies of this went out by mistake. Please take the earlier
"reject PDUs shorter than the LLCP header" and disregard the
near-identical "guard against short PDUs in nfc_llcp_rx_skb()" that
followed. Same one-line fix.

Doruk

^ permalink raw reply

* [PATCH net] net/smc: guard the CDC receive path against an unset RMB
From: Bryam Vargas via B4 Relay @ 2026-07-11  7:45 UTC (permalink / raw)
  To: Paolo Abeni, Wen Gu, Eric Dumazet, D. Wythe, Sidraya Jayagond,
	David S. Miller, Tony Lu, Dust Li, Mahanta Jambigi, Wenjia Zhang,
	Jakub Kicinski
  Cc: netdev, Simon Horman, linux-rdma, linux-kernel, linux-s390

From: Bryam Vargas <hexlabsecurity@proton.me>

The SMC CDC receive handlers look up a connection by its wire token and
then dereference conn->rmb_desc -- smcd_cdc_rx_tsklet() reads
rmb_desc->cpu_addr and smc_cdc_msg_recv_action() reads rmb_desc->len --
but a connection is published to the link group's token tree in
smc_conn_create(), before smc_buf_create() allocates its RMB.  A peer
that sends a CDC message carrying the connection's token in that setup
window reaches the handlers with conn->rmb_desc still NULL and crashes
the host with a NULL pointer dereference.

Bail out of both receive handlers when the RMB is not yet allocated, as
they already do for a killed or out-of-sync connection.

Closes: https://sashiko.dev/#/patchset/20260705-b4-disp-28a1bbca-v4-1-be089b98acc6@proton.me?part=1
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
Reachability is by source inspection: smc_conn_create() registers the
connection before smc_buf_create() allocates conn->rmb_desc, and the
receive handlers guard only !conn/out_of_sync/killed.  Verified with an
in-kernel KASAN model reproducing the deref with rmb_desc == NULL (fault
at the ->len / ->cpu_addr offsets) and clean with the guard.  af_smc runs
over an RDMA fabric or an ISM device, so this is the model rather than a
live trigger; reproducer available on request.

No Fixes: tag: the CDC receive path predates the history available here;
the introducing commits are the original CDC support and its SMC-D
addition -- please add whichever tag you prefer.
---
 net/smc/smc_cdc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c
index 32d6d03df321..d60f68facbc3 100644
--- a/net/smc/smc_cdc.c
+++ b/net/smc/smc_cdc.c
@@ -446,7 +446,7 @@ static void smcd_cdc_rx_tsklet(struct tasklet_struct *t)
 	struct smcd_cdc_msg cdc;
 	struct smc_sock *smc;
 
-	if (!conn || conn->killed)
+	if (!conn || conn->killed || !conn->rmb_desc)
 		return;
 
 	data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr;
@@ -483,7 +483,7 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
 	lgr = smc_get_lgr(link);
 	read_lock_bh(&lgr->conns_lock);
 	conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
-	if (!conn || conn->out_of_sync) {
+	if (!conn || conn->out_of_sync || !conn->rmb_desc) {
 		read_unlock_bh(&lgr->conns_lock);
 		return;
 	}

---
base-commit: dd3210c47e8d3ac6b4e9141fc68acc03b38c0ba3
change-id: 20260711-b4-disp-c36a9798-1b35bd69fd72

Best regards,
-- 
Bryam Vargas <hexlabsecurity@proton.me>



^ permalink raw reply related

* [PATCH v2 0/8] clk: sunxi-ng: Add support for Allwinner A733 CCU and PRCM
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet

Add support for the main CCU and the PRCM module (R-CCU) found in the
Allwinner A733 SoC. The clock architecture of the A733 is an evolution
of the previous A523 design but introduces several significant changes.

One of the key changes is the introduction of a "pll-ref" clock that
normalizes the physical oscillator frequency (which can be 19.2MHz,
24MHz, or 26MHz) into a consistent 24MHz reference for the entire clock
tree. Additionally, while the A733 inherits many module clock structures
from the A523, the MCU_CCU has been removed, and the overall clock tree
has been expanded to support more new functional units.

Also update the sunxi-ng SDM (Sigma-Delta Modulation) helper to support
a new dual-pattern register design. On the A733, the SDM enable bit has
been moved from the main PLL register to a second pattern register
(PATTERN1). The driver is updated to handle this register layout to
ensure accurate frequency synthesis for "pll-audio0".

The parent clocks for several instances in the main CCU are difficult
to determine as the user manual provides limited information on their
specific clock sources. In these cases, the implementation follows
vendor practices and previous SoC designs, generally defaulting to
"hosc" where documentation is lacking. The bus clock gates in the PRCM
(R-CCU) are explicitly defined based on the Memory Map in the manual,
which clearly associates each module with its respective bus.

---
Changes in v2:
- Rework the clocks property in DT bindings to source hosc directly from
  the oscillator to adapt to the new RTC binding
- Clean up the SDM dual-pattern helper macro and add the missing enable
  parameter for pll-audio0
- Number single-instance clocks with 0, e.g. GPU0, VE_ENC0/VE_DEC0,
  DRAM0, NAND0, GPADC0, THS0, etc
- Correct 8x PLL post-divider names to 12x for video and DE PLLs
  (pll-videoX-12x, pll-de-12x)
- Drop gic, cpu-peri, nsi clocks
- Rename clocks: OWA -> SPDIF, USB_REF -> USB01_REF, HDMI_ESM -> HDCP_ESM,
  PWMCTRL -> PWM
- Mark bus-dram0, ahb-store and mbus-store critical to avoid DMA, MMC and
  SPI NOR failures when unused clocks are disabled
- Switch PRCM timer clocks to P-only helpers
- Fix bus gate bit positions for UARTs and correct the sysdap register
  address
- Interleave bus clocks into function clocks
- Link to v1: https://lore.kernel.org/r/20260310-a733-clk-v1-0-36b4e9b24457@pigmoral.tech

---
Junhui Liu (8):
      dt-bindings: clk: sun60i-a733-ccu: Add allwinner A733 support
      clk: sunxi-ng: sdm: Add dual patterns support
      clk: sunxi-ng: a733: Add PRCM CCU
      clk: sunxi-ng: a733: Add PLL clocks support
      clk: sunxi-ng: a733: Add bus clocks support
      clk: sunxi-ng: a733: Add mod clocks support
      clk: sunxi-ng: a733: Add bus clock gates
      clk: sunxi-ng: a733: Add reset lines

 .../bindings/clock/allwinner,sun60i-a733-ccu.yaml  |  107 +
 drivers/clk/sunxi-ng/Kconfig                       |   10 +
 drivers/clk/sunxi-ng/Makefile                      |    4 +
 drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c           |  273 +++
 drivers/clk/sunxi-ng/ccu-sun60i-a733.c             | 2360 ++++++++++++++++++++
 drivers/clk/sunxi-ng/ccu_sdm.c                     |   51 +-
 drivers/clk/sunxi-ng/ccu_sdm.h                     |   29 +-
 include/dt-bindings/clock/sun60i-a733-ccu.h        |  290 +++
 include/dt-bindings/clock/sun60i-a733-r-ccu.h      |   39 +
 include/dt-bindings/reset/sun60i-a733-ccu.h        |  129 ++
 include/dt-bindings/reset/sun60i-a733-r-ccu.h      |   23 +
 11 files changed, 3289 insertions(+), 26 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260202-a733-clk-0d4fc00a9f9c

Best regards,
--  
Junhui Liu <junhui.liu@pigmoral.tech>


^ permalink raw reply

* [PATCH v2 1/8] dt-bindings: clk: sun60i-a733-ccu: Add allwinner A733 support
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

The CCU and R-CCU (PRCM) modules provide clocks and reset functions for
the Allwinner A733 SoC. The clock architecture of the A733 is evolved
from the A523, though the root clocking strategy transitions from a
static oscillator frequency in the Devicetree to the "hosc" clock, which
is determined by choosing from three possible frequencies (19.2MHz,
24MHz, or 26MHz) by the RTC hardware, and finally feeds the CCU and
R-CCU.

Additionally, the MCU_CCU module found in previous designs is removed
from the A733, and the clock tree is expanded with more clock outputs
to support new functional modules.

Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 .../bindings/clock/allwinner,sun60i-a733-ccu.yaml  | 107 ++++++++
 include/dt-bindings/clock/sun60i-a733-ccu.h        | 290 +++++++++++++++++++++
 include/dt-bindings/clock/sun60i-a733-r-ccu.h      |  39 +++
 include/dt-bindings/reset/sun60i-a733-ccu.h        | 129 +++++++++
 include/dt-bindings/reset/sun60i-a733-r-ccu.h      |  23 ++
 5 files changed, 588 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/allwinner,sun60i-a733-ccu.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun60i-a733-ccu.yaml
new file mode 100644
index 000000000000..5a831b5c4029
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/allwinner,sun60i-a733-ccu.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/allwinner,sun60i-a733-ccu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A733 Clock Control Unit
+
+maintainers:
+  - Junhui Liu <junhui.liu@pigmoral.tech>
+
+properties:
+  "#clock-cells":
+    const: 1
+
+  "#reset-cells":
+    const: 1
+
+  compatible:
+    enum:
+      - allwinner,sun60i-a733-ccu
+      - allwinner,sun60i-a733-r-ccu
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 4
+    maxItems: 7
+
+  clock-names:
+    minItems: 4
+    maxItems: 7
+
+required:
+  - "#clock-cells"
+  - "#reset-cells"
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          enum:
+            - allwinner,sun60i-a733-ccu
+
+    then:
+      properties:
+        clocks:
+          items:
+            - description: High Frequency Oscillator (19.2MHz, 24MHz, or 26MHz)
+            - description: Low Frequency Oscillator (usually at 32kHz)
+            - description: Internal Oscillator
+            - description: Low Frequency Oscillator fanout
+
+        clock-names:
+          items:
+            - const: hosc
+            - const: losc
+            - const: iosc
+            - const: losc-fanout
+
+  - if:
+      properties:
+        compatible:
+          enum:
+            - allwinner,sun60i-a733-r-ccu
+
+    then:
+      properties:
+        clocks:
+          items:
+            - description: High Frequency Oscillator (19.2MHz, 24MHz, or 26MHz)
+            - description: Low Frequency Oscillator (usually at 32kHz)
+            - description: Internal Oscillator
+            - description: System 24MHz Clock
+            - description: Peripherals PLL 0 (200 MHz output)
+            - description: Peripherals PLL 0 (300 MHz output)
+            - description: Peripherals PLL 1 (300 MHz output)
+
+        clock-names:
+          items:
+            - const: hosc
+            - const: losc
+            - const: iosc
+            - const: sys-24m
+            - const: pll-periph0-200m
+            - const: pll-periph0-300m
+            - const: pll-periph1-300m
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@2002000 {
+      compatible = "allwinner,sun60i-a733-ccu";
+      reg = <0x02002000 0x2000>;
+      clocks = <&osc>, <&rtc 0>, <&rtc 2>, <&rtc 1>;
+      clock-names = "hosc", "losc", "iosc", "losc-fanout";
+      #clock-cells = <1>;
+      #reset-cells = <1>;
+    };
+
+...
diff --git a/include/dt-bindings/clock/sun60i-a733-ccu.h b/include/dt-bindings/clock/sun60i-a733-ccu.h
new file mode 100644
index 000000000000..6742070e3086
--- /dev/null
+++ b/include/dt-bindings/clock/sun60i-a733-ccu.h
@@ -0,0 +1,290 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ *
+ * The user manual uses PCLK, HCLK, and MCLK suffixes for some bus clocks.
+ * These are represented here as APB, AHB, and MBUS prefixes respectively,
+ * with the bus type moved to the front of the clock name.
+ */
+
+#ifndef _DT_BINDINGS_CLK_SUN60I_A733_CCU_H_
+#define _DT_BINDINGS_CLK_SUN60I_A733_CCU_H_
+
+#define CLK_PLL_REF		0
+#define CLK_SYS_24M		1
+#define CLK_PLL_DDR		2
+#define CLK_PLL_PERIPH0_4X	3
+#define CLK_PLL_PERIPH0_2X	4
+#define CLK_PLL_PERIPH0_800M	5
+#define CLK_PLL_PERIPH0_480M	6
+#define CLK_PLL_PERIPH0_600M	7
+#define CLK_PLL_PERIPH0_400M	8
+#define CLK_PLL_PERIPH0_300M	9
+#define CLK_PLL_PERIPH0_200M	10
+#define CLK_PLL_PERIPH0_160M	11
+#define CLK_PLL_PERIPH0_150M	12
+#define CLK_PLL_PERIPH1_4X	13
+#define CLK_PLL_PERIPH1_2X	14
+#define CLK_PLL_PERIPH1_800M	15
+#define CLK_PLL_PERIPH1_480M	16
+#define CLK_PLL_PERIPH1_600M	17
+#define CLK_PLL_PERIPH1_400M	18
+#define CLK_PLL_PERIPH1_300M	19
+#define CLK_PLL_PERIPH1_200M	20
+#define CLK_PLL_PERIPH1_160M	21
+#define CLK_PLL_PERIPH1_150M	22
+#define CLK_PLL_GPU0		23
+#define CLK_PLL_VIDEO0_12X	24
+#define CLK_PLL_VIDEO0_4X	25
+#define CLK_PLL_VIDEO0_3X	26
+#define CLK_PLL_VIDEO1_12X	27
+#define CLK_PLL_VIDEO1_4X	28
+#define CLK_PLL_VIDEO1_3X	29
+#define CLK_PLL_VIDEO2_12X	30
+#define CLK_PLL_VIDEO2_4X	31
+#define CLK_PLL_VIDEO2_3X	32
+#define CLK_PLL_VE0		33
+#define CLK_PLL_VE1		34
+#define CLK_PLL_AUDIO0_4X	35
+#define CLK_PLL_AUDIO1		36
+#define CLK_PLL_AUDIO1_DIV2	37
+#define CLK_PLL_AUDIO1_DIV5	38
+#define CLK_PLL_NPU		39
+#define CLK_PLL_DE_12X		40
+#define CLK_PLL_DE_4X		41
+#define CLK_PLL_DE_3X		42
+#define CLK_AHB			43
+#define CLK_APB0		44
+#define CLK_APB1		45
+#define CLK_APB_UART		46
+#define CLK_TRACE		47
+#define CLK_BUS_ITS_PCIE0_ACLK	48
+#define CLK_MBUS		49
+#define CLK_MBUS_IOMMU0_SYS	50
+#define CLK_APB_IOMMU0_SYS	51
+#define CLK_AHB_IOMMU0_SYS	52
+#define CLK_BUS_MSI_LITE0	53
+#define CLK_BUS_MSI_LITE1	54
+#define CLK_BUS_MSI_LITE2	55
+#define CLK_MBUS_IOMMU1_SYS	56
+#define CLK_APB_IOMMU1_SYS	57
+#define CLK_AHB_IOMMU1_SYS	58
+#define CLK_AHB_VE_DEC		59
+#define CLK_AHB_VE_ENC		60
+#define CLK_AHB_VID_IN		61
+#define CLK_AHB_VID_COUT0	62
+#define CLK_AHB_VID_COUT1	63
+#define CLK_AHB_DE		64
+#define CLK_AHB_NPU		65
+#define CLK_AHB_GPU0		66
+#define CLK_AHB_SERDES		67
+#define CLK_AHB_USB_SYS		68
+#define CLK_AHB_MSI_LITE0	69
+#define CLK_AHB_STORE		70
+#define CLK_AHB_CPUS		71
+#define CLK_MBUS_IOMMU0		72
+#define CLK_MBUS_IOMMU1		73
+#define CLK_MBUS_DESYS		74
+#define CLK_MBUS_VE_ENC0_GATE	75
+#define CLK_MBUS_VE_DEC0_GATE	76
+#define CLK_MBUS_GPU0		77
+#define CLK_MBUS_NPU		78
+#define CLK_MBUS_VID_IN		79
+#define CLK_MBUS_SERDES		80
+#define CLK_MBUS_MSI_LITE0	81
+#define CLK_MBUS_STORE		82
+#define CLK_MBUS_MSI_LITE2	83
+#define CLK_MBUS_DMA0		84
+#define CLK_MBUS_VE_ENC0	85
+#define CLK_MBUS_CE		86
+#define CLK_MBUS_DMA1		87
+#define CLK_MBUS_NAND		88
+#define CLK_MBUS_CSI		89
+#define CLK_MBUS_ISP		90
+#define CLK_MBUS_GMAC0		91
+#define CLK_MBUS_GMAC1		92
+#define CLK_MBUS_VE_DEC0	93
+#define CLK_BUS_DMA0		94
+#define CLK_BUS_DMA1		95
+#define CLK_BUS_SPINLOCK	96
+#define CLK_BUS_MSGBOX0		97
+#define CLK_BUS_PWM0		98
+#define CLK_BUS_PWM1		99
+#define CLK_BUS_DBG		100
+#define CLK_BUS_SYSDAP		101
+#define CLK_TIMER0		102
+#define CLK_TIMER1		103
+#define CLK_TIMER2		104
+#define CLK_TIMER3		105
+#define CLK_TIMER4		106
+#define CLK_TIMER5		107
+#define CLK_TIMER6		108
+#define CLK_TIMER7		109
+#define CLK_TIMER8		110
+#define CLK_TIMER9		111
+#define CLK_BUS_TIMER		112
+#define CLK_AVS			113
+#define CLK_DE0			114
+#define CLK_BUS_DE0		115
+#define CLK_DI			116
+#define CLK_BUS_DI		117
+#define CLK_G2D			118
+#define CLK_BUS_G2D		119
+#define CLK_EINK		120
+#define CLK_EINK_PANEL		121
+#define CLK_BUS_EINK		122
+#define CLK_VE_ENC0		123
+#define CLK_VE_DEC0		124
+#define CLK_BUS_VE_ENC0		125
+#define CLK_BUS_VE_DEC0		126
+#define CLK_CE			127
+#define CLK_BUS_CE		128
+#define CLK_BUS_CE_SYS		129
+#define CLK_NPU			130
+#define CLK_BUS_NPU		131
+#define CLK_GPU0		132
+#define CLK_BUS_GPU0		133
+#define CLK_DRAM0		134
+#define CLK_BUS_DRAM0		135
+#define CLK_NAND0_CLK0		136
+#define CLK_NAND0_CLK1		137
+#define CLK_BUS_NAND0		138
+#define CLK_MMC0		139
+#define CLK_BUS_MMC0		140
+#define CLK_MMC1		141
+#define CLK_BUS_MMC1		142
+#define CLK_MMC2		143
+#define CLK_BUS_MMC2		144
+#define CLK_MMC3		145
+#define CLK_BUS_MMC3		146
+#define CLK_UFS_AXI		147
+#define CLK_UFS_CFG		148
+#define CLK_BUS_UFS		149
+#define CLK_BUS_UART0		150
+#define CLK_BUS_UART1		151
+#define CLK_BUS_UART2		152
+#define CLK_BUS_UART3		153
+#define CLK_BUS_UART4		154
+#define CLK_BUS_UART5		155
+#define CLK_BUS_UART6		156
+#define CLK_BUS_I2C0		157
+#define CLK_BUS_I2C1		158
+#define CLK_BUS_I2C2		159
+#define CLK_BUS_I2C3		160
+#define CLK_BUS_I2C4		161
+#define CLK_BUS_I2C5		162
+#define CLK_BUS_I2C6		163
+#define CLK_BUS_I2C7		164
+#define CLK_BUS_I2C8		165
+#define CLK_BUS_I2C9		166
+#define CLK_BUS_I2C10		167
+#define CLK_BUS_I2C11		168
+#define CLK_BUS_I2C12		169
+#define CLK_SPI0		170
+#define CLK_BUS_SPI0		171
+#define CLK_SPI1		172
+#define CLK_BUS_SPI1		173
+#define CLK_SPI2		174
+#define CLK_BUS_SPI2		175
+#define CLK_SPIF		176
+#define CLK_BUS_SPIF		177
+#define CLK_SPI3		178
+#define CLK_BUS_SPI3		179
+#define CLK_SPI4		180
+#define CLK_BUS_SPI4		181
+#define CLK_GPADC0_24M		182
+#define CLK_BUS_GPADC0		183
+#define CLK_BUS_THS0		184
+#define CLK_IRRX		185
+#define CLK_BUS_IRRX		186
+#define CLK_IRTX		187
+#define CLK_BUS_IRTX		188
+#define CLK_BUS_LRADC		189
+#define CLK_SGPIO		190
+#define CLK_BUS_SGPIO		191
+#define CLK_LPC			192
+#define CLK_BUS_LPC		193
+#define CLK_I2SPCM0		194
+#define CLK_BUS_I2SPCM0		195
+#define CLK_I2SPCM1		196
+#define CLK_BUS_I2SPCM1		197
+#define CLK_I2SPCM2		198
+#define CLK_I2SPCM2_ASRC	199
+#define CLK_BUS_I2SPCM2		200
+#define CLK_I2SPCM3		201
+#define CLK_BUS_I2SPCM3		202
+#define CLK_I2SPCM4		203
+#define CLK_BUS_I2SPCM4		204
+#define CLK_SPDIF_TX		205
+#define CLK_SPDIF_RX		206
+#define CLK_BUS_SPDIF		207
+#define CLK_DMIC		208
+#define CLK_BUS_DMIC		209
+#define CLK_USB_OHCI0		210
+#define CLK_BUS_OHCI0		211
+#define CLK_BUS_EHCI0		212
+#define CLK_BUS_OTG		213
+#define CLK_USB_OHCI1		214
+#define CLK_BUS_OHCI1		215
+#define CLK_BUS_EHCI1		216
+#define CLK_USB01_REF		217
+#define CLK_USB2_U2_REF		218
+#define CLK_USB2_SUSPEND	219
+#define CLK_USB2_MF		220
+#define CLK_USB2_U3_UTMI	221
+#define CLK_USB2_U2_PIPE	222
+#define CLK_PCIE_AUX		223
+#define CLK_PCIE_AXI_SLV	224
+#define CLK_SERDES_PHY		225
+#define CLK_GMAC_PTP		226
+#define CLK_GMAC0_PHY		227
+#define CLK_BUS_GMAC0		228
+#define CLK_GMAC1_PHY		229
+#define CLK_BUS_GMAC1		230
+#define CLK_TCON_LCD0		231
+#define CLK_BUS_TCON_LCD0	232
+#define CLK_TCON_LCD1		233
+#define CLK_BUS_TCON_LCD1	234
+#define CLK_TCON_LCD2		235
+#define CLK_BUS_TCON_LCD2	236
+#define CLK_DSI0		237
+#define CLK_BUS_DSI0		238
+#define CLK_DSI1		239
+#define CLK_BUS_DSI1		240
+#define CLK_COMBPHY0		241
+#define CLK_COMBPHY1		242
+#define CLK_BUS_TCON_TV0	243
+#define CLK_BUS_TCON_TV1	244
+#define CLK_EDP_TV		245
+#define CLK_BUS_EDP_TV		246
+#define CLK_HDMI_CEC_32K	247
+#define CLK_HDMI_CEC		248
+#define CLK_HDMI_TV		249
+#define CLK_BUS_HDMI_TV		250
+#define CLK_HDMI_SFR		251
+#define CLK_HDCP_ESM		252
+#define CLK_BUS_DPSS_TOP0	253
+#define CLK_BUS_DPSS_TOP1	254
+#define CLK_LEDC		255
+#define CLK_BUS_LEDC		256
+#define CLK_BUS_DSC		257
+#define CLK_CSI_MASTER0		258
+#define CLK_CSI_MASTER1		259
+#define CLK_CSI_MASTER2		260
+#define CLK_CSI			261
+#define CLK_BUS_CSI		262
+#define CLK_ISP			263
+#define CLK_RES_DCAP_24M	264
+#define CLK_APB2JTAG		265
+#define CLK_FANOUT_24M		266
+#define CLK_FANOUT_12M		267
+#define CLK_FANOUT_16M		268
+#define CLK_FANOUT_25M		269
+#define CLK_FANOUT_27M		270
+#define CLK_FANOUT_PCLK		271
+#define CLK_FANOUT0		272
+#define CLK_FANOUT1		273
+#define CLK_FANOUT2		274
+#define CLK_FANOUT3		275
+
+#endif /* _DT_BINDINGS_CLK_SUN60I_A733_CCU_H_ */
diff --git a/include/dt-bindings/clock/sun60i-a733-r-ccu.h b/include/dt-bindings/clock/sun60i-a733-r-ccu.h
new file mode 100644
index 000000000000..1d3845b483bf
--- /dev/null
+++ b/include/dt-bindings/clock/sun60i-a733-r-ccu.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _DT_BINDINGS_CLK_SUN60I_A733_R_CCU_H_
+#define _DT_BINDINGS_CLK_SUN60I_A733_R_CCU_H_
+
+#define CLK_R_AHB		0
+#define CLK_R_APB0		1
+#define CLK_R_APB1		2
+#define CLK_R_TIMER0		3
+#define CLK_R_TIMER1		4
+#define CLK_R_TIMER2		5
+#define CLK_R_TIMER3		6
+#define CLK_BUS_R_TIMER		7
+#define CLK_BUS_R_TWD		8
+#define CLK_R_PWM		9
+#define CLK_BUS_R_PWM		10
+#define CLK_R_SPI		11
+#define CLK_BUS_R_SPI		12
+#define CLK_BUS_R_MSGBOX	13
+#define CLK_BUS_R_UART0		14
+#define CLK_BUS_R_UART1		15
+#define CLK_BUS_R_I2C0		16
+#define CLK_BUS_R_I2C1		17
+#define CLK_BUS_R_I2C2		18
+#define CLK_BUS_R_PPU		19
+#define CLK_BUS_R_TZMA		20
+#define CLK_BUS_R_CPU_BIST	21
+#define CLK_R_IR_RX		22
+#define CLK_BUS_R_IR_RX		23
+#define CLK_BUS_R_RTC		24
+#define CLK_R_RISCV		25
+#define CLK_BUS_R_RISCV		26
+#define CLK_BUS_R_RISCV_CFG	27
+#define CLK_BUS_R_CPUCFG	28
+
+#endif /* _DT_BINDINGS_CLK_SUN60I_A733_R_CCU_H_ */
diff --git a/include/dt-bindings/reset/sun60i-a733-ccu.h b/include/dt-bindings/reset/sun60i-a733-ccu.h
new file mode 100644
index 000000000000..8946f08925dc
--- /dev/null
+++ b/include/dt-bindings/reset/sun60i-a733-ccu.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _DT_BINDINGS_RST_SUN60I_A733_CCU_H_
+#define _DT_BINDINGS_RST_SUN60I_A733_CCU_H_
+
+#define RST_BUS_ITS_PCIE0	0
+#define RST_BUS_IOMMU0_SYS	1
+#define RST_BUS_MSI_LITE0_AHB	2
+#define RST_BUS_MSI_LITE0_MBUS	3
+#define RST_BUS_MSI_LITE1_AHB	4
+#define RST_BUS_MSI_LITE1_MBUS	5
+#define RST_BUS_MSI_LITE2_AHB	6
+#define RST_BUS_MSI_LITE2_MBUS	7
+#define RST_BUS_IOMMU1_SYS	8
+#define RST_BUS_DMA0		9
+#define RST_BUS_DMA1		10
+#define RST_BUS_SPINLOCK	11
+#define RST_BUS_MSGBOX		12
+#define RST_BUS_PWM0		13
+#define RST_BUS_PWM1		14
+#define RST_BUS_DBG		15
+#define RST_BUS_SYSDAP		16
+#define RST_BUS_TIMER0		17
+#define RST_BUS_DE0		18
+#define RST_BUS_DI		19
+#define RST_BUS_G2D		20
+#define RST_BUS_EINK		21
+#define RST_BUS_DE_SYS		22
+#define RST_BUS_VE_ENC0		23
+#define RST_BUS_VE_DEC0		24
+#define RST_BUS_CE		25
+#define RST_BUS_CE_SYS		26
+#define RST_BUS_NPU_CORE	27
+#define RST_BUS_NPU_AXI		28
+#define RST_BUS_NPU_AHB		29
+#define RST_BUS_NPU_SRAM	30
+#define RST_BUS_GPU0		31
+#define RST_BUS_DRAM0		32
+#define RST_BUS_NAND0		33
+#define RST_BUS_MMC0		34
+#define RST_BUS_MMC1		35
+#define RST_BUS_MMC2		36
+#define RST_BUS_MMC3		37
+#define RST_BUS_UFS_AHB		38
+#define RST_BUS_UFS_AXI		39
+#define RST_BUS_UFS_PHY		40
+#define RST_BUS_UFS_CORE	41
+#define RST_BUS_UART0		42
+#define RST_BUS_UART1		43
+#define RST_BUS_UART2		44
+#define RST_BUS_UART3		45
+#define RST_BUS_UART4		46
+#define RST_BUS_UART5		47
+#define RST_BUS_UART6		48
+#define RST_BUS_I2C0		49
+#define RST_BUS_I2C1		50
+#define RST_BUS_I2C2		51
+#define RST_BUS_I2C3		52
+#define RST_BUS_I2C4		53
+#define RST_BUS_I2C5		54
+#define RST_BUS_I2C6		55
+#define RST_BUS_I2C7		56
+#define RST_BUS_I2C8		57
+#define RST_BUS_I2C9		58
+#define RST_BUS_I2C10		59
+#define RST_BUS_I2C11		60
+#define RST_BUS_I2C12		61
+#define RST_BUS_SPI0		62
+#define RST_BUS_SPI1		63
+#define RST_BUS_SPI2		64
+#define RST_BUS_SPIF		65
+#define RST_BUS_SPI3		66
+#define RST_BUS_SPI4		67
+#define RST_BUS_GPADC0		68
+#define RST_BUS_THS0		69
+#define RST_BUS_IRRX		70
+#define RST_BUS_IRTX		71
+#define RST_BUS_LRADC		72
+#define RST_BUS_SGPIO		73
+#define RST_BUS_LPC		74
+#define RST_BUS_I2SPCM0		75
+#define RST_BUS_I2SPCM1		76
+#define RST_BUS_I2SPCM2		77
+#define RST_BUS_I2SPCM3		78
+#define RST_BUS_I2SPCM4		79
+#define RST_BUS_SPDIF		80
+#define RST_BUS_DMIC		81
+#define RST_USB_PHY0		82
+#define RST_BUS_OHCI0		83
+#define RST_BUS_EHCI0		84
+#define RST_BUS_OTG		85
+#define RST_USB_PHY1		86
+#define RST_BUS_OHCI1		87
+#define RST_BUS_EHCI1		88
+#define RST_BUS_USB2		89
+#define RST_BUS_PCIE_PWRUP	90
+#define RST_BUS_PCIE		91
+#define RST_BUS_SERDES		92
+#define RST_BUS_GMAC0		93
+#define RST_BUS_GMAC0_AXI	94
+#define RST_BUS_GMAC1		95
+#define RST_BUS_GMAC1_AXI	96
+#define RST_BUS_TCON_LCD0	97
+#define RST_BUS_TCON_LCD1	98
+#define RST_BUS_TCON_LCD2	99
+#define RST_BUS_LVDS0		100
+#define RST_BUS_LVDS1		101
+#define RST_BUS_DSI0		102
+#define RST_BUS_DSI1		103
+#define RST_BUS_TCON_TV0	104
+#define RST_BUS_TCON_TV1	105
+#define RST_BUS_EDP		106
+#define RST_BUS_HDMI_MAIN	107
+#define RST_BUS_HDMI_SUB	108
+#define RST_BUS_HDMI_HDCP	109
+#define RST_BUS_DPSS_TOP0	110
+#define RST_BUS_DPSS_TOP1	111
+#define RST_BUS_VIDEO_OUT0	112
+#define RST_BUS_VIDEO_OUT1	113
+#define RST_BUS_LEDC		114
+#define RST_BUS_DSC		115
+#define RST_BUS_CSI		116
+#define RST_BUS_VIDEO_IN	117
+#define RST_BUS_APB2JTAG	118
+
+#endif /* _DT_BINDINGS_RST_SUN60I_A733_CCU_H_ */
diff --git a/include/dt-bindings/reset/sun60i-a733-r-ccu.h b/include/dt-bindings/reset/sun60i-a733-r-ccu.h
new file mode 100644
index 000000000000..629e546d1998
--- /dev/null
+++ b/include/dt-bindings/reset/sun60i-a733-r-ccu.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _DT_BINDINGS_RST_SUN60I_A733_R_CCU_H_
+#define _DT_BINDINGS_RST_SUN60I_A733_R_CCU_H_
+
+#define RST_BUS_R_TIMER		0
+#define RST_BUS_R_PWM		1
+#define RST_BUS_R_SPI		2
+#define RST_BUS_R_MSGBOX	3
+#define RST_BUS_R_UART0		4
+#define RST_BUS_R_UART1		5
+#define RST_BUS_R_I2C0		6
+#define RST_BUS_R_I2C1		7
+#define RST_BUS_R_I2C2		8
+#define RST_BUS_R_IR_RX		9
+#define RST_BUS_R_RTC		10
+#define RST_BUS_R_RISCV_CFG	11
+#define RST_BUS_R_CPUCFG	12
+
+#endif /* _DT_BINDINGS_RST_SUN60I_A733_R_CCU_H_ */

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 2/8] clk: sunxi-ng: sdm: Add dual patterns support
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

On newer Allwinner platforms like the A733, the Sigma-Delta Modulation
(SDM) control logic is more complex. The SDM enable bit, which was
previously located in the PLL register, is now moved to a second
pattern register (PATTERN1).

To support this, rename the existing "tuning" members to "pattern0" to
align with the datasheet, and introduce the _SUNXI_CCU_SDM_DUAL_PAT
macro to provide pattern1 register support. Related operations are also
updated.

Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/ccu_sdm.c | 51 +++++++++++++++++++++++++++++-------------
 drivers/clk/sunxi-ng/ccu_sdm.h | 29 +++++++++++++++---------
 2 files changed, 54 insertions(+), 26 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_sdm.c b/drivers/clk/sunxi-ng/ccu_sdm.c
index c564e5f9e610..204e25feaa36 100644
--- a/drivers/clk/sunxi-ng/ccu_sdm.c
+++ b/drivers/clk/sunxi-ng/ccu_sdm.c
@@ -18,7 +18,10 @@ bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
 	if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
 		return false;
 
-	return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
+	if (sdm->pat1_enable && !(readl(common->base + sdm->pat1_reg) & sdm->pat1_enable))
+		return false;
+
+	return !!(readl(common->base + sdm->pat0_reg) & sdm->pat0_enable);
 }
 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, "SUNXI_CCU");
 
@@ -37,18 +40,27 @@ void ccu_sdm_helper_enable(struct ccu_common *common,
 	for (i = 0; i < sdm->table_size; i++)
 		if (sdm->table[i].rate == rate)
 			writel(sdm->table[i].pattern,
-			       common->base + sdm->tuning_reg);
+			       common->base + sdm->pat0_reg);
 
 	/* Make sure SDM is enabled */
 	spin_lock_irqsave(common->lock, flags);
-	reg = readl(common->base + sdm->tuning_reg);
-	writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
+	reg = readl(common->base + sdm->pat0_reg);
+	writel(reg | sdm->pat0_enable, common->base + sdm->pat0_reg);
 	spin_unlock_irqrestore(common->lock, flags);
 
-	spin_lock_irqsave(common->lock, flags);
-	reg = readl(common->base + common->reg);
-	writel(reg | sdm->enable, common->base + common->reg);
-	spin_unlock_irqrestore(common->lock, flags);
+	if (sdm->enable) {
+		spin_lock_irqsave(common->lock, flags);
+		reg = readl(common->base + common->reg);
+		writel(reg | sdm->enable, common->base + common->reg);
+		spin_unlock_irqrestore(common->lock, flags);
+	}
+
+	if (sdm->pat1_enable) {
+		spin_lock_irqsave(common->lock, flags);
+		reg = readl(common->base + sdm->pat1_reg);
+		writel(reg | sdm->pat1_enable, common->base + sdm->pat1_reg);
+		spin_unlock_irqrestore(common->lock, flags);
+	}
 }
 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, "SUNXI_CCU");
 
@@ -61,14 +73,23 @@ void ccu_sdm_helper_disable(struct ccu_common *common,
 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
 		return;
 
-	spin_lock_irqsave(common->lock, flags);
-	reg = readl(common->base + common->reg);
-	writel(reg & ~sdm->enable, common->base + common->reg);
-	spin_unlock_irqrestore(common->lock, flags);
+	if (sdm->enable) {
+		spin_lock_irqsave(common->lock, flags);
+		reg = readl(common->base + common->reg);
+		writel(reg & ~sdm->enable, common->base + common->reg);
+		spin_unlock_irqrestore(common->lock, flags);
+	}
+
+	if (sdm->pat1_enable) {
+		spin_lock_irqsave(common->lock, flags);
+		reg = readl(common->base + sdm->pat1_reg);
+		writel(reg & ~sdm->pat1_enable, common->base + sdm->pat1_reg);
+		spin_unlock_irqrestore(common->lock, flags);
+	}
 
 	spin_lock_irqsave(common->lock, flags);
-	reg = readl(common->base + sdm->tuning_reg);
-	writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
+	reg = readl(common->base + sdm->pat0_reg);
+	writel(reg & ~sdm->pat0_enable, common->base + sdm->pat0_reg);
 	spin_unlock_irqrestore(common->lock, flags);
 }
 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, "SUNXI_CCU");
@@ -123,7 +144,7 @@ unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
 	pr_debug("%s: clock is sigma-delta modulated\n",
 		 clk_hw_get_name(&common->hw));
 
-	reg = readl(common->base + sdm->tuning_reg);
+	reg = readl(common->base + sdm->pat0_reg);
 
 	pr_debug("%s: pattern reg is 0x%x",
 		 clk_hw_get_name(&common->hw), reg);
diff --git a/drivers/clk/sunxi-ng/ccu_sdm.h b/drivers/clk/sunxi-ng/ccu_sdm.h
index c1a7159b89c3..e94cef141c7d 100644
--- a/drivers/clk/sunxi-ng/ccu_sdm.h
+++ b/drivers/clk/sunxi-ng/ccu_sdm.h
@@ -33,21 +33,28 @@ struct ccu_sdm_internal {
 	u32		table_size;
 	/* early SoCs don't have the SDM enable bit in the PLL register */
 	u32		enable;
-	/* second enable bit in tuning register */
-	u32		tuning_enable;
-	u16		tuning_reg;
+	/* second enable bit in pattern0 register */
+	u32		pat0_enable;
+	u16		pat0_reg;
+	/* on some platforms, the sdm enable bit in pattern1 register */
+	u32		pat1_enable;
+	u16		pat1_reg;
 };
 
-#define _SUNXI_CCU_SDM(_table, _enable,			\
-		       _reg, _reg_enable)		\
-	{						\
-		.table		= _table,		\
-		.table_size	= ARRAY_SIZE(_table),	\
-		.enable		= _enable,		\
-		.tuning_enable	= _reg_enable,		\
-		.tuning_reg	= _reg,			\
+#define _SUNXI_CCU_SDM_DUAL_PAT(_table, _enable, _pat0, _pat0_enable, _pat1, _pat1_enable) \
+	{								\
+		.table			= _table,			\
+		.table_size		= ARRAY_SIZE(_table),		\
+		.enable			= _enable,			\
+		.pat0_enable		= _pat0_enable,			\
+		.pat0_reg		= _pat0,			\
+		.pat1_enable		= _pat1_enable,			\
+		.pat1_reg		= _pat1,			\
 	}
 
+#define _SUNXI_CCU_SDM(_table, _enable, _pat0, _pat0_enable)	\
+	_SUNXI_CCU_SDM_DUAL_PAT(_table, _enable, _pat0, _pat0_enable, 0, 0)
+
 bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
 			       struct ccu_sdm_internal *sdm);
 void ccu_sdm_helper_enable(struct ccu_common *common,

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 3/8] clk: sunxi-ng: a733: Add PRCM CCU
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

Add support for the Power Reset Clock Management (PRCM) module found in
the Allwinner A733 SoC. This clock controller manages the clock control
and reset functions for device modules within the CPUS domain.

The PRCM module includes the management of three primary buses: r-ahb,
r-apb0, and r-apb1. It also provides clocking for several key
peripherals, such as R-UART, R-I2C, R-SPI, and the R-RISCV subsystem.
Additionally, the reset lines for these modules are integrated.

Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/Kconfig             |   5 +
 drivers/clk/sunxi-ng/Makefile            |   2 +
 drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c | 273 +++++++++++++++++++++++++++++++
 3 files changed, 280 insertions(+)

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 6af2d020e03e..202e793dc754 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
 	default ARCH_SUNXI
 	depends on ARM64 || COMPILE_TEST
 
+config SUN60I_A733_R_CCU
+	tristate "Support for the Allwinner A733 PRCM CCU"
+	default ARCH_SUNXI
+	depends on ARM64 || COMPILE_TEST
+
 config SUN4I_A10_CCU
 	tristate "Support for the Allwinner A10/A20 CCU"
 	default ARCH_SUNXI
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index a1c4087d7241..d3702bdb7a23 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU)	+= sun50i-h616-ccu.o
 obj-$(CONFIG_SUN55I_A523_CCU)	+= sun55i-a523-ccu.o
 obj-$(CONFIG_SUN55I_A523_MCU_CCU)	+= sun55i-a523-mcu-ccu.o
 obj-$(CONFIG_SUN55I_A523_R_CCU)	+= sun55i-a523-r-ccu.o
+obj-$(CONFIG_SUN60I_A733_R_CCU)	+= sun60i-a733-r-ccu.o
 obj-$(CONFIG_SUN4I_A10_CCU)	+= sun4i-a10-ccu.o
 obj-$(CONFIG_SUN5I_CCU)		+= sun5i-ccu.o
 obj-$(CONFIG_SUN6I_A31_CCU)	+= sun6i-a31-ccu.o
@@ -64,6 +65,7 @@ sun50i-h616-ccu-y		+= ccu-sun50i-h616.o
 sun55i-a523-ccu-y		+= ccu-sun55i-a523.o
 sun55i-a523-mcu-ccu-y		+= ccu-sun55i-a523-mcu.o
 sun55i-a523-r-ccu-y		+= ccu-sun55i-a523-r.o
+sun60i-a733-r-ccu-y		+= ccu-sun60i-a733-r.o
 sun4i-a10-ccu-y			+= ccu-sun4i-a10.o
 sun5i-ccu-y			+= ccu-sun5i.o
 sun6i-a31-ccu-y			+= ccu-sun6i-a31.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c
new file mode 100644
index 000000000000..a56cd3edff5f
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733-r.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 rengaomin@allwinnertech.com
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ * Based on the A523 CCU driver:
+ *   Copyright (C) 2024 Arm Ltd.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/sun60i-a733-r-ccu.h>
+#include <dt-bindings/reset/sun60i-a733-r-ccu.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+
+static const struct clk_parent_data r_ahb_parents[] = {
+	{ .fw_name = "hosc" },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .fw_name = "pll-periph0-200m" },
+	{ .fw_name = "pll-periph0-300m" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX(r_ahb_clk, "r-ahb", r_ahb_parents, 0x000,
+				 0, 5,	/* M */
+				 24, 3,	/* mux */
+				 0);
+
+static const struct clk_parent_data r_apb_parents[] = {
+	{ .fw_name = "hosc" },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .fw_name = "pll-periph0-200m" },
+	{ .fw_name = "sys-24m" },
+};
+
+static SUNXI_CCU_M_DATA_WITH_MUX(r_apb0_clk, "r-apb0", r_apb_parents, 0x00c,
+				 0, 5,	/* M */
+				 24, 3,	/* mux */
+				 0);
+
+static SUNXI_CCU_M_DATA_WITH_MUX(r_apb1_clk, "r-apb1", r_apb_parents, 0x010,
+				 0, 5,	/* M */
+				 24, 3,	/* mux */
+				 0);
+
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer0, "r-timer0", r_apb_parents, 0x100,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),	/* gate */
+				      0);
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer1, "r-timer1", r_apb_parents, 0x104,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),	/* gate */
+				      0);
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer2, "r-timer2", r_apb_parents, 0x108,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),	/* gate */
+				      0);
+static SUNXI_CCU_P_DATA_WITH_MUX_GATE(r_cpu_timer3, "r-timer3", r_apb_parents, 0x10c,
+				      1, 3,	/* P */
+				      4, 3,	/* mux */
+				      BIT(0),	/* gate */
+				      0);
+
+static SUNXI_CCU_GATE_HW(bus_r_timer_clk, "bus-r-timer", &r_ahb_clk.common.hw, 0x11c, BIT(0), 0);
+static SUNXI_CCU_GATE_HW(bus_r_twd_clk, "bus-r-twd", &r_apb0_clk.common.hw, 0x12c, BIT(0), 0);
+
+static const struct clk_parent_data r_pwm_parents[] = {
+	{ .fw_name = "hosc" },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .fw_name = "sys-24m" },
+};
+static SUNXI_CCU_MUX_DATA_WITH_GATE(r_pwm_clk, "r-pwm", r_pwm_parents, 0x130,
+				    24, 2,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_GATE_HW(bus_r_pwm_clk, "bus-r-pwm",
+			 &r_apb0_clk.common.hw, 0x13c, BIT(0), 0);
+
+static const struct clk_parent_data r_spi_parents[] = {
+	{ .fw_name = "hosc" },
+	{ .fw_name = "pll-periph0-200m" },
+	{ .fw_name = "pll-periph0-300m" },
+	{ .fw_name = "pll-periph1-300m" },
+	{ .fw_name = "sys-24m" },
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(r_spi_clk, "r-spi", r_spi_parents, 0x150,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_GATE_HW(bus_r_spi_clk, "bus-r-spi", &r_ahb_clk.common.hw, 0x15c, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_msgbox_clk, "bus-r-msgbox", &r_ahb_clk.common.hw, 0x17c, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_uart0_clk, "bus-r-uart0", &r_apb1_clk.common.hw, 0x18c, BIT(0), 0);
+static SUNXI_CCU_GATE_HW(bus_r_uart1_clk, "bus-r-uart1", &r_apb1_clk.common.hw, 0x18c, BIT(1), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_i2c0_clk, "bus-r-i2c0", &r_apb1_clk.common.hw, 0x19c, BIT(0), 0);
+static SUNXI_CCU_GATE_HW(bus_r_i2c1_clk, "bus-r-i2c1", &r_apb1_clk.common.hw, 0x19c, BIT(1), 0);
+static SUNXI_CCU_GATE_HW(bus_r_i2c2_clk, "bus-r-i2c2", &r_apb1_clk.common.hw, 0x19c, BIT(2), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_ppu_clk, "bus-r-ppu", &r_apb0_clk.common.hw, 0x1ac, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_tzma_clk, "bus-r-tzma", &r_apb0_clk.common.hw, 0x1b0, BIT(0), 0);
+static SUNXI_CCU_GATE_HW(bus_r_cpu_bist_clk, "bus-r-cpu-bist", &r_apb0_clk.common.hw,
+			 0x1bc, BIT(0), 0);
+
+static const struct clk_parent_data r_ir_rx_parents[] = {
+	{ .fw_name = "losc" },
+	{ .fw_name = "hosc" },
+	{ .fw_name = "sys-24m" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(r_ir_rx_clk, "r-ir-rx", r_ir_rx_parents, 0x1c0,
+				      0, 5,	/* M */
+				      24, 2,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+static SUNXI_CCU_GATE_HW(bus_r_ir_rx_clk, "bus-r-ir-rx", &r_apb0_clk.common.hw, 0x1cc, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_rtc_clk, "bus-r-rtc", &r_ahb_clk.common.hw, 0x20c, BIT(0), 0);
+
+static const struct clk_parent_data r_riscv_parents[] = {
+	{ .fw_name = "hosc" },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+};
+static SUNXI_CCU_MUX_DATA_WITH_GATE(r_riscv_clk, "r-riscv", r_riscv_parents, 0x210,
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_GATE_HW(bus_r_riscv_clk, "bus-r-riscv", &r_apb0_clk.common.hw,
+			 0x21c, BIT(0), 0);
+static SUNXI_CCU_GATE_HW(bus_r_riscv_cfg_clk, "bus-r-riscv-cfg", &r_apb0_clk.common.hw,
+			 0x21c, BIT(1), 0);
+
+static SUNXI_CCU_GATE_HW(bus_r_cpucfg_clk, "bus-r-cpucfg", &r_apb0_clk.common.hw,
+			 0x22c, BIT(0), CLK_IS_CRITICAL);
+
+static struct ccu_common *sun60i_a733_r_ccu_clks[] = {
+	&r_ahb_clk.common,
+	&r_apb0_clk.common,
+	&r_apb1_clk.common,
+	&r_cpu_timer0.common,
+	&r_cpu_timer1.common,
+	&r_cpu_timer2.common,
+	&r_cpu_timer3.common,
+	&bus_r_timer_clk.common,
+	&bus_r_twd_clk.common,
+	&r_pwm_clk.common,
+	&bus_r_pwm_clk.common,
+	&r_spi_clk.common,
+	&bus_r_spi_clk.common,
+	&bus_r_msgbox_clk.common,
+	&bus_r_uart0_clk.common,
+	&bus_r_uart1_clk.common,
+	&bus_r_i2c0_clk.common,
+	&bus_r_i2c1_clk.common,
+	&bus_r_i2c2_clk.common,
+	&bus_r_ppu_clk.common,
+	&bus_r_tzma_clk.common,
+	&bus_r_cpu_bist_clk.common,
+	&r_ir_rx_clk.common,
+	&bus_r_ir_rx_clk.common,
+	&bus_r_rtc_clk.common,
+	&r_riscv_clk.common,
+	&bus_r_riscv_clk.common,
+	&bus_r_riscv_cfg_clk.common,
+	&bus_r_cpucfg_clk.common,
+};
+
+static struct clk_hw_onecell_data sun60i_a733_r_hw_clks = {
+	.hws = {
+		[CLK_R_AHB]		= &r_ahb_clk.common.hw,
+		[CLK_R_APB0]		= &r_apb0_clk.common.hw,
+		[CLK_R_APB1]		= &r_apb1_clk.common.hw,
+		[CLK_R_TIMER0]		= &r_cpu_timer0.common.hw,
+		[CLK_R_TIMER1]		= &r_cpu_timer1.common.hw,
+		[CLK_R_TIMER2]		= &r_cpu_timer2.common.hw,
+		[CLK_R_TIMER3]		= &r_cpu_timer3.common.hw,
+		[CLK_BUS_R_TIMER]	= &bus_r_timer_clk.common.hw,
+		[CLK_BUS_R_TWD]		= &bus_r_twd_clk.common.hw,
+		[CLK_R_PWM]		= &r_pwm_clk.common.hw,
+		[CLK_BUS_R_PWM]		= &bus_r_pwm_clk.common.hw,
+		[CLK_R_SPI]		= &r_spi_clk.common.hw,
+		[CLK_BUS_R_SPI]		= &bus_r_spi_clk.common.hw,
+		[CLK_BUS_R_MSGBOX]	= &bus_r_msgbox_clk.common.hw,
+		[CLK_BUS_R_UART0]	= &bus_r_uart0_clk.common.hw,
+		[CLK_BUS_R_UART1]	= &bus_r_uart1_clk.common.hw,
+		[CLK_BUS_R_I2C0]	= &bus_r_i2c0_clk.common.hw,
+		[CLK_BUS_R_I2C1]	= &bus_r_i2c1_clk.common.hw,
+		[CLK_BUS_R_I2C2]	= &bus_r_i2c2_clk.common.hw,
+		[CLK_BUS_R_PPU]		= &bus_r_ppu_clk.common.hw,
+		[CLK_BUS_R_TZMA]	= &bus_r_tzma_clk.common.hw,
+		[CLK_BUS_R_CPU_BIST]	= &bus_r_cpu_bist_clk.common.hw,
+		[CLK_R_IR_RX]		= &r_ir_rx_clk.common.hw,
+		[CLK_BUS_R_IR_RX]	= &bus_r_ir_rx_clk.common.hw,
+		[CLK_BUS_R_RTC]		= &bus_r_rtc_clk.common.hw,
+		[CLK_R_RISCV]		= &r_riscv_clk.common.hw,
+		[CLK_BUS_R_RISCV]	= &bus_r_riscv_clk.common.hw,
+		[CLK_BUS_R_RISCV_CFG]	= &bus_r_riscv_cfg_clk.common.hw,
+		[CLK_BUS_R_CPUCFG]	= &bus_r_cpucfg_clk.common.hw,
+	},
+	.num = CLK_BUS_R_CPUCFG + 1,
+};
+
+static struct ccu_reset_map sun60i_a733_r_ccu_resets[] = {
+	[RST_BUS_R_TIMER]	= { 0x11c, BIT(16) },
+	[RST_BUS_R_PWM]		= { 0x13c, BIT(16) },
+	[RST_BUS_R_SPI]		= { 0x15c, BIT(16) },
+	[RST_BUS_R_MSGBOX]	= { 0x17c, BIT(16) },
+	[RST_BUS_R_UART0]	= { 0x18c, BIT(16) },
+	[RST_BUS_R_UART1]	= { 0x18c, BIT(17) },
+	[RST_BUS_R_I2C0]	= { 0x19c, BIT(16) },
+	[RST_BUS_R_I2C1]	= { 0x19c, BIT(17) },
+	[RST_BUS_R_I2C2]	= { 0x19c, BIT(18) },
+	[RST_BUS_R_IR_RX]	= { 0x1cc, BIT(16) },
+	[RST_BUS_R_RTC]		= { 0x20c, BIT(16) },
+	[RST_BUS_R_RISCV_CFG]	= { 0x21c, BIT(16) },
+	[RST_BUS_R_CPUCFG]	= { 0x22c, BIT(16) },
+};
+
+static const struct sunxi_ccu_desc sun60i_a733_r_ccu_desc = {
+	.ccu_clks	= sun60i_a733_r_ccu_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sun60i_a733_r_ccu_clks),
+
+	.hw_clks	= &sun60i_a733_r_hw_clks,
+
+	.resets		= sun60i_a733_r_ccu_resets,
+	.num_resets	= ARRAY_SIZE(sun60i_a733_r_ccu_resets),
+};
+
+static int sun60i_a733_r_ccu_probe(struct platform_device *pdev)
+{
+	void __iomem *reg;
+
+	reg = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun60i_a733_r_ccu_desc);
+}
+
+static const struct of_device_id sun60i_a733_r_ccu_ids[] = {
+	{ .compatible = "allwinner,sun60i-a733-r-ccu" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun60i_a733_r_ccu_ids);
+
+static struct platform_driver sun60i_a733_r_ccu_driver = {
+	.probe	= sun60i_a733_r_ccu_probe,
+	.driver	= {
+		.name			= "sun60i-a733-r-ccu",
+		.suppress_bind_attrs	= true,
+		.of_match_table		= sun60i_a733_r_ccu_ids,
+	},
+};
+module_platform_driver(sun60i_a733_r_ccu_driver);
+
+MODULE_IMPORT_NS("SUNXI_CCU");
+MODULE_DESCRIPTION("Support for the Allwinner A733 PRCM CCU");
+MODULE_LICENSE("GPL");

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 4/8] clk: sunxi-ng: a733: Add PLL clocks support
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

Add PLL clock support for the main CCU of the Allwinner A733 SoC. The
structure is mostly similar to the sun55i, with the addition of a
PLL_REF clock that normalizes the hardware-detected DCXO/hosc frequency
(19.2MHz, 24MHz, or 26MHz) into a consistent 24MHz reference for all
subsequent PLLs.

The behaviors of PLL_AUDIO0 and PLL_AUDIO1 are ported from the vendor
driver. Specifically, PLL_AUDIO0 is configured with SDM parameters to
provide a 22.5792MHz * 4 output, while PLL_AUDIO1 is integrated into
the main CCU without using SDM.

Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/Kconfig           |   5 +
 drivers/clk/sunxi-ng/Makefile          |   2 +
 drivers/clk/sunxi-ng/ccu-sun60i-a733.c | 539 +++++++++++++++++++++++++++++++++
 3 files changed, 546 insertions(+)

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 202e793dc754..cffa83056934 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU
 	default ARCH_SUNXI
 	depends on ARM64 || COMPILE_TEST
 
+config SUN60I_A733_CCU
+	tristate "Support for the Allwinner A733 CCU"
+	default ARCH_SUNXI
+	depends on ARM64 || COMPILE_TEST
+
 config SUN60I_A733_R_CCU
 	tristate "Support for the Allwinner A733 PRCM CCU"
 	default ARCH_SUNXI
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index d3702bdb7a23..3a39eb9287da 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU)	+= sun50i-h616-ccu.o
 obj-$(CONFIG_SUN55I_A523_CCU)	+= sun55i-a523-ccu.o
 obj-$(CONFIG_SUN55I_A523_MCU_CCU)	+= sun55i-a523-mcu-ccu.o
 obj-$(CONFIG_SUN55I_A523_R_CCU)	+= sun55i-a523-r-ccu.o
+obj-$(CONFIG_SUN60I_A733_CCU)	+= sun60i-a733-ccu.o
 obj-$(CONFIG_SUN60I_A733_R_CCU)	+= sun60i-a733-r-ccu.o
 obj-$(CONFIG_SUN4I_A10_CCU)	+= sun4i-a10-ccu.o
 obj-$(CONFIG_SUN5I_CCU)		+= sun5i-ccu.o
@@ -65,6 +66,7 @@ sun50i-h616-ccu-y		+= ccu-sun50i-h616.o
 sun55i-a523-ccu-y		+= ccu-sun55i-a523.o
 sun55i-a523-mcu-ccu-y		+= ccu-sun55i-a523-mcu.o
 sun55i-a523-r-ccu-y		+= ccu-sun55i-a523-r.o
+sun60i-a733-ccu-y		+= ccu-sun60i-a733.o
 sun60i-a733-r-ccu-y		+= ccu-sun60i-a733-r.o
 sun4i-a10-ccu-y			+= ccu-sun4i-a10.o
 sun5i-ccu-y			+= ccu-sun5i.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
new file mode 100644
index 000000000000..f80db6ab1a98
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023 rengaomin@allwinnertech.com
+ * Copyright (C) 2026 Junhui Liu <junhui.liu@pigmoral.tech>
+ * Based on the A523 CCU driver:
+ *   Copyright (C) 2023-2024 Arm Ltd.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/sun60i-a733-ccu.h>
+#include <dt-bindings/reset/sun60i-a733-ccu.h>
+
+#include "../clk.h"
+
+#include "ccu_common.h"
+
+#include "ccu_div.h"
+#include "ccu_mult.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+
+/*
+ * The DCXO oscillator, the root of most of the clock tree, which may be
+ * 19.2MHz, 24MHz, or 26MHz.
+ */
+static const struct clk_parent_data hosc[] = {
+	{ .fw_name = "hosc" }
+};
+
+/**************************************************************************
+ *                              PLLs                                      *
+ **************************************************************************/
+
+/*
+ * Undocumented, taken from the vendor kernel.
+ * PLL_REF normalizes the DCXO frequency to a 24MHz reference for downstream
+ * PLLs.
+ */
+#define SUN60I_A733_PLL_REF_REG		0x000
+static struct ccu_nkmp pll_ref_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT(8, 8),
+	.m		= _SUNXI_CCU_DIV(16, 7), /* output divider */
+	.p		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_REF_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_DATA("pll-ref", hosc,
+							   &ccu_nkmp_ops,
+							   CLK_SET_RATE_GATE),
+	},
+};
+
+/*
+ * Most clock-defining macros expect an *array* of parent clocks, even if
+ * they do not contain a muxer to select between different parents.
+ * The macros ending in just _HW take a simple clock pointer, but then create
+ * a single-entry array out of that. The macros using _HWS take such an
+ * array (even when it is a single entry one), this avoids having those
+ * helper arrays created inside *every* clock definition.
+ * This means for every clock that is referenced more than once it is
+ * useful to create such a dummy array and use _HWS.
+ */
+static const struct clk_hw *pll_ref_hws[] = {
+	&pll_ref_clk.common.hw
+};
+
+#define SUN60I_A733_PLL_DDR_REG		0x020
+static struct ccu_nkmp pll_ddr_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(20, 3), /* output divider */
+	.p		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_DDR_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-ddr", pll_ref_hws,
+							 &ccu_nkmp_ops,
+							 CLK_SET_RATE_GATE |
+							 CLK_IS_CRITICAL),
+	},
+};
+
+/*
+ * There is no actual clock output with that frequency (2.4 GHz), instead it
+ * has multiple outputs with adjustable dividers from that base frequency.
+ * Model them separately as divider clocks based on that parent here.
+ */
+#define SUN60I_A733_PLL_PERIPH0_REG	0x0a0
+static struct ccu_nm pll_periph0_4x_clk = {
+	.enable		= BIT(25) | BIT(26) | BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_PERIPH0_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-periph0-4x",
+							 pll_ref_hws, &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_periph0_4x_hws[] = {
+	&pll_periph0_4x_clk.common.hw
+};
+
+static SUNXI_CCU_M_HWS(pll_periph0_2x_clk, "pll-periph0-2x", pll_periph0_4x_hws,
+		       SUN60I_A733_PLL_PERIPH0_REG, 20, 3, 0);
+static const struct clk_hw *pll_periph0_2x_hws[] = {
+	&pll_periph0_2x_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_periph0_800M_clk, "pll-periph0-800M", pll_periph0_4x_hws,
+		       SUN60I_A733_PLL_PERIPH0_REG, 16, 3, 0);
+static SUNXI_CCU_M_HWS(pll_periph0_480M_clk, "pll-periph0-480M", pll_periph0_4x_hws,
+		       SUN60I_A733_PLL_PERIPH0_REG, 2, 3, 0);
+static const struct clk_hw *pll_periph0_480M_hws[] = {
+	&pll_periph0_480M_clk.common.hw
+};
+static CLK_FIXED_FACTOR_HWS(pll_periph0_600M_clk, "pll-periph0-600M",
+			    pll_periph0_2x_hws, 2, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph0_400M_clk, "pll-periph0-400M",
+			    pll_periph0_2x_hws, 3, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph0_300M_clk, "pll-periph0-300M",
+			    pll_periph0_2x_hws, 4, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph0_200M_clk, "pll-periph0-200M",
+			    pll_periph0_2x_hws, 6, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph0_150M_clk, "pll-periph0-150M",
+			    pll_periph0_2x_hws, 8, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph0_160M_clk, "pll-periph0-160M",
+			    pll_periph0_480M_hws, 3, 1, 0);
+
+#define SUN60I_A733_PLL_PERIPH1_REG	0x0c0
+static struct ccu_nm pll_periph1_4x_clk = {
+	.enable		= BIT(25) | BIT(26) | BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_PERIPH1_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-periph1-4x",
+							 pll_ref_hws, &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_periph1_4x_hws[] = {
+	&pll_periph1_4x_clk.common.hw
+};
+
+static SUNXI_CCU_M_HWS(pll_periph1_2x_clk, "pll-periph1-2x", pll_periph1_4x_hws,
+		       SUN60I_A733_PLL_PERIPH1_REG, 20, 3, 0);
+static const struct clk_hw *pll_periph1_2x_hws[] = {
+	&pll_periph1_2x_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_periph1_800M_clk, "pll-periph1-800M", pll_periph1_4x_hws,
+		       SUN60I_A733_PLL_PERIPH1_REG, 16, 3, 0);
+static SUNXI_CCU_M_HWS(pll_periph1_480M_clk, "pll-periph1-480M", pll_periph1_4x_hws,
+		       SUN60I_A733_PLL_PERIPH1_REG, 2, 3, 0);
+static const struct clk_hw *pll_periph1_480M_hws[] = {
+	&pll_periph1_480M_clk.common.hw
+};
+static CLK_FIXED_FACTOR_HWS(pll_periph1_600M_clk, "pll-periph1-600M",
+			    pll_periph1_2x_hws, 2, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph1_400M_clk, "pll-periph1-400M",
+			    pll_periph1_2x_hws, 3, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph1_300M_clk, "pll-periph1-300M",
+			    pll_periph1_2x_hws, 4, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph1_200M_clk, "pll-periph1-200M",
+			    pll_periph1_2x_hws, 6, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph1_150M_clk, "pll-periph1-150M",
+			    pll_periph1_2x_hws, 8, 1, 0);
+static CLK_FIXED_FACTOR_HWS(pll_periph1_160M_clk, "pll-periph1-160M",
+			    pll_periph1_480M_hws, 3, 1, 0);
+
+#define SUN60I_A733_PLL_GPU_REG		0x0e0
+static struct ccu_nkmp pll_gpu0_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(20, 3), /* output divider */
+	.p		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_GPU_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-gpu0", pll_ref_hws,
+							 &ccu_nkmp_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+#define SUN60I_A733_PLL_VIDEO0_REG	0x120
+static struct ccu_nm pll_video0_12x_clk = {
+	.enable		= BIT(26) | BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_VIDEO0_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-video0-12x", pll_ref_hws,
+							 &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_video0_12x_hws[] = {
+	&pll_video0_12x_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_video0_4x_clk, "pll-video0-4x", pll_video0_12x_hws,
+		       SUN60I_A733_PLL_VIDEO0_REG, 20, 3, 0);
+static SUNXI_CCU_M_HWS(pll_video0_3x_clk, "pll-video0-3x", pll_video0_12x_hws,
+		       SUN60I_A733_PLL_VIDEO0_REG, 16, 3, 0);
+
+#define SUN60I_A733_PLL_VIDEO1_REG	0x140
+static struct ccu_nm pll_video1_12x_clk = {
+	.enable		= BIT(26) | BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_VIDEO1_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-video1-12x", pll_ref_hws,
+							 &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_video1_12x_hws[] = {
+	&pll_video1_12x_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_video1_4x_clk, "pll-video1-4x", pll_video1_12x_hws,
+		       SUN60I_A733_PLL_VIDEO1_REG, 20, 3, 0);
+static SUNXI_CCU_M_HWS(pll_video1_3x_clk, "pll-video1-3x", pll_video1_12x_hws,
+		       SUN60I_A733_PLL_VIDEO1_REG, 16, 3, 0);
+
+#define SUN60I_A733_PLL_VIDEO2_REG	0x160
+static struct ccu_nm pll_video2_12x_clk = {
+	.enable		= BIT(26) | BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_VIDEO2_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-video2-12x", pll_ref_hws,
+							 &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_video2_12x_hws[] = {
+	&pll_video2_12x_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_video2_4x_clk, "pll-video2-4x", pll_video2_12x_hws,
+		       SUN60I_A733_PLL_VIDEO2_REG, 20, 3, 0);
+static SUNXI_CCU_M_HWS(pll_video2_3x_clk, "pll-video2-3x", pll_video2_12x_hws,
+		       SUN60I_A733_PLL_VIDEO2_REG, 16, 3, 0);
+
+#define SUN60I_A733_PLL_VE0_REG		0x220
+static struct ccu_nkmp pll_ve0_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(20, 3), /* output divider */
+	.p		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_VE0_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-ve0", pll_ref_hws,
+							 &ccu_nkmp_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+#define SUN60I_A733_PLL_VE1_REG		0x240
+static struct ccu_nkmp pll_ve1_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(20, 3), /* output divider */
+	.p		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_VE1_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-ve1", pll_ref_hws,
+							 &ccu_nkmp_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+/*
+ * PLL_AUDIO0 has a m1 divider in addition to the usual N, M factors.
+ * Since we only need some fixed frequency from this PLL (22.5792MHz x 4),
+ * ignore the divider and force it to 1 (encoded as 0), in the probe function
+ * below.
+ * The M factor must be an even number to produce a 50% duty cycle output.
+ */
+#define SUN60I_A733_PLL_AUDIO0_REG	0x260
+static struct ccu_sdm_setting pll_audio0_sdm_table[] = {
+	{ .rate = 90316800, .pattern = 0xa002872b, .m = 20, .n = 75 }, /* 22.5792 * 4 */
+};
+
+static struct ccu_nm pll_audio0_4x_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(16, 7),
+	.sdm		= _SUNXI_CCU_SDM_DUAL_PAT(pll_audio0_sdm_table, 0,
+						  0x268, BIT(31),
+						  0x26c, BIT(27) | BIT(31)),
+	.common		= {
+		.reg		= SUN60I_A733_PLL_AUDIO0_REG,
+		.features	= CCU_FEATURE_SIGMA_DELTA_MOD,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-audio0-4x", pll_ref_hws,
+							 &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+#define SUN60I_A733_PLL_AUDIO1_REG	0x280
+static struct ccu_nm pll_audio1_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1),	/* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_AUDIO1_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-audio1", pll_ref_hws,
+							 &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_audio1_hws[] = {
+	&pll_audio1_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_audio1_div2_clk, "pll-audio1-div2", pll_audio1_hws,
+		       SUN60I_A733_PLL_AUDIO1_REG, 20, 3, 0);
+static SUNXI_CCU_M_HWS(pll_audio1_div5_clk, "pll-audio1-div5", pll_audio1_hws,
+		       SUN60I_A733_PLL_AUDIO1_REG, 16, 3, 0);
+
+#define SUN60I_A733_PLL_NPU_REG		0x2a0
+static struct ccu_nkmp pll_npu_clk = {
+	.enable		= BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(20, 3), /* output divider */
+	.p		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_NPU_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-npu", pll_ref_hws,
+							 &ccu_nkmp_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+#define SUN60I_A733_PLL_DE_REG		0x2e0
+static struct ccu_nm pll_de_12x_clk = {
+	.enable		= BIT(26) | BIT(27),
+	.lock		= BIT(28),
+	.n		= _SUNXI_CCU_MULT_MIN(8, 8, 11),
+	.m		= _SUNXI_CCU_DIV(1, 1), /* input divider */
+	.common		= {
+		.reg		= SUN60I_A733_PLL_DE_REG,
+		.hw.init	= CLK_HW_INIT_PARENTS_HW("pll-de-12x", pll_ref_hws,
+							 &ccu_nm_ops,
+							 CLK_SET_RATE_GATE),
+	},
+};
+
+static const struct clk_hw *pll_de_hws[] = {
+	&pll_de_12x_clk.common.hw
+};
+static SUNXI_CCU_M_HWS(pll_de_4x_clk, "pll-de-4x", pll_de_hws,
+		       SUN60I_A733_PLL_DE_REG, 20, 3, 0);
+static SUNXI_CCU_M_HWS(pll_de_3x_clk, "pll-de-3x", pll_de_hws,
+		       SUN60I_A733_PLL_DE_REG, 16, 3, 0);
+
+/*
+ * Contains all clocks that are controlled by a hardware register. They
+ * have a (sunxi) .common member, which needs to be initialised by the common
+ * sunxi CCU code, to be filled with the MMIO base address and the shared lock.
+ */
+static struct ccu_common *sun60i_a733_ccu_clks[] = {
+	&pll_ref_clk.common,
+	&pll_ddr_clk.common,
+	&pll_periph0_4x_clk.common,
+	&pll_periph0_2x_clk.common,
+	&pll_periph0_800M_clk.common,
+	&pll_periph0_480M_clk.common,
+	&pll_periph1_4x_clk.common,
+	&pll_periph1_2x_clk.common,
+	&pll_periph1_800M_clk.common,
+	&pll_periph1_480M_clk.common,
+	&pll_gpu0_clk.common,
+	&pll_video0_12x_clk.common,
+	&pll_video0_4x_clk.common,
+	&pll_video0_3x_clk.common,
+	&pll_video1_12x_clk.common,
+	&pll_video1_4x_clk.common,
+	&pll_video1_3x_clk.common,
+	&pll_video2_12x_clk.common,
+	&pll_video2_4x_clk.common,
+	&pll_video2_3x_clk.common,
+	&pll_ve0_clk.common,
+	&pll_ve1_clk.common,
+	&pll_audio0_4x_clk.common,
+	&pll_audio1_clk.common,
+	&pll_audio1_div2_clk.common,
+	&pll_audio1_div5_clk.common,
+	&pll_npu_clk.common,
+	&pll_de_12x_clk.common,
+	&pll_de_4x_clk.common,
+	&pll_de_3x_clk.common,
+};
+
+static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
+	.hws	= {
+		[CLK_PLL_REF]		= &pll_ref_clk.common.hw,
+		[CLK_PLL_DDR]		= &pll_ddr_clk.common.hw,
+		[CLK_PLL_PERIPH0_4X]	= &pll_periph0_4x_clk.common.hw,
+		[CLK_PLL_PERIPH0_2X]	= &pll_periph0_2x_clk.common.hw,
+		[CLK_PLL_PERIPH0_800M]	= &pll_periph0_800M_clk.common.hw,
+		[CLK_PLL_PERIPH0_480M]	= &pll_periph0_480M_clk.common.hw,
+		[CLK_PLL_PERIPH0_600M]	= &pll_periph0_600M_clk.hw,
+		[CLK_PLL_PERIPH0_400M]	= &pll_periph0_400M_clk.hw,
+		[CLK_PLL_PERIPH0_300M]	= &pll_periph0_300M_clk.hw,
+		[CLK_PLL_PERIPH0_200M]	= &pll_periph0_200M_clk.hw,
+		[CLK_PLL_PERIPH0_160M]	= &pll_periph0_160M_clk.hw,
+		[CLK_PLL_PERIPH0_150M]	= &pll_periph0_150M_clk.hw,
+		[CLK_PLL_PERIPH1_4X]	= &pll_periph1_4x_clk.common.hw,
+		[CLK_PLL_PERIPH1_2X]	= &pll_periph1_2x_clk.common.hw,
+		[CLK_PLL_PERIPH1_800M]	= &pll_periph1_800M_clk.common.hw,
+		[CLK_PLL_PERIPH1_480M]	= &pll_periph1_480M_clk.common.hw,
+		[CLK_PLL_PERIPH1_600M]	= &pll_periph1_600M_clk.hw,
+		[CLK_PLL_PERIPH1_400M]	= &pll_periph1_400M_clk.hw,
+		[CLK_PLL_PERIPH1_300M]	= &pll_periph1_300M_clk.hw,
+		[CLK_PLL_PERIPH1_200M]	= &pll_periph1_200M_clk.hw,
+		[CLK_PLL_PERIPH1_160M]	= &pll_periph1_160M_clk.hw,
+		[CLK_PLL_PERIPH1_150M]	= &pll_periph1_150M_clk.hw,
+		[CLK_PLL_GPU0]		= &pll_gpu0_clk.common.hw,
+		[CLK_PLL_VIDEO0_12X]	= &pll_video0_12x_clk.common.hw,
+		[CLK_PLL_VIDEO0_4X]	= &pll_video0_4x_clk.common.hw,
+		[CLK_PLL_VIDEO0_3X]	= &pll_video0_3x_clk.common.hw,
+		[CLK_PLL_VIDEO1_12X]	= &pll_video1_12x_clk.common.hw,
+		[CLK_PLL_VIDEO1_4X]	= &pll_video1_4x_clk.common.hw,
+		[CLK_PLL_VIDEO1_3X]	= &pll_video1_3x_clk.common.hw,
+		[CLK_PLL_VIDEO2_12X]	= &pll_video2_12x_clk.common.hw,
+		[CLK_PLL_VIDEO2_4X]	= &pll_video2_4x_clk.common.hw,
+		[CLK_PLL_VIDEO2_3X]	= &pll_video2_3x_clk.common.hw,
+		[CLK_PLL_VE0]		= &pll_ve0_clk.common.hw,
+		[CLK_PLL_VE1]		= &pll_ve1_clk.common.hw,
+		[CLK_PLL_AUDIO0_4X]	= &pll_audio0_4x_clk.common.hw,
+		[CLK_PLL_AUDIO1]	= &pll_audio1_clk.common.hw,
+		[CLK_PLL_AUDIO1_DIV2]	= &pll_audio1_div2_clk.common.hw,
+		[CLK_PLL_AUDIO1_DIV5]	= &pll_audio1_div5_clk.common.hw,
+		[CLK_PLL_NPU]		= &pll_npu_clk.common.hw,
+		[CLK_PLL_DE_12X]	= &pll_de_12x_clk.common.hw,
+		[CLK_PLL_DE_4X]		= &pll_de_4x_clk.common.hw,
+		[CLK_PLL_DE_3X]		= &pll_de_3x_clk.common.hw,
+	},
+	.num	= CLK_FANOUT3 + 1,
+};
+
+static const struct sunxi_ccu_desc sun60i_a733_ccu_desc = {
+	.ccu_clks	= sun60i_a733_ccu_clks,
+	.num_ccu_clks	= ARRAY_SIZE(sun60i_a733_ccu_clks),
+
+	.hw_clks	= &sun60i_a733_hw_clks,
+};
+
+static const u32 pll_regs[] = {
+	SUN60I_A733_PLL_REF_REG,
+	SUN60I_A733_PLL_DDR_REG,
+	SUN60I_A733_PLL_PERIPH0_REG,
+	SUN60I_A733_PLL_PERIPH1_REG,
+	SUN60I_A733_PLL_GPU_REG,
+	SUN60I_A733_PLL_VIDEO0_REG,
+	SUN60I_A733_PLL_VIDEO1_REG,
+	SUN60I_A733_PLL_VIDEO2_REG,
+	SUN60I_A733_PLL_VE0_REG,
+	SUN60I_A733_PLL_VE1_REG,
+	SUN60I_A733_PLL_AUDIO0_REG,
+	SUN60I_A733_PLL_AUDIO1_REG,
+	SUN60I_A733_PLL_NPU_REG,
+	SUN60I_A733_PLL_DE_REG,
+};
+
+static int sun60i_a733_ccu_probe(struct platform_device *pdev)
+{
+	void __iomem *reg;
+	u32 val;
+	int i, ret;
+
+	reg = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(reg))
+		return PTR_ERR(reg);
+
+	/*
+	 * The PLL clock code does not model all bits, for instance it does
+	 * not support a separate enable and gate bit. We present the
+	 * gate bit(27) as the enable bit, but then have to set the
+	 * PLL Enable, LDO Enable, and Lock Enable bits on all PLLs here.
+	 */
+	for (i = 0; i < ARRAY_SIZE(pll_regs); i++) {
+		val = readl(reg + pll_regs[i]);
+		val |= BIT(31) | BIT(30) | BIT(29);
+		writel(val, reg + pll_regs[i]);
+	}
+
+	/* Enforce m1 = 0 for PLL_AUDIO0 */
+	val = readl(reg + SUN60I_A733_PLL_AUDIO0_REG);
+	val &= ~BIT(1);
+	writel(val, reg + SUN60I_A733_PLL_AUDIO0_REG);
+
+	ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun60i_a733_ccu_desc);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct of_device_id sun60i_a733_ccu_ids[] = {
+	{ .compatible = "allwinner,sun60i-a733-ccu" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver sun60i_a733_ccu_driver = {
+	.probe	= sun60i_a733_ccu_probe,
+	.driver	= {
+		.name			= "sun60i-a733-ccu",
+		.suppress_bind_attrs	= true,
+		.of_match_table		= sun60i_a733_ccu_ids,
+	},
+};
+module_platform_driver(sun60i_a733_ccu_driver);
+
+MODULE_IMPORT_NS("SUNXI_CCU");
+MODULE_DESCRIPTION("Support for the Allwinner A733 CCU");
+MODULE_LICENSE("GPL");

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 5/8] clk: sunxi-ng: a733: Add bus clocks support
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

Add the essential bus clocks in the Allwinner A733 CCU, including AHB,
APB0, APB1, APB_UART, and MBUS. These buses are necessary for many other
functional modules. An additional trace clock is also added as it fall
within the register address range of the bus clocks, even though it is
not strictly bus clocks.

The MBUS clock is marked as critical to ensure the memory bus remains
operational at all times. And the hardware requires an update bit
(bit 27) to be set so that the configuration takes effect and the
updated parameters can be correctly read back for the MBUS clock.

Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/ccu-sun60i-a733.c | 91 ++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
index f80db6ab1a98..c42619afb52e 100644
--- a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
@@ -19,6 +19,7 @@
 #include "ccu_common.h"
 
 #include "ccu_div.h"
+#include "ccu_mp.h"
 #include "ccu_mult.h"
 #include "ccu_nkmp.h"
 #include "ccu_nm.h"
@@ -69,6 +70,16 @@ static const struct clk_hw *pll_ref_hws[] = {
 	&pll_ref_clk.common.hw
 };
 
+/*
+ * There is a non-software-configurable mux selecting between the DCXO and the
+ * PLL_REF in hardware, whose output is fed to the sys-24M clock. Although both
+ * sys-24M and pll-ref are fixed at 24 MHz, define a 1:1 fixed factor clock to
+ * provide logical separation:
+ * - pll-ref is dedicated to feeding other PLLs
+ * - sys-24M serves as reference clock for downstream functional modules
+ */
+static CLK_FIXED_FACTOR_HWS(sys_24M_clk, "sys-24M", pll_ref_hws, 1, 1, 0);
+
 #define SUN60I_A733_PLL_DDR_REG		0x020
 static struct ccu_nkmp pll_ddr_clk = {
 	.enable		= BIT(27),
@@ -375,6 +386,73 @@ static SUNXI_CCU_M_HWS(pll_de_4x_clk, "pll-de-4x", pll_de_hws,
 static SUNXI_CCU_M_HWS(pll_de_3x_clk, "pll-de-3x", pll_de_hws,
 		       SUN60I_A733_PLL_DE_REG, 16, 3, 0);
 
+/**************************************************************************
+ *                           bus clocks                                   *
+ **************************************************************************/
+
+static const struct clk_parent_data ahb_apb_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .hw = &pll_periph0_600M_clk.hw },
+};
+
+static SUNXI_CCU_M_DATA_WITH_MUX(ahb_clk, "ahb", ahb_apb_parents, 0x500,
+				 0, 5,		/* M */
+				 24, 2,		/* mux */
+				 0);
+
+static SUNXI_CCU_M_DATA_WITH_MUX(apb0_clk, "apb0", ahb_apb_parents, 0x510,
+				 0, 5,		/* M */
+				 24, 2,		/* mux */
+				 0);
+
+static SUNXI_CCU_M_DATA_WITH_MUX(apb1_clk, "apb1", ahb_apb_parents, 0x518,
+				 0, 5,		/* M */
+				 24, 2,		/* mux */
+				 0);
+
+static const struct clk_parent_data apb_uart_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .hw = &pll_periph0_600M_clk.hw },
+	{ .hw = &pll_periph0_480M_clk.common.hw },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX(apb_uart_clk, "apb-uart", apb_uart_parents, 0x538,
+				 0, 5,		/* M */
+				 24, 3,		/* mux */
+				 0);
+
+static const struct clk_parent_data trace_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .hw = &pll_periph0_300M_clk.hw },
+	{ .hw = &pll_periph0_400M_clk.hw },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(trace_clk, "trace", trace_parents, 0x540,
+				 0, 5,		/* M */
+				 24, 3,		/* mux */
+				 BIT(31),	/* gate */
+				 0);
+
+static const struct clk_parent_data mbus_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph1_600M_clk.hw },
+	{ .hw = &pll_ddr_clk.common.hw },
+	{ .hw = &pll_periph1_480M_clk.common.hw },
+	{ .hw = &pll_periph1_400M_clk.hw },
+	{ .hw = &pll_npu_clk.common.hw },
+};
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(mbus_clk, "mbus", mbus_parents, 0x588,
+					    0, 5,	/* M */
+					    0, 0,	/* no P */
+					    24, 3,	/* mux */
+					    BIT(31),	/* gate */
+					    CLK_IS_CRITICAL,
+					    CCU_FEATURE_UPDATE_BIT);
+
 /*
  * Contains all clocks that are controlled by a hardware register. They
  * have a (sunxi) .common member, which needs to be initialised by the common
@@ -411,11 +489,18 @@ static struct ccu_common *sun60i_a733_ccu_clks[] = {
 	&pll_de_12x_clk.common,
 	&pll_de_4x_clk.common,
 	&pll_de_3x_clk.common,
+	&ahb_clk.common,
+	&apb0_clk.common,
+	&apb1_clk.common,
+	&apb_uart_clk.common,
+	&trace_clk.common,
+	&mbus_clk.common,
 };
 
 static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
 	.hws	= {
 		[CLK_PLL_REF]		= &pll_ref_clk.common.hw,
+		[CLK_SYS_24M]		= &sys_24M_clk.hw,
 		[CLK_PLL_DDR]		= &pll_ddr_clk.common.hw,
 		[CLK_PLL_PERIPH0_4X]	= &pll_periph0_4x_clk.common.hw,
 		[CLK_PLL_PERIPH0_2X]	= &pll_periph0_2x_clk.common.hw,
@@ -457,6 +542,12 @@ static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
 		[CLK_PLL_DE_12X]	= &pll_de_12x_clk.common.hw,
 		[CLK_PLL_DE_4X]		= &pll_de_4x_clk.common.hw,
 		[CLK_PLL_DE_3X]		= &pll_de_3x_clk.common.hw,
+		[CLK_AHB]		= &ahb_clk.common.hw,
+		[CLK_APB0]		= &apb0_clk.common.hw,
+		[CLK_APB1]		= &apb1_clk.common.hw,
+		[CLK_APB_UART]		= &apb_uart_clk.common.hw,
+		[CLK_TRACE]		= &trace_clk.common.hw,
+		[CLK_MBUS]		= &mbus_clk.common.hw,
 	},
 	.num	= CLK_FANOUT3 + 1,
 };

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 6/8] clk: sunxi-ng: a733: Add mod clocks support
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

Add the module clocks found in the Allwinner A733 SoC, including video,
storage, interfaces and others. While most of these clocks are similar
to those in the A523 SoC, this implementation accounts for changes in
register offsets and introduces support for new modules.

Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/ccu-sun60i-a733.c | 1118 ++++++++++++++++++++++++++++++++
 1 file changed, 1118 insertions(+)

diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
index c42619afb52e..bf26e310f08a 100644
--- a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
@@ -19,8 +19,10 @@
 #include "ccu_common.h"
 
 #include "ccu_div.h"
+#include "ccu_gate.h"
 #include "ccu_mp.h"
 #include "ccu_mult.h"
+#include "ccu_mux.h"
 #include "ccu_nkmp.h"
 #include "ccu_nm.h"
 
@@ -79,6 +81,9 @@ static const struct clk_hw *pll_ref_hws[] = {
  * - sys-24M serves as reference clock for downstream functional modules
  */
 static CLK_FIXED_FACTOR_HWS(sys_24M_clk, "sys-24M", pll_ref_hws, 1, 1, 0);
+static const struct clk_hw *sys_24M_hws[] = {
+	&sys_24M_clk.hw
+};
 
 #define SUN60I_A733_PLL_DDR_REG		0x020
 static struct ccu_nkmp pll_ddr_clk = {
@@ -137,10 +142,16 @@ static CLK_FIXED_FACTOR_HWS(pll_periph0_400M_clk, "pll-periph0-400M",
 			    pll_periph0_2x_hws, 3, 1, 0);
 static CLK_FIXED_FACTOR_HWS(pll_periph0_300M_clk, "pll-periph0-300M",
 			    pll_periph0_2x_hws, 4, 1, 0);
+static const struct clk_hw *pll_periph0_300M_hws[] = {
+	&pll_periph0_300M_clk.hw
+};
 static CLK_FIXED_FACTOR_HWS(pll_periph0_200M_clk, "pll-periph0-200M",
 			    pll_periph0_2x_hws, 6, 1, 0);
 static CLK_FIXED_FACTOR_HWS(pll_periph0_150M_clk, "pll-periph0-150M",
 			    pll_periph0_2x_hws, 8, 1, 0);
+static const struct clk_hw *pll_periph0_150M_hws[] = {
+	&pll_periph0_150M_clk.hw
+};
 static CLK_FIXED_FACTOR_HWS(pll_periph0_160M_clk, "pll-periph0-160M",
 			    pll_periph0_480M_hws, 3, 1, 0);
 
@@ -453,6 +464,923 @@ static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(mbus_clk, "mbus", mbus_parents, 0x58
 					    CLK_IS_CRITICAL,
 					    CCU_FEATURE_UPDATE_BIT);
 
+/**************************************************************************
+ *                          mod clocks                                    *
+ **************************************************************************/
+
+static const struct clk_parent_data timer_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "iosc" },
+	{ .fw_name = "losc" },
+	{ .hw = &pll_periph0_200M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer0_clk, "timer0", timer_parents, 0x800,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer1_clk, "timer1", timer_parents, 0x804,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer2_clk, "timer2", timer_parents, 0x808,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer3_clk, "timer3", timer_parents, 0x80c,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer4_clk, "timer4", timer_parents, 0x810,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer5_clk, "timer5", timer_parents, 0x814,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer6_clk, "timer6", timer_parents, 0x818,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer7_clk, "timer7", timer_parents, 0x81c,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer8_clk, "timer8", timer_parents, 0x820,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer9_clk, "timer9", timer_parents, 0x824,
+				       0, 0,		/* no M */
+				       0, 3,		/* P */
+				       24, 3,		/* mux */
+				       BIT(31),		/* gate */
+				       0);
+
+/* Undocumented, taken from the vendor kernel. */
+static const struct clk_parent_data avs_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_MUX_DATA_WITH_GATE(avs_clk, "avs-clk", avs_parents, 0x880,
+				    24, 3,		/* mux */
+				    BIT(31),		/* gate */
+				    0);
+
+static const struct clk_hw *de_parents[] = {
+	&pll_de_3x_clk.common.hw,
+	&pll_de_4x_clk.common.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_periph0_400M_clk.hw,
+	&pll_periph0_300M_clk.hw,
+	&pll_video0_4x_clk.common.hw,
+	&pll_video2_4x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(de0_clk, "de0", de_parents, 0xa00,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *di_parents[] = {
+	&pll_periph0_600M_clk.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_periph0_400M_clk.hw,
+	&pll_video0_4x_clk.common.hw,
+	&pll_video2_4x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(di_clk, "di", di_parents, 0xa20,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *g2d_parents[] = {
+	&pll_periph0_400M_clk.hw,
+	&pll_periph0_300M_clk.hw,
+	&pll_video0_4x_clk.common.hw,
+	&pll_video1_4x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(g2d_clk, "g2d", g2d_parents, 0xa40,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *eink_parents[] = {
+	&pll_periph0_480M_clk.common.hw,
+	&pll_periph0_400M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(eink_clk, "eink", eink_parents, 0xa60,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *eink_panel_parents[] = {
+	&pll_video0_4x_clk.common.hw,
+	&pll_video0_3x_clk.common.hw,
+	&pll_video1_4x_clk.common.hw,
+	&pll_video1_3x_clk.common.hw,
+	&pll_periph0_300M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(eink_panel_clk, "eink-panel", eink_panel_parents, 0xa64,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *ve_enc_parents[] = {
+	&pll_ve0_clk.common.hw,
+	&pll_ve1_clk.common.hw,
+	&pll_periph0_800M_clk.common.hw,
+	&pll_periph0_600M_clk.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_de_3x_clk.common.hw,
+	&pll_npu_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(ve_enc0_clk, "ve-enc0", ve_enc_parents, 0xa80,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *ve_dec_parents[] = {
+	&pll_ve1_clk.common.hw,
+	&pll_ve0_clk.common.hw,
+	&pll_periph0_800M_clk.common.hw,
+	&pll_periph0_600M_clk.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_de_3x_clk.common.hw,
+	&pll_npu_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(ve_dec0_clk, "ve-dec0", ve_dec_parents, 0xa88,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    CLK_SET_RATE_PARENT);
+
+static const struct clk_hw *ce_parents[] = {
+	&sys_24M_clk.hw,
+	&pll_periph0_400M_clk.hw,
+	&pll_periph0_600M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0xac0,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *npu_parents[] = {
+	&pll_npu_clk.common.hw,
+	&pll_periph0_800M_clk.common.hw,
+	&pll_periph0_600M_clk.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_ve0_clk.common.hw,
+	&pll_ve1_clk.common.hw,
+	&pll_de_3x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(npu_clk, "npu", npu_parents, 0xb00,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+/*
+ * GPU_CLK = ClockSource * ((16 - M) / 16)
+ * Here we use a div_table to select M values that result in integer divisors.
+ */
+static struct clk_div_table gpu_div_table[] = {
+	{ .val = 0, .div = 1 },
+	{ .val = 8, .div = 2 },
+	{ .val = 12, .div = 4 },
+	{ .val = 14, .div = 8 },
+	{ .val = 15, .div = 16 },
+	{ /* sentinel */ },
+};
+static const struct clk_parent_data gpu_parents[] = {
+	{ .hw = &pll_gpu0_clk.common.hw, },
+	{ .hw = &pll_periph0_800M_clk.common.hw, },
+	{ .hw = &pll_periph0_600M_clk.hw, },
+	{ .hw = &pll_periph0_400M_clk.hw, },
+	{ .hw = &pll_periph0_300M_clk.hw, },
+	{ .hw = &pll_periph0_200M_clk.hw, },
+};
+static struct ccu_div gpu0_clk = {
+	.enable		= BIT(31),
+	.div		= _SUNXI_CCU_DIV_TABLE(0, 4, gpu_div_table),
+	.mux		= _SUNXI_CCU_MUX(24, 3),
+	.common		= {
+		.reg		= 0xb20,
+		.features	= CCU_FEATURE_UPDATE_BIT,
+		.hw.init	= CLK_HW_INIT_PARENTS_DATA("gpu0", gpu_parents,
+							   &ccu_div_ops, 0),
+	}
+};
+
+static const struct clk_parent_data dram_parents[] = {
+	{ .hw = &pll_ddr_clk.common.hw, },
+	{ .hw = &pll_periph0_800M_clk.common.hw, },
+	{ .hw = &pll_periph0_600M_clk.hw, },
+	{ .hw = &pll_de_12x_clk.common.hw, },
+	{ .hw = &pll_npu_clk.common.hw, },
+};
+static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(dram0_clk, "dram0", dram_parents, 0xc00,
+					    0, 4,	/* M */
+					    0, 0,	/* no P */
+					    24, 3,	/* mux */
+					    BIT(31),	/* gate */
+					    CLK_IS_CRITICAL,
+					    CCU_FEATURE_UPDATE_BIT);
+
+static const struct clk_parent_data nand_mmc_parents[] = {
+	{ .hw = &sys_24M_clk.hw, },
+	{ .hw = &pll_periph0_400M_clk.hw, },
+	{ .hw = &pll_periph0_300M_clk.hw, },
+	{ .hw = &pll_periph1_400M_clk.hw, },
+	{ .hw = &pll_periph1_300M_clk.hw, },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(nand0_clk0_clk, "nand0-clk0", nand_mmc_parents, 0xc80,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(nand0_clk1_clk, "nand0-clk1", nand_mmc_parents, 0xc84,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc0_clk, "mmc0", nand_mmc_parents, 0xd00,
+					     0, 5,	/* M */
+					     8, 5,	/* P */
+					     24, 3,	/* mux */
+					     BIT(31),	/* gate */
+					     2,		/* post div */
+					     0);
+static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc1_clk, "mmc1", nand_mmc_parents, 0xd10,
+					     0, 5,	/* M */
+					     8, 5,	/* P */
+					     24, 3,	/* mux */
+					     BIT(31),	/* gate */
+					     2,		/* post div */
+					     0);
+
+static const struct clk_parent_data mmc2_mmc3_parents[] = {
+	{ .hw = &sys_24M_clk.hw, },
+	{ .hw = &pll_periph0_800M_clk.common.hw },
+	{ .hw = &pll_periph0_600M_clk.hw },
+	{ .hw = &pll_periph1_800M_clk.common.hw },
+	{ .hw = &pll_periph1_600M_clk.hw },
+};
+static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc2_clk, "mmc2", mmc2_mmc3_parents, 0xd20,
+					     0, 5,	/* M */
+					     8, 5,	/* P */
+					     24, 3,	/* mux */
+					     BIT(31),	/* gate */
+					     2,		/* post div */
+					     0);
+static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc3_clk, "mmc3", mmc2_mmc3_parents, 0xd30,
+					     0, 5,	/* M */
+					     8, 5,	/* P */
+					     24, 3,	/* mux */
+					     BIT(31),	/* gate */
+					     2,		/* post div */
+					     0);
+
+static const struct clk_hw *ufs_axi_parents[] = {
+	&pll_periph0_300M_clk.hw,
+	&pll_periph0_200M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(ufs_axi_clk, "ufs-axi", ufs_axi_parents, 0xd80,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_parent_data ufs_cfg_parents[] = {
+	{ .hw = &pll_periph0_480M_clk.common.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(ufs_cfg_clk, "ufs-cfg", ufs_cfg_parents, 0xd84,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data spi_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_300M_clk.hw },
+	{ .hw = &pll_periph0_200M_clk.hw },
+	{ .hw = &pll_periph1_300M_clk.hw },
+	{ .hw = &pll_periph1_200M_clk.hw },
+	{ .hw = &pll_periph0_480M_clk.common.hw },
+	{ .hw = &pll_periph1_480M_clk.common.hw },
+	{ .fw_name = "hosc"},
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(spi0_clk, "spi0", spi_parents, 0xf00,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_DUALDIV_MUX_GATE(spi1_clk, "spi1", spi_parents, 0xf08,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_DUALDIV_MUX_GATE(spi2_clk, "spi2", spi_parents, 0xf10,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const struct clk_parent_data spif_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_400M_clk.hw },
+	{ .hw = &pll_periph0_300M_clk.hw },
+	{ .hw = &pll_periph1_400M_clk.hw },
+	{ .hw = &pll_periph1_300M_clk.hw },
+	{ .hw = &pll_periph0_160M_clk.hw },
+	{ .hw = &pll_periph1_160M_clk.hw },
+	{ .fw_name = "hosc"},
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(spif_clk, "spif", spif_parents, 0xf18,
+				  0, 5,		/* M */
+				  8, 5,		/* P */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_DUALDIV_MUX_GATE(spi3_clk, "spi3", spi_parents, 0xf20,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+/* Undocumented, taken from the vendor kernel. */
+static SUNXI_CCU_DUALDIV_MUX_GATE(spi4_clk, "spi4", spi_parents, 0xf28,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const struct clk_parent_data gpadc0_24m_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "hosc"},
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(gpadc0_24m_clk, "gpadc0-24m", gpadc0_24m_parents, 0xfc0,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data irrx_parents[] = {
+	{ .fw_name = "losc"},
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "hosc"},
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(irrx_clk, "irrx", irrx_parents, 0x1000,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data irtx_parents[] = {
+	{ .fw_name = "losc"},
+	{ .hw = &pll_periph1_600M_clk.hw },
+	{ .fw_name = "hosc"},
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(irtx_clk, "irtx", irtx_parents, 0x1008,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+/* Undocumented, taken from the vendor kernel. */
+static const struct clk_parent_data sgpio_parents[] = {
+	{ .fw_name = "losc"},
+	{ .hw = &sys_24M_clk.hw },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(sgpio_clk, "sgpio", sgpio_parents, 0x1060,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+/* Undocumented, taken from the vendor kernel. */
+static const struct clk_hw *lpc_parents[] = {
+	&pll_video0_3x_clk.common.hw,
+	&pll_video1_3x_clk.common.hw,
+	&pll_video2_3x_clk.common.hw,
+	&pll_periph0_300M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(lpc_clk, "lpc", lpc_parents, 0x1080,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *i2spcm_parents[] = {
+	&pll_audio0_4x_clk.common.hw,
+	&pll_audio1_div2_clk.common.hw,
+	&pll_audio1_div5_clk.common.hw,
+	&pll_periph0_200M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm0_clk, "i2spcm0", i2spcm_parents, 0x1200,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm1_clk, "i2spcm1", i2spcm_parents, 0x1210,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm2_clk, "i2spcm2", i2spcm_parents, 0x1220,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *i2spcm2_asrc_parents[] = {
+	&pll_audio0_4x_clk.common.hw,
+	&pll_audio1_div2_clk.common.hw,
+	&pll_audio1_div5_clk.common.hw,
+	&pll_periph0_300M_clk.hw,
+	&pll_periph1_300M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm2_asrc_clk, "i2spcm2_asrc", i2spcm2_asrc_parents, 0x1224,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm3_clk, "i2spcm3", i2spcm_parents, 0x1230,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm4_clk, "i2spcm4", i2spcm_parents, 0x1240,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *spdif_tx_parents[] = {
+	&pll_audio0_4x_clk.common.hw,
+	&pll_audio1_div2_clk.common.hw,
+	&pll_audio1_div5_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(spdif_tx_clk, "spdif-tx", spdif_tx_parents, 0x1280,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *spdif_rx_parents[] = {
+	&pll_periph0_200M_clk.hw,
+	&pll_periph0_300M_clk.hw,
+	&pll_periph0_400M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(spdif_rx_clk, "spdif-rx", spdif_rx_parents, 0x1284,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *dmic_parents[] = {
+	&pll_audio0_4x_clk.common.hw,
+	&pll_audio1_div2_clk.common.hw,
+	&pll_audio1_div5_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(dmic_clk, "dmic", dmic_parents, 0x12c0,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+/*
+ * The first parent is a 48 MHz input clock divided by 4. That 48 MHz clock is
+ * a 2x multiplier from pll-ref synchronized by pll-periph0, and is also used by
+ * the OHCI module.
+ */
+static const struct clk_parent_data usb_ohci_parents[] = {
+	{ .hw = &pll_periph0_4x_clk.common.hw },
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+};
+static const struct ccu_mux_fixed_prediv usb_ohci_predivs[] = {
+	{ .index = 0, .div = 200 },
+	{ .index = 1, .div = 2 },
+};
+
+static struct ccu_mux usb_ohci0_clk = {
+	.enable		= BIT(31),
+	.mux		= {
+		.shift		= 24,
+		.width		= 2,
+		.fixed_predivs	= usb_ohci_predivs,
+		.n_predivs	= ARRAY_SIZE(usb_ohci_predivs),
+	},
+	.common		= {
+		.reg		= 0x1300,
+		.features	= CCU_FEATURE_FIXED_PREDIV,
+		.hw.init	= CLK_HW_INIT_PARENTS_DATA("usb-ohci0", usb_ohci_parents,
+							   &ccu_mux_ops, 0),
+	},
+};
+
+static struct ccu_mux usb_ohci1_clk = {
+	.enable		= BIT(31),
+	.mux		= {
+		.shift		= 24,
+		.width		= 2,
+		.fixed_predivs	= usb_ohci_predivs,
+		.n_predivs	= ARRAY_SIZE(usb_ohci_predivs),
+	},
+	.common		= {
+		.reg		= 0x1308,
+		.features	= CCU_FEATURE_FIXED_PREDIV,
+		.hw.init	= CLK_HW_INIT_PARENTS_DATA("usb-ohci1", usb_ohci_parents,
+							   &ccu_mux_ops, 0),
+	},
+};
+
+static const struct clk_parent_data usb01_ref_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_MUX_DATA(usb01_ref_clk, "usb01-ref", usb01_ref_parents, 0x1340, 24, 3, 0);
+
+static SUNXI_CCU_MUX_DATA(usb2_u2_ref_clk, "usb2-u2-ref", usb01_ref_parents, 0x1348, 24, 3, 0);
+
+static const struct clk_parent_data usb2_suspend_parents[] = {
+	{ .fw_name = "losc" },
+	{ .hw = &sys_24M_clk.hw },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(usb2_suspend_clk, "usb2-suspend", usb2_suspend_parents,
+				      0x1350,
+				      0, 5,	/* M */
+				      24, 1,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data usb2_mf_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_300M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(usb2_mf_clk, "usb2-mf", usb2_mf_parents,
+				      0x1354,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data usb2_u3_utmi_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_300M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(usb2_u3_utmi_clk, "usb2-u3-utmi", usb2_u3_utmi_parents,
+				      0x1360,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data usb2_u2_pipe_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_480M_clk.common.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(usb2_u2_pipe_clk, "usb2-u2-pipe", usb2_u2_pipe_parents,
+				      0x1364,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data pcie_aux_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "losc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(pcie_aux_clk, "pcie-aux", pcie_aux_parents, 0x1380,
+				      0, 5,	/* M */
+				      24, 1,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_hw *pcie_axi_slv_parents[] = {
+	&pll_periph0_600M_clk.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_periph0_400M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(pcie_axi_slv_clk, "pcie-axi-slv", pcie_axi_slv_parents, 0x1384,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *serdes_phy_parents[] = {
+	&sys_24M_clk.hw,
+	&pll_periph0_600M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(serdes_phy_clk, "serdes-phy", serdes_phy_parents, 0x13c0,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_parent_data gmac_ptp_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_200M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(gmac_ptp_clk, "gmac-ptp", gmac_ptp_parents, 0x1400,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static SUNXI_CCU_M_HWS_WITH_GATE(gmac0_phy_clk, "gmac0-phy", pll_periph0_150M_hws, 0x1410,
+				 0, 5,		/* M */
+				 BIT(31),	/* gate */
+				 0);
+/* Undocumented, taken from the vendor kernel. */
+static SUNXI_CCU_M_HWS_WITH_GATE(gmac1_phy_clk, "gmac1-phy", pll_periph0_150M_hws, 0x1420,
+				 0, 5,		/* M */
+				 BIT(31),	/* gate */
+				 0);
+
+static const struct clk_hw *tcon_lcd_parents[] = {
+	&pll_video0_4x_clk.common.hw,
+	&pll_video1_4x_clk.common.hw,
+	&pll_video2_4x_clk.common.hw,
+	&pll_periph0_2x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_lcd_parents, 0x1500,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(tcon_lcd1_clk, "tcon-lcd1", tcon_lcd_parents, 0x1508,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+/* Undocumented, taken from the vendor kernel. */
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(tcon_lcd2_clk, "tcon-lcd2", tcon_lcd_parents, 0x1510,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *dsi_parents[] = {
+	&sys_24M_clk.hw,
+	&pll_periph0_200M_clk.hw,
+	&pll_periph0_150M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(dsi0_clk, "dsi0", dsi_parents, 0x1580,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(dsi1_clk, "dsi1", dsi_parents, 0x1588,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *combphy_parents[] = {
+	&pll_video0_4x_clk.common.hw,
+	&pll_video1_4x_clk.common.hw,
+	&pll_video2_4x_clk.common.hw,
+	&pll_periph0_2x_clk.common.hw,
+	&pll_video0_3x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(combphy0_clk, "combphy0", combphy_parents, 0x15c0,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(combphy1_clk, "combphy1", combphy_parents, 0x15c4,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *edp_tv_parents[] = {
+	&pll_video0_4x_clk.common.hw,
+	&pll_video1_4x_clk.common.hw,
+	&pll_video2_4x_clk.common.hw,
+	&pll_periph0_2x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(edp_tv_clk, "edp-tv", edp_tv_parents, 0x1640,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static SUNXI_CCU_GATE_HWS_WITH_PREDIV(hdmi_cec_32k_clk, "hdmi-cec-32k", pll_periph0_2x_hws, 0x1680,
+				      BIT(30),	/* gate */
+				      36621,	/* pre div */
+				      0);
+
+static const struct clk_parent_data hdmi_cec_parents[] = {
+	{ .fw_name = "losc" },
+	{ .hw = &hdmi_cec_32k_clk.common.hw },
+};
+static SUNXI_CCU_MUX_DATA_WITH_GATE(hdmi_cec_clk, "hdmi-cec", hdmi_cec_parents, 0x1680,
+				    24, 1,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_parent_data hdmi_tv_parents[] = {
+	{ .hw = &pll_video0_4x_clk.common.hw },
+	{ .hw = &pll_video1_4x_clk.common.hw },
+	{ .hw = &pll_video2_4x_clk.common.hw },
+	{ .hw = &pll_periph0_2x_clk.common.hw },
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(hdmi_tv_clk, "hdmi-tv", hdmi_tv_parents, 0x1684,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const struct clk_parent_data hdmi_sfr_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_MUX_DATA_WITH_GATE(hdmi_sfr_clk, "hdmi-sfr", hdmi_sfr_parents, 0x1690,
+				    24, 1,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static SUNXI_CCU_GATE_HWS(hdcp_esm_clk, "hdcp-esm", pll_periph0_300M_hws, 0x1694, BIT(31), 0);
+
+static const struct clk_parent_data ledc_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_periph0_600M_clk.hw },
+	{ .fw_name = "hosc" },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(ledc_clk, "ledc", ledc_parents, 0x1700,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static const struct clk_parent_data csi_master_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .hw = &pll_video0_4x_clk.common.hw },
+	{ .hw = &pll_video0_3x_clk.common.hw },
+	{ .hw = &pll_video1_4x_clk.common.hw },
+	{ .hw = &pll_video1_3x_clk.common.hw },
+	{ .hw = &pll_video2_4x_clk.common.hw },
+	{ .hw = &pll_video2_3x_clk.common.hw },
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(csi_master0_clk, "csi_master0", csi_master_parents, 0x1800,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_DUALDIV_MUX_GATE(csi_master1_clk, "csi_master1", csi_master_parents, 0x1804,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+static SUNXI_CCU_DUALDIV_MUX_GATE(csi_master2_clk, "csi_master2", csi_master_parents, 0x1808,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 3,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const struct clk_hw *csi_parents[] = {
+	&pll_video2_4x_clk.common.hw,
+	&pll_de_4x_clk.common.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_periph0_400M_clk.hw,
+	&pll_periph0_600M_clk.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(csi_clk, "csi", csi_parents, 0x1840,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static const struct clk_hw *isp_parents[] = {
+	&pll_video2_4x_clk.common.hw,
+	&pll_periph0_480M_clk.common.hw,
+	&pll_periph0_400M_clk.hw,
+	&pll_periph0_600M_clk.hw,
+	&pll_video0_4x_clk.common.hw,
+	&pll_video1_4x_clk.common.hw,
+};
+static SUNXI_CCU_M_HW_WITH_MUX_GATE(isp_clk, "isp", isp_parents, 0x1860,
+				    0, 5,	/* M */
+				    24, 3,	/* mux */
+				    BIT(31),	/* gate */
+				    0);
+
+static SUNXI_CCU_GATE_DATA(res_dcap_24m_clk, "res-dcap-24m", hosc, 0x1a00, BIT(3), 0);
+
+static const struct clk_parent_data apb2jtag_parents[] = {
+	{ .hw = &sys_24M_clk.hw },
+	{ .fw_name = "losc" },
+	{ .fw_name = "iosc" },
+	{ .hw = &pll_periph0_480M_clk.common.hw },
+	{ .hw = &pll_periph1_480M_clk.common.hw },
+	{ .hw = &pll_periph0_200M_clk.hw },
+	{ .hw = &pll_periph1_200M_clk.hw },
+};
+static SUNXI_CCU_M_DATA_WITH_MUX_GATE(apb2jtag_clk, "apb2jtag", apb2jtag_parents, 0x1c00,
+				      0, 5,	/* M */
+				      24, 3,	/* mux */
+				      BIT(31),	/* gate */
+				      0);
+
+static SUNXI_CCU_GATE_HWS(fanout_24M_clk, "fanout-24M", sys_24M_hws, 0x1f30, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_12M_clk, "fanout-12M", sys_24M_hws, 0x1f30,
+				      BIT(1), 2, 0);
+static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_16M_clk, "fanout-16M", pll_periph0_480M_hws, 0x1f30,
+				      BIT(2), 30, 0);
+static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_25M_clk, "fanout-25M", pll_periph0_2x_hws, 0x1f30,
+				      BIT(3), 48, 0);
+
+static const struct clk_parent_data fanout_27M_parents[] = {
+	{ .hw = &pll_video0_4x_clk.common.hw },
+	{ .hw = &pll_video1_4x_clk.common.hw },
+	{ .hw = &pll_video2_4x_clk.common.hw },
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(fanout_27M_clk, "fanout-27M", fanout_27M_parents, 0x1f34,
+				  0, 5,		/* M */
+				  8, 5,		/* N */
+				  24, 2,	/* mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const struct clk_parent_data fanout_pclk_parents[] = {
+	{ .hw = &apb0_clk.common.hw }
+};
+static SUNXI_CCU_DUALDIV_MUX_GATE(fanout_pclk_clk, "fanout-pclk", fanout_pclk_parents, 0x1f38,
+				  0, 5,		/* M */
+				  5, 5,		/* N */
+				  0, 0,		/* no mux */
+				  BIT(31),	/* gate */
+				  0);
+
+static const struct clk_parent_data fanout_parents[] = {
+	{ .fw_name = "losc-fanout" },
+	{ .hw = &fanout_12M_clk.common.hw, },
+	{ .hw = &fanout_16M_clk.common.hw, },
+	{ .hw = &fanout_24M_clk.common.hw, },
+	{ .hw = &fanout_25M_clk.common.hw, },
+	{ .hw = &fanout_27M_clk.common.hw, },
+	{ .hw = &fanout_pclk_clk.common.hw, },
+};
+static SUNXI_CCU_MUX_DATA_WITH_GATE(fanout0_clk, "fanout0", fanout_parents, 0x1f3c,
+				    0, 3,	/* mux */
+				    BIT(21),	/* gate */
+				    0);
+static SUNXI_CCU_MUX_DATA_WITH_GATE(fanout1_clk, "fanout1", fanout_parents, 0x1f3c,
+				    3, 3,	/* mux */
+				    BIT(22),	/* gate */
+				    0);
+static SUNXI_CCU_MUX_DATA_WITH_GATE(fanout2_clk, "fanout2", fanout_parents, 0x1f3c,
+				    6, 3,	/* mux */
+				    BIT(23),	/* gate */
+				    0);
+static SUNXI_CCU_MUX_DATA_WITH_GATE(fanout3_clk, "fanout3", fanout_parents, 0x1f3c,
+				    9, 3,	/* mux */
+				    BIT(24),	/* gate */
+				    0);
+
 /*
  * Contains all clocks that are controlled by a hardware register. They
  * have a (sunxi) .common member, which needs to be initialised by the common
@@ -495,6 +1423,101 @@ static struct ccu_common *sun60i_a733_ccu_clks[] = {
 	&apb_uart_clk.common,
 	&trace_clk.common,
 	&mbus_clk.common,
+	&timer0_clk.common,
+	&timer1_clk.common,
+	&timer2_clk.common,
+	&timer3_clk.common,
+	&timer4_clk.common,
+	&timer5_clk.common,
+	&timer6_clk.common,
+	&timer7_clk.common,
+	&timer8_clk.common,
+	&timer9_clk.common,
+	&avs_clk.common,
+	&de0_clk.common,
+	&di_clk.common,
+	&g2d_clk.common,
+	&eink_clk.common,
+	&eink_panel_clk.common,
+	&ve_enc0_clk.common,
+	&ve_dec0_clk.common,
+	&ce_clk.common,
+	&npu_clk.common,
+	&gpu0_clk.common,
+	&dram0_clk.common,
+	&nand0_clk0_clk.common,
+	&nand0_clk1_clk.common,
+	&mmc0_clk.common,
+	&mmc1_clk.common,
+	&mmc2_clk.common,
+	&mmc3_clk.common,
+	&ufs_axi_clk.common,
+	&ufs_cfg_clk.common,
+	&spi0_clk.common,
+	&spi1_clk.common,
+	&spi2_clk.common,
+	&spif_clk.common,
+	&spi3_clk.common,
+	&spi4_clk.common,
+	&gpadc0_24m_clk.common,
+	&irrx_clk.common,
+	&irtx_clk.common,
+	&sgpio_clk.common,
+	&lpc_clk.common,
+	&i2spcm0_clk.common,
+	&i2spcm1_clk.common,
+	&i2spcm2_clk.common,
+	&i2spcm2_asrc_clk.common,
+	&i2spcm3_clk.common,
+	&i2spcm4_clk.common,
+	&spdif_tx_clk.common,
+	&spdif_rx_clk.common,
+	&dmic_clk.common,
+	&usb_ohci0_clk.common,
+	&usb_ohci1_clk.common,
+	&usb01_ref_clk.common,
+	&usb2_u2_ref_clk.common,
+	&usb2_suspend_clk.common,
+	&usb2_mf_clk.common,
+	&usb2_u3_utmi_clk.common,
+	&usb2_u2_pipe_clk.common,
+	&pcie_aux_clk.common,
+	&pcie_axi_slv_clk.common,
+	&serdes_phy_clk.common,
+	&gmac_ptp_clk.common,
+	&gmac0_phy_clk.common,
+	&gmac1_phy_clk.common,
+	&tcon_lcd0_clk.common,
+	&tcon_lcd1_clk.common,
+	&tcon_lcd2_clk.common,
+	&dsi0_clk.common,
+	&dsi1_clk.common,
+	&combphy0_clk.common,
+	&combphy1_clk.common,
+	&edp_tv_clk.common,
+	&hdmi_cec_32k_clk.common,
+	&hdmi_cec_clk.common,
+	&hdmi_tv_clk.common,
+	&hdmi_sfr_clk.common,
+	&hdcp_esm_clk.common,
+	&ledc_clk.common,
+	&csi_master0_clk.common,
+	&csi_master1_clk.common,
+	&csi_master2_clk.common,
+	&csi_clk.common,
+	&isp_clk.common,
+	&res_dcap_24m_clk.common,
+	&apb2jtag_clk.common,
+	&fanout_24M_clk.common,
+	&fanout_12M_clk.common,
+	&fanout_16M_clk.common,
+	&fanout_25M_clk.common,
+	&fanout_27M_clk.common,
+	&fanout_pclk_clk.common,
+	&fanout0_clk.common,
+	&fanout1_clk.common,
+	&fanout2_clk.common,
+	&fanout3_clk.common,
 };
 
 static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
@@ -548,6 +1571,101 @@ static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
 		[CLK_APB_UART]		= &apb_uart_clk.common.hw,
 		[CLK_TRACE]		= &trace_clk.common.hw,
 		[CLK_MBUS]		= &mbus_clk.common.hw,
+		[CLK_TIMER0]		= &timer0_clk.common.hw,
+		[CLK_TIMER1]		= &timer1_clk.common.hw,
+		[CLK_TIMER2]		= &timer2_clk.common.hw,
+		[CLK_TIMER3]		= &timer3_clk.common.hw,
+		[CLK_TIMER4]		= &timer4_clk.common.hw,
+		[CLK_TIMER5]		= &timer5_clk.common.hw,
+		[CLK_TIMER6]		= &timer6_clk.common.hw,
+		[CLK_TIMER7]		= &timer7_clk.common.hw,
+		[CLK_TIMER8]		= &timer8_clk.common.hw,
+		[CLK_TIMER9]		= &timer9_clk.common.hw,
+		[CLK_AVS]		= &avs_clk.common.hw,
+		[CLK_DE0]		= &de0_clk.common.hw,
+		[CLK_DI]		= &di_clk.common.hw,
+		[CLK_G2D]		= &g2d_clk.common.hw,
+		[CLK_EINK]		= &eink_clk.common.hw,
+		[CLK_EINK_PANEL]	= &eink_panel_clk.common.hw,
+		[CLK_VE_ENC0]		= &ve_enc0_clk.common.hw,
+		[CLK_VE_DEC0]		= &ve_dec0_clk.common.hw,
+		[CLK_CE]		= &ce_clk.common.hw,
+		[CLK_NPU]		= &npu_clk.common.hw,
+		[CLK_GPU0]		= &gpu0_clk.common.hw,
+		[CLK_DRAM0]		= &dram0_clk.common.hw,
+		[CLK_NAND0_CLK0]	= &nand0_clk0_clk.common.hw,
+		[CLK_NAND0_CLK1]	= &nand0_clk1_clk.common.hw,
+		[CLK_MMC0]		= &mmc0_clk.common.hw,
+		[CLK_MMC1]		= &mmc1_clk.common.hw,
+		[CLK_MMC2]		= &mmc2_clk.common.hw,
+		[CLK_MMC3]		= &mmc3_clk.common.hw,
+		[CLK_UFS_AXI]		= &ufs_axi_clk.common.hw,
+		[CLK_UFS_CFG]		= &ufs_cfg_clk.common.hw,
+		[CLK_SPI0]		= &spi0_clk.common.hw,
+		[CLK_SPI1]		= &spi1_clk.common.hw,
+		[CLK_SPI2]		= &spi2_clk.common.hw,
+		[CLK_SPIF]		= &spif_clk.common.hw,
+		[CLK_SPI3]		= &spi3_clk.common.hw,
+		[CLK_SPI4]		= &spi4_clk.common.hw,
+		[CLK_GPADC0_24M]	= &gpadc0_24m_clk.common.hw,
+		[CLK_IRRX]		= &irrx_clk.common.hw,
+		[CLK_IRTX]		= &irtx_clk.common.hw,
+		[CLK_SGPIO]		= &sgpio_clk.common.hw,
+		[CLK_LPC]		= &lpc_clk.common.hw,
+		[CLK_I2SPCM0]		= &i2spcm0_clk.common.hw,
+		[CLK_I2SPCM1]		= &i2spcm1_clk.common.hw,
+		[CLK_I2SPCM2]		= &i2spcm2_clk.common.hw,
+		[CLK_I2SPCM2_ASRC]	= &i2spcm2_asrc_clk.common.hw,
+		[CLK_I2SPCM3]		= &i2spcm3_clk.common.hw,
+		[CLK_I2SPCM4]		= &i2spcm4_clk.common.hw,
+		[CLK_SPDIF_TX]		= &spdif_tx_clk.common.hw,
+		[CLK_SPDIF_RX]		= &spdif_rx_clk.common.hw,
+		[CLK_DMIC]		= &dmic_clk.common.hw,
+		[CLK_USB_OHCI0]		= &usb_ohci0_clk.common.hw,
+		[CLK_USB_OHCI1]		= &usb_ohci1_clk.common.hw,
+		[CLK_USB01_REF]		= &usb01_ref_clk.common.hw,
+		[CLK_USB2_U2_REF]	= &usb2_u2_ref_clk.common.hw,
+		[CLK_USB2_SUSPEND]	= &usb2_suspend_clk.common.hw,
+		[CLK_USB2_MF]		= &usb2_mf_clk.common.hw,
+		[CLK_USB2_U3_UTMI]	= &usb2_u3_utmi_clk.common.hw,
+		[CLK_USB2_U2_PIPE]	= &usb2_u2_pipe_clk.common.hw,
+		[CLK_PCIE_AUX]		= &pcie_aux_clk.common.hw,
+		[CLK_PCIE_AXI_SLV]	= &pcie_axi_slv_clk.common.hw,
+		[CLK_SERDES_PHY]	= &serdes_phy_clk.common.hw,
+		[CLK_GMAC_PTP]		= &gmac_ptp_clk.common.hw,
+		[CLK_GMAC0_PHY]		= &gmac0_phy_clk.common.hw,
+		[CLK_GMAC1_PHY]		= &gmac1_phy_clk.common.hw,
+		[CLK_TCON_LCD0]		= &tcon_lcd0_clk.common.hw,
+		[CLK_TCON_LCD1]		= &tcon_lcd1_clk.common.hw,
+		[CLK_TCON_LCD2]		= &tcon_lcd2_clk.common.hw,
+		[CLK_DSI0]		= &dsi0_clk.common.hw,
+		[CLK_DSI1]		= &dsi1_clk.common.hw,
+		[CLK_COMBPHY0]		= &combphy0_clk.common.hw,
+		[CLK_COMBPHY1]		= &combphy1_clk.common.hw,
+		[CLK_EDP_TV]		= &edp_tv_clk.common.hw,
+		[CLK_HDMI_CEC_32K]	= &hdmi_cec_32k_clk.common.hw,
+		[CLK_HDMI_CEC]		= &hdmi_cec_clk.common.hw,
+		[CLK_HDMI_TV]		= &hdmi_tv_clk.common.hw,
+		[CLK_HDMI_SFR]		= &hdmi_sfr_clk.common.hw,
+		[CLK_HDCP_ESM]		= &hdcp_esm_clk.common.hw,
+		[CLK_LEDC]		= &ledc_clk.common.hw,
+		[CLK_CSI_MASTER0]	= &csi_master0_clk.common.hw,
+		[CLK_CSI_MASTER1]	= &csi_master1_clk.common.hw,
+		[CLK_CSI_MASTER2]	= &csi_master2_clk.common.hw,
+		[CLK_CSI]		= &csi_clk.common.hw,
+		[CLK_ISP]		= &isp_clk.common.hw,
+		[CLK_RES_DCAP_24M]	= &res_dcap_24m_clk.common.hw,
+		[CLK_APB2JTAG]		= &apb2jtag_clk.common.hw,
+		[CLK_FANOUT_24M]	= &fanout_24M_clk.common.hw,
+		[CLK_FANOUT_12M]	= &fanout_12M_clk.common.hw,
+		[CLK_FANOUT_16M]	= &fanout_16M_clk.common.hw,
+		[CLK_FANOUT_25M]	= &fanout_25M_clk.common.hw,
+		[CLK_FANOUT_27M]	= &fanout_27M_clk.common.hw,
+		[CLK_FANOUT_PCLK]	= &fanout_pclk_clk.common.hw,
+		[CLK_FANOUT0]		= &fanout0_clk.common.hw,
+		[CLK_FANOUT1]		= &fanout1_clk.common.hw,
+		[CLK_FANOUT2]		= &fanout2_clk.common.hw,
+		[CLK_FANOUT3]		= &fanout3_clk.common.hw,
 	},
 	.num	= CLK_FANOUT3 + 1,
 };

-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 7/8] clk: sunxi-ng: a733: Add bus clock gates
From: Junhui Liu @ 2026-07-11  8:10 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran
  Cc: linux-clk, devicetree, linux-arm-kernel, linux-sunxi,
	linux-kernel, linux-riscv, netdev, Jerome Brunet
In-Reply-To: <20260711-a733-clk-v2-0-974d188cbe0c@pigmoral.tech>

Add the bus clock gates that control access to the devices' register
interface on the Allwinner A733 SoC. These clocks are typically
single-bit controls in the BGR registers, covering UARTs, SPI, I2C, and
various multimedia engines. It also includes bus gates for system
components like the IOMMU and MSI-lite interfaces.

Also mark ahb-store and mbus-store clocks as critical, since disabling
either gate breaks access to boot/storage devices such as MMC and SPI
NOR when unused clocks are disabled.

Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/sunxi-ng/ccu-sun60i-a733.c | 488 ++++++++++++++++++++++++++++++++-
 1 file changed, 487 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
index bf26e310f08a..48d7c0395ae8 100644
--- a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
+++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c
@@ -412,16 +412,19 @@ static SUNXI_CCU_M_DATA_WITH_MUX(ahb_clk, "ahb", ahb_apb_parents, 0x500,
 				 0, 5,		/* M */
 				 24, 2,		/* mux */
 				 0);
+static const struct clk_hw *ahb_hws[] = { &ahb_clk.common.hw };
 
 static SUNXI_CCU_M_DATA_WITH_MUX(apb0_clk, "apb0", ahb_apb_parents, 0x510,
 				 0, 5,		/* M */
 				 24, 2,		/* mux */
 				 0);
+static const struct clk_hw *apb0_hws[] = { &apb0_clk.common.hw };
 
 static SUNXI_CCU_M_DATA_WITH_MUX(apb1_clk, "apb1", ahb_apb_parents, 0x518,
 				 0, 5,		/* M */
 				 24, 2,		/* mux */
 				 0);
+static const struct clk_hw *apb1_hws[] = { &apb1_clk.common.hw };
 
 static const struct clk_parent_data apb_uart_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -434,6 +437,9 @@ static SUNXI_CCU_M_DATA_WITH_MUX(apb_uart_clk, "apb-uart", apb_uart_parents, 0x5
 				 0, 5,		/* M */
 				 24, 3,		/* mux */
 				 0);
+static const struct clk_hw *apb_uart_hws[] = {
+	&apb_uart_clk.common.hw
+};
 
 static const struct clk_parent_data trace_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -448,6 +454,8 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(trace_clk, "trace", trace_parents, 0x540,
 				 BIT(31),	/* gate */
 				 0);
 
+static SUNXI_CCU_GATE_DATA(bus_its_pcie0_aclk_clk, "bus-its-pcie0-aclk", hosc, 0x574, BIT(1), 0);
+
 static const struct clk_parent_data mbus_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
 	{ .hw = &pll_periph1_600M_clk.hw },
@@ -463,9 +471,118 @@ static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(mbus_clk, "mbus", mbus_parents, 0x58
 					    BIT(31),	/* gate */
 					    CLK_IS_CRITICAL,
 					    CCU_FEATURE_UPDATE_BIT);
+static const struct clk_hw *mbus_hws[] = { &mbus_clk.common.hw };
+
+static SUNXI_CCU_GATE_HWS(mbus_iommu0_sys_clk, "mbus-iommu0-sys", mbus_hws, 0x58c, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(apb_iommu0_sys_clk, "apb-iommu0-sys", apb0_hws, 0x58c, BIT(1), 0);
+static SUNXI_CCU_GATE_HWS(ahb_iommu0_sys_clk, "ahb-iommu0-sys", ahb_hws, 0x58c, BIT(2), 0);
+
+static SUNXI_CCU_GATE_DATA(bus_msi_lite0_clk, "bus-msi-lite0", hosc, 0x594, BIT(0), 0);
+static SUNXI_CCU_GATE_DATA(bus_msi_lite1_clk, "bus-msi-lite1", hosc, 0x59c, BIT(0), 0);
+static SUNXI_CCU_GATE_DATA(bus_msi_lite2_clk, "bus-msi-lite2", hosc, 0x5a4, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(mbus_iommu1_sys_clk, "mbus-iommu1-sys", mbus_hws, 0x5b4, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(apb_iommu1_sys_clk, "apb_iommu1-sys", apb0_hws, 0x5b4, BIT(1), 0);
+static SUNXI_CCU_GATE_HWS(ahb_iommu1_sys_clk, "ahb_iommu1-sys", ahb_hws, 0x5b4, BIT(2), 0);
+
+static SUNXI_CCU_GATE_HWS(ahb_ve_dec_clk, "ahb-ve-dec", ahb_hws,
+			  0x5c0, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(ahb_ve_enc_clk, "ahb-ve-enc", ahb_hws,
+			  0x5c0, BIT(1), 0);
+static SUNXI_CCU_GATE_HWS(ahb_vid_in_clk, "ahb-vid-in", ahb_hws,
+			  0x5c0, BIT(2), 0);
+static SUNXI_CCU_GATE_HWS(ahb_vid_cout0_clk, "ahb-vid-cout0", ahb_hws,
+			  0x5c0, BIT(3), 0);
+static SUNXI_CCU_GATE_HWS(ahb_vid_cout1_clk, "ahb-vid-cout1", ahb_hws,
+			  0x5c0, BIT(4), 0);
+static SUNXI_CCU_GATE_HWS(ahb_de_clk, "ahb-de", ahb_hws,
+			  0x5c0, BIT(5), 0);
+static SUNXI_CCU_GATE_HWS(ahb_npu_clk, "ahb-npu", ahb_hws,
+			  0x5c0, BIT(6), 0);
+static SUNXI_CCU_GATE_HWS(ahb_gpu0_clk, "ahb-gpu0", ahb_hws,
+			  0x5c0, BIT(7), 0);
+static SUNXI_CCU_GATE_HWS(ahb_serdes_clk, "ahb-serdes", ahb_hws,
+			  0x5c0, BIT(8), 0);
+static SUNXI_CCU_GATE_HWS(ahb_usb_sys_clk, "ahb-usb-sys", ahb_hws,
+			  0x5c0, BIT(9), 0);
+static SUNXI_CCU_GATE_HWS(ahb_msi_lite0_clk, "ahb-msi-lite0", ahb_hws,
+			  0x5c0, BIT(16), 0);
+static SUNXI_CCU_GATE_HWS(ahb_store_clk, "ahb-store", ahb_hws,
+			  0x5c0, BIT(24), CLK_IS_CRITICAL);
+static SUNXI_CCU_GATE_HWS(ahb_cpus_clk, "ahb-cpus", ahb_hws,
+			  0x5c0, BIT(28), 0);
+
+static SUNXI_CCU_GATE_HWS(mbus_iommu0_clk, "mbus-iommu0", mbus_hws,
+			  0x5e0, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(mbus_iommu1_clk, "mbus-iommu1", mbus_hws,
+			  0x5e0, BIT(1), 0);
+static SUNXI_CCU_GATE_HWS(mbus_desys_clk, "mbus-desys", mbus_hws,
+			  0x5e0, BIT(11), 0);
+static SUNXI_CCU_GATE_HWS(mbus_ve_enc0_gate_clk, "mbus-ve-enc0-gate", mbus_hws,
+			  0x5e0, BIT(12), 0);
+static SUNXI_CCU_GATE_HWS(mbus_ve_dec0_gate_clk, "mbus-ve-dec0-gate", mbus_hws,
+			  0x5e0, BIT(14), 0);
+static SUNXI_CCU_GATE_HWS(mbus_gpu0_clk, "mbus-gpu0", mbus_hws,
+			  0x5e0, BIT(16), 0);
+static SUNXI_CCU_GATE_HWS(mbus_npu_clk, "mbus-npu", mbus_hws,
+			  0x5e0, BIT(18), 0);
+static SUNXI_CCU_GATE_HWS(mbus_vid_in_clk, "mbus-vid-in", mbus_hws,
+			  0x5e0, BIT(24), 0);
+static SUNXI_CCU_GATE_HWS(mbus_serdes_clk, "mbus-serdes", mbus_hws,
+			  0x5e0, BIT(28), 0);
+static SUNXI_CCU_GATE_HWS(mbus_msi_lite0_clk, "mbus-msi-lite0", mbus_hws,
+			  0x5e0, BIT(29), 0);
+static SUNXI_CCU_GATE_HWS(mbus_store_clk, "mbus-store", mbus_hws,
+			  0x5e0, BIT(30), CLK_IS_CRITICAL);
+static SUNXI_CCU_GATE_HWS(mbus_msi_lite2_clk, "mbus-msi-lite2", mbus_hws,
+			  0x5e0, BIT(31), 0);
+
+static SUNXI_CCU_GATE_HWS(mbus_dma0_clk, "mbus-dma0", mbus_hws,
+			  0x5e4, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(mbus_ve_enc0_clk, "mbus-ve-enc0", mbus_hws,
+			  0x5e4, BIT(1), 0);
+static SUNXI_CCU_GATE_HWS(mbus_ce_clk, "mbus-ce", mbus_hws,
+			  0x5e4, BIT(2), 0);
+static SUNXI_CCU_GATE_HWS(mbus_dma1_clk, "mbus-dma1", mbus_hws,
+			  0x5e4, BIT(3), 0);
+static SUNXI_CCU_GATE_HWS(mbus_nand_clk, "mbus-nand", mbus_hws,
+			  0x5e4, BIT(5), 0);
+static SUNXI_CCU_GATE_HWS(mbus_csi_clk, "mbus-csi", mbus_hws,
+			  0x5e4, BIT(8), 0);
+static SUNXI_CCU_GATE_HWS(mbus_isp_clk, "mbus-isp", mbus_hws,
+			  0x5e4, BIT(9), 0);
+static SUNXI_CCU_GATE_HWS(mbus_gmac0_clk, "mbus-gmac0", mbus_hws,
+			  0x5e4, BIT(11), 0);
+/* Undocumented, taken from the vendor kernel. */
+static SUNXI_CCU_GATE_HWS(mbus_gmac1_clk, "mbus-gmac1", mbus_hws,
+			  0x5e4, BIT(12), 0);
+static SUNXI_CCU_GATE_HWS(mbus_ve_dec0_clk, "mbus-ve-dec0", mbus_hws,
+			  0x5e4, BIT(18), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_dma0_clk, "bus-dma0", ahb_hws,
+			  0x704, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_dma1_clk, "bus-dma1", ahb_hws,
+			  0x70c, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_spinlock_clk, "bus-spinlock", ahb_hws,
+			  0x724, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_msgbox0_clk, "bus-msgbox0", ahb_hws,
+			  0x744, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_pwm0_clk, "bus-pwm0", apb0_hws,
+			  0x784, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_pwm1_clk, "bus-pwm1", apb0_hws,
+			  0x78c, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_dbg_clk, "bus-dbg", sys_24M_hws,
+			  0x7a4, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_sysdap_clk, "bus-sysdap", apb1_hws,
+			  0x7ac, BIT(0), 0);
 
 /**************************************************************************
- *                          mod clocks                                    *
+ *                          mod clocks with gates                         *
  **************************************************************************/
 
 static const struct clk_parent_data timer_parents[] = {
@@ -535,6 +652,7 @@ static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(timer9_clk, "timer9", timer_parents, 0x82
 				       24, 3,		/* mux */
 				       BIT(31),		/* gate */
 				       0);
+static SUNXI_CCU_GATE_HWS(bus_timer_clk, "bus-timer", ahb_hws, 0x850, BIT(0), 0);
 
 /* Undocumented, taken from the vendor kernel. */
 static const struct clk_parent_data avs_parents[] = {
@@ -560,6 +678,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(de0_clk, "de0", de_parents, 0xa00,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE_HWS(bus_de0_clk, "bus-de0", ahb_hws, 0xa04, BIT(0), 0);
 
 static const struct clk_hw *di_parents[] = {
 	&pll_periph0_600M_clk.hw,
@@ -573,6 +692,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(di_clk, "di", di_parents, 0xa20,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE_HWS(bus_di_clk, "bus-di", ahb_hws, 0xa24, BIT(0), 0);
 
 static const struct clk_hw *g2d_parents[] = {
 	&pll_periph0_400M_clk.hw,
@@ -585,6 +705,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(g2d_clk, "g2d", g2d_parents, 0xa40,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE_HWS(bus_g2d_clk, "bus-g2d", ahb_hws, 0xa44, BIT(0), 0);
 
 static const struct clk_hw *eink_parents[] = {
 	&pll_periph0_480M_clk.common.hw,
@@ -608,6 +729,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(eink_panel_clk, "eink-panel", eink_panel_par
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE_HWS(bus_eink_clk, "bus-eink", ahb_hws, 0xa6c, BIT(0), 0);
 
 static const struct clk_hw *ve_enc_parents[] = {
 	&pll_ve0_clk.common.hw,
@@ -639,6 +761,9 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(ve_dec0_clk, "ve-dec0", ve_dec_parents, 0xa8
 				    BIT(31),	/* gate */
 				    CLK_SET_RATE_PARENT);
 
+static SUNXI_CCU_GATE_HWS(bus_ve_enc0_clk, "bus-ve-enc0", ahb_hws, 0xa8c, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_ve_dec0_clk, "bus-ve-dec0", ahb_hws, 0xa8c, BIT(2), 0);
+
 static const struct clk_hw *ce_parents[] = {
 	&sys_24M_clk.hw,
 	&pll_periph0_400M_clk.hw,
@@ -649,6 +774,8 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0xac0,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_ce_clk, "bus-ce", ahb_hws, 0xac4, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_ce_sys_clk, "bus-ce-sys", ahb_hws, 0xac4, BIT(1), 0);
 
 static const struct clk_hw *npu_parents[] = {
 	&pll_npu_clk.common.hw,
@@ -664,6 +791,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(npu_clk, "npu", npu_parents, 0xb00,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_DATA(bus_npu_clk, "bus-npu", hosc, 0xb04, BIT(0), 0);
 
 /*
  * GPU_CLK = ClockSource * ((16 - M) / 16)
@@ -696,6 +824,7 @@ static struct ccu_div gpu0_clk = {
 							   &ccu_div_ops, 0),
 	}
 };
+static SUNXI_CCU_GATE_HWS(bus_gpu0_clk, "bus-gpu0", ahb_hws, 0xb24, BIT(0), 0);
 
 static const struct clk_parent_data dram_parents[] = {
 	{ .hw = &pll_ddr_clk.common.hw, },
@@ -711,6 +840,8 @@ static SUNXI_CCU_MP_DATA_WITH_MUX_GATE_FEAT(dram0_clk, "dram0", dram_parents, 0x
 					    BIT(31),	/* gate */
 					    CLK_IS_CRITICAL,
 					    CCU_FEATURE_UPDATE_BIT);
+static SUNXI_CCU_GATE_HWS(bus_dram0_clk, "bus-dram0", ahb_hws, 0xc0c,
+			  BIT(0), CLK_IS_CRITICAL);
 
 static const struct clk_parent_data nand_mmc_parents[] = {
 	{ .hw = &sys_24M_clk.hw, },
@@ -729,6 +860,7 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(nand0_clk1_clk, "nand0-clk1", nand_mmc_par
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_HWS(bus_nand0_clk, "bus-nand0", ahb_hws, 0xc8c, BIT(0), 0);
 
 static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc0_clk, "mmc0", nand_mmc_parents, 0xd00,
 					     0, 5,	/* M */
@@ -737,6 +869,8 @@ static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc0_clk, "mmc0", nand_mmc_parents,
 					     BIT(31),	/* gate */
 					     2,		/* post div */
 					     0);
+static SUNXI_CCU_GATE_HWS(bus_mmc0_clk, "bus-mmc0", ahb_hws, 0xd0c, BIT(0), 0);
+
 static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc1_clk, "mmc1", nand_mmc_parents, 0xd10,
 					     0, 5,	/* M */
 					     8, 5,	/* P */
@@ -744,6 +878,7 @@ static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc1_clk, "mmc1", nand_mmc_parents,
 					     BIT(31),	/* gate */
 					     2,		/* post div */
 					     0);
+static SUNXI_CCU_GATE_HWS(bus_mmc1_clk, "bus-mmc1", ahb_hws, 0xd1c, BIT(0), 0);
 
 static const struct clk_parent_data mmc2_mmc3_parents[] = {
 	{ .hw = &sys_24M_clk.hw, },
@@ -759,6 +894,8 @@ static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc2_clk, "mmc2", mmc2_mmc3_parents
 					     BIT(31),	/* gate */
 					     2,		/* post div */
 					     0);
+static SUNXI_CCU_GATE_HWS(bus_mmc2_clk, "bus-mmc2", ahb_hws, 0xd2c, BIT(0), 0);
+
 static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc3_clk, "mmc3", mmc2_mmc3_parents, 0xd30,
 					     0, 5,	/* M */
 					     8, 5,	/* P */
@@ -766,6 +903,7 @@ static SUNXI_CCU_MP_MUX_GATE_POSTDIV_DUALDIV(mmc3_clk, "mmc3", mmc2_mmc3_parents
 					     BIT(31),	/* gate */
 					     2,		/* post div */
 					     0);
+static SUNXI_CCU_GATE_HWS(bus_mmc3_clk, "bus-mmc3", ahb_hws, 0xd3c, BIT(0), 0);
 
 static const struct clk_hw *ufs_axi_parents[] = {
 	&pll_periph0_300M_clk.hw,
@@ -786,6 +924,29 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(ufs_cfg_clk, "ufs-cfg", ufs_cfg_parents, 0
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_DATA(bus_ufs_clk, "bus-ufs", hosc, 0xd8c, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_uart0_clk, "bus-uart0", apb_uart_hws, 0xe00, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_uart1_clk, "bus-uart1", apb_uart_hws, 0xe04, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_uart2_clk, "bus-uart2", apb_uart_hws, 0xe08, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_uart3_clk, "bus-uart3", apb_uart_hws, 0xe0c, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_uart4_clk, "bus-uart4", apb_uart_hws, 0xe10, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_uart5_clk, "bus-uart5", apb_uart_hws, 0xe14, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_uart6_clk, "bus-uart6", apb_uart_hws, 0xe18, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_i2c0_clk, "bus-i2c0", apb1_hws, 0xe80, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c1_clk, "bus-i2c1", apb1_hws, 0xe84, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c2_clk, "bus-i2c2", apb1_hws, 0xe88, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c3_clk, "bus-i2c3", apb1_hws, 0xe8c, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c4_clk, "bus-i2c4", apb1_hws, 0xe90, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c5_clk, "bus-i2c5", apb1_hws, 0xe94, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c6_clk, "bus-i2c6", apb1_hws, 0xe98, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c7_clk, "bus-i2c7", apb1_hws, 0xe9c, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c8_clk, "bus-i2c8", apb1_hws, 0xea0, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c9_clk, "bus-i2c9", apb1_hws, 0xea4, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c10_clk, "bus-i2c10", apb1_hws, 0xea8, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c11_clk, "bus-i2c11", apb1_hws, 0xeac, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_i2c12_clk, "bus-i2c12", apb1_hws, 0xeb0, BIT(0), 0);
 
 static const struct clk_parent_data spi_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -803,18 +964,23 @@ static SUNXI_CCU_DUALDIV_MUX_GATE(spi0_clk, "spi0", spi_parents, 0xf00,
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_spi0_clk, "bus-spi0", ahb_hws, 0xf04, BIT(0), 0);
+
 static SUNXI_CCU_DUALDIV_MUX_GATE(spi1_clk, "spi1", spi_parents, 0xf08,
 				  0, 5,		/* M */
 				  8, 5,		/* N */
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_spi1_clk, "bus-spi1", ahb_hws, 0xf0c, BIT(0), 0);
+
 static SUNXI_CCU_DUALDIV_MUX_GATE(spi2_clk, "spi2", spi_parents, 0xf10,
 				  0, 5,		/* M */
 				  8, 5,		/* N */
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_spi2_clk, "bus-spi2", ahb_hws, 0xf14, BIT(0), 0);
 
 static const struct clk_parent_data spif_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -832,12 +998,16 @@ static SUNXI_CCU_DUALDIV_MUX_GATE(spif_clk, "spif", spif_parents, 0xf18,
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_spif_clk, "bus-spif", ahb_hws, 0xf1c, BIT(0), 0);
+
 static SUNXI_CCU_DUALDIV_MUX_GATE(spi3_clk, "spi3", spi_parents, 0xf20,
 				  0, 5,		/* M */
 				  8, 5,		/* N */
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_spi3_clk, "bus-spi3", ahb_hws, 0xf24, BIT(0), 0);
+
 /* Undocumented, taken from the vendor kernel. */
 static SUNXI_CCU_DUALDIV_MUX_GATE(spi4_clk, "spi4", spi_parents, 0xf28,
 				  0, 5,		/* M */
@@ -845,6 +1015,7 @@ static SUNXI_CCU_DUALDIV_MUX_GATE(spi4_clk, "spi4", spi_parents, 0xf28,
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_spi4_clk, "bus-spi4", ahb_hws, 0xf2c, BIT(0), 0);
 
 static const struct clk_parent_data gpadc0_24m_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -855,6 +1026,9 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(gpadc0_24m_clk, "gpadc0-24m", gpadc0_24m_p
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_HWS(bus_gpadc0_clk, "bus-gpadc0", ahb_hws, 0xfc4, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_ths0_clk, "bus-ths0", apb0_hws, 0xfe4, BIT(0), 0);
 
 static const struct clk_parent_data irrx_parents[] = {
 	{ .fw_name = "losc"},
@@ -866,6 +1040,7 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(irrx_clk, "irrx", irrx_parents, 0x1000,
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_HWS(bus_irrx_clk, "bus-irrx", apb0_hws, 0x1004, BIT(0), 0);
 
 static const struct clk_parent_data irtx_parents[] = {
 	{ .fw_name = "losc"},
@@ -877,6 +1052,9 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(irtx_clk, "irtx", irtx_parents, 0x1008,
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_HWS(bus_irtx_clk, "bus-irtx", apb0_hws, 0x100c, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_lradc_clk, "bus-lradc", apb0_hws, 0x1024, BIT(0), 0);
 
 /* Undocumented, taken from the vendor kernel. */
 static const struct clk_parent_data sgpio_parents[] = {
@@ -888,6 +1066,7 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(sgpio_clk, "sgpio", sgpio_parents, 0x1060,
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_DATA(bus_sgpio_clk, "bus-sgpio", hosc, 0x1064, BIT(0), 0);
 
 /* Undocumented, taken from the vendor kernel. */
 static const struct clk_hw *lpc_parents[] = {
@@ -901,6 +1080,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(lpc_clk, "lpc", lpc_parents, 0x1080,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_DATA(bus_lpc_clk, "bus-lpc", hosc, 0x1084, BIT(0), 0);
 
 static const struct clk_hw *i2spcm_parents[] = {
 	&pll_audio0_4x_clk.common.hw,
@@ -913,11 +1093,15 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm0_clk, "i2spcm0", i2spcm_parents, 0x12
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_DATA(bus_i2spcm0_clk, "bus-i2spcm0", hosc, 0x120c, BIT(0), 0);
+
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm1_clk, "i2spcm1", i2spcm_parents, 0x1210,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_DATA(bus_i2spcm1_clk, "bus-i2spcm1", hosc, 0x121c, BIT(0), 0);
+
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm2_clk, "i2spcm2", i2spcm_parents, 0x1220,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
@@ -936,16 +1120,22 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm2_asrc_clk, "i2spcm2_asrc", i2spcm2_as
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+
+static SUNXI_CCU_GATE_DATA(bus_i2spcm2_clk, "bus-i2spcm2", hosc, 0x122c, BIT(0), 0);
+
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm3_clk, "i2spcm3", i2spcm_parents, 0x1230,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_DATA(bus_i2spcm3_clk, "bus-i2spcm3", hosc, 0x123c, BIT(0), 0);
+
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(i2spcm4_clk, "i2spcm4", i2spcm_parents, 0x1240,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_DATA(bus_i2spcm4_clk, "bus-i2spcm4", hosc, 0x124c, BIT(0), 0);
 
 static const struct clk_hw *spdif_tx_parents[] = {
 	&pll_audio0_4x_clk.common.hw,
@@ -969,6 +1159,8 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(spdif_rx_clk, "spdif-rx", spdif_rx_parents,
 				    BIT(31),	/* gate */
 				    0);
 
+static SUNXI_CCU_GATE_HWS(bus_spdif_clk, "bus-spdif", apb1_hws, 0x128c, BIT(0), 0);
+
 static const struct clk_hw *dmic_parents[] = {
 	&pll_audio0_4x_clk.common.hw,
 	&pll_audio1_div2_clk.common.hw,
@@ -980,6 +1172,8 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(dmic_clk, "dmic", dmic_parents, 0x12c0,
 				    BIT(31),	/* gate */
 				    0);
 
+static SUNXI_CCU_GATE_HWS(bus_dmic_clk, "bus-dmic", apb1_hws, 0x12cc, BIT(0), 0);
+
 /*
  * The first parent is a 48 MHz input clock divided by 4. That 48 MHz clock is
  * a 2x multiplier from pll-ref synchronized by pll-periph0, and is also used by
@@ -1011,6 +1205,9 @@ static struct ccu_mux usb_ohci0_clk = {
 							   &ccu_mux_ops, 0),
 	},
 };
+static SUNXI_CCU_GATE_HWS(bus_ohci0_clk, "bus-ohci0", ahb_hws, 0x1304, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_ehci0_clk, "bus-ehci0", ahb_hws, 0x1304, BIT(4), 0);
+static SUNXI_CCU_GATE_HWS(bus_otg_clk, "bus-otg", ahb_hws, 0x1304, BIT(8), 0);
 
 static struct ccu_mux usb_ohci1_clk = {
 	.enable		= BIT(31),
@@ -1027,6 +1224,8 @@ static struct ccu_mux usb_ohci1_clk = {
 							   &ccu_mux_ops, 0),
 	},
 };
+static SUNXI_CCU_GATE_HWS(bus_ohci1_clk, "bus-ohci1", ahb_hws, 0x130c, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_ehci1_clk, "bus-ehci1", ahb_hws, 0x130c, BIT(4), 0);
 
 static const struct clk_parent_data usb01_ref_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -1129,11 +1328,14 @@ static SUNXI_CCU_M_HWS_WITH_GATE(gmac0_phy_clk, "gmac0-phy", pll_periph0_150M_hw
 				 0, 5,		/* M */
 				 BIT(31),	/* gate */
 				 0);
+static SUNXI_CCU_GATE_HWS(bus_gmac0_clk, "bus-gmac0", ahb_hws, 0x141c, BIT(0), 0);
+
 /* Undocumented, taken from the vendor kernel. */
 static SUNXI_CCU_M_HWS_WITH_GATE(gmac1_phy_clk, "gmac1-phy", pll_periph0_150M_hws, 0x1420,
 				 0, 5,		/* M */
 				 BIT(31),	/* gate */
 				 0);
+static SUNXI_CCU_GATE_HWS(bus_gmac1_clk, "bus-gmac1", ahb_hws, 0x142c, BIT(0), 0);
 
 static const struct clk_hw *tcon_lcd_parents[] = {
 	&pll_video0_4x_clk.common.hw,
@@ -1146,17 +1348,22 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_lcd_parents
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_tcon_lcd0_clk, "bus-tcon-lcd0", ahb_hws, 0x1504, BIT(0), 0);
+
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(tcon_lcd1_clk, "tcon-lcd1", tcon_lcd_parents, 0x1508,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_tcon_lcd1_clk, "bus-tcon-lcd1", ahb_hws, 0x150c, BIT(0), 0);
+
 /* Undocumented, taken from the vendor kernel. */
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(tcon_lcd2_clk, "tcon-lcd2", tcon_lcd_parents, 0x1510,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_tcon_lcd2_clk, "bus-tcon-lcd2", ahb_hws, 0x1514, BIT(0), 0);
 
 static const struct clk_hw *dsi_parents[] = {
 	&sys_24M_clk.hw,
@@ -1168,11 +1375,14 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(dsi0_clk, "dsi0", dsi_parents, 0x1580,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_dsi0_clk, "bus-dsi0", ahb_hws, 0x1584, BIT(0), 0);
+
 static SUNXI_CCU_M_HW_WITH_MUX_GATE(dsi1_clk, "dsi1", dsi_parents, 0x1588,
 				    0, 5,	/* M */
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_dsi1_clk, "bus-dsi1", ahb_hws, 0x158c, BIT(0), 0);
 
 static const struct clk_hw *combphy_parents[] = {
 	&pll_video0_4x_clk.common.hw,
@@ -1192,6 +1402,9 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(combphy1_clk, "combphy1", combphy_parents, 0
 				    BIT(31),	/* gate */
 				    0);
 
+static SUNXI_CCU_GATE_HWS(bus_tcon_tv0_clk, "bus-tcon-tv0", ahb_hws, 0x1604, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_tcon_tv1_clk, "bus-tcon-tv1", ahb_hws, 0x160c, BIT(0), 0);
+
 static const struct clk_hw *edp_tv_parents[] = {
 	&pll_video0_4x_clk.common.hw,
 	&pll_video1_4x_clk.common.hw,
@@ -1203,6 +1416,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(edp_tv_clk, "edp-tv", edp_tv_parents, 0x1640
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_edp_tv_clk, "bus-edp-tv", ahb_hws, 0x164c, BIT(0), 0);
 
 static SUNXI_CCU_GATE_HWS_WITH_PREDIV(hdmi_cec_32k_clk, "hdmi-cec-32k", pll_periph0_2x_hws, 0x1680,
 				      BIT(30),	/* gate */
@@ -1230,6 +1444,7 @@ static SUNXI_CCU_DUALDIV_MUX_GATE(hdmi_tv_clk, "hdmi-tv", hdmi_tv_parents, 0x168
 				  24, 3,	/* mux */
 				  BIT(31),	/* gate */
 				  0);
+static SUNXI_CCU_GATE_HWS(bus_hdmi_tv_clk, "bus-hdmi-tv", ahb_hws, 0x168c, BIT(0), 0);
 
 static const struct clk_parent_data hdmi_sfr_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -1242,6 +1457,9 @@ static SUNXI_CCU_MUX_DATA_WITH_GATE(hdmi_sfr_clk, "hdmi-sfr", hdmi_sfr_parents,
 
 static SUNXI_CCU_GATE_HWS(hdcp_esm_clk, "hdcp-esm", pll_periph0_300M_hws, 0x1694, BIT(31), 0);
 
+static SUNXI_CCU_GATE_HWS(bus_dpss_top0_clk, "bus-dpss-top0", ahb_hws, 0x16c4, BIT(0), 0);
+static SUNXI_CCU_GATE_HWS(bus_dpss_top1_clk, "bus-dpss-top1", ahb_hws, 0x16cc, BIT(0), 0);
+
 static const struct clk_parent_data ledc_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
 	{ .hw = &pll_periph0_600M_clk.hw },
@@ -1252,6 +1470,9 @@ static SUNXI_CCU_M_DATA_WITH_MUX_GATE(ledc_clk, "ledc", ledc_parents, 0x1700,
 				      24, 3,	/* mux */
 				      BIT(31),	/* gate */
 				      0);
+static SUNXI_CCU_GATE_HWS(bus_ledc_clk, "bus-ledc", apb0_hws, 0x1704, BIT(0), 0);
+
+static SUNXI_CCU_GATE_HWS(bus_dsc_clk, "bus-dsc", ahb_hws, 0x1744, BIT(0), 0);
 
 static const struct clk_parent_data csi_master_parents[] = {
 	{ .hw = &sys_24M_clk.hw },
@@ -1293,6 +1514,7 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(csi_clk, "csi", csi_parents, 0x1840,
 				    24, 3,	/* mux */
 				    BIT(31),	/* gate */
 				    0);
+static SUNXI_CCU_GATE_HWS(bus_csi_clk, "bus-csi", ahb_hws, 0x1844, BIT(0), 0);
 
 static const struct clk_hw *isp_parents[] = {
 	&pll_video2_4x_clk.common.hw,
@@ -1422,7 +1644,60 @@ static struct ccu_common *sun60i_a733_ccu_clks[] = {
 	&apb1_clk.common,
 	&apb_uart_clk.common,
 	&trace_clk.common,
+	&bus_its_pcie0_aclk_clk.common,
 	&mbus_clk.common,
+	&mbus_iommu0_sys_clk.common,
+	&apb_iommu0_sys_clk.common,
+	&ahb_iommu0_sys_clk.common,
+	&bus_msi_lite0_clk.common,
+	&bus_msi_lite1_clk.common,
+	&bus_msi_lite2_clk.common,
+	&mbus_iommu1_sys_clk.common,
+	&apb_iommu1_sys_clk.common,
+	&ahb_iommu1_sys_clk.common,
+	&ahb_ve_dec_clk.common,
+	&ahb_ve_enc_clk.common,
+	&ahb_vid_in_clk.common,
+	&ahb_vid_cout0_clk.common,
+	&ahb_vid_cout1_clk.common,
+	&ahb_de_clk.common,
+	&ahb_npu_clk.common,
+	&ahb_gpu0_clk.common,
+	&ahb_serdes_clk.common,
+	&ahb_usb_sys_clk.common,
+	&ahb_msi_lite0_clk.common,
+	&ahb_store_clk.common,
+	&ahb_cpus_clk.common,
+	&mbus_iommu0_clk.common,
+	&mbus_iommu1_clk.common,
+	&mbus_desys_clk.common,
+	&mbus_ve_enc0_gate_clk.common,
+	&mbus_ve_dec0_gate_clk.common,
+	&mbus_gpu0_clk.common,
+	&mbus_npu_clk.common,
+	&mbus_vid_in_clk.common,
+	&mbus_serdes_clk.common,
+	&mbus_msi_lite0_clk.common,
+	&mbus_store_clk.common,
+	&mbus_msi_lite2_clk.common,
+	&mbus_dma0_clk.common,
+	&mbus_ve_enc0_clk.common,
+	&mbus_ce_clk.common,
+	&mbus_dma1_clk.common,
+	&mbus_nand_clk.common,
+	&mbus_csi_clk.common,
+	&mbus_isp_clk.common,
+	&mbus_gmac0_clk.common,
+	&mbus_gmac1_clk.common,
+	&mbus_ve_dec0_clk.common,
+	&bus_dma0_clk.common,
+	&bus_dma1_clk.common,
+	&bus_spinlock_clk.common,
+	&bus_msgbox0_clk.common,
+	&bus_pwm0_clk.common,
+	&bus_pwm1_clk.common,
+	&bus_dbg_clk.common,
+	&bus_sysdap_clk.common,
 	&timer0_clk.common,
 	&timer1_clk.common,
 	&timer2_clk.common,
@@ -1433,48 +1708,111 @@ static struct ccu_common *sun60i_a733_ccu_clks[] = {
 	&timer7_clk.common,
 	&timer8_clk.common,
 	&timer9_clk.common,
+	&bus_timer_clk.common,
 	&avs_clk.common,
 	&de0_clk.common,
+	&bus_de0_clk.common,
 	&di_clk.common,
+	&bus_di_clk.common,
 	&g2d_clk.common,
+	&bus_g2d_clk.common,
 	&eink_clk.common,
 	&eink_panel_clk.common,
+	&bus_eink_clk.common,
 	&ve_enc0_clk.common,
 	&ve_dec0_clk.common,
+	&bus_ve_enc0_clk.common,
+	&bus_ve_dec0_clk.common,
 	&ce_clk.common,
+	&bus_ce_clk.common,
+	&bus_ce_sys_clk.common,
 	&npu_clk.common,
+	&bus_npu_clk.common,
 	&gpu0_clk.common,
+	&bus_gpu0_clk.common,
 	&dram0_clk.common,
+	&bus_dram0_clk.common,
 	&nand0_clk0_clk.common,
 	&nand0_clk1_clk.common,
+	&bus_nand0_clk.common,
 	&mmc0_clk.common,
+	&bus_mmc0_clk.common,
 	&mmc1_clk.common,
+	&bus_mmc1_clk.common,
 	&mmc2_clk.common,
+	&bus_mmc2_clk.common,
 	&mmc3_clk.common,
+	&bus_mmc3_clk.common,
 	&ufs_axi_clk.common,
 	&ufs_cfg_clk.common,
+	&bus_ufs_clk.common,
+	&bus_uart0_clk.common,
+	&bus_uart1_clk.common,
+	&bus_uart2_clk.common,
+	&bus_uart3_clk.common,
+	&bus_uart4_clk.common,
+	&bus_uart5_clk.common,
+	&bus_uart6_clk.common,
+	&bus_i2c0_clk.common,
+	&bus_i2c1_clk.common,
+	&bus_i2c2_clk.common,
+	&bus_i2c3_clk.common,
+	&bus_i2c4_clk.common,
+	&bus_i2c5_clk.common,
+	&bus_i2c6_clk.common,
+	&bus_i2c7_clk.common,
+	&bus_i2c8_clk.common,
+	&bus_i2c9_clk.common,
+	&bus_i2c10_clk.common,
+	&bus_i2c11_clk.common,
+	&bus_i2c12_clk.common,
 	&spi0_clk.common,
+	&bus_spi0_clk.common,
 	&spi1_clk.common,
+	&bus_spi1_clk.common,
 	&spi2_clk.common,
+	&bus_spi2_clk.common,
 	&spif_clk.common,
+	&bus_spif_clk.common,
 	&spi3_clk.common,
+	&bus_spi3_clk.common,
 	&spi4_clk.common,
+	&bus_spi4_clk.common,
 	&gpadc0_24m_clk.common,
+	&bus_gpadc0_clk.common,
+	&bus_ths0_clk.common,
 	&irrx_clk.common,
+	&bus_irrx_clk.common,
 	&irtx_clk.common,
+	&bus_irtx_clk.common,
+	&bus_lradc_clk.common,
 	&sgpio_clk.common,
+	&bus_sgpio_clk.common,
 	&lpc_clk.common,
+	&bus_lpc_clk.common,
 	&i2spcm0_clk.common,
+	&bus_i2spcm0_clk.common,
 	&i2spcm1_clk.common,
+	&bus_i2spcm1_clk.common,
 	&i2spcm2_clk.common,
 	&i2spcm2_asrc_clk.common,
+	&bus_i2spcm2_clk.common,
 	&i2spcm3_clk.common,
+	&bus_i2spcm3_clk.common,
 	&i2spcm4_clk.common,
+	&bus_i2spcm4_clk.common,
 	&spdif_tx_clk.common,
 	&spdif_rx_clk.common,
+	&bus_spdif_clk.common,
 	&dmic_clk.common,
+	&bus_dmic_clk.common,
 	&usb_ohci0_clk.common,
+	&bus_ohci0_clk.common,
+	&bus_ehci0_clk.common,
+	&bus_otg_clk.common,
 	&usb_ohci1_clk.common,
+	&bus_ohci1_clk.common,
+	&bus_ehci1_clk.common,
 	&usb01_ref_clk.common,
 	&usb2_u2_ref_clk.common,
 	&usb2_suspend_clk.common,
@@ -1486,25 +1824,41 @@ static struct ccu_common *sun60i_a733_ccu_clks[] = {
 	&serdes_phy_clk.common,
 	&gmac_ptp_clk.common,
 	&gmac0_phy_clk.common,
+	&bus_gmac0_clk.common,
 	&gmac1_phy_clk.common,
+	&bus_gmac1_clk.common,
 	&tcon_lcd0_clk.common,
+	&bus_tcon_lcd0_clk.common,
 	&tcon_lcd1_clk.common,
+	&bus_tcon_lcd1_clk.common,
 	&tcon_lcd2_clk.common,
+	&bus_tcon_lcd2_clk.common,
 	&dsi0_clk.common,
+	&bus_dsi0_clk.common,
 	&dsi1_clk.common,
+	&bus_dsi1_clk.common,
 	&combphy0_clk.common,
 	&combphy1_clk.common,
+	&bus_tcon_tv0_clk.common,
+	&bus_tcon_tv1_clk.common,
 	&edp_tv_clk.common,
+	&bus_edp_tv_clk.common,
 	&hdmi_cec_32k_clk.common,
 	&hdmi_cec_clk.common,
 	&hdmi_tv_clk.common,
+	&bus_hdmi_tv_clk.common,
 	&hdmi_sfr_clk.common,
 	&hdcp_esm_clk.common,
+	&bus_dpss_top0_clk.common,
+	&bus_dpss_top1_clk.common,
 	&ledc_clk.common,
+	&bus_ledc_clk.common,
+	&bus_dsc_clk.common,
 	&csi_master0_clk.common,
 	&csi_master1_clk.common,
 	&csi_master2_clk.common,
 	&csi_clk.common,
+	&bus_csi_clk.common,
 	&isp_clk.common,
 	&res_dcap_24m_clk.common,
 	&apb2jtag_clk.common,
@@ -1570,7 +1924,60 @@ static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
 		[CLK_APB1]		= &apb1_clk.common.hw,
 		[CLK_APB_UART]		= &apb_uart_clk.common.hw,
 		[CLK_TRACE]		= &trace_clk.common.hw,
+		[CLK_BUS_ITS_PCIE0_ACLK] = &bus_its_pcie0_aclk_clk.common.hw,
 		[CLK_MBUS]		= &mbus_clk.common.hw,
+		[CLK_MBUS_IOMMU0_SYS]	= &mbus_iommu0_sys_clk.common.hw,
+		[CLK_APB_IOMMU0_SYS]	= &apb_iommu0_sys_clk.common.hw,
+		[CLK_AHB_IOMMU0_SYS]	= &ahb_iommu0_sys_clk.common.hw,
+		[CLK_BUS_MSI_LITE0]	= &bus_msi_lite0_clk.common.hw,
+		[CLK_BUS_MSI_LITE1]	= &bus_msi_lite1_clk.common.hw,
+		[CLK_BUS_MSI_LITE2]	= &bus_msi_lite2_clk.common.hw,
+		[CLK_MBUS_IOMMU1_SYS]	= &mbus_iommu1_sys_clk.common.hw,
+		[CLK_APB_IOMMU1_SYS]	= &apb_iommu1_sys_clk.common.hw,
+		[CLK_AHB_IOMMU1_SYS]	= &ahb_iommu1_sys_clk.common.hw,
+		[CLK_AHB_VE_DEC]	= &ahb_ve_dec_clk.common.hw,
+		[CLK_AHB_VE_ENC]	= &ahb_ve_enc_clk.common.hw,
+		[CLK_AHB_VID_IN]	= &ahb_vid_in_clk.common.hw,
+		[CLK_AHB_VID_COUT0]	= &ahb_vid_cout0_clk.common.hw,
+		[CLK_AHB_VID_COUT1]	= &ahb_vid_cout1_clk.common.hw,
+		[CLK_AHB_DE]		= &ahb_de_clk.common.hw,
+		[CLK_AHB_NPU]		= &ahb_npu_clk.common.hw,
+		[CLK_AHB_GPU0]		= &ahb_gpu0_clk.common.hw,
+		[CLK_AHB_SERDES]	= &ahb_serdes_clk.common.hw,
+		[CLK_AHB_USB_SYS]	= &ahb_usb_sys_clk.common.hw,
+		[CLK_AHB_MSI_LITE0]	= &ahb_msi_lite0_clk.common.hw,
+		[CLK_AHB_STORE]		= &ahb_store_clk.common.hw,
+		[CLK_AHB_CPUS]		= &ahb_cpus_clk.common.hw,
+		[CLK_MBUS_IOMMU0]	= &mbus_iommu0_clk.common.hw,
+		[CLK_MBUS_IOMMU1]	= &mbus_iommu1_clk.common.hw,
+		[CLK_MBUS_DESYS]	= &mbus_desys_clk.common.hw,
+		[CLK_MBUS_VE_ENC0_GATE]	= &mbus_ve_enc0_gate_clk.common.hw,
+		[CLK_MBUS_VE_DEC0_GATE]	= &mbus_ve_dec0_gate_clk.common.hw,
+		[CLK_MBUS_GPU0]		= &mbus_gpu0_clk.common.hw,
+		[CLK_MBUS_NPU]		= &mbus_npu_clk.common.hw,
+		[CLK_MBUS_VID_IN]	= &mbus_vid_in_clk.common.hw,
+		[CLK_MBUS_SERDES]	= &mbus_serdes_clk.common.hw,
+		[CLK_MBUS_MSI_LITE0]	= &mbus_msi_lite0_clk.common.hw,
+		[CLK_MBUS_STORE]	= &mbus_store_clk.common.hw,
+		[CLK_MBUS_MSI_LITE2]	= &mbus_msi_lite2_clk.common.hw,
+		[CLK_MBUS_DMA0]		= &mbus_dma0_clk.common.hw,
+		[CLK_MBUS_VE_ENC0]	= &mbus_ve_enc0_clk.common.hw,
+		[CLK_MBUS_CE]		= &mbus_ce_clk.common.hw,
+		[CLK_MBUS_DMA1]		= &mbus_dma1_clk.common.hw,
+		[CLK_MBUS_NAND]		= &mbus_nand_clk.common.hw,
+		[CLK_MBUS_CSI]		= &mbus_csi_clk.common.hw,
+		[CLK_MBUS_ISP]		= &mbus_isp_clk.common.hw,
+		[CLK_MBUS_GMAC0]	= &mbus_gmac0_clk.common.hw,
+		[CLK_MBUS_GMAC1]	= &mbus_gmac1_clk.common.hw,
+		[CLK_MBUS_VE_DEC0]	= &mbus_ve_dec0_clk.common.hw,
+		[CLK_BUS_DMA0]		= &bus_dma0_clk.common.hw,
+		[CLK_BUS_DMA1]		= &bus_dma1_clk.common.hw,
+		[CLK_BUS_SPINLOCK]	= &bus_spinlock_clk.common.hw,
+		[CLK_BUS_MSGBOX0]	= &bus_msgbox0_clk.common.hw,
+		[CLK_BUS_PWM0]		= &bus_pwm0_clk.common.hw,
+		[CLK_BUS_PWM1]		= &bus_pwm1_clk.common.hw,
+		[CLK_BUS_DBG]		= &bus_dbg_clk.common.hw,
+		[CLK_BUS_SYSDAP]	= &bus_sysdap_clk.common.hw,
 		[CLK_TIMER0]		= &timer0_clk.common.hw,
 		[CLK_TIMER1]		= &timer1_clk.common.hw,
 		[CLK_TIMER2]		= &timer2_clk.common.hw,
@@ -1581,48 +1988,111 @@ static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
 		[CLK_TIMER7]		= &timer7_clk.common.hw,
 		[CLK_TIMER8]		= &timer8_clk.common.hw,
 		[CLK_TIMER9]		= &timer9_clk.common.hw,
+		[CLK_BUS_TIMER]		= &bus_timer_clk.common.hw,
 		[CLK_AVS]		= &avs_clk.common.hw,
 		[CLK_DE0]		= &de0_clk.common.hw,
+		[CLK_BUS_DE0]		= &bus_de0_clk.common.hw,
 		[CLK_DI]		= &di_clk.common.hw,
+		[CLK_BUS_DI]		= &bus_di_clk.common.hw,
 		[CLK_G2D]		= &g2d_clk.common.hw,
+		[CLK_BUS_G2D]		= &bus_g2d_clk.common.hw,
 		[CLK_EINK]		= &eink_clk.common.hw,
 		[CLK_EINK_PANEL]	= &eink_panel_clk.common.hw,
+		[CLK_BUS_EINK]		= &bus_eink_clk.common.hw,
 		[CLK_VE_ENC0]		= &ve_enc0_clk.common.hw,
 		[CLK_VE_DEC0]		= &ve_dec0_clk.common.hw,
+		[CLK_BUS_VE_ENC0]	= &bus_ve_enc0_clk.common.hw,
+		[CLK_BUS_VE_DEC0]	= &bus_ve_dec0_clk.common.hw,
 		[CLK_CE]		= &ce_clk.common.hw,
+		[CLK_BUS_CE]		= &bus_ce_clk.common.hw,
+		[CLK_BUS_CE_SYS]	= &bus_ce_sys_clk.common.hw,
 		[CLK_NPU]		= &npu_clk.common.hw,
+		[CLK_BUS_NPU]		= &bus_npu_clk.common.hw,
 		[CLK_GPU0]		= &gpu0_clk.common.hw,
+		[CLK_BUS_GPU0]		= &bus_gpu0_clk.common.hw,
 		[CLK_DRAM0]		= &dram0_clk.common.hw,
+		[CLK_BUS_DRAM0]		= &bus_dram0_clk.common.hw,
 		[CLK_NAND0_CLK0]	= &nand0_clk0_clk.common.hw,
 		[CLK_NAND0_CLK1]	= &nand0_clk1_clk.common.hw,
+		[CLK_BUS_NAND0]		= &bus_nand0_clk.common.hw,
 		[CLK_MMC0]		= &mmc0_clk.common.hw,
+		[CLK_BUS_MMC0]		= &bus_mmc0_clk.common.hw,
 		[CLK_MMC1]		= &mmc1_clk.common.hw,
+		[CLK_BUS_MMC1]		= &bus_mmc1_clk.common.hw,
 		[CLK_MMC2]		= &mmc2_clk.common.hw,
+		[CLK_BUS_MMC2]		= &bus_mmc2_clk.common.hw,
 		[CLK_MMC3]		= &mmc3_clk.common.hw,
+		[CLK_BUS_MMC3]		= &bus_mmc3_clk.common.hw,
 		[CLK_UFS_AXI]		= &ufs_axi_clk.common.hw,
 		[CLK_UFS_CFG]		= &ufs_cfg_clk.common.hw,
+		[CLK_BUS_UFS]		= &bus_ufs_clk.common.hw,
+		[CLK_BUS_UART0]		= &bus_uart0_clk.common.hw,
+		[CLK_BUS_UART1]		= &bus_uart1_clk.common.hw,
+		[CLK_BUS_UART2]		= &bus_uart2_clk.common.hw,
+		[CLK_BUS_UART3]		= &bus_uart3_clk.common.hw,
+		[CLK_BUS_UART4]		= &bus_uart4_clk.common.hw,
+		[CLK_BUS_UART5]		= &bus_uart5_clk.common.hw,
+		[CLK_BUS_UART6]		= &bus_uart6_clk.common.hw,
+		[CLK_BUS_I2C0]		= &bus_i2c0_clk.common.hw,
+		[CLK_BUS_I2C1]		= &bus_i2c1_clk.common.hw,
+		[CLK_BUS_I2C2]		= &bus_i2c2_clk.common.hw,
+		[CLK_BUS_I2C3]		= &bus_i2c3_clk.common.hw,
+		[CLK_BUS_I2C4]		= &bus_i2c4_clk.common.hw,
+		[CLK_BUS_I2C5]		= &bus_i2c5_clk.common.hw,
+		[CLK_BUS_I2C6]		= &bus_i2c6_clk.common.hw,
+		[CLK_BUS_I2C7]		= &bus_i2c7_clk.common.hw,
+		[CLK_BUS_I2C8]		= &bus_i2c8_clk.common.hw,
+		[CLK_BUS_I2C9]		= &bus_i2c9_clk.common.hw,
+		[CLK_BUS_I2C10]		= &bus_i2c10_clk.common.hw,
+		[CLK_BUS_I2C11]		= &bus_i2c11_clk.common.hw,
+		[CLK_BUS_I2C12]		= &bus_i2c12_clk.common.hw,
 		[CLK_SPI0]		= &spi0_clk.common.hw,
+		[CLK_BUS_SPI0]		= &bus_spi0_clk.common.hw,
 		[CLK_SPI1]		= &spi1_clk.common.hw,
+		[CLK_BUS_SPI1]		= &bus_spi1_clk.common.hw,
 		[CLK_SPI2]		= &spi2_clk.common.hw,
+		[CLK_BUS_SPI2]		= &bus_spi2_clk.common.hw,
 		[CLK_SPIF]		= &spif_clk.common.hw,
+		[CLK_BUS_SPIF]		= &bus_spif_clk.common.hw,
 		[CLK_SPI3]		= &spi3_clk.common.hw,
+		[CLK_BUS_SPI3]		= &bus_spi3_clk.common.hw,
 		[CLK_SPI4]		= &spi4_clk.common.hw,
+		[CLK_BUS_SPI4]		= &bus_spi4_clk.common.hw,
 		[CLK_GPADC0_24M]	= &gpadc0_24m_clk.common.hw,
+		[CLK_BUS_GPADC0]	= &bus_gpadc0_clk.common.hw,
+		[CLK_BUS_THS0]		= &bus_ths0_clk.common.hw,
 		[CLK_IRRX]		= &irrx_clk.common.hw,
+		[CLK_BUS_IRRX]		= &bus_irrx_clk.common.hw,
 		[CLK_IRTX]		= &irtx_clk.common.hw,
+		[CLK_BUS_IRTX]		= &bus_irtx_clk.common.hw,
+		[CLK_BUS_LRADC]		= &bus_lradc_clk.common.hw,
 		[CLK_SGPIO]		= &sgpio_clk.common.hw,
+		[CLK_BUS_SGPIO]		= &bus_sgpio_clk.common.hw,
 		[CLK_LPC]		= &lpc_clk.common.hw,
+		[CLK_BUS_LPC]		= &bus_lpc_clk.common.hw,
 		[CLK_I2SPCM0]		= &i2spcm0_clk.common.hw,
+		[CLK_BUS_I2SPCM0]	= &bus_i2spcm0_clk.common.hw,
 		[CLK_I2SPCM1]		= &i2spcm1_clk.common.hw,
+		[CLK_BUS_I2SPCM1]	= &bus_i2spcm1_clk.common.hw,
 		[CLK_I2SPCM2]		= &i2spcm2_clk.common.hw,
 		[CLK_I2SPCM2_ASRC]	= &i2spcm2_asrc_clk.common.hw,
+		[CLK_BUS_I2SPCM2]	= &bus_i2spcm2_clk.common.hw,
 		[CLK_I2SPCM3]		= &i2spcm3_clk.common.hw,
+		[CLK_BUS_I2SPCM3]	= &bus_i2spcm3_clk.common.hw,
 		[CLK_I2SPCM4]		= &i2spcm4_clk.common.hw,
+		[CLK_BUS_I2SPCM4]	= &bus_i2spcm4_clk.common.hw,
 		[CLK_SPDIF_TX]		= &spdif_tx_clk.common.hw,
 		[CLK_SPDIF_RX]		= &spdif_rx_clk.common.hw,
+		[CLK_BUS_SPDIF]		= &bus_spdif_clk.common.hw,
 		[CLK_DMIC]		= &dmic_clk.common.hw,
+		[CLK_BUS_DMIC]		= &bus_dmic_clk.common.hw,
 		[CLK_USB_OHCI0]		= &usb_ohci0_clk.common.hw,
+		[CLK_BUS_OHCI0]		= &bus_ohci0_clk.common.hw,
+		[CLK_BUS_EHCI0]		= &bus_ehci0_clk.common.hw,
+		[CLK_BUS_OTG]		= &bus_otg_clk.common.hw,
 		[CLK_USB_OHCI1]		= &usb_ohci1_clk.common.hw,
+		[CLK_BUS_OHCI1]		= &bus_ohci1_clk.common.hw,
+		[CLK_BUS_EHCI1]		= &bus_ehci1_clk.common.hw,
 		[CLK_USB01_REF]		= &usb01_ref_clk.common.hw,
 		[CLK_USB2_U2_REF]	= &usb2_u2_ref_clk.common.hw,
 		[CLK_USB2_SUSPEND]	= &usb2_suspend_clk.common.hw,
@@ -1634,25 +2104,41 @@ static struct clk_hw_onecell_data sun60i_a733_hw_clks = {
 		[CLK_SERDES_PHY]	= &serdes_phy_clk.common.hw,
 		[CLK_GMAC_PTP]		= &gmac_ptp_clk.common.hw,
 		[CLK_GMAC0_PHY]		= &gmac0_phy_clk.common.hw,
+		[CLK_BUS_GMAC0]		= &bus_gmac0_clk.common.hw,
 		[CLK_GMAC1_PHY]		= &gmac1_phy_clk.common.hw,
+		[CLK_BUS_GMAC1]		= &bus_gmac1_clk.common.hw,
 		[CLK_TCON_LCD0]		= &tcon_lcd0_clk.common.hw,
+		[CLK_BUS_TCON_LCD0]	= &bus_tcon_lcd0_clk.common.hw,
 		[CLK_TCON_LCD1]		= &tcon_lcd1_clk.common.hw,
+		[CLK_BUS_TCON_LCD1]	= &bus_tcon_lcd1_clk.common.hw,
 		[CLK_TCON_LCD2]		= &tcon_lcd2_clk.common.hw,
+		[CLK_BUS_TCON_LCD2]	= &bus_tcon_lcd2_clk.common.hw,
 		[CLK_DSI0]		= &dsi0_clk.common.hw,
+		[CLK_BUS_DSI0]		= &bus_dsi0_clk.common.hw,
 		[CLK_DSI1]		= &dsi1_clk.common.hw,
+		[CLK_BUS_DSI1]		= &bus_dsi1_clk.common.hw,
 		[CLK_COMBPHY0]		= &combphy0_clk.common.hw,
 		[CLK_COMBPHY1]		= &combphy1_clk.common.hw,
+		[CLK_BUS_TCON_TV0]	= &bus_tcon_tv0_clk.common.hw,
+		[CLK_BUS_TCON_TV1]	= &bus_tcon_tv1_clk.common.hw,
 		[CLK_EDP_TV]		= &edp_tv_clk.common.hw,
+		[CLK_BUS_EDP_TV]	= &bus_edp_tv_clk.common.hw,
 		[CLK_HDMI_CEC_32K]	= &hdmi_cec_32k_clk.common.hw,
 		[CLK_HDMI_CEC]		= &hdmi_cec_clk.common.hw,
 		[CLK_HDMI_TV]		= &hdmi_tv_clk.common.hw,
+		[CLK_BUS_HDMI_TV]	= &bus_hdmi_tv_clk.common.hw,
 		[CLK_HDMI_SFR]		= &hdmi_sfr_clk.common.hw,
 		[CLK_HDCP_ESM]		= &hdcp_esm_clk.common.hw,
+		[CLK_BUS_DPSS_TOP0]	= &bus_dpss_top0_clk.common.hw,
+		[CLK_BUS_DPSS_TOP1]	= &bus_dpss_top1_clk.common.hw,
 		[CLK_LEDC]		= &ledc_clk.common.hw,
+		[CLK_BUS_LEDC]		= &bus_ledc_clk.common.hw,
+		[CLK_BUS_DSC]		= &bus_dsc_clk.common.hw,
 		[CLK_CSI_MASTER0]	= &csi_master0_clk.common.hw,
 		[CLK_CSI_MASTER1]	= &csi_master1_clk.common.hw,
 		[CLK_CSI_MASTER2]	= &csi_master2_clk.common.hw,
 		[CLK_CSI]		= &csi_clk.common.hw,
+		[CLK_BUS_CSI]		= &bus_csi_clk.common.hw,
 		[CLK_ISP]		= &isp_clk.common.hw,
 		[CLK_RES_DCAP_24M]	= &res_dcap_24m_clk.common.hw,
 		[CLK_APB2JTAG]		= &apb2jtag_clk.common.hw,

-- 
2.54.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox