Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH net-next v2 9/9] net: dsa: lan9645x: add port statistics
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-9-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: lan9645x: add port statistics
    
Add statistics support for the port counters. Chip registers are 32 bit,
so this unit is responsible maintaining a 64bit software cache, and
updating it frequently to handle overflows in hardware.

> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> index 24a60f40f6b8..9a46c08dad59 100644
> --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
[ ... ]
> @@ -262,6 +264,12 @@ static int lan9645x_setup(struct dsa_switch *ds)
>  	if (!lan9645x->owq)
>  		return -ENOMEM;
>  
> +	err = lan9645x_stats_init(lan9645x);
> +	if (err) {
> +		dev_err(dev, "Failed to init stats.\n");
> +		goto owq_destroy;
> +	}
> +
[ ... ]
> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c
> index 000000000000..3e82a859f173
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_stats.c
[ ... ]
> +static const struct lan9645x_ethtool_stat lan9645x_port_ethtool_stats[] = {
> +	{ "rx_uc",              SCNT_RX_UC },
> +	{ "rx_cat_drop",        SCNT_RX_CAT_DROP },
[ ... ]
> +	{ "tx_uc",              SCNT_TX_UC },
> +	{ "tx_drop",            SCNT_TX_DROP },
[ ... ]
> +};

Is it necessary to duplicate standard networking statistics in the driver
private ethtool output?

The networking subsystem guidelines state that stats possessing a standard UAPI
representation, such as tx_dropped and rx_packets in struct rtnl_link_stats64,
should not be duplicated in the ethtool -S output.

Could these redundant strings be removed since the driver natively supports
them via lan9645x_stats_get_stats64?

[ ... ]
> +static const struct lan9645x_view_stats lan9645x_view_stat_cfgs[] = {
> +	[LAN9645X_STAT_PORTS] = {
> +		.name = "ports",
> +		.type = LAN9645X_STAT_PORTS,
> +		.layout = lan9645x_port_stats_layout,
> +		.num_cnts = ARRAY_SIZE(lan9645x_port_stats_layout),
> +		.num_indexes = NUM_PHYS_PORTS,
> +	},
> +};
[ ... ]
> +static u64 *lan9645x_stats_view_idx_update(struct lan9645x *lan9645x,
> +					   enum lan9645x_view_stat_type vtype,
> +					   int idx)
> +{
> +	struct lan9645x_stats *s = lan9645x->stats;
> +
> +	mutex_lock(&s->hw_lock);
> +	__lan9645x_stats_view_idx_update(lan9645x, vtype, idx);
> +	mutex_unlock(&s->hw_lock);
> +
> +	return STAT_COUNTERS(lan9645x, vtype, idx);
> +}

Does this code overflow the allocated buffer when called for the CPU port?

The stats buffer cnts is allocated using num_indexes, which is NUM_PHYS_PORTS
(9). This covers ports 0 to 8. However, the CPU port is defined as index 9
(CPU_PORT = 9).

When the DSA core invokes stat callbacks like get_ethtool_stats or get_stats64
for the CPU port, the idx argument is 9. While the helper
__lan9645x_stats_view_idx_update skips the hardware read for out-of-bounds
indices, lan9645x_stats_view_idx_update ignores that failure and returns the
result of STAT_COUNTERS(lan9645x, vtype, idx).

This computes a pointer past the end of the cnts array, which the callers then
blindly dereference, potentially leaking kernel memory or causing a crash. Can
bounds checking be added to the callers, or should the buffer be sized to
include the CPU port?

[ ... ]
> +void lan9645x_stats_get_ethtool_stats(struct lan9645x *lan9645x, int port,
> +				      u64 *data)
> +{
> +	struct lan9645x_stats *stats = lan9645x->stats;
> +	u64 *c;
> +	int i;
> +
> +	c = lan9645x_stats_view_idx_update(lan9645x, LAN9645X_STAT_PORTS, port);
> +
> +	spin_lock(&stats->sw_lock);
> +	for (i = 0; i < ARRAY_SIZE(lan9645x_port_ethtool_stats); i++)
> +		*data++ = c[lan9645x_port_ethtool_stats[i].idx];
> +	spin_unlock(&stats->sw_lock);
> +}
[ ... ]
> +/* Called in atomic context */
> +void lan9645x_stats_get_stats64(struct lan9645x *lan9645x, int port,
> +				struct rtnl_link_stats64 *stats)
> +{
> +	struct lan9645x_stats *s = lan9645x->stats;
> +	u64 *c;
> +
> +	c = STAT_COUNTERS(lan9645x, LAN9645X_STAT_PORTS, port);
> +
> +	spin_lock(&s->sw_lock);

Can this code deadlock when a softirq interrupts a process holding the lock?

The sw_lock spinlock protects the 64-bit software stats counters. In
lan9645x_stats_get_stats64, which acts as the ndo_get_stats64 callback, this
lock is acquired using a plain spin_lock. The code comment notes this function
is called in atomic context (such as from NET_RX_SOFTIRQ via dev_get_stats).

Concurrently, process context paths like lan9645x_stats_get_ethtool_stats and
lan9645x_check_stats_work also acquire sw_lock using plain spin_lock. Because
plain spin_lock does not disable softirqs, if a softirq preempts the execution
on a CPU while process context holds sw_lock, the softirq will spin
indefinitely waiting for the lock.

Should the process-context acquisitions use spin_lock_bh instead?

[ ... ]
> +int lan9645x_stats_init(struct lan9645x *lan9645x)
> +{
> +	const struct lan9645x_view_stats *vs;
> +	struct lan9645x_stats *stats;
> +	int err, i;
> +
> +	lan9645x->stats = devm_kzalloc(lan9645x->dev, sizeof(*stats),
> +				       GFP_KERNEL);

Will this memory be leaked when the DSA switch unbinds and rebinds?

The lan9645x_stats_init function allocates lan9645x->stats, vstat->cnts, and
vstat->buf using devm_kzalloc and devm_kcalloc. This function is called from
lan9645x_setup, which is the DSA .setup callback.

The DSA tree lifecycle is independent of the platform device lifecycle. If the
master netdevice is unbound and bound again, the DSA switch's .teardown and
.setup callbacks will be invoked multiple times. Because .teardown cannot free
devres-managed memory, the memory tied to the platform device will be leaked
on every setup cycle until the switch's platform device itself is destroyed.

Could this use standard kzalloc allocations paired with kfree in teardown?

^ permalink raw reply

* Re: [PATCH net-next v2 8/9] net: dsa: lan9645x: add mdb management
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-8-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
Subject: net: dsa: lan9645x: add mdb management

Add support for dsa mdb callbacks. L2 multicast and IP multicast is
handled differently. IP multicast stores the port group mask inline in
the mac table. L2 multicast points to a PGID index, which encodes the
port group mask.

> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c
> new file mode 100644
> index 000000000000..4cf92887a4eb
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mdb.c
> @@ -0,0 +1,391 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (C) 2026 Microchip Technology Inc.
> + */
> +
> +#include "lan9645x_main.h"

[ ... ]

> +static void lan9645x_mdb_encode_mac(unsigned char *mac,
> +				    struct lan9645x_mdb_entry *mdb_entry,
> +				    enum macaccess_entry_type type)
> +{
> +	ether_addr_copy(mac, mdb_entry->mac);
> +
> +	/* The HW encodes the portmask in the high bits of the mac for ip
> +	 * multicast entries, to save on the limited PGID resources.
> +	 *
> +	 * IPv4 Multicast DMAC: 0x01005Exxxxxx
> +	 * IPv6 Multicast DMAC: 0x3333xxxxxxxx
> +	 *
> +	 * which gives us 24 or 16 bits to encode the portmask.
> +	 */
> +	if (type == ENTRYTYPE_MACV4) {
> +		mac[0] = 0;
> +		mac[1] = mdb_entry->ports >> 8;
> +		mac[2] = mdb_entry->ports & 0xff;
> +	} else if (type == ENTRYTYPE_MACV6) {
> +		mac[0] = mdb_entry->ports >> 8;
> +		mac[1] = mdb_entry->ports & 0xff;
> +	}
> +}

Does this code need to mask out BIT(CPU_PORT) from mdb_entry->ports?

For L2 multicast, the CPU port is explicitly stripped from the PGID portmask
to avoid conflicting instructions, since CPU_PORT forwarding is handled by the
cpu_copy flag. Here, the IP multicast portmask is encoded directly into the
MAC address using the full mdb_entry->ports. Could this cause hardware to
receive conflicting instructions (forwarding to port 9 and copying to the CPU
extraction queue), leading to duplicate multicast packets for the CPU?

[ ... ]

> +static struct lan9645x_pgid_entry *
> +lan9645x_mdb_pgid_entry_create(struct lan9645x *lan9645x, u16 ports)
> +{
> +	struct lan9645x_pgid_entry *pgid_entry = NULL;
> +	int index;
> +
> +	for (index = PGID_GP_START; index < PGID_GP_END; index++) {

Is it safe to start the search at PGID_GP_START (defined as CPU_PORT)?

Hardware uses ANA_PGID(DEST_IDX) to forward unicast traffic, and MAC table
entries for the CPU have DEST_IDX = 9. When the first L2 multicast group is
added, it appears this loop will allocate PGID index 9, overwriting the
unicast portmask for the CPU port. Could this cause all unicast traffic
destined for the CPU to be flooded to the external physical ports in the
multicast group instead?

> +		bool used = false;
> +
> +		list_for_each_entry(pgid_entry, &lan9645x->pgid_entries, list) {
> +			if (pgid_entry->index == index) {
> +				used = true;
> +				break;
> +			}
> +		}
> +
> +		if (!used)
> +			return lan9645x_pgid_entry_alloc(lan9645x, index,
> +							 ports);
> +	}
> +
> +	return ERR_PTR(-ENOSPC);
> +}

[ ... ]

> +static int __lan9645x_mdb_add(struct lan9645x *lan9645x, int chip_port,
> +			      const unsigned char addr[ETH_ALEN], u16 vid,
> +			      enum macaccess_entry_type type)
> +{
> +	struct lan9645x_pgid_entry *old_pgid, *new_pgid;
> +	struct lan9645x_mdb_entry *mdb_entry;
> +	unsigned char mac[ETH_ALEN];
> +	int err, pgid_index;
> +	bool cpu_copy;
> +
> +	mdb_entry = lan9645x_mdb_entry_lookup(lan9645x, addr, vid);
> +	if (!mdb_entry) {
> +		mdb_entry = lan9645x_mdb_entry_alloc(lan9645x, addr, vid);
> +		if (IS_ERR(mdb_entry))
> +			return PTR_ERR(mdb_entry);
> +	}
> +
> +	if (mdb_entry->ports & BIT(chip_port))
> +		return 0;
> +
> +	mdb_entry->ports |= BIT(chip_port);
> +
> +	/* Encode mac for IP mc */
> +	lan9645x_mdb_encode_mac(mac, mdb_entry, type);
> +
> +	/* Update PGID ptr for non-IP entries (L2 multicast) */
> +	old_pgid = mdb_entry->pgid;
> +	new_pgid = lan9645x_mdb_pgid_entry_get(lan9645x, mdb_entry, type);
> +	if (IS_ERR(new_pgid)) {
> +		/* Out of PGIDs or mem. Continue forwarding to old port
> +		 * group, or remove if fresh mdb_entry.
> +		 */
> +		mdb_entry->ports &= ~BIT(chip_port);
> +		if (!mdb_entry->ports)
> +			lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry);
> +
> +		return PTR_ERR(new_pgid);
> +	}
> +	mdb_entry->pgid = new_pgid;
> +
> +	cpu_copy = !!(mdb_entry->ports & BIT(CPU_PORT));
> +	pgid_index = lan9645x_mdb_pgid_index(mdb_entry, type);
> +
> +	/* Make sure to write on top of existing entry, so we do not disrupt
> +	 * flowing traffic.
> +	 */
> +	err = lan9645x_mact_learn_cpu_copy(lan9645x, pgid_index, mac,
> +					   mdb_entry->vid, type, cpu_copy);
> +	lan9645x_pgid_entry_put(lan9645x, old_pgid);
> +	return err;
> +}

What happens if lan9645x_mact_learn_cpu_copy() fails and returns an error?

The software MDB state (mdb_entry->ports and mdb_entry->pgid) is updated and
the old PGID reference is dropped via lan9645x_pgid_entry_put() before
verifying if the hardware MAC table update succeeds.

If the hardware update fails, the software state isn't rolled back, leaving
them out of sync. Furthermore, the dropped old_pgid reference could allow the
index to be reallocated to a new multicast group while the hardware still
points to the old PGID index, potentially causing silent traffic cross-talk.

> +
> +static int __lan9645x_mdb_del(struct lan9645x *lan9645x, int chip_port,
> +			      const unsigned char addr[ETH_ALEN], u16 vid,
> +			      enum macaccess_entry_type type)
> +{
> +	struct lan9645x_pgid_entry *old_pgid, *new_pgid;
> +	struct lan9645x_mdb_entry *mdb_entry;
> +	unsigned char mac[ETH_ALEN];
> +	int err, pgid_index;
> +	bool cpu_copy;
> +
> +	mdb_entry = lan9645x_mdb_entry_lookup(lan9645x, addr, vid);
> +	if (!mdb_entry)
> +		return -ENOENT;
> +
> +	if (!(mdb_entry->ports & BIT(chip_port)))
> +		return 0;
> +
> +	mdb_entry->ports &= ~BIT(chip_port);
> +
> +	/* Encode mac for IP mc */
> +	lan9645x_mdb_encode_mac(mac, mdb_entry, type);
> +
> +	/* Update PGID ptr for non-IP entries (L2 multicast) */
> +	old_pgid = mdb_entry->pgid;
> +	new_pgid = lan9645x_mdb_pgid_entry_get(lan9645x, mdb_entry, type);
> +	if (IS_ERR(new_pgid)) {
> +		/* Continue forwarding to old port group. */
> +		mdb_entry->ports |= BIT(chip_port);
> +		return PTR_ERR(new_pgid);
> +	}
> +	mdb_entry->pgid = new_pgid;
> +
> +	if (!mdb_entry->ports) {
> +		lan9645x_mact_forget(lan9645x, mac, mdb_entry->vid, type);
> +		lan9645x_pgid_entry_put(lan9645x, old_pgid);
> +		lan9645x_mdb_entry_dealloc(lan9645x, mdb_entry);
> +		return 0;
> +	}

Similar to the addition path above, if lan9645x_mact_forget() fails, its
return value is ignored, and the software MDB entry and PGID are freed
anyway. This could also lead to hardware/software state desynchronization.

> +
> +	cpu_copy = !!(mdb_entry->ports & BIT(CPU_PORT));
> +	pgid_index = lan9645x_mdb_pgid_index(mdb_entry, type);
> +
> +	err = lan9645x_mact_learn_cpu_copy(lan9645x, pgid_index, mac,
> +					   mdb_entry->vid, type, cpu_copy);
> +	lan9645x_pgid_entry_put(lan9645x, old_pgid);
> +	return err;
> +}

^ permalink raw reply

* Re: [PATCH net-next v2 7/9] net: dsa: lan9645x: add mac table integration
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-7-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: lan9645x: add mac table integration

Add MAC table support, and dsa fdb callback integration. The mactable is
keyed on (vid,mac) and each bucket has 4 slots. A mac table entry typically
points to a PGID index, the first 9 of which represent a front port.

> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
> index 000000000000..6335714dca21
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_mac.c
[ ... ]
> +int lan9645x_mact_entry_add(struct lan9645x *lan9645x, int pgid,
> +			    const unsigned char *mac, u16 vid)
> +{
> +	struct lan9645x_mact_entry *entry;
> +	int err;
> +
> +	mutex_lock(&lan9645x->mact_lock);
> +
> +	/* Users can not move (vid,mac) to a different port, without removing
> +	 * the original entry first. But we overwrite entry in HW, and update
> +	 * software pgid for good measure.
> +	 */
> +	entry = lan9645x_mact_entry_find(lan9645x, mac, vid);
> +	if (entry) {
> +		entry->common.pgid = pgid;
> +		goto mac_learn;
> +	}
> +
> +	entry = lan9645x_mact_entry_alloc(lan9645x, mac, vid, pgid,
> +					  ENTRYTYPE_LOCKED);
> +	if (!entry) {
> +		mutex_unlock(&lan9645x->mact_lock);
> +		return -ENOMEM;
> +	}
> +
> +	list_add_tail(&entry->list, &lan9645x->mac_entries);
> +
> +mac_learn:
> +	err = __lan9645x_mact_learn(lan9645x, pgid, mac, vid, ENTRYTYPE_LOCKED);
> +	if (err)
> +		lan9645x_mact_entry_dealloc(lan9645x, entry);

If a pre-existing entry is updated but the hardware learning command fails,
will this inadvertently delete the valid software entry and cause the hardware
and software tracking to become desynchronized?

> +
> +	mutex_unlock(&lan9645x->mact_lock);
> +	return err;
> +}
> +
> +int lan9645x_mact_entry_del(struct lan9645x *lan9645x, int pgid,
> +			    const unsigned char *mac, u16 vid)
> +{
> +	struct lan9645x_mact_entry *entry;
> +	int err;
> +
> +	mutex_lock(&lan9645x->mact_lock);
> +
> +	entry = lan9645x_mact_entry_find(lan9645x, mac, vid);
> +	if (!entry) {
> +		mutex_unlock(&lan9645x->mact_lock);
> +		return -ENOENT;
> +	}
> +
> +	WARN_ON(entry->common.pgid != pgid);
> +	lan9645x_mact_entry_dealloc(lan9645x, entry);
> +	err = __lan9645x_mact_forget(lan9645x, mac, vid, ENTRYTYPE_LOCKED);

Does this permanently remove the software entry even if the hardware
command to forget the entry fails?

> +
> +	mutex_unlock(&lan9645x->mact_lock);
> +	return err;
> +}
[ ... ]
> +void lan9645x_mac_deinit(struct lan9645x *lan9645x)
> +{
> +	mutex_destroy(&lan9645x->mact_lock);
> +}

Are the static MAC entries in the mac_entries list leaked here during driver
teardown, or are they freed elsewhere?

> +
> +int lan9645x_mact_dsa_dump(struct lan9645x *lan9645x, int port,
> +			   dsa_fdb_dump_cb_t *cb, void *data)
> +{
> +	struct lan9645x_mact_entry entry = {};
> +	u32 mach, macl, maca;
> +	int err = 0;
> +	u32 autoage;
[ ... ]
> +	while (1) {
> +		/* NOTE: we rely on mach, macl and type being set correctly in
> +		 * the registers from previous round, vis a vis the GET_NEXT
> +		 * semantics, so locking entire loop is important.
> +		 */
> +		lan_wr(ANA_MACACCESS_MAC_TABLE_CMD_SET(CMD_GET_NEXT) |
> +		       ANA_MACACCESS_ENTRYTYPE_SET(entry.common.type),
> +		       lan9645x, ANA_MACACCESS);
> +
> +		if (lan9645x_mac_wait_for_completion(lan9645x, &maca))
> +			break;

If the hardware completion times out, the loop breaks out and returns the
local err variable which is initialized to 0. Will this suppress the hardware
timeout error code and incorrectly report a successful dump?

> +
> +		if (ANA_MACACCESS_VALID_GET(maca) == 0)
> +			break;
> +
> +		mach = lan_rd(lan9645x, ANA_MACHDATA);
> +		macl = lan_rd(lan9645x, ANA_MACLDATA);
> +
> +		lan9645x_mact_parse(mach, macl, maca, &entry.common);
> +
> +		if (ANA_MACACCESS_DEST_IDX_GET(maca) == port &&
> +		    entry.common.type == ENTRYTYPE_NORMAL) {

Since static FDB entries are programmed with ENTRYTYPE_LOCKED, will this
condition exclude them from the FDB dump, preventing them from showing up
during bridge FDB queries?

> +			if (entry.common.key.vid > VLAN_MAX)
> +				entry.common.key.vid = 0;
> +
> +			err = cb(entry.common.key.mac, entry.common.key.vid,
> +				 false, data);
> +			if (err)
> +				break;
> +		}
> +	}
> +
> +	/* Remove aging filters and restore aging */
> +	lan_wr(0, lan9645x, ANA_ANAGEFIL);
> +	lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ANA_AUTOAGE_AGE_PERIOD_GET(autoage)),
> +		ANA_AUTOAGE_AGE_PERIOD,
> +		lan9645x, ANA_AUTOAGE);

Does restoring the ANA_AUTOAGE register using the autoage value saved at the
start of the function race with other operations setting the aging time?

If the aging time is updated concurrently without taking mact_lock, its newly
configured aging period might be overwritten here.

> +
> +	mutex_unlock(&lan9645x->mact_lock);
> +
> +	return err;
> +}
> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> index 046c95a72242..32c0301030a4 100644
> --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
[ ... ]
> +static int lan9645x_fdb_del(struct dsa_switch *ds, int port,
> +			    const unsigned char *addr, u16 vid,
> +			    struct dsa_db db)
> +{
> +	struct net_device *br = lan9645x_db2bridge(db);
> +	struct dsa_port *dp = dsa_to_port(ds, port);
> +	struct lan9645x *lan9645x = ds->priv;
> +	int err;
> +
> +	if (IS_ERR(br))
> +		return PTR_ERR(br);
> +
> +	if (dsa_port_is_cpu(dp) && !br &&
> +	    dsa_fdb_present_in_other_db(ds, port, addr, vid, db))
> +		return 0;
> +
> +	if (!vid)
> +		vid = lan9645x_vlan_unaware_pvid(!!br);
> +
> +	if (dsa_port_is_cpu(dp))
> +		return lan9645x_mact_forget(lan9645x, addr, vid,
> +					    ENTRYTYPE_LOCKED);
> +
> +	err = lan9645x_mact_entry_del(lan9645x, port, addr, vid);
> +	if (err == -ENOENT) {
> +		dev_dbg(lan9645x->dev,
> +			"fdb not found port=%d addr=%pM vid=%u\n", port, addr,
> +			vid);
> +		return 0;
> +	}
> +
> +	return err;
> +}

Since lan9645x_mact_entry_del returns -ENOENT when a MAC address is not found
in the software tracking list, and the software list only contains statically
added entries, won't this intercept attempts to manually delete dynamically
learned MAC addresses and return success without actually removing them from
the hardware table?

^ permalink raw reply

* Re: [PATCH net-next v2 6/9] net: dsa: lan9645x: add vlan support
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-6-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: lan9645x: add vlan support

Add support for vlanaware bridge. We reserve vid 4095 for standalone
mode, to implement fdb-isolation. A vlan-unaware bridge uses vid 0.

> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> index 599e589c4ec3..046c95a72242 100644
> --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
[ ... ]
> @@ -535,11 +536,54 @@ static void lan9645x_port_bridge_leave(struct dsa_switch *ds, int port,
[ ... ]
> +static int lan9645x_port_vlan_filtering(struct dsa_switch *ds, int port,
> +					bool enabled,
> +					struct netlink_ext_ack *extack)
> +{
> +	struct lan9645x *lan9645x = ds->priv;
> +	struct lan9645x_port *p;
> +
> +	p = lan9645x_to_port(lan9645x, port);
> +	p->vlan_aware = enabled;
> +	lan9645x_vlan_port_apply(p);

In other places, the driver avoids calling egress functions on the npi
port because it conflicts with cpu port header extraction. Since this
unconditionally applies the vlan configuration to both ingress and egress,
could this overwrite the npi port's configuration when the state is
synchronized?

> +
> +	return 0;
> +}
> +
> +static int lan9645x_port_vlan_add(struct dsa_switch *ds, int port,
[ ... ]
> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c
> new file mode 100644
> index 000000000000..0459932a626c
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_vlan.c
[ ... ]
> +static void lan9645x_vlan_hw_wr(struct lan9645x *lan9645x, u16 vid)
> +{
> +	struct lan9645x_vlan *v = &lan9645x->vlans[vid];
> +	bool cpu_dis = !(v->portmask & BIT(CPU_PORT));
> +	u32 val;
> +
> +	val = ANA_VLANTIDX_VLAN_PGID_CPU_DIS_SET(cpu_dis) |
> +	      ANA_VLANTIDX_V_INDEX_SET(vid) |
> +	      ANA_VLANTIDX_VLAN_SEC_FWD_ENA_SET(v->s_fwd_ena) |
> +	      ANA_VLANTIDX_VLAN_FLOOD_DIS_SET(v->fld_dis) |
> +	      ANA_VLANTIDX_VLAN_PRIV_VLAN_SET(v->prv_vlan) |
> +	      ANA_VLANTIDX_VLAN_LEARN_DISABLED_SET(v->lrn_dis) |
> +	      ANA_VLANTIDX_VLAN_MIRROR_SET(v->mir) |
> +	      ANA_VLANTIDX_VLAN_SRC_CHK_SET(v->src_chk);
> +
> +	lan_wr(val, lan9645x, ANA_VLANTIDX);
> +	lan_wr(v->portmask, lan9645x, ANA_VLAN_PORT_MASK);
> +	lan_wr(VLANACCESS_CMD_WRITE, lan9645x, ANA_VLANACCESS);

Are these registers written with raw values rather than using their respective
set macros? This circumvents the register abstraction and might risk failures
if the hardware layout changes.

> +
> +	if (lan9645x_vlan_wait_for_completion(lan9645x))
> +		dev_err(lan9645x->dev, "Vlan set mask failed\n");
> +}
[ ... ]
> +static void
> +lan9645x_vlan_port_apply_egress(struct lan9645x_port *p,
> +				struct lan9645x_vlan_port_info *info)
> +{
[ ... ]
> +	lan_rmw(REW_TAG_CFG_TAG_TPID_CFG_SET(3) |
> +		REW_TAG_CFG_TAG_CFG_SET(tag_cfg),
> +		REW_TAG_CFG_TAG_TPID_CFG |
> +		REW_TAG_CFG_TAG_CFG,
> +		lan9645x, REW_TAG_CFG(p->chip_port));
> +
> +	lan_rmw(REW_PORT_VLAN_CFG_PORT_TPID_SET(ETH_P_8021AD) |
> +		REW_PORT_VLAN_CFG_PORT_VID_SET(port_vid),
> +		REW_PORT_VLAN_CFG_PORT_TPID |
> +		REW_PORT_VLAN_CFG_PORT_VID,
> +		lan9645x, REW_PORT_VLAN_CFG(p->chip_port));
> +}

Setting the port tpid to 0x88a8 causes untagged ingress frames to egress trunk
ports with 802.1ad tags. Could this break interoperability with standard
networks that expect 0x8100 tags?

> +static void lan9645x_vlan_port_apply_ingress(struct lan9645x_port *p)
> +{
[ ... ]
> +	/* Drop frames with multicast source address */
> +	val = ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1);
> +	if (p->vlan_aware && !pvid)
> +		/* If port is vlan-aware and tagged, drop untagged and priority
> +		 * tagged frames.
> +		 */
> +		val |= ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(1) |
> +		       ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(1) |
> +		       ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(1);
> +
> +	lan_wr(val, lan9645x, ANA_DROP_CFG(p->chip_port));
> +}

Does this overwrite the entire 32-bit register and silently clear other
initialized bitfields? Would it be safer to use a read-modify-write here
instead?

[ ... ]
> +static int lan9645x_vlan_cpu_add(struct lan9645x_port *p, u16 vid, bool pvid,
> +				 bool untagged)
> +{
> +	struct lan9645x_vlan *v;
> +
> +	v = lan9645x_vlan_port_modify(p, vid, pvid, untagged);

Is it intentional that this bypasses the bounds check on the vlan id?

> +	v->portmask |= BIT(CPU_PORT) | BIT(p->chip_port);
> +	lan9645x_vlan_hw_wr(p->lan9645x, vid);
> +	lan9645x_vlan_port_apply_ingress(p);
> +
> +	return 0;
> +}
> +
> +int lan9645x_vlan_port_add_vlan(struct lan9645x_port *p, u16 vid, bool pvid,
> +				bool untagged, struct netlink_ext_ack *extack)
> +{
[ ... ]
> +void lan9645x_vlan_port_del_vlan(struct lan9645x_port *p, u16 vid)
> +{
> +	struct lan9645x *lan9645x = p->lan9645x;
> +	struct lan9645x_vlan *v;
> +
> +	if (!vid)
> +		return;
> +
> +	v = lan9645x_vlan_port_modify(p, vid, false, false);

There is a check for the maximum vlan id when adding a vlan, but no bounds
check when deleting one. Could userspace use this to delete the reserved
vid 4095 and break standalone forwarding?

> +	v->portmask &= ~BIT(p->chip_port);
> +
> +	if (p->chip_port == lan9645x->npi)
> +		v->portmask &= ~BIT(CPU_PORT);
> +
> +	lan9645x_vlan_hw_wr(lan9645x, vid);
[ ... ]
> +void lan9645x_vlan_init(struct lan9645x *lan9645x)
> +{
[ ... ]
> +	for (vid = 1; vid < VLAN_N_VID; vid++)
> +		lan9645x_vlan_hw_wr(lan9645x, vid);

If the indirect table access times out, the write function prints an error
but does not return a status code. Will this cause the initialization loop
to stall for the full timeout duration 4095 consecutive times if the
hardware is unresponsive?

> +
> +	/* Set all the ports + cpu to be part of HOST_PVID and UNAWARE_PVID */
> +	lan9645x->vlans[HOST_PVID].portmask = all_ports;

^ permalink raw reply

* Re: [PATCH net-next v2 5/9] net: dsa: lan9645x: add bridge support
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-5-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: lan9645x: add bridge support

This commit adds support for hardware offloading of the bridge in the
lan9645x DSA driver. It enables a single bridge device to be used with
the switch.

> diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> index a0908cbf89c7..599e589c4ec3 100644
> --- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
> +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
[ ... ]
> @@ -145,6 +155,8 @@ static int lan9645x_setup(struct dsa_switch *ds)
>  		return err;
>  	}
>  
> +	mutex_init(&lan9645x->fwd_domain_lock);
> +
>  	/* Link Aggregation Mode: NETDEV_LAG_HASH_L2 */
>  	lan_wr(ANA_AGGR_CFG_AC_SMAC_ENA |
>  	       ANA_AGGR_CFG_AC_DMAC_ENA,
[ ... ]
> @@ -240,6 +252,11 @@ static int lan9645x_setup(struct dsa_switch *ds)
>  
>  	lan9645x_port_set_tail_drop_wm(lan9645x);
>  
> +	lan9645x->owq = alloc_ordered_workqueue("%s-owq", 0,
> +						dev_name(lan9645x->dev));
> +	if (!lan9645x->owq)
> +		return -ENOMEM;
> +

Is it possible that the hardware remains initialized if the call to
alloc_ordered_workqueue fails? Because the setup callback returns an error,
the DSA core will skip the teardown callback. This would bypass
lan9645x_npi_port_deinit, potentially leaving the conduit netdev in a
corrupted state with hardware tagging enabled.

Also in lan9645x_setup, the initialization loop for PGID_ENTRIES iterates
through index 88:

for (int i = 0; i < PGID_ENTRIES; ++i)
        lan_wr(ANA_PGID_CFG_OBEY_VLAN_SET(1),
               lan9645x, ANA_PGID_CFG(i));

Could this leave the CPU port's source configuration in an undefined state?
The CPU port is port 9, which has its source analysis register at PGID_SRC
(80) + 9 = 89, but the loop terminates before reaching it.

> @@ -258,6 +275,271 @@ static void lan9645x_port_phylink_get_caps(struct dsa_switch *ds, int port,
>  	lan9645x_phylink_get_caps(ds->priv, port, config);
>  }
>  
> +static int lan9645x_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
> +{
> +	u32 age_secs = max(1, msecs / MSEC_PER_SEC / 2);
> +	struct lan9645x *lan9645x = ds->priv;
> +
> +	/* Entry is must suffer two aging scans before it is removed, so it is
> +	 * aged after 2*AGE_PERIOD, and the unit is in seconds.
> +	 * An age period of 0 disables automatic aging.
> +	 */
> +	lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(age_secs),
> +		ANA_AUTOAGE_AGE_PERIOD,
> +		lan9645x, ANA_AUTOAGE);
> +	return 0;
> +}

Does this function properly disable automatic aging when requested? When the
Linux bridge passes msecs = 0 to disable aging, the max macro evaluates to 1.
Since writing 1 configures an aggressive 2-second aging period, it appears
this might not fulfill the user's intent.

[ ... ]
> +static void __lan9645x_port_set_host_flood(struct lan9645x *lan9645x, int port,
> +					   bool uc, bool mc)
> +{
> +	bool mc_ena, uc_ena;
> +
> +	lockdep_assert_held(&lan9645x->fwd_domain_lock);
> +
> +	/* We want promiscuous and all_multi to affect standalone ports, for
> +	 * debug and test purposes.
> +	 *
> +	 * However, the linux bridge is incredibly eager to put bridged ports in
> +	 * promiscuous mode.
> +
> +	 * This is unfortunate since lan9645x flood masks are global and not per
> +	 * ingress port. When some port triggers unknown uc/mc to the CPU, the
> +	 * traffic from any port is forwarded to the CPU.
> +	 *
> +	 * If the host CPU is weak, this can cause tremendous stress. Therefore,
> +	 * we compromise by ignoring this host flood request for bridged ports.
> +	 */
> +	if (lan9645x_port_is_bridged(lan9645x_to_port(lan9645x, port)))
> +		return;

Will ignoring the host flood request for bridged ports break IPv6 Neighbor
Discovery? IPv6 ND relies on multicast, and the PGID_MC and PGID_MCIPV6 masks
do not include the CPU port by default. By ignoring the bridge's request to
enable multicast flooding, the CPU port may fail to receive ICMPv6 ND packets.

[ ... ]
> +/* Called in atomic context */
> +static void lan9645x_port_set_host_flood(struct dsa_switch *ds, int port,
> +					 bool uc, bool mc)
> +{
> +	struct lan9645x *lan9645x = ds->priv;
> +	struct lan9645x_host_flood_work *w;
> +
> +	w = kzalloc_obj(*w, GFP_ATOMIC);

Is kzalloc_obj an available kernel function? Using it might cause a build
error, as the standard API would be kzalloc(sizeof(*w), GFP_ATOMIC).

> +	if (!w)
> +		return;

Are there consequences to returning silently when the allocation fails?
If this allocation fails under memory pressure, the network stack will still
assume the configuration was successfully applied, leaving the hardware state
out of sync.

[ ... ]
> +static int lan9645x_port_bridge_join(struct dsa_switch *ds, int port,
> +				     struct dsa_bridge bridge,
> +				     bool *tx_fwd_offload,
> +				     struct netlink_ext_ack *extack)
> +{
[ ... ]
> +	/* The bridge puts ports in IFF_ALLMULTI before calling
> +	 * port_bridge_join, so clean up before the port is marked as bridged.
> +	 */
> +	__lan9645x_port_set_host_flood(lan9645x, port, false, false);
> +	lan9645x->bridge_mask |= BIT(p->chip_port);
> +
> +	mutex_unlock(&lan9645x->fwd_domain_lock);
> +
> +	/* Later: stp_state_set updates forwarding */
> +
> +	return 0;
> +}

Could clearing the host flood masks here lead to lost hardware state when the
port leaves the bridge? If a user previously enabled IFF_PROMISC or
IFF_ALLMULTI on the standalone port, the software netdev flags remain set.
When lan9645x_port_bridge_leave is called, it does not restore the hardware's
promiscuous or multicast state to match the unchanged software flags.

> +static void lan9645x_port_bridge_stp_state_set(struct dsa_switch *ds, int port,
> +					       u8 state)
> +{
> +	struct lan9645x *lan9645x;
> +	struct lan9645x_port *p;
> +	bool learn_ena;
> +
> +	lan9645x = ds->priv;
> +	p = lan9645x_to_port(lan9645x, port);
> +
> +	mutex_lock(&lan9645x->fwd_domain_lock);
> +
> +	p->stp_state = state;
> +
> +	if (state == BR_STATE_FORWARDING)
> +		lan9645x->bridge_fwd_mask |= BIT(p->chip_port);
> +	else
> +		lan9645x->bridge_fwd_mask &= ~BIT(p->chip_port);
> +
> +	learn_ena = (state == BR_STATE_LEARNING ||
> +		     state == BR_STATE_FORWARDING) && p->learn_ena;
> +
> +	lan_rmw(ANA_PORT_CFG_LEARN_ENA_SET(learn_ena),
> +		ANA_PORT_CFG_LEARN_ENA, lan9645x,
> +		ANA_PORT_CFG(p->chip_port));
> +
> +	lan9645x_update_fwd_mask(lan9645x);
> +	mutex_unlock(&lan9645x->fwd_domain_lock);
> +}

Are ingress data frames correctly dropped for ports in non-forwarding STP
states? When the port is removed from bridge_fwd_mask,
lan9645x_update_fwd_mask calculates the source PGID mask as
bridge_mask & bridge_fwd_mask & ~BIT(dp->index).

This evaluates to the set of all other forwarding ports in the bridge. Since
ingress forwarding is not explicitly disabled on the port (for example, by
setting ANA_PORT_CFG_PORTID_VAL to NONE), data frames received on a blocking
or learning port can enter the analyzer and be forwarded.

> +static void lan9645x_port_bridge_leave(struct dsa_switch *ds, int port,
> +				       struct dsa_bridge bridge)
> +{
> +	struct lan9645x *lan9645x = ds->priv;
> +	struct lan9645x_port *p;
> +
> +	p = lan9645x_to_port(lan9645x, port);
> +
> +	mutex_lock(&lan9645x->fwd_domain_lock);
> +
> +	lan9645x->bridge_mask &= ~BIT(p->chip_port);
> +
> +	/* Last port leaving clears bridge dev */
> +	if (!lan9645x->bridge_mask)
> +		lan9645x->bridge = NULL;
> +
> +	lan9645x_update_fwd_mask(lan9645x);
> +
> +	mutex_unlock(&lan9645x->fwd_domain_lock);
> +}

Should p->learn_ena be reset when a port leaves the bridge?

During port_bridge_flags, p->learn_ena is set to true. When leaving the
bridge, this flag remains true. The DSA core will transition the leaving
port to BR_STATE_FORWARDING, which calls lan9645x_port_bridge_stp_state_set
and leaves hardware learning enabled. This can pollute the shared FDB with
MAC addresses from standalone ports, leading to silent packet drops if
bridged ports attempt to forward traffic to them.

^ permalink raw reply

* Re: [PATCH net-next v2 4/9] net: dsa: lan9645x: add basic dsa driver for LAN9645X
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-4-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
Subject: net: dsa: lan9645x: add basic dsa driver for LAN9645X

This commit adds a basic DSA driver for the Microchip LAN9645X switch. It
includes initialization, port module setup for NPI, CPU, and front ports,
and phylink integration for MAC side configuration.

diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_npi.c
[ ... ]
> +static void disable_conduit_ipv6(struct lan9645x *lan9645x,
> +				 struct net_device *conduit)
> +{
> +	struct inet6_dev *dev_v6;
> +
> +	if (!conduit)
> +		return;
> +
> +	/* IPv6 on the conduit will generate frames bypassing our tag driver, so
> +	 * they lack an IFH. This will be garbage in garbage out and we want to
> +	 * avoid this.
> +	 */
> +	rtnl_lock();
> +	dev_v6 = __in6_dev_get(conduit);
> +	if (dev_v6) {
> +		WRITE_ONCE(dev_v6->cnf.disable_ipv6, 1);
> +		dev_warn(lan9645x->dev, "Disabled IPv6 on conduit device: %s\n",
> +			 netdev_name(conduit));
> +	}
> +	rtnl_lock();
> +}

Does directly modifying dev_v6->cnf.disable_ipv6 bypass the proper sysctl
teardown path (addrconf_disable_ipv6) and leave the IPv6 stack in an
inconsistent state on the conduit device by failing to clean up routes,
addresses, and MLD memberships?

Is it a layering violation for a switch driver to alter the networking
configuration of an external upper device, especially since this change
isn't reverted during driver unbind?

[ ... ]
> +void lan9645x_npi_port_init(struct lan9645x *lan9645x,
> +			    struct dsa_port *cpu_port)
> +{
> +	int port = cpu_port->index;
> +	struct lan9645x_port *p;
> +
> +	p = lan9645x_to_port(lan9645x, port);
> +	lan9645x->npi = port;

In a cascaded multi-switch DSA topology, cpu_port->index may belong to a
different switch and be greater than 8. If lan9645x_to_port returns NULL,
will this cause a panic when dereferencing p->chip_port below?

[ ... ]
> +	/* Configure IFH prefix mode for NPI port. */
> +	lan_rmw(SYS_PORT_MODE_INCL_XTR_HDR_SET(LAN9645X_TAG_PREFIX_LONG) |
> +		SYS_PORT_MODE_INCL_INJ_HDR_SET(LAN9645X_TAG_PREFIX_NONE),
> +		SYS_PORT_MODE_INCL_XTR_HDR |
> +		SYS_PORT_MODE_INCL_INJ_HDR,
> +		lan9645x,
> +		SYS_PORT_MODE(p->chip_port));

Setting LAN9645X_TAG_PREFIX_NONE means the hardware interprets the first
bytes of any frame received from the host as an IFH command structure.

Does this allow a local user to transmit crafted frames (for example, via
raw sockets or standard protocols) whose MAC address bytes map to malicious
IFH commands, thereby bypassing VLAN isolation or flooding the switch?

Should the hardware be configured to require a strict prefix, such as
LAN9645X_TAG_PREFIX_LONG, for injected frames to prevent this?

diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_main.c
[ ... ]
> +static int lan9645x_setup(struct dsa_switch *ds)
> +{
> +	struct lan9645x *lan9645x = ds->priv;
> +	struct device *dev = lan9645x->dev;
> +	u32 all_phys_ports;
> +	int err;
> +
> +	lan9645x->num_phys_ports = ds->num_ports;
> +	all_phys_ports = GENMASK(lan9645x->num_phys_ports - 1, 0);
> +
> +	err = lan9645x_reset_switch(lan9645x);
> +	if (err)
> +		return err;
> +
> +	lan9645x->ports = devm_kcalloc(lan9645x->dev, lan9645x->num_phys_ports,
> +				       sizeof(struct lan9645x_port *),
> +				       GFP_KERNEL);

Since the DSA .setup callback can be invoked multiple times during the
platform device's lifetime (for example, when the DSA master interface is
unbound and bound again), does using devm_kcalloc here cause a memory leak?
Should these allocations be moved to .probe or managed with kcalloc and
freed in .teardown?

[ ... ]
> +	/* Set all the entries to obey VLAN. */
> +	for (int i = 0; i < PGID_ENTRIES; ++i)
> +		lan_wr(ANA_PGID_CFG_OBEY_VLAN_SET(1),
> +		       lan9645x, ANA_PGID_CFG(i));

PGID_ENTRIES is defined as 89, so this loop initializes indices 0 through
88. Since the CPU port is index 9, its source PGID is PGID_SRC + CPU_PORT
(80 + 9 = 89).

Is index 89 left uninitialized, breaking the OBEY_VLAN rule and allowing
CPU-injected frames to leak across VLAN boundaries?

[ ... ]
> +	/* Multicast to all front ports */
> +	lan_wr(all_phys_ports, lan9645x, ANA_PGID(PGID_MC));
> +
> +	/* IP multicast to all front ports */
> +	lan_wr(all_phys_ports, lan9645x, ANA_PGID(PGID_MCIPV4));
> +	lan_wr(all_phys_ports, lan9645x, ANA_PGID(PGID_MCIPV6));
> +
> +	/* Unicast to all front ports */
> +	lan_wr(all_phys_ports, lan9645x, ANA_PGID(PGID_UC));
> +
> +	/* Broadcast to all ports */
> +	lan_wr(BIT(CPU_PORT) | all_phys_ports, lan9645x, ANA_PGID(PGID_BC));

PGID_BC includes BIT(CPU_PORT) and all_phys_ports (which includes the NPI
port). Will this forward broadcast frames to both the CPU extraction queue
and the NPI port's normal egress queue, causing duplicate frames for the host?

Conversely, the multicast masks and PGID_UC exclude BIT(CPU_PORT). Does
this cause them to bypass the CPU extraction queue entirely, thereby
lacking the LONG extraction prefix and breaking the host's DSA tagger parsing?

diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_phylink.c
[ ... ]
> +static int lan9645x_phylink_mac_prepare(struct phylink_config *config,
> +					unsigned int mode,
> +					phy_interface_t iface)
> +{
> +	struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
> +	struct lan9645x *lan9645x = p->lan9645x;
> +	int port = p->chip_port;
> +	u32 mask;
> +
> +	if (port == 5 || port == 6 || port > 8)
> +		return -EINVAL;
> +
> +	mask = HSIO_HW_CFG_GMII_ENA_SET(BIT(port));
> +	lan_rmw(mask, mask, lan9645x, HSIO_HW_CFG);
> +
> +	if (port == 4 && phy_interface_mode_is_rgmii(iface))
> +		lan_rmw(HSIO_HW_CFG_RGMII_0_CFG_SET(1),
> +			HSIO_HW_CFG_RGMII_0_CFG,
> +			lan9645x, HSIO_HW_CFG);

The lan9645x_phylink_get_caps function advertises RGMII support for ports
4, 7, and 8. Does restricting the hardware multiplexer configuration here to
port 4 break connectivity for ports 7 and 8 when used in RGMII mode?

diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
--- a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
+++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
[ ... ]
> +int lan9645x_port_set_maxlen(struct lan9645x *lan9645x, int port, size_t sdu)
> +{
> +	struct lan9645x_port *p = lan9645x_to_port(lan9645x, port);
> +	int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN;

Does this maximum frame length calculation drop standard 1500-byte MTU
frames that are 802.1Q VLAN tagged (1522 bytes total), since it does not
account for VLAN_HLEN?

[ ... ]
> +int lan9645x_port_setup(struct dsa_switch *ds, int port)
> +{
> +	struct dsa_port *dp = dsa_to_port(ds, port);
> +	struct lan9645x *lan9645x = ds->priv;
> +	struct lan9645x_port *p;
> +
> +	p = lan9645x_to_port(lan9645x, port);
> +
> +	if (dp->dn) {
> +		p->rx_internal_delay =
> +			of_property_present(dp->dn, "rx-internal-delay-ps");
> +		p->tx_internal_delay =
> +			of_property_present(dp->dn, "tx-internal-delay-ps");
> +	}

These are standard integer properties specifying delays in picoseconds. If
a user explicitly disables the delay via devicetree using a value of 0,
will of_property_present evaluate to true and enable the hardware delay
anyway? Should of_property_read_u32 be used instead to check the value?

^ permalink raw reply

* Re: [PATCH net-next v2 3/9] net: dsa: lan9645x: add autogenerated register macros
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-3-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
No issues found.

^ permalink raw reply

* Re: [PATCH net-next v2 2/9] dt-bindings: net: lan9645x: add LAN9645X switch bindings
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-2-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
dt-bindings: net: lan9645x: add LAN9645X switch bindings

Add bindings for LAN9645X switch. We use a fallback compatible for the
smallest SKU microchip,lan96455s-switch.

> diff --git a/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml b/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml
> new file mode 100644
> index 000000000000..0282e25c05d4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/dsa/microchip,lan96455s-switch.yaml

[ ... ]

> +patternProperties:
> +  "^(ethernet-)?ports$":
> +    type: object
> +    additionalProperties: true
> +    patternProperties:
> +      "^(ethernet-)?port@[0-8]$":

Does having additionalProperties set to true bypass the port number
restriction?

Since dsa.yaml allows port@[0-9a-f]+, it seems port@9 would silently pass
validation as an additional property. Should this be unevaluatedProperties
set to false instead?

> +        type: object
> +        description: Ethernet switch ports
> +
> +        $ref: dsa-port.yaml#
> +
> +        properties:
> +          rx-internal-delay-ps:
> +            const: 2000
> +
> +          tx-internal-delay-ps:
> +            const: 2000

Does enforcing a 2000ps delay conflict with standard rgmii bindings?

As documented in net/ethernet-controller.yaml, a full 2ns delay is
typically modeled using phy-mode set to rgmii-id rather than hardcoding the
delay properties with a base rgmii mode.

[ ... ]

> +required:
> +  - compatible
> +  - reg
> +
> +unevaluatedProperties: false

How are the five integrated 10/100/1000Base-T phys mentioned in the
description modeled?

Since unevaluatedProperties is false at the top level and there is no
definition or reference for an mdio node, wouldn't adding an mdio subnode
to configure these phys fail schema validation?

Should there be a reference to /schemas/net/mdio.yaml#?

^ permalink raw reply

* Re: [PATCH net-next v2 1/9] net: dsa: add tag driver for LAN9645X
From: Jakub Kicinski @ 2026-03-29 19:56 UTC (permalink / raw)
  To: jensemil.schulzostergaard
  Cc: Jakub Kicinski, UNGLinuxDriver, andrew, olteanv, davem, edumazet,
	pabeni, horms, robh, krzk+dt, conor+dt, woojung.huh, linux,
	Steen.Hegelund, daniel.machon, linux-kernel, netdev, devicetree
In-Reply-To: <20260324-dsa_lan9645x_switch_driver_base-v2-1-f7504e3b0681@microchip.com>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: dsa: add tag driver for LAN9645X

Add a tag driver for LAN9645x switches using a front port as the CPU port
(NPI port). The driver configures a long prefix for extraction and no
prefix for injection to handle the internal frame header (IFH).

> diff --git a/net/dsa/tag_lan9645x.c b/net/dsa/tag_lan9645x.c
> new file mode 100644
> index 000000000000..9130fb0d61be
> --- /dev/null
> +++ b/net/dsa/tag_lan9645x.c

[ ... ]

> +static void lan9645x_xmit_get_vlan_info(struct sk_buff *skb,
> +					struct net_device *br,
> +					u32 *vlan_tci, u32 *tag_type)
> +{
> +	struct vlan_ethhdr *hdr;
> +	u16 proto, tci;
> +
> +	if (!br || !br_vlan_enabled(br)) {
> +		*vlan_tci = 0;
> +		*tag_type = LAN9645X_IFH_TAG_TYPE_C;
> +		return;
> +	}
> +
> +	hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
> +	br_vlan_get_proto(br, &proto);
> +
> +	if (ntohs(hdr->h_vlan_proto) == proto) {
> +		vlan_remove_tag(skb, &tci);

Is it possible for a maliciously crafted packet (e.g., via AF_PACKET) to have a
length of 14 bytes with h_vlan_proto set to ETH_P_8021Q?

If so, vlan_remove_tag() would read vhdr->h_vlan_TCI at offset 14, causing an
out-of-bounds read.

Furthermore, if the packet contains fragments and the VLAN header is not in the
linear data area, vlan_remove_tag() could corrupt the SKB when shifting the MAC
header.

Would it be safer to use skb_vlan_untag() instead?

> +		*vlan_tci = tci;
> +	} else {
> +		rcu_read_lock();
> +		br_vlan_get_pvid_rcu(br, &tci);
> +		rcu_read_unlock();
> +		*vlan_tci = tci;
> +	}
> +
> +	*tag_type = (proto != ETH_P_8021Q) ? LAN9645X_IFH_TAG_TYPE_S :
> +					     LAN9645X_IFH_TAG_TYPE_C;
> +}

[ ... ]

> +static struct sk_buff *lan9645x_rcv(struct sk_buff *skb,
> +				    struct net_device *ndev)
> +{
> +	u32 src_port, qos_class, vlan_tci, tag_type, popcnt, etype_ofs;
> +	u8 *orig_skb_data = skb->data;
> +	struct dsa_port *dp;
> +	u32 ifh_gap_len = 0;
> +	u16 vlan_tpid;
> +	u8 *ifh;
> +
> +	/* DSA master already consumed DMAC,SMAC,ETYPE from long prefix. Go back
> +	 * to beginning of frame.
> +	 */
> +	skb_push(skb, ETH_HLEN);
> +	/* IFH starts after our long prefix */
> +	ifh = skb_pull(skb, LAN9645X_LONG_PREFIX_LEN);

Could skb_pull() return NULL here if skb->len is less than 16 bytes?

If it returns NULL, the blind dereference of ifh in the subsequent
lan9645x_ifh_get() calls would cause a NULL pointer dereference.

Additionally, is the 44-byte tag (16-byte prefix + 28-byte IFH) guaranteed to
be in the linear data buffer (skb->data)?

Without calling pskb_may_pull(skb, LAN9645X_TOTAL_TAG_LEN) first, accessing it
via the ifh pointer might result in out-of-bounds reads if the packet payload
is in paged fragments.

> +
> +	popcnt = lan9645x_ifh_get(ifh, IFH_POP_CNT, IFH_POP_CNT_SZ);
> +	etype_ofs = lan9645x_ifh_get(ifh, IFH_ETYPE_OFS, IFH_ETYPE_OFS_SZ);
> +	src_port = lan9645x_ifh_get(ifh, IFH_SRCPORT, IFH_SRCPORT_SZ);
-- 
pw-bot: cr

^ permalink raw reply

* Re: [PATCH] dt-bindings: power: reset: cortina,gemini-power-controller: convert to DT schema
From: Linus Walleij @ 2026-03-29 18:57 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Krzysztof Kozlowski, Rob Herring (Arm), Khushal Chitturi,
	devicetree, linux-kernel, linux-pm, Krzysztof Kozlowski,
	Conor Dooley
In-Reply-To: <aciItMCdBbrVvMKB@venus>

On Sun, Mar 29, 2026 at 4:09 AM Sebastian Reichel <sre@kernel.org> wrote:

> The problem is the node name (power-controller@4b000000), which is
> reserved for power domains. You can keep the compatible.

Ah, sweet, Khushal can you change this?

I think you can use gemini-poweroff@4b000000 because
this is pretty much a poweroff thingie.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH] dt-bindings: rtc: moxa,moxart-rtc: convert to json-schema
From: Serban-Pascu Robert @ 2026-03-29 18:46 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: robh, krzk+dt, conor+dt, devicetree, linux-rtc, linux-kernel,
	daniel.baluta, Serban-Pascu Robert

Convert the MOXA ART real-time clock text binding to DT schema.

Signed-off-by: Serban-Pascu Robert <robyserbanpascu06@gmail.com>
---
 .../bindings/rtc/moxa,moxart-rtc.txt          | 17 --------
 .../bindings/rtc/moxa,moxart-rtc.yaml         | 43 +++++++++++++++++++
 2 files changed, 43 insertions(+), 17 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.txt
 create mode 100644 Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.yaml

diff --git a/Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.txt b/Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.txt
deleted file mode 100644
index 1374df7bf9d6..000000000000
--- a/Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-MOXA ART real-time clock
-
-Required properties:
-
-- compatible : Should be "moxa,moxart-rtc"
-- rtc-sclk-gpios : RTC sclk gpio, with zero flags
-- rtc-data-gpios : RTC data gpio, with zero flags
-- rtc-reset-gpios : RTC reset gpio, with zero flags
-
-Example:
-
-	rtc: rtc {
-		compatible = "moxa,moxart-rtc";
-		rtc-sclk-gpios = <&gpio 5 0>;
-		rtc-data-gpios = <&gpio 6 0>;
-		rtc-reset-gpios = <&gpio 7 0>;
-	};
diff --git a/Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.yaml b/Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.yaml
new file mode 100644
index 000000000000..6b8f6e5f99e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/moxa,moxart-rtc.yaml
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rtc/moxa,moxart-rtc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MOXA ART Real-time Clock
+
+maintainers:
+  - Serban-Pascu Robert <robyserbanpascu06@gmail.com>
+
+properties:
+  compatible:
+    const: moxa,moxart-rtc
+
+  rtc-sclk-gpios:
+    maxItems: 1
+    description: GPIO line for the RTC clock signal.
+
+  rtc-data-gpios:
+    maxItems: 1
+    description: GPIO line for the RTC data signal.
+
+  rtc-reset-gpios:
+    maxItems: 1
+    description: GPIO line for the RTC reset signal.
+
+required:
+  - compatible
+  - rtc-sclk-gpios
+  - rtc-data-gpios
+  - rtc-reset-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    rtc {
+        compatible = "moxa,moxart-rtc";
+        rtc-sclk-gpios = <&gpio 5 0>;
+        rtc-data-gpios = <&gpio 6 0>;
+        rtc-reset-gpios = <&gpio 7 0>;
+    };
-- 
2.43.0


^ permalink raw reply related

* [GIT PULL] RISC-V T-HEAD Devicetrees for v7.1, part 2
From: Drew Fustini @ 2026-03-29 18:44 UTC (permalink / raw)
  To: soc
  Cc: devicetree, Alexandre Belloni, Arnd Bergmann, Linus Walleij,
	Han Gao, Conor Dooley, Krzysztof Kozlowski, linux-kernel,
	Icenowy Zheng, Jisheng Zhang, Michal Wilczynski, Yao Zi, Guo Ren,
	linux-riscv, Luca Ceresoli, Fu Wei, Robert Mazur

Two minor improvements that weren't ready when I sent the originall pull
request. They have now been tested in next. No problem if it is too late
for this cycle.

Thanks,
Drew

The following changes since commit 9c99a784d9117a192ebf779d4f72ebec435ada97:

  riscv: dts: thead: lichee-pi-4a: enable HDMI (2026-03-14 09:19:26 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/fustini/linux.git tags/thead-dt-for-v7.1-p2

for you to fetch changes up to 74ec3d52c0035b662ec295bef2bbffad68446391:

  riscv: dts: thead: beaglev-ahead: enable HDMI output (2026-03-25 09:20:38 -0700)

----------------------------------------------------------------
T-HEAD Devicetrees for 7.1, part 2

Additional updates to T-Head device trees for v7.1:

 - Enable the display pipeline for the TH1520-based BeagleV Ahead board
   by adding the HDMI connector node, connecting it to the HDMI
   controller, and activating the DPU and HDMI nodes.

 - Add coefficients to the TH1520 PVT node as the values in the TH1520
   manual differ from the defaults in the driver.

----------------------------------------------------------------
Icenowy Zheng (1):
      riscv: dts: thead: th1520: add coefficients to the PVT node

Robert Mazur (1):
      riscv: dts: thead: beaglev-ahead: enable HDMI output

 arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts | 25 ++++++++++++++++++++++
 arch/riscv/boot/dts/thead/th1520.dtsi              |  4 ++++
 2 files changed, 29 insertions(+)

^ permalink raw reply

* Re: [PATCH] dt-bindings: display: bridge: ldb: Require reg property only for i.MX6SX/8MP LDBs
From: Marek Vasut @ 2026-03-29 18:29 UTC (permalink / raw)
  To: Marco Felsch, Liu Ying
  Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Marek Vasut, Laurentiu Palcu,
	dri-devel, devicetree, linux-kernel
In-Reply-To: <crqzju5cqhvmy5msxvuquydmnpb2ft2t3gsyr6qsre6ccqjvzz@46gfcrelczsr>

On 3/29/26 7:42 PM, Marco Felsch wrote:

Hello Marco,

> sorry for not writting back earlier, the last weeks were quite busy.

Tell me about it ...

> On 26-03-29, Liu Ying wrote:
>> LDB's parent device could be a syscon which doesn't allow a reg property
>> to be present in it's child devices, e.g., NXP i.MX93 Media blk-ctrl
>> has a child device NXP i.MX93 Parallel Display Format Configuration(PDFC)
>> without a reg property(LDB is also a child device of the Media blk-ctrl).
>> To make the LDB schema be able to describe LDBs without the reg property
>> like i.MX93 LDB, require the reg property only for i.MX6SX/8MP LDBs.
> 
> NACK, we want to describe the HW and from HW PoV the LDB is and was
> always part of a syscon. This is the case for all SoCs i.MX6SX/8MP/93.
> 
>> Fixes: 8aa2f0ac08d3 ("dt-bindings: display: bridge: ldb: Add check for reg and reg-names")
> 
> Therefore I would just revert this patch completely.
Last time, I pointed out the hardware is part of syscon, but as a 
subnode and therefore with reg properties. What is the problem there ?

^ permalink raw reply

* Re: [PATCH v2 3/3] arm64: dts: qcom: kaanpaali: Add USB support for QRD platform
From: Krishna Kurapati @ 2026-03-29 18:15 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Konrad Dybcio, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-arm-msm, devicetree, linux-kernel,
	Ronak Raheja, Jingyi Wang
In-Reply-To: <qycxxxlt3koyt7snnwpkmpo2udskhad3l5vjpj3mpdi5qyoriy@akxv27lrpi3n>



On 3/29/2026 11:37 PM, Dmitry Baryshkov wrote:
> On Sun, Mar 29, 2026 at 11:22:49PM +0530, Krishna Kurapati wrote:
>> From: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
>>
>> Enable USB support on Kaanapali QRD variant. Enable USB controller in
>> device mode till glink node is added.
> 
> Why can't it be added as a part of this patchset?
> 

Hi Dmitry,

  SoCCP changes are not yet acked. Hence I wanted to get the base 
changes in.

Regards,
Krishna,

>>
>> Signed-off-by: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
>> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
>> Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
>> ---
>>   arch/arm64/boot/dts/qcom/kaanapali-qrd.dts | 27 ++++++++++++++++++++++
>>   1 file changed, 27 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
>> index 6a7eb7f4050a..1929ea273a4f 100644
>> --- a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
>> +++ b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
>> @@ -80,6 +80,11 @@ key-volume-up {
>>   			wakeup-source;
>>   		};
>>   	};
>> +
>> +	pmih0108_e1_eusb2_repeater {
>> +		vdd18-supply = <&vreg_l15b_1p8>;
>> +		vdd3-supply = <&vreg_l5b_3p1>;
>> +	};
>>   };
>>   
>>   &apps_rsc {
>> @@ -821,3 +826,25 @@ &ufs_mem_phy {
>>   
>>   	status = "okay";
>>   };
>> +
>> +&usb {
>> +	dr_mode = "peripheral";
>> +
>> +	status = "okay";
>> +};
>> +
>> +&usb_hsphy {
>> +	vdd-supply = <&vreg_l4f_0p8>;
>> +	vdda12-supply = <&vreg_l1d_1p2>;
>> +
>> +	phys = <&pmih0108_e1_eusb2_repeater>;
>> +
>> +	status = "okay";
>> +};
>> +
>> +&usb_dp_qmpphy {
>> +	vdda-phy-supply = <&vreg_l1d_1p2>;
>> +	vdda-pll-supply = <&vreg_l4f_0p8>;
>> +
>> +	status = "okay";
>> +};
>> -- 
>> 2.34.1
>>
> 


^ permalink raw reply

* Re: [PATCH v2 3/3] arm64: dts: qcom: kaanpaali: Add USB support for QRD platform
From: Dmitry Baryshkov @ 2026-03-29 18:07 UTC (permalink / raw)
  To: Krishna Kurapati
  Cc: Konrad Dybcio, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-arm-msm, devicetree, linux-kernel,
	Ronak Raheja, Jingyi Wang
In-Reply-To: <20260329175249.2946508-4-krishna.kurapati@oss.qualcomm.com>

On Sun, Mar 29, 2026 at 11:22:49PM +0530, Krishna Kurapati wrote:
> From: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
> 
> Enable USB support on Kaanapali QRD variant. Enable USB controller in
> device mode till glink node is added.

Why can't it be added as a part of this patchset?

> 
> Signed-off-by: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
> Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
> Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
> ---
>  arch/arm64/boot/dts/qcom/kaanapali-qrd.dts | 27 ++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
> index 6a7eb7f4050a..1929ea273a4f 100644
> --- a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
> +++ b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
> @@ -80,6 +80,11 @@ key-volume-up {
>  			wakeup-source;
>  		};
>  	};
> +
> +	pmih0108_e1_eusb2_repeater {
> +		vdd18-supply = <&vreg_l15b_1p8>;
> +		vdd3-supply = <&vreg_l5b_3p1>;
> +	};
>  };
>  
>  &apps_rsc {
> @@ -821,3 +826,25 @@ &ufs_mem_phy {
>  
>  	status = "okay";
>  };
> +
> +&usb {
> +	dr_mode = "peripheral";
> +
> +	status = "okay";
> +};
> +
> +&usb_hsphy {
> +	vdd-supply = <&vreg_l4f_0p8>;
> +	vdda12-supply = <&vreg_l1d_1p2>;
> +
> +	phys = <&pmih0108_e1_eusb2_repeater>;
> +
> +	status = "okay";
> +};
> +
> +&usb_dp_qmpphy {
> +	vdda-phy-supply = <&vreg_l1d_1p2>;
> +	vdda-pll-supply = <&vreg_l4f_0p8>;
> +
> +	status = "okay";
> +};
> -- 
> 2.34.1
> 

-- 
With best wishes
Dmitry

^ permalink raw reply

* [PATCH v5 3/4] rust: add visibility to of_device_table macro
From: Markus Probst via B4 Relay @ 2026-03-29 18:02 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Rafael J. Wysocki, Len Brown, Saravana Kannan
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, linux-acpi, Markus Probst
In-Reply-To: <20260329-synology_microp_initial-v5-0-27cb80bdf591@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Add visibility argument to macro to allow defining an of device table in
a different module than the driver, which can improve readability.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 rust/kernel/of.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
index 58b20c367f99..a0de98a23416 100644
--- a/rust/kernel/of.rs
+++ b/rust/kernel/of.rs
@@ -53,8 +53,9 @@ pub const fn new(compatible: &'static CStr) -> Self {
 /// Create an OF `IdTable` with an "alias" for modpost.
 #[macro_export]
 macro_rules! of_device_table {
-    ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
-        const $table_name: $crate::device_id::IdArray<
+    ($table_vis:vis $table_name:ident, $module_table_name:ident, $id_info_type: ty,
+        $table_data: expr) => {
+        $table_vis const $table_name: $crate::device_id::IdArray<
             $crate::of::DeviceId,
             $id_info_type,
             { $table_data.len() },

-- 
2.52.0



^ permalink raw reply related

* [PATCH v5 4/4] platform: Add initial synology microp driver
From: Markus Probst via B4 Relay @ 2026-03-29 18:02 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Rafael J. Wysocki, Len Brown, Saravana Kannan
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, linux-acpi, Markus Probst
In-Reply-To: <20260329-synology_microp_initial-v5-0-27cb80bdf591@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Add a initial synology microp driver, written in Rust.
The driver targets a microcontroller found in Synology NAS devices. It
currently only supports controlling of the power led, status led, alert
led and usb led. Other components such as fan control or handling
on-device buttons will be added once the required rust abstractions are
there.

This driver can be used both on arm and x86, thus it goes into the root
directory of drivers/platform.

Tested successfully on a Synology DS923+.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                                        |   6 +
 drivers/platform/Kconfig                           |   2 +
 drivers/platform/Makefile                          |   1 +
 drivers/platform/synology_microp/Kconfig           |  13 +
 drivers/platform/synology_microp/Makefile          |   3 +
 drivers/platform/synology_microp/TODO              |   7 +
 drivers/platform/synology_microp/command.rs        |  55 ++++
 drivers/platform/synology_microp/led.rs            | 276 +++++++++++++++++++++
 drivers/platform/synology_microp/model.rs          | 171 +++++++++++++
 .../platform/synology_microp/synology_microp.rs    |  54 ++++
 10 files changed, 588 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 83b5a45de729..24cc4f63cce6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25544,6 +25544,12 @@ F:	drivers/dma-buf/sync_*
 F:	include/linux/sync_file.h
 F:	include/uapi/linux/sync_file.h
 
+SYNOLOGY MICROP DRIVER
+M:	Markus Probst <markus.probst@posteo.de>
+S:	Maintained
+F:	Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml
+F:	drivers/platform/synology_microp/
+
 SYNOPSYS ARC ARCHITECTURE
 M:	Vineet Gupta <vgupta@kernel.org>
 L:	linux-snps-arc@lists.infradead.org
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 312788f249c9..996050566a4a 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -22,3 +22,5 @@ source "drivers/platform/arm64/Kconfig"
 source "drivers/platform/raspberrypi/Kconfig"
 
 source "drivers/platform/wmi/Kconfig"
+
+source "drivers/platform/synology_microp/Kconfig"
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index fa322e7f8716..2381872e9133 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_SURFACE_PLATFORMS)	+= surface/
 obj-$(CONFIG_ARM64_PLATFORM_DEVICES)	+= arm64/
 obj-$(CONFIG_BCM2835_VCHIQ)	+= raspberrypi/
 obj-$(CONFIG_ACPI_WMI)		+= wmi/
+obj-$(CONFIG_SYNOLOGY_MICROP)	+= synology_microp/
diff --git a/drivers/platform/synology_microp/Kconfig b/drivers/platform/synology_microp/Kconfig
new file mode 100644
index 000000000000..0c145a5b7174
--- /dev/null
+++ b/drivers/platform/synology_microp/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SYNOLOGY_MICROP
+	tristate "Synology Microp driver"
+	depends on LEDS_CLASS && LEDS_CLASS_MULTICOLOR
+	depends on RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	help
+	  Enable support for the MCU found in Synology NAS devices.
+
+	  This is needed to properly shutdown and reboot the device, as well as
+	  additional functionality like fan and LED control.
+
+	  This driver is work in progress and may not be fully functional.
diff --git a/drivers/platform/synology_microp/Makefile b/drivers/platform/synology_microp/Makefile
new file mode 100644
index 000000000000..63585ccf76e4
--- /dev/null
+++ b/drivers/platform/synology_microp/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += synology_microp.o
diff --git a/drivers/platform/synology_microp/TODO b/drivers/platform/synology_microp/TODO
new file mode 100644
index 000000000000..1961a33115db
--- /dev/null
+++ b/drivers/platform/synology_microp/TODO
@@ -0,0 +1,7 @@
+TODO:
+- add missing components:
+  - handle on-device buttons (Power, Factory reset, "USB Copy")
+  - handle fan failure
+  - beeper
+  - fan speed control
+  - correctly perform device power-off and restart on Synology devices
diff --git a/drivers/platform/synology_microp/command.rs b/drivers/platform/synology_microp/command.rs
new file mode 100644
index 000000000000..5b3dd715afac
--- /dev/null
+++ b/drivers/platform/synology_microp/command.rs
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    error::Result,
+    serdev, //
+};
+
+use crate::led;
+
+#[derive(Copy, Clone)]
+#[expect(
+    clippy::enum_variant_names,
+    reason = "future variants will not end with Led"
+)]
+pub(crate) enum Command {
+    PowerLed(led::State),
+    StatusLed(led::StatusLedColor, led::State),
+    AlertLed(led::State),
+    UsbLed(led::State),
+    EsataLed(led::State),
+}
+
+impl Command {
+    pub(crate) fn write(self, dev: &serdev::Device<Bound>) -> Result {
+        dev.write_all(
+            match self {
+                Self::PowerLed(led::State::On) => &[0x34],
+                Self::PowerLed(led::State::Blink) => &[0x35],
+                Self::PowerLed(led::State::Off) => &[0x36],
+
+                Self::StatusLed(_, led::State::Off) => &[0x37],
+                Self::StatusLed(led::StatusLedColor::Green, led::State::On) => &[0x38],
+                Self::StatusLed(led::StatusLedColor::Green, led::State::Blink) => &[0x39],
+                Self::StatusLed(led::StatusLedColor::Orange, led::State::On) => &[0x3A],
+                Self::StatusLed(led::StatusLedColor::Orange, led::State::Blink) => &[0x3B],
+
+                Self::AlertLed(led::State::On) => &[0x4C, 0x41, 0x31],
+                Self::AlertLed(led::State::Blink) => &[0x4C, 0x41, 0x32],
+                Self::AlertLed(led::State::Off) => &[0x4C, 0x41, 0x33],
+
+                Self::UsbLed(led::State::On) => &[0x40],
+                Self::UsbLed(led::State::Blink) => &[0x41],
+                Self::UsbLed(led::State::Off) => &[0x42],
+
+                Self::EsataLed(led::State::On) => &[0x4C, 0x45, 0x31],
+                Self::EsataLed(led::State::Blink) => &[0x4C, 0x45, 0x32],
+                Self::EsataLed(led::State::Off) => &[0x4C, 0x45, 0x33],
+            },
+            serdev::Timeout::Max,
+        )?;
+        dev.wait_until_sent(serdev::Timeout::Max);
+        Ok(())
+    }
+}
diff --git a/drivers/platform/synology_microp/led.rs b/drivers/platform/synology_microp/led.rs
new file mode 100644
index 000000000000..a78a95588456
--- /dev/null
+++ b/drivers/platform/synology_microp/led.rs
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    devres::{
+        self,
+        Devres, //
+    },
+    led::{
+        self,
+        LedOps,
+        MultiColorSubLed, //
+    },
+    new_mutex,
+    prelude::*,
+    serdev,
+    str::CString,
+    sync::Mutex, //
+};
+use pin_init::pin_init_scope;
+
+use crate::{
+    command::Command,
+    model::Model, //
+};
+
+#[pin_data]
+pub(crate) struct Data {
+    #[pin]
+    status: Devres<led::MultiColorDevice<StatusLedHandler>>,
+    power_name: CString,
+    #[pin]
+    power: Devres<led::Device<LedHandler>>,
+}
+
+impl Data {
+    pub(super) fn register<'a>(
+        dev: &'a serdev::Device<Bound>,
+        model: &'a Model,
+    ) -> impl PinInit<Self, Error> + 'a {
+        pin_init_scope(move || {
+            if let Some(color) = model.led_alert {
+                let name = CString::try_from_fmt(fmt!("{}:alarm", color.as_c_str().to_str()?))?;
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new().color(color).name(&name).build(
+                        dev,
+                        try_pin_init!(LedHandler {
+                            blink <- new_mutex!(false),
+                            command: Command::AlertLed,
+                        }),
+                    ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            if model.led_usb_copy {
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new()
+                        .color(led::Color::Green)
+                        .name(c"green:usb")
+                        .build(
+                            dev,
+                            try_pin_init!(LedHandler {
+                                blink <- new_mutex!(false),
+                                command: Command::UsbLed,
+                            }),
+                        ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            if model.led_esata {
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new()
+                        .color(led::Color::Green)
+                        .name(c"green:esata")
+                        .build(
+                            dev,
+                            try_pin_init!(LedHandler {
+                                blink <- new_mutex!(false),
+                                command: Command::EsataLed,
+                            }),
+                        ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            Ok(try_pin_init!(Self {
+                status <- led::DeviceBuilder::new()
+                    .color(led::Color::Multi)
+                    .name(c"multicolor:status")
+                    .build_multicolor(
+                        dev,
+                        try_pin_init!(StatusLedHandler {
+                            blink <- new_mutex!(false),
+                        }),
+                        StatusLedHandler::SUBLEDS,
+                    ),
+                power_name: CString::try_from_fmt(fmt!(
+                    "{}:power",
+                    model.led_power.as_c_str().to_str()?
+                ))?,
+                power <- led::DeviceBuilder::new()
+                    .color(model.led_power)
+                    .name(power_name)
+                    .build(
+                        dev,
+                        try_pin_init!(LedHandler {
+                            blink <- new_mutex!(true),
+                            command: Command::PowerLed,
+                        }),
+                    ),
+            }))
+        })
+    }
+}
+
+#[derive(Copy, Clone)]
+pub(crate) enum StatusLedColor {
+    Green,
+    Orange,
+}
+
+#[derive(Copy, Clone)]
+pub(crate) enum State {
+    On,
+    Blink,
+    Off,
+}
+
+#[pin_data]
+struct LedHandler {
+    #[pin]
+    blink: Mutex<bool>,
+    command: fn(State) -> Command,
+}
+
+#[vtable]
+impl LedOps for LedHandler {
+    type Bus = serdev::Device<Bound>;
+    type Mode = led::Normal;
+    const BLOCKING: bool = true;
+    const MAX_BRIGHTNESS: u32 = 1;
+
+    fn brightness_set(
+        &self,
+        dev: &Self::Bus,
+        _classdev: &led::Device<Self>,
+        brightness: u32,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        (self.command)(if brightness == 0 {
+            *blink = false;
+            State::Off
+        } else if *blink {
+            State::Blink
+        } else {
+            State::On
+        })
+        .write(dev)?;
+
+        Ok(())
+    }
+
+    fn blink_set(
+        &self,
+        dev: &Self::Bus,
+        _classdev: &led::Device<Self>,
+        delay_on: &mut usize,
+        delay_off: &mut usize,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+
+        (self.command)(if *delay_on == 0 && *delay_off != 0 {
+            State::Off
+        } else if *delay_on != 0 && *delay_off == 0 {
+            State::On
+        } else {
+            *blink = true;
+            *delay_on = 167;
+            *delay_off = 167;
+
+            State::Blink
+        })
+        .write(dev)
+    }
+}
+
+#[pin_data]
+struct StatusLedHandler {
+    #[pin]
+    blink: Mutex<bool>,
+}
+
+impl StatusLedHandler {
+    const SUBLEDS: &[MultiColorSubLed] = &[
+        MultiColorSubLed::new(led::Color::Green).initial_intensity(1),
+        MultiColorSubLed::new(led::Color::Orange),
+    ];
+}
+
+#[vtable]
+impl LedOps for StatusLedHandler {
+    type Bus = serdev::Device<Bound>;
+    type Mode = led::MultiColor;
+    const BLOCKING: bool = true;
+    const MAX_BRIGHTNESS: u32 = 1;
+
+    fn brightness_set(
+        &self,
+        dev: &Self::Bus,
+        classdev: &led::MultiColorDevice<Self>,
+        brightness: u32,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        if brightness == 0 {
+            *blink = false;
+        }
+
+        let (color, subled_brightness) = if classdev.subleds()[1].intensity == 0 {
+            (StatusLedColor::Green, classdev.subleds()[0].brightness)
+        } else {
+            (StatusLedColor::Orange, classdev.subleds()[1].brightness)
+        };
+
+        Command::StatusLed(
+            color,
+            if subled_brightness == 0 {
+                State::Off
+            } else if *blink {
+                State::Blink
+            } else {
+                State::On
+            },
+        )
+        .write(dev)
+    }
+
+    fn blink_set(
+        &self,
+        dev: &Self::Bus,
+        classdev: &led::MultiColorDevice<Self>,
+        delay_on: &mut usize,
+        delay_off: &mut usize,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        *blink = true;
+
+        let (color, subled_intensity) = if classdev.subleds()[1].intensity == 0 {
+            (StatusLedColor::Green, classdev.subleds()[0].intensity)
+        } else {
+            (StatusLedColor::Orange, classdev.subleds()[1].intensity)
+        };
+        Command::StatusLed(
+            color,
+            if *delay_on == 0 && *delay_off != 0 {
+                *blink = false;
+                State::Off
+            } else if subled_intensity == 0 {
+                State::Off
+            } else if *delay_on != 0 && *delay_off == 0 {
+                *blink = false;
+                State::On
+            } else {
+                *delay_on = 167;
+                *delay_off = 167;
+
+                State::Blink
+            },
+        )
+        .write(dev)
+    }
+}
diff --git a/drivers/platform/synology_microp/model.rs b/drivers/platform/synology_microp/model.rs
new file mode 100644
index 000000000000..b972aae2b805
--- /dev/null
+++ b/drivers/platform/synology_microp/model.rs
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    led::{
+        self,
+        Color, //
+    },
+    of::DeviceId, //
+};
+
+pub(crate) struct Architecture;
+
+impl Architecture {
+    const fn new() -> Self {
+        Self
+    }
+}
+
+pub(crate) struct Model {
+    #[expect(
+        dead_code,
+        reason = "needed later for architecture specific properties, like poweroff behaviour"
+    )]
+    pub(crate) arch: Architecture,
+    pub(crate) led_power: led::Color,
+    pub(crate) led_alert: Option<led::Color>,
+    pub(crate) led_usb_copy: bool,
+    pub(crate) led_esata: bool,
+}
+
+impl Model {
+    const fn new(arch: Architecture) -> Self {
+        Self {
+            arch,
+            led_power: led::Color::Blue,
+            led_alert: None,
+            led_usb_copy: false,
+            led_esata: false,
+        }
+    }
+
+    const fn led_power(self, color: led::Color) -> Self {
+        Self {
+            led_power: color,
+            ..self
+        }
+    }
+
+    const fn led_alert(self, color: led::Color) -> Self {
+        Self {
+            led_alert: Some(color),
+            ..self
+        }
+    }
+
+    const fn led_esata(self) -> Self {
+        Self {
+            led_esata: true,
+            ..self
+        }
+    }
+
+    const fn led_usb_copy(self) -> Self {
+        Self {
+            led_usb_copy: true,
+            ..self
+        }
+    }
+}
+
+macro_rules! models {
+    [
+        $($arch:ident $(.$arch_func:ident( $($arch_arg:tt)* ))*
+            @ [
+                $($model:ident $(.$func:ident( $($arg:tt)* ))*, )*
+            ],
+        )*
+    ] => {
+        models![
+            $(
+                {
+                    Architecture::new()
+                    $(
+                        .$arch_func($($arch_arg)*)
+                    )*
+                }
+                @
+                [
+                    $(
+                        $model $(.$func($($arg)*))*,
+                    )*
+                ],
+            )*
+        ]
+    };
+    [
+        $($arch:block
+            @ [
+                $($model:ident $(.$func:ident( $($arg:tt)* ))*, )*
+            ],
+        )*
+    ] => {
+        [
+            $(
+                $((
+                    DeviceId::new(::kernel::c_str!(
+                        ::core::concat!(
+                            "synology,",
+                            ::core::stringify!($model),
+                            "-microp",
+                        )
+                    )),
+                    Model::new($arch)
+                    $(
+                        .$func($($arg)*)
+                    )*
+                ),)*
+            )*
+        ]
+    };
+}
+
+kernel::of_device_table!(
+    pub(crate) OF_TABLE,
+    MODULE_OF_TABLE,
+    Model,
+    models![
+        apollolake @ [
+            ds918p,
+        ],
+        evansport @ [
+            ds214play,
+        ],
+        geminilakenk @ [
+            ds225p.led_usb_copy(),
+            ds425p,
+        ],
+        pineview @ [
+            ds710p.led_esata(),
+            ds1010p.led_alert(Color::Orange),
+        ],
+        r1000 @ [
+            ds923p,
+            ds723p,
+            ds1522p,
+            rs422p.led_power(Color::Green),
+        ],
+        r1000nk @ [
+            ds725p,
+        ],
+        rtd1296 @ [
+            ds118,
+        ],
+        rtd1619b @ [
+            ds124,
+            ds223.led_usb_copy(),
+            ds223j,
+        ],
+        v1000 @ [
+            ds1823xsp,
+            rs822p.led_power(Color::Green),
+            rs1221p.led_power(Color::Green),
+            rs1221rpp.led_power(Color::Green),
+        ],
+        v1000nk @ [
+            ds925p,
+            ds1525p,
+            ds1825p,
+        ],
+    ]
+);
diff --git a/drivers/platform/synology_microp/synology_microp.rs b/drivers/platform/synology_microp/synology_microp.rs
new file mode 100644
index 000000000000..51152cc14b1e
--- /dev/null
+++ b/drivers/platform/synology_microp/synology_microp.rs
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Synology Microp driver
+
+use kernel::{
+    device,
+    of,
+    prelude::*,
+    serdev, //
+};
+use pin_init::pin_init_scope;
+
+use crate::model::Model;
+
+pub(crate) mod command;
+mod led;
+mod model;
+
+kernel::module_serdev_device_driver! {
+    type: SynologyMicropDriver,
+    name: "synology_microp",
+    authors: ["Markus Probst <markus.probst@posteo.de>"],
+    description: "Synology Microp driver",
+    license: "GPL v2",
+}
+
+#[pin_data]
+struct SynologyMicropDriver {
+    #[pin]
+    led: led::Data,
+}
+
+#[vtable]
+impl serdev::Driver for SynologyMicropDriver {
+    type IdInfo = Model;
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&model::OF_TABLE);
+
+    fn probe(
+        dev: &serdev::Device<device::Core>,
+        model: Option<&Model>,
+    ) -> impl PinInit<Self, kernel::error::Error> {
+        pin_init_scope(move || {
+            let model = model.ok_or(EINVAL)?;
+
+            dev.set_baudrate(9600).map_err(|_| EINVAL)?;
+            dev.set_flow_control(false);
+            dev.set_parity(serdev::Parity::None)?;
+
+            Ok(try_pin_init!(Self {
+                led <- led::Data::register(dev, model),
+            }))
+        })
+    }
+}

-- 
2.52.0



^ permalink raw reply related

* [PATCH v5 2/4] ACPI: of: match PRP0001 in of_match_device
From: Markus Probst via B4 Relay @ 2026-03-29 18:02 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Rafael J. Wysocki, Len Brown, Saravana Kannan
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, linux-acpi, Markus Probst
In-Reply-To: <20260329-synology_microp_initial-v5-0-27cb80bdf591@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Export `acpi_of_match_device` function and use it to match for PRP0001
in `of_match_device`, if the device does not have a device node.

This fixes the match data being NULL when using ACPI PRP0001, even though
the device was matched against an of device table.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 drivers/acpi/bus.c   |  7 ++++---
 drivers/of/device.c  |  9 +++++++--
 include/linux/acpi.h | 11 +++++++++++
 3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 2ec095e2009e..cd02f04cf685 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -831,9 +831,9 @@ const struct acpi_device *acpi_companion_match(const struct device *dev)
  * identifiers and a _DSD object with the "compatible" property, use that
  * property to match against the given list of identifiers.
  */
-static bool acpi_of_match_device(const struct acpi_device *adev,
-				 const struct of_device_id *of_match_table,
-				 const struct of_device_id **of_id)
+bool acpi_of_match_device(const struct acpi_device *adev,
+			  const struct of_device_id *of_match_table,
+			  const struct of_device_id **of_id)
 {
 	const union acpi_object *of_compatible, *obj;
 	int i, nval;
@@ -866,6 +866,7 @@ static bool acpi_of_match_device(const struct acpi_device *adev,
 
 	return false;
 }
+EXPORT_SYMBOL_GPL(acpi_of_match_device);
 
 static bool acpi_of_modalias(struct acpi_device *adev,
 			     char *modalias, size_t len)
diff --git a/drivers/of/device.c b/drivers/of/device.c
index f7e75e527667..128682390058 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -11,6 +11,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
+#include <linux/acpi.h>
 
 #include <asm/errno.h>
 #include "of_private.h"
@@ -26,8 +27,12 @@
 const struct of_device_id *of_match_device(const struct of_device_id *matches,
 					   const struct device *dev)
 {
-	if (!matches || !dev->of_node || dev->of_node_reused)
-		return NULL;
+	if (!matches || !dev->of_node || dev->of_node_reused) {
+		const struct of_device_id *id = NULL;
+
+		acpi_of_match_device(ACPI_COMPANION(dev), matches, &id);
+		return id;
+	}
 	return of_match_node(matches, dev->of_node);
 }
 EXPORT_SYMBOL(of_match_device);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 4d2f0bed7a06..1cf23edcbfbb 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -736,6 +736,10 @@ const struct acpi_device_id *acpi_match_acpi_device(const struct acpi_device_id
 const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
 					       const struct device *dev);
 
+bool acpi_of_match_device(const struct acpi_device *adev,
+			  const struct of_device_id *of_match_table,
+			  const struct of_device_id **of_id);
+
 const void *acpi_device_get_match_data(const struct device *dev);
 extern bool acpi_driver_match_device(struct device *dev,
 				     const struct device_driver *drv);
@@ -965,6 +969,13 @@ static inline const struct acpi_device_id *acpi_match_device(
 	return NULL;
 }
 
+static inline bool acpi_of_match_device(const struct acpi_device *adev,
+					const struct of_device_id *of_match_table,
+					const struct of_device_id **of_id)
+{
+	return false;
+}
+
 static inline const void *acpi_device_get_match_data(const struct device *dev)
 {
 	return NULL;

-- 
2.52.0



^ permalink raw reply related

* [PATCH v5 0/4] Introduce Synology Microp driver
From: Markus Probst via B4 Relay @ 2026-03-29 18:02 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Rafael J. Wysocki, Len Brown, Saravana Kannan
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, linux-acpi, Markus Probst

Synology uses a microcontroller in their NAS devices connected to a
serial port to control certain LEDs, fan speeds, a beeper, to handle
proper shutdown and restart, buttons and fan failures.

This patch series depends on the rust led abstraction [1] and the rust
serdev abstraction [2].

This is only a initial version of the driver able to control LEDs.
The following rust abstractions would be required, to implement the
remaining features:
- hwmon (include/linux/hwmon.h)
- input (include/linux/input.h)
- sysoff handler + hardware protection shutdown (include/linux/reboot.h)

[1] https://lore.kernel.org/rust-for-linux/20260329-rust_leds-v13-0-21a599c5b2d1@posteo.de/
[2] https://lore.kernel.org/rust-for-linux/20260313-rust_serdev-v3-0-c9a3af214f7f@posteo.de/

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v5:
- add esata led support
- use different compatible for each model
- add visibility modifier to of_device_table macro
- fix match data missing when using PRP0001
- Link to v4: https://lore.kernel.org/r/20260320-synology_microp_initial-v4-0-0423ddb83ca4@posteo.de

Changes in v4:
- convert to monolithic driver and moved it into drivers/platform
- removed mfd rust abstraction
- moved dt-bindings to embedded-controller
- Link to v3: https://lore.kernel.org/r/20260313-synology_microp_initial-v3-0-ad6ac463a201@posteo.de

Changes in v3:
- remove `default n` from Kconfig entry, as n is the default already.
- select RUST_SERIAL_DEV_BUS_ABSTRACTIONS in Kconfig
- add mfd rust abstraction
- split core and led parts into their own driver. It should now be considered a
  MFD device.
- split led part of dt binding into its own file
- Link to v2: https://lore.kernel.org/r/20260308-synology_microp_initial-v2-0-9389963f31c5@posteo.de

Changes in v2:
- fix missing tabs in MAINTAINERS file
- remove word binding from patch subject
- add missing signed-off-by
- add missing help entry in Kconfig
- add missing spdx license headers
- remove no-check{,-cpu}-fan properties from the dt-bindings and replace
  them with the check_fan module parameter
- use patternProperties for leds in dt-bindings
- license dt-binding as GPL-2.0-only OR BSD-2-Clause
- move driver from staging tree into mfd tree and mark it as work in
  progress inside Kconfig
- only register alert and usb led if fwnode is present
- Link to v1: https://lore.kernel.org/r/20260306-synology_microp_initial-v1-0-fcffede6448c@posteo.de

---
Markus Probst (4):
      dt-bindings: embedded-controller: Add synology microp devices
      ACPI: of: match PRP0001 in of_match_device
      rust: add visibility to of_device_table macro
      platform: Add initial synology microp driver

 .../synology,ds923p-microp.yaml                    |  65 +++++
 MAINTAINERS                                        |   6 +
 drivers/acpi/bus.c                                 |   7 +-
 drivers/of/device.c                                |   9 +-
 drivers/platform/Kconfig                           |   2 +
 drivers/platform/Makefile                          |   1 +
 drivers/platform/synology_microp/Kconfig           |  13 +
 drivers/platform/synology_microp/Makefile          |   3 +
 drivers/platform/synology_microp/TODO              |   7 +
 drivers/platform/synology_microp/command.rs        |  55 ++++
 drivers/platform/synology_microp/led.rs            | 276 +++++++++++++++++++++
 drivers/platform/synology_microp/model.rs          | 171 +++++++++++++
 .../platform/synology_microp/synology_microp.rs    |  54 ++++
 include/linux/acpi.h                               |  11 +
 rust/kernel/of.rs                                  |   5 +-
 15 files changed, 678 insertions(+), 7 deletions(-)
---
base-commit: d1d81e9d1a4dd846aee9ae77ff9ecc2800d72148
change-id: 20260306-synology_microp_initial-0f7dac7b7496
prerequisite-change-id: 20251217-rust_serdev-ee5481e9085c:v3
prerequisite-patch-id: 52b17274481cc770c257d8f95335293eca32a2c5
prerequisite-patch-id: eec47e5051640d08bcd34a9670b98804449cad52
prerequisite-patch-id: f24b68c71c3f69371e8ac0251efca0a023b31cc4
prerequisite-patch-id: 3dfc1f7e5ecd3e0dd65d676aeb16f55260847b25
prerequisite-change-id: 20251114-rust_leds-a959f7c2f7f9:v13
prerequisite-patch-id: 818700f22dcb9676157c985f82762d7c607b861e
prerequisite-patch-id: b15ffa7d95d9260151bfb116b259c4473f721c82
prerequisite-patch-id: 8c47e0d107530f577a1be0b79f8ee791f95d3cbe



^ permalink raw reply

* [PATCH v5 1/4] dt-bindings: embedded-controller: Add synology microp devices
From: Markus Probst via B4 Relay @ 2026-03-29 18:02 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman,
	Rafael J. Wysocki, Len Brown, Saravana Kannan
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, linux-acpi, Markus Probst
In-Reply-To: <20260329-synology_microp_initial-v5-0-27cb80bdf591@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Add the Synology Microp devicetree bindings. Those devices are
microcontrollers found on Synology NAS devices. They are connected to a
serial port on the host device.

Those devices are used to control certain LEDs, fan speeds, a beeper, to
handle buttons, fan failures and to properly shutdown and reboot the
device.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 .../synology,ds923p-microp.yaml                    | 65 ++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml b/Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml
new file mode 100644
index 000000000000..599d32ce2be9
--- /dev/null
+++ b/Documentation/devicetree/bindings/embedded-controller/synology,ds923p-microp.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/embedded-controller/synology,ds923p-microp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synology NAS on-board Microcontroller
+
+maintainers:
+  - Markus Probst <markus.probst@posteo.de>
+
+description: |
+  Synology Microp is a microcontroller found in Synology NAS devices.
+  It is connected to a serial port on the host device.
+
+  It is necessary to properly shutdown and reboot the NAS device and
+  provides additional functionality such as led control, fan speed control,
+  a beeper and buttons on the NAS device.
+
+properties:
+  compatible:
+    enum:
+      - synology,ds923p-microp
+      - synology,ds918p-microp
+      - synology,ds214play-microp
+      - synology,ds225p-microp
+      - synology,ds425p-microp
+      - synology,ds710p-microp
+      - synology,ds1010p-microp
+      - synology,ds723p-microp
+      - synology,ds1522p-microp
+      - synology,rs422p-microp
+      - synology,ds725p-microp
+      - synology,ds118-microp
+      - synology,ds124-microp
+      - synology,ds223-microp
+      - synology,ds223j-microp
+      - synology,ds1823xsp-microp
+      - synology,rs822p-microp
+      - synology,rs1221p-microp
+      - synology,rs1221rpp-microp
+      - synology,ds925p-microp
+      - synology,ds1525p-microp
+      - synology,ds1825p-microp
+
+  fan-failure-gpios:
+    description: GPIOs needed to determine which fans stopped working on a fan failure event.
+    minItems: 2
+    maxItems: 3
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/leds/common.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    embedded-controller {
+      compatible = "synology,ds923p-microp";
+
+      fan-failure-gpios = <&gpio 68 GPIO_ACTIVE_HIGH>, <&gpio 69 GPIO_ACTIVE_HIGH>;
+    };

-- 
2.52.0



^ permalink raw reply related

* Re: [PATCH v3 06/24] dt-bindings: firmware: arm,scmi: Add support for telemetry protocol
From: Cristian Marussi @ 2026-03-29 18:00 UTC (permalink / raw)
  To: Rob Herring (Arm)
  Cc: Cristian Marussi, etienne.carriere, Krzysztof Kozlowski, d-gole,
	linux-fsdevel, Conor Dooley, linux-doc, f.fainelli,
	vincent.guittot, philip.radford, souvik.chakravarty, peng.fan,
	dan.carpenter, lukasz.luba, arm-scmi, sudeep.holla, michal.simek,
	linux-kernel, jonathan.cameron, elif.topuz, linux-arm-kernel,
	james.quinlan, devicetree, brauner
In-Reply-To: <177480549380.3925363.5137815678176793743.robh@kernel.org>

On Sun, Mar 29, 2026 at 12:31:33PM -0500, Rob Herring (Arm) wrote:
> 
> On Sun, 29 Mar 2026 17:33:17 +0100, Cristian Marussi wrote:
> > Add new SCMI v4.0 Telemetry protocol bindings definitions.
> > 
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> > Cc: Rob Herring <robh@kernel.org>
> > Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> > Cc: Conor Dooley <conor+dt@kernel.org>
> > Cc: devicetree@vger.kernel.org
> > ---
> >  Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 8 ++++++++
> >  1 file changed, 8 insertions(+)
> > 
> 
> My bot found errors running 'make dt_binding_check' on your patch:
> 
> yamllint warnings/errors:
> 
> dtschema/dtc warnings/errors:
> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/firmware/arm,scmi.example.dtb: scmi (arm,scmi): protocol@19: Unevaluated properties are not allowed ('i2c2-pins', 'keys-pins', 'mdio-pins' were unexpected)
> 	from schema $id: http://devicetree.org/schemas/firmware/arm,scmi.yaml
> 
> doc reference errors (make refcheckdocs):
> 
> See https://patchwork.kernel.org/project/devicetree/patch/20260329163337.637393-7-cristian.marussi@arm.com
> 
> The base for the series is generally the latest rc1. A different dependency
> should be noted in *this* patch.
> 
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
> 
> pip3 install dtschema --upgrade
> 

Yes...the new protocol block definition ended up intermixed with the
previous protocol block...totally wrong.

My bad.

I will fix in V4.

Thanks,
Cristian

^ permalink raw reply

* [PATCH v2 3/3] arm64: dts: qcom: kaanpaali: Add USB support for QRD platform
From: Krishna Kurapati @ 2026-03-29 17:52 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel, Ronak Raheja,
	Jingyi Wang, Krishna Kurapati
In-Reply-To: <20260329175249.2946508-1-krishna.kurapati@oss.qualcomm.com>

From: Ronak Raheja <ronak.raheja@oss.qualcomm.com>

Enable USB support on Kaanapali QRD variant. Enable USB controller in
device mode till glink node is added.

Signed-off-by: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/kaanapali-qrd.dts | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
index 6a7eb7f4050a..1929ea273a4f 100644
--- a/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
+++ b/arch/arm64/boot/dts/qcom/kaanapali-qrd.dts
@@ -80,6 +80,11 @@ key-volume-up {
 			wakeup-source;
 		};
 	};
+
+	pmih0108_e1_eusb2_repeater {
+		vdd18-supply = <&vreg_l15b_1p8>;
+		vdd3-supply = <&vreg_l5b_3p1>;
+	};
 };
 
 &apps_rsc {
@@ -821,3 +826,25 @@ &ufs_mem_phy {
 
 	status = "okay";
 };
+
+&usb {
+	dr_mode = "peripheral";
+
+	status = "okay";
+};
+
+&usb_hsphy {
+	vdd-supply = <&vreg_l4f_0p8>;
+	vdda12-supply = <&vreg_l1d_1p2>;
+
+	phys = <&pmih0108_e1_eusb2_repeater>;
+
+	status = "okay";
+};
+
+&usb_dp_qmpphy {
+	vdda-phy-supply = <&vreg_l1d_1p2>;
+	vdda-pll-supply = <&vreg_l4f_0p8>;
+
+	status = "okay";
+};
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 2/3] arm64: dts: qcom: kaanpaali: Add USB support for MTP platform
From: Krishna Kurapati @ 2026-03-29 17:52 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel, Ronak Raheja,
	Jingyi Wang, Krishna Kurapati
In-Reply-To: <20260329175249.2946508-1-krishna.kurapati@oss.qualcomm.com>

From: Ronak Raheja <ronak.raheja@oss.qualcomm.com>

Enable USB support on Kaanapali MTP variant. Enable USB controller in
device mode till glink node is added.

Signed-off-by: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/kaanapali-mtp.dts | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts b/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts
index a603f3056d83..38269aebdf03 100644
--- a/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/kaanapali-mtp.dts
@@ -82,6 +82,11 @@ key-volume-up {
 		};
 	};
 
+	pmih0108_e1_eusb2_repeater {
+		vdd18-supply = <&vreg_l15b_1p8>;
+		vdd3-supply = <&vreg_l5b_3p1>;
+	};
+
 	sound {
 		compatible = "qcom,kaanapali-sndcard", "qcom,sm8450-sndcard";
 		model = "Kaanapali-MTP";
@@ -1326,3 +1331,25 @@ &ufs_mem_phy {
 
 	status = "okay";
 };
+
+&usb {
+	dr_mode = "peripheral";
+
+	status = "okay";
+};
+
+&usb_hsphy {
+	vdd-supply = <&vreg_l4f_0p8>;
+	vdda12-supply = <&vreg_l1d_1p2>;
+
+	phys = <&pmih0108_e1_eusb2_repeater>;
+
+	status = "okay";
+};
+
+&usb_dp_qmpphy {
+	vdda-phy-supply = <&vreg_l1d_1p2>;
+	vdda-pll-supply = <&vreg_l4f_0p8>;
+
+	status = "okay";
+};
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 1/3] arm64: dts: qcom: kaanapali: Add USB support for Kaanapali SoC
From: Krishna Kurapati @ 2026-03-29 17:52 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel, Ronak Raheja,
	Jingyi Wang, Krishna Kurapati
In-Reply-To: <20260329175249.2946508-1-krishna.kurapati@oss.qualcomm.com>

From: Ronak Raheja <ronak.raheja@oss.qualcomm.com>

Add the base USB devicetree definitions for Kaanapali platform. The overall
chipset contains a single DWC3 USB3 controller (rev. 200a), SS QMP PHY
(rev. v8) and M31 eUSB2 PHY.

Signed-off-by: Ronak Raheja <ronak.raheja@oss.qualcomm.com>
Signed-off-by: Jingyi Wang <jingyi.wang@oss.qualcomm.com>
Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/kaanapali.dtsi | 154 ++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/kaanapali.dtsi b/arch/arm64/boot/dts/qcom/kaanapali.dtsi
index ac6a6c789902..08d7c1a1d829 100644
--- a/arch/arm64/boot/dts/qcom/kaanapali.dtsi
+++ b/arch/arm64/boot/dts/qcom/kaanapali.dtsi
@@ -6026,6 +6026,160 @@ pdp_tx: scp-sram-section@100 {
 				reg = <0x100 0x80>;
 			};
 		};
+
+		usb_hsphy: phy@88e3000 {
+			compatible = "qcom,kaanapali-m31-eusb2-phy",
+				     "qcom,sm8750-m31-eusb2-phy";
+			reg = <0x0 0x88e3000 0x0 0x29c>;
+
+			clocks = <&tcsr TCSR_USB2_CLKREF_EN>;
+			clock-names = "ref";
+
+			resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
+
+			#phy-cells = <0>;
+
+			status = "disabled";
+		};
+
+		usb_dp_qmpphy: phy@88e8000 {
+			compatible = "qcom,kaanapali-qmp-usb3-dp-phy",
+				     "qcom,sm8750-qmp-usb3-dp-phy";
+			reg = <0x0 0x088e8000 0x0 0x4000>;
+
+			clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
+				 <&tcsr TCSR_USB3_CLKREF_EN>,
+				 <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>,
+				 <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+			clock-names = "aux",
+				      "ref",
+				      "com_aux",
+				      "usb3_pipe";
+
+			resets = <&gcc GCC_USB3_PHY_PRIM_BCR>,
+				 <&gcc GCC_USB3_DP_PHY_PRIM_BCR>;
+			reset-names = "phy",
+				      "common";
+
+			power-domains = <&gcc GCC_USB3_PHY_GDSC>;
+
+			#clock-cells = <1>;
+			#phy-cells = <1>;
+
+			orientation-switch;
+
+			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+
+					usb_dp_qmpphy_out: endpoint {
+					};
+				};
+
+				port@1 {
+					reg = <1>;
+
+					usb_dp_qmpphy_usb_ss_in: endpoint {
+						remote-endpoint = <&usb_dwc3_ss>;
+					};
+				};
+
+				port@2 {
+					reg = <2>;
+
+					usb_dp_qmpphy_dp_in: endpoint {
+					};
+				};
+			};
+		};
+
+		usb: usb@a600000 {
+			compatible = "qcom,kaanapali-dwc3", "qcom,snps-dwc3";
+			reg = <0x0 0x0a600000 0x0 0xfc100>;
+
+			clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
+				 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
+				 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
+				 <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+				 <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+			clock-names = "cfg_noc",
+				      "core",
+				      "iface",
+				      "sleep",
+				      "mock_utmi";
+
+			assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
+					  <&gcc GCC_USB30_PRIM_MASTER_CLK>;
+			assigned-clock-rates = <19200000>, <200000000>;
+
+			interrupts-extended = <&intc GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>,
+					      <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
+					      <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
+					      <&pdc 14 IRQ_TYPE_EDGE_BOTH>,
+					      <&pdc 15 IRQ_TYPE_EDGE_BOTH>,
+					      <&pdc 17 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "dwc_usb3",
+					  "pwr_event",
+					  "hs_phy_irq",
+					  "dp_hs_phy_irq",
+					  "dm_hs_phy_irq",
+					  "ss_phy_irq";
+
+			power-domains = <&gcc GCC_USB30_PRIM_GDSC>;
+			required-opps = <&rpmhpd_opp_nom>;
+
+			resets = <&gcc GCC_USB30_PRIM_BCR>;
+
+			interconnects = <&aggre_noc MASTER_USB3 QCOM_ICC_TAG_ALWAYS
+					 &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
+					<&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
+					 &config_noc SLAVE_USB3 QCOM_ICC_TAG_ACTIVE_ONLY>;
+			interconnect-names = "usb-ddr", "apps-usb";
+			iommus = <&apps_smmu 0x40 0x0>;
+
+			phys = <&usb_hsphy>, <&usb_dp_qmpphy QMP_USB43DP_USB3_PHY>;
+			phy-names = "usb2-phy", "usb3-phy";
+
+			snps,hird-threshold = /bits/ 8 <0x0>;
+			snps,usb2-gadget-lpm-disable;
+			snps,dis_u2_susphy_quirk;
+			snps,dis_enblslpm_quirk;
+			snps,dis-u1-entry-quirk;
+			snps,dis-u2-entry-quirk;
+			snps,is-utmi-l1-suspend;
+			snps,usb3_lpm_capable;
+			snps,usb2-lpm-disable;
+			snps,has-lpm-erratum;
+			tx-fifo-resize;
+			dma-coherent;
+
+			status = "disabled";
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+
+					usb_dwc3_hs: endpoint {
+					};
+				};
+
+				port@1 {
+					reg = <1>;
+
+					usb_dwc3_ss: endpoint {
+						remote-endpoint = <&usb_dp_qmpphy_usb_ss_in>;
+					};
+				};
+			};
+		};
 	};
 
 	thermal-zones {
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 0/3] Add USB support for Kaanapali
From: Krishna Kurapati @ 2026-03-29 17:52 UTC (permalink / raw)
  To: Konrad Dybcio, Bjorn Andersson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel, Krishna Kurapati

Add support for the PHYs and controllers used for USB on Kaanapali SoCs.

V1 of this was a consolidated series of many functionlities on Kaanapali.
The first patch is patch-6 from v1 unchanges. The second and third patches
are parts of MTP and QRD specific changes (patches 14 and 16) and commit
text modified to indicate mtp and qrd specific changes are being made.

Ronak is the original author of the patches. Kept Jingyi's and mine SoB
(with no CDB) since we just rebased and send the patches in v1 and v2.

Since SoCCP changes are not acked yet, enabling only device mode.

Changes in v2:
- Sent USB specific changes instead of all MTP and QRD specific changes

Link to v1:
https://lore.kernel.org/all/20250924-knp-dts-v1-0-3fdbc4b9e1b1@oss.qualcomm.com/

Ronak Raheja (3):
  arm64: dts: qcom: kaanapali: Add USB support for Kaanapali SoC
  arm64: dts: qcom: kaanpaali: Add USB support for MTP platform
  arm64: dts: qcom: kaanpaali: Add USB support for QRD platform

 arch/arm64/boot/dts/qcom/kaanapali-mtp.dts |  27 ++++
 arch/arm64/boot/dts/qcom/kaanapali-qrd.dts |  27 ++++
 arch/arm64/boot/dts/qcom/kaanapali.dtsi    | 154 +++++++++++++++++++++
 3 files changed, 208 insertions(+)

-- 
2.34.1


^ permalink raw reply


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