* Re: [PATCH net-next v9 2/3] net: airoha: fix ETS QoS stats counter underflow and cross-channel corruption
From: Jacob Keller @ 2026-07-20 22:42 UTC (permalink / raw)
To: Lorenzo Bianconi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260721-airoha-ethtool-priv_flags-v9-2-9c15d8b71a56@kernel.org>
On 7/20/2026 3:03 PM, Lorenzo Bianconi wrote:
> airoha_qdma_get_tx_ets_stats() has two bugs:
> - The hardware counters read via airoha_qdma_rr() are 32-bit values
> but are stored in u64 locals and subtracted from u64 baselines. When
> a 32-bit hardware counter wraps around, the subtraction produces a
> large underflow value passed to _bstats_update().
This issue would only be a problem during rollover, which depending on
how fast the counts increment may not be a big problem. I could see this
not being worth going to net since it could be rare enough that it isn't
considered a widespread issue...
> - The baseline counters (cpu_tx_packets, fwd_tx_packets) are stored as
> single per-device fields, but airoha_qdma_get_tx_ets_stats() is
> called with different channel values (0-3). Each call reads a
> different channel's hardware counter but overwrites the same
> baseline, corrupting the delta computation for other channels.
>
However, this issue seems like its going to cause a problem every time
you read because any time you use a mix of channels you will get
corrupted values?
> Fix both by:
> - Narrowing the counter locals and baselines to u32 so that 32-bit
> unsigned subtraction handles wrap-around naturally.
> - Grouping the baselines into a per-channel qos_stats array so each
> channel tracks its own previous counter value independently.
> - Splitting the delta addition into two statements so the first u32
> delta is widened to u64 on assignment and the second is added in
> u64 arithmetic, preventing overflow when both deltas are large.
>
> Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support")
This targets a commit which merged in v6.14, but the patch is part of a
series aimed at net-next. Could you explain why this shouldn't be
separated out and put as a fix in net? It seems pretty obvious that
users can easily reproduce problems by requesting stats from each
channel? Or is this not really possible to trigger from userspace until
patch 3/3?
> Reviewed-by: Simon Horman <horms@kernel.org>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++-------
> drivers/net/ethernet/airoha/airoha_eth.h | 7 ++++---
> 2 files changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 41c1a0ffbdd8..aaf2a4717d12 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2482,16 +2482,20 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> struct airoha_qdma *qdma = dev->qdma;
> + u32 cpu_tx_packets, fwd_tx_packets;
> + u64 tx_packets;
>
> - u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
> - u64 fwd_tx_packets = airoha_qdma_rr(qdma,
> - REG_CNTR_VAL((channel << 1) + 1));
> - u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
> - (fwd_tx_packets - dev->fwd_tx_packets);
> + cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
> + fwd_tx_packets = airoha_qdma_rr(qdma,
> + REG_CNTR_VAL((channel << 1) + 1));
> + tx_packets = (u32)(cpu_tx_packets -
> + dev->qos_stats[channel].cpu_tx_packets);
> + tx_packets += (u32)(fwd_tx_packets -
> + dev->qos_stats[channel].fwd_tx_packets);
>
> _bstats_update(opt->stats.bstats, 0, tx_packets);
> - dev->cpu_tx_packets = cpu_tx_packets;
> - dev->fwd_tx_packets = fwd_tx_packets;
> + dev->qos_stats[channel].cpu_tx_packets = cpu_tx_packets;
> + dev->qos_stats[channel].fwd_tx_packets = fwd_tx_packets;
>
> return 0;
> }
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
> index bf1c249255bd..bf44be9f0954 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.h
> +++ b/drivers/net/ethernet/airoha/airoha_eth.h
> @@ -553,9 +553,10 @@ struct airoha_gdm_dev {
> struct airoha_eth *eth;
>
> DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
> - /* qos stats counters */
> - u64 cpu_tx_packets;
> - u64 fwd_tx_packets;
> + struct {
> + u32 cpu_tx_packets;
> + u32 fwd_tx_packets;
> + } qos_stats[AIROHA_NUM_QOS_CHANNELS];
>
> u32 flags;
> int nbq;
>
^ permalink raw reply
* Re: [PATCH net-next v9 1/3] net: airoha: rename airoha_priv_flags to airoha_dev_flags
From: Jacob Keller @ 2026-07-20 22:36 UTC (permalink / raw)
To: Lorenzo Bianconi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260721-airoha-ethtool-priv_flags-v9-1-9c15d8b71a56@kernel.org>
On 7/20/2026 3:03 PM, Lorenzo Bianconi wrote:
> Rename the airoha_priv_flags enum to airoha_dev_flags and the
> AIROHA_PRIV_F_WAN flag to AIROHA_DEV_F_WAN. The "priv_flags" naming
> dates back to an earlier design that used ethtool private flags; since
> this series switched to tc qdisc offload for LAN/WAN configuration,
> align the naming to reflect that these are per-device flags rather than
> ethtool private flags. No functional change.
>
> Reviewed-by: Simon Horman <horms@kernel.org>
> Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
^ permalink raw reply
* [PATCH net-next v9 3/3] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
From: Lorenzo Bianconi @ 2026-07-20 22:03 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
In-Reply-To: <20260721-airoha-ethtool-priv_flags-v9-0-9c15d8b71a56@kernel.org>
GDM3 and GDM4 ports require GDM2 loopback to be enabled for hardware
QoS offload to function. Without it, HTB and ETS offload on these ports
do not work.
Previously, GDM3/GDM4 ports were automatically configured as WAN with
GDM2 loopback enabled during ndo_init(). Add the capability to configure
GDM3/GDM4 as WAN/LAN on demand when QoS offload is created or destroyed.
Hook airoha_enable_qos_for_gdm34() into TC_HTB_CREATE so that requesting
HTB offload on a GDM3/GDM4 LAN port switches it to WAN mode and enables
GDM2 loopback, with proper rollback on failure. Introduce the
AIROHA_DEV_F_QOS flag to track whether a device has an active HTB
qdisc; clear it on TC_HTB_DESTROY. The device keeps its WAN role after
qdisc teardown so that its configuration is preserved until another
device explicitly needs the WAN role for QoS offload.
If another GDM3/GDM4 device already holds the WAN role without an active
QoS qdisc, demote it to LAN before promoting the requesting device. Skip
the demotion when the requesting device is itself already the WAN device.
Since airoha_dev_set_qdma() can now be called on a running device to
migrate between QDMA blocks, make dev->qdma an RCU pointer so the TX
path can safely dereference it without holding RTNL.
Hold flow_offload_mutex in airoha_enable_qos_for_gdm34() and
airoha_disable_qos_for_gdm34() around the dev->flags update,
airoha_dev_set_qdma() and GDM2 loopback configuration, serializing
against concurrent airoha_ppe_hw_init() in the TC_SETUP_CLSFLOWER
offload path.
Introduce airoha_qdma_deref() helper that wraps rcu_dereference_protected()
with a lockdep condition accepting either rtnl_lock or flow_offload_mutex,
and use it across all control-path dereferences of the RCU-protected
dev->qdma pointer.
Add airoha_disable_gdm2_loopback() to disable GDM2 hw loopback.
Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 233 ++++++++++++++++++++++++++----
drivers/net/ethernet/airoha/airoha_eth.h | 13 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 9 +-
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
4 files changed, 227 insertions(+), 29 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index aaf2a4717d12..fe2e2af67173 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -934,7 +934,7 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
if (!dev)
continue;
- if (dev->qdma != qdma)
+ if (rcu_access_pointer(dev->qdma) != qdma)
continue;
netdev = netdev_from_priv(dev);
@@ -1866,8 +1866,8 @@ static int airoha_dev_open(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = dev->qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
+ struct airoha_qdma *qdma;
int err;
netif_tx_start_all_queues(netdev);
@@ -1875,6 +1875,7 @@ static int airoha_dev_open(struct net_device *netdev)
if (err)
return err;
+ qdma = airoha_qdma_deref(dev);
if (netdev_uses_dsa(netdev))
airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
GDM_STAG_EN_MASK);
@@ -1898,7 +1899,6 @@ static int airoha_dev_stop(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = dev->qdma;
netif_tx_disable(netdev);
airoha_set_vip_for_gdm_port(dev, false);
@@ -1906,7 +1906,7 @@ static int airoha_dev_stop(struct net_device *netdev)
if (--port->users)
airoha_ppe_set_xmit_frame_size(dev);
else
- airoha_set_gdm_port_fwd_cfg(qdma->eth,
+ airoha_set_gdm_port_fwd_cfg(dev->eth,
REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
return 0;
@@ -1989,6 +1989,53 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
return 0;
}
+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
+{
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ int i, src_port;
+ u32 pse_port;
+
+ src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
+ if (src_port < 0)
+ return src_port;
+
+ airoha_fe_clear(eth,
+ REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
+ SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
+
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ GDM_STRIP_CRC_MASK);
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ FE_PSE_PORT_DROP);
+ airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
+ LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
+ pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+ : FE_PSE_PORT_PPE1;
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ pse_port);
+
+ airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
+ FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
+
+ for (i = 0; i < eth->soc->num_ppe; i++)
+ airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
+ DFT_CPORT_MASK(AIROHA_GDM2_IDX));
+
+ /* Enable VIP and IFC for GDM2 */
+ airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+ airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
+
+ if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
+ u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
+
+ airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
+ FC_MAP6_DEF_VALUE & mask);
+ }
+
+ return 0;
+}
+
static struct airoha_gdm_dev *
airoha_get_wan_gdm_dev(struct airoha_eth *eth)
{
@@ -2015,15 +2062,35 @@ airoha_get_wan_gdm_dev(struct airoha_eth *eth)
static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
{
struct net_device *netdev = netdev_from_priv(dev);
+ struct airoha_qdma *cur_qdma, *qdma;
struct airoha_eth *eth = dev->eth;
- int ppe_id;
+ int i, ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
- netdev->irq = dev->qdma->irq_banks[0].irq;
+ qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
+ cur_qdma = airoha_qdma_deref(dev);
+
+ rcu_assign_pointer(dev->qdma, qdma);
+ netdev->irq = qdma->irq_banks[0].irq;
ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1);
airoha_ppe_set_cpu_port(dev, ppe_id, airoha_get_fe_port(dev));
+
+ if (!cur_qdma)
+ return;
+
+ /* Seed qos_stats baselines with the new QDMA block's current
+ * counter values to avoid a spurious spike on the first stats
+ * query after migration.
+ */
+ for (i = 0; i < ARRAY_SIZE(dev->qos_stats); i++) {
+ dev->qos_stats[i].cpu_tx_packets =
+ airoha_qdma_rr(qdma, REG_CNTR_VAL(i << 1));
+ dev->qos_stats[i].fwd_tx_packets =
+ airoha_qdma_rr(qdma, REG_CNTR_VAL((i << 1) + 1));
+ }
+ synchronize_rcu();
+ netif_tx_wake_all_queues(netdev);
}
static int airoha_dev_init(struct net_device *netdev)
@@ -2178,9 +2245,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
u32 nr_frags, tag, msg0, msg1, len;
struct airoha_queue_entry *e;
+ struct airoha_qdma *qdma;
struct netdev_queue *txq;
struct airoha_queue *q;
LIST_HEAD(tx_list);
@@ -2189,6 +2256,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
u16 index;
u8 fport;
+ rcu_read_lock();
+ qdma = rcu_dereference(dev->qdma);
qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb));
tag = airoha_get_dsa_tag(skb, netdev);
@@ -2238,6 +2307,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
netif_tx_stop_queue(txq);
q->txq_stopped = true;
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
+
return NETDEV_TX_BUSY;
}
@@ -2303,6 +2374,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
return NETDEV_TX_OK;
@@ -2315,6 +2387,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
error:
dev_kfree_skb_any(skb);
netdev->stats.tx_dropped++;
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
@@ -2394,17 +2467,19 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
const u16 *weights, u8 n_weights)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma;
int i;
+ qdma = airoha_qdma_deref(dev);
for (i = 0; i < AIROHA_NUM_QOS_QUEUES; i++)
- airoha_qdma_clear(dev->qdma, REG_QUEUE_CLOSE_CFG(channel),
+ airoha_qdma_clear(qdma, REG_QUEUE_CLOSE_CFG(channel),
TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i));
for (i = 0; i < n_weights; i++) {
u32 status;
int err;
- airoha_qdma_wr(dev->qdma, REG_TXWRR_WEIGHT_CFG,
+ airoha_qdma_wr(qdma, REG_TXWRR_WEIGHT_CFG,
TWRR_RW_CMD_MASK |
FIELD_PREP(TWRR_CHAN_IDX_MASK, channel) |
FIELD_PREP(TWRR_QUEUE_IDX_MASK, i) |
@@ -2412,12 +2487,12 @@ static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
err = read_poll_timeout(airoha_qdma_rr, status,
status & TWRR_RW_CMD_DONE,
USEC_PER_MSEC, 10 * USEC_PER_MSEC,
- true, dev->qdma, REG_TXWRR_WEIGHT_CFG);
+ true, qdma, REG_TXWRR_WEIGHT_CFG);
if (err)
return err;
}
- airoha_qdma_rmw(dev->qdma, REG_CHAN_QOS_MODE(channel >> 3),
+ airoha_qdma_rmw(qdma, REG_CHAN_QOS_MODE(channel >> 3),
CHAN_QOS_MODE_MASK(channel),
__field_prep(CHAN_QOS_MODE_MASK(channel), mode));
@@ -2481,10 +2556,11 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
u32 cpu_tx_packets, fwd_tx_packets;
+ struct airoha_qdma *qdma;
u64 tx_packets;
+ qdma = airoha_qdma_deref(dev);
cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
fwd_tx_packets = airoha_qdma_rr(qdma,
REG_CNTR_VAL((channel << 1) + 1));
@@ -2751,16 +2827,18 @@ static int airoha_qdma_set_tx_rate_limit(struct net_device *netdev,
u32 bucket_size)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma;
int i, err;
+ qdma = airoha_qdma_deref(dev);
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
- err = airoha_qdma_set_trtcm_config(dev->qdma, channel,
+ err = airoha_qdma_set_trtcm_config(qdma, channel,
REG_EGRESS_TRTCM_CFG, i,
!!rate, TRTCM_METER_MODE);
if (err)
return err;
- err = airoha_qdma_set_trtcm_token_bucket(dev->qdma, channel,
+ err = airoha_qdma_set_trtcm_token_bucket(qdma, channel,
REG_EGRESS_TRTCM_CFG,
i, rate, bucket_size);
if (err)
@@ -2796,11 +2874,12 @@ static int airoha_tc_htb_alloc_leaf_queue(struct net_device *netdev,
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
int err, num_tx_queues = AIROHA_NUM_TX_RING + channel + 1;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
/* Here we need to check the requested QDMA channel is not already
* in use by another net_device running on the same QDMA block.
*/
+ qdma = airoha_qdma_deref(dev);
if (test_and_set_bit(channel, qdma->qos_channel_map)) {
NL_SET_ERR_MSG_MOD(opt->extack,
"qdma qos channel already in use");
@@ -2836,7 +2915,7 @@ static int airoha_qdma_set_rx_meter(struct airoha_gdm_dev *dev,
u32 rate, u32 bucket_size,
enum trtcm_unit_type unit_type)
{
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
int i;
for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) {
@@ -3011,10 +3090,11 @@ static void airoha_tc_remove_htb_queue(struct net_device *netdev, int queue)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
int num_tx_queues = AIROHA_NUM_TX_RING;
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
airoha_qdma_set_tx_rate_limit(netdev, queue, 0, 0);
+ qdma = airoha_qdma_deref(dev);
clear_bit(queue, qdma->qos_channel_map);
clear_bit(queue, dev->qos_sq_bmap);
@@ -3040,6 +3120,98 @@ static int airoha_tc_htb_delete_leaf_queue(struct net_device *netdev,
return 0;
}
+static void airoha_disable_qos_for_gdm34(struct net_device *netdev)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ int err;
+
+ if (port->id != AIROHA_GDM3_IDX &&
+ port->id != AIROHA_GDM4_IDX)
+ return;
+
+ err = airoha_disable_gdm2_loopback(dev);
+ if (err)
+ netdev_warn(netdev,
+ "failed disabling GDM2 loopback: %d\n", err);
+
+ dev->flags &= ~AIROHA_DEV_F_WAN;
+ airoha_dev_set_qdma(dev);
+
+ airoha_set_macaddr(dev, netdev->dev_addr);
+ airoha_ppe_set_xmit_frame_size(dev);
+
+ if (netif_running(netdev))
+ airoha_set_gdm_port_fwd_cfg(dev->eth,
+ REG_GDM_FWD_CFG(port->id),
+ FE_PSE_PORT_PPE1);
+}
+
+static int airoha_enable_qos_for_gdm34(struct net_device *netdev,
+ struct netlink_ext_ack *extack)
+{
+ struct airoha_gdm_dev *wan_dev, *dev = netdev_priv(netdev);
+ struct airoha_gdm_port *port = dev->port;
+ struct airoha_eth *eth = dev->eth;
+ int err = -EBUSY;
+
+ if (port->id != AIROHA_GDM3_IDX &&
+ port->id != AIROHA_GDM4_IDX) {
+ /* HW QoS is always supported by GDM1 and GDM2 */
+ return 0;
+ }
+
+ if (!airoha_is_lan_gdm_dev(dev)) /* Already enabled */
+ return 0;
+
+ mutex_lock(&flow_offload_mutex);
+
+ wan_dev = airoha_get_wan_gdm_dev(eth);
+ if (wan_dev) {
+ if ((wan_dev->flags & AIROHA_DEV_F_QOS) ||
+ wan_dev->port->id == AIROHA_GDM2_IDX) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "QoS configured for WAN device");
+ goto error_unlock;
+ }
+ airoha_disable_qos_for_gdm34(netdev_from_priv(wan_dev));
+ }
+
+ dev->flags |= AIROHA_DEV_F_WAN;
+ airoha_dev_set_qdma(dev);
+ err = airoha_enable_gdm2_loopback(dev);
+ if (err)
+ goto error_disable_wan;
+
+ err = airoha_set_macaddr(dev, netdev->dev_addr);
+ if (err)
+ goto error_disable_loopback;
+
+ airoha_dev_set_xmit_frame_size(netdev);
+ if (netif_running(netdev)) {
+ u32 pse_port;
+
+ pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+ : FE_PSE_PORT_PPE1;
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id),
+ pse_port);
+ }
+
+ mutex_unlock(&flow_offload_mutex);
+
+ return 0;
+
+error_disable_loopback:
+ airoha_disable_gdm2_loopback(dev);
+error_disable_wan:
+ dev->flags &= ~AIROHA_DEV_F_WAN;
+ airoha_dev_set_qdma(dev);
+error_unlock:
+ mutex_unlock(&flow_offload_mutex);
+
+ return err;
+}
+
static int airoha_tc_htb_destroy(struct net_device *netdev)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
@@ -3048,6 +3220,8 @@ static int airoha_tc_htb_destroy(struct net_device *netdev)
for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
airoha_tc_remove_htb_queue(netdev, q);
+ dev->flags &= ~AIROHA_DEV_F_QOS;
+
return 0;
}
@@ -3067,24 +3241,33 @@ static int airoha_tc_get_htb_get_leaf_queue(struct net_device *netdev,
return 0;
}
-static int airoha_tc_setup_qdisc_htb(struct net_device *dev,
+static int airoha_tc_setup_qdisc_htb(struct net_device *netdev,
struct tc_htb_qopt_offload *opt)
{
switch (opt->command) {
- case TC_HTB_CREATE:
+ case TC_HTB_CREATE: {
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ int err;
+
+ err = airoha_enable_qos_for_gdm34(netdev, opt->extack);
+ if (err)
+ return err;
+
+ dev->flags |= AIROHA_DEV_F_QOS;
break;
+ }
case TC_HTB_DESTROY:
- return airoha_tc_htb_destroy(dev);
+ return airoha_tc_htb_destroy(netdev);
case TC_HTB_NODE_MODIFY:
- return airoha_tc_htb_modify_queue(dev, opt);
+ return airoha_tc_htb_modify_queue(netdev, opt);
case TC_HTB_LEAF_ALLOC_QUEUE:
- return airoha_tc_htb_alloc_leaf_queue(dev, opt);
+ return airoha_tc_htb_alloc_leaf_queue(netdev, opt);
case TC_HTB_LEAF_DEL:
case TC_HTB_LEAF_DEL_LAST:
case TC_HTB_LEAF_DEL_LAST_FORCE:
- return airoha_tc_htb_delete_leaf_queue(dev, opt);
+ return airoha_tc_htb_delete_leaf_queue(netdev, opt);
case TC_HTB_LEAF_QUERY_QUEUE:
- return airoha_tc_get_htb_get_leaf_queue(dev, opt);
+ return airoha_tc_get_htb_get_leaf_queue(netdev, opt);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index bf44be9f0954..4d03ebae1a2d 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -545,11 +545,12 @@ struct airoha_qdma {
enum airoha_dev_flags {
AIROHA_DEV_F_WAN = BIT(0),
+ AIROHA_DEV_F_QOS = BIT(1),
};
struct airoha_gdm_dev {
+ struct airoha_qdma __rcu *qdma;
struct airoha_gdm_port *port;
- struct airoha_qdma *qdma;
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
@@ -685,6 +686,16 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev);
bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
struct airoha_gdm_dev *dev);
+extern struct mutex flow_offload_mutex;
+
+static inline struct airoha_qdma *
+airoha_qdma_deref(struct airoha_gdm_dev *dev)
+{
+ return rcu_dereference_protected(dev->qdma,
+ lockdep_rtnl_is_held() ||
+ lockdep_is_held(&flow_offload_mutex));
+}
+
void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev);
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index fdb973fc779c..3ccdf9fccb4a 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -15,7 +15,10 @@
#include "airoha_regs.h"
#include "airoha_eth.h"
-static DEFINE_MUTEX(flow_offload_mutex);
+/* Serialize airoha_gdm_dev flags, QDMA pointer and PPE CPU port
+ * configuration.
+ */
+DEFINE_MUTEX(flow_offload_mutex);
static DEFINE_SPINLOCK(ppe_lock);
static const struct rhashtable_params airoha_flow_table_params = {
@@ -86,8 +89,8 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport)
{
- struct airoha_qdma *qdma = dev->qdma;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+ struct airoha_eth *eth = dev->eth;
u8 qdma_id = qdma - ð->qdma[0];
u32 fe_cpu_port;
diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
index 6fed63d013b4..442b48c9b991 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -375,6 +375,7 @@
#define REG_SRC_PORT_FC_MAP6 0x2298
#define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3))
+#define FC_MAP6_DEF_VALUE 0x1b1a1918
#define REG_WAN_MTU0 0x2300
#define WAN_MTU1_MASK GENMASK(29, 16)
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v9 2/3] net: airoha: fix ETS QoS stats counter underflow and cross-channel corruption
From: Lorenzo Bianconi @ 2026-07-20 22:03 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260721-airoha-ethtool-priv_flags-v9-0-9c15d8b71a56@kernel.org>
airoha_qdma_get_tx_ets_stats() has two bugs:
- The hardware counters read via airoha_qdma_rr() are 32-bit values
but are stored in u64 locals and subtracted from u64 baselines. When
a 32-bit hardware counter wraps around, the subtraction produces a
large underflow value passed to _bstats_update().
- The baseline counters (cpu_tx_packets, fwd_tx_packets) are stored as
single per-device fields, but airoha_qdma_get_tx_ets_stats() is
called with different channel values (0-3). Each call reads a
different channel's hardware counter but overwrites the same
baseline, corrupting the delta computation for other channels.
Fix both by:
- Narrowing the counter locals and baselines to u32 so that 32-bit
unsigned subtraction handles wrap-around naturally.
- Grouping the baselines into a per-channel qos_stats array so each
channel tracks its own previous counter value independently.
- Splitting the delta addition into two statements so the first u32
delta is widened to u64 on assignment and the second is added in
u64 arithmetic, preventing overflow when both deltas are large.
Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support")
Reviewed-by: Simon Horman <horms@kernel.org>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++-------
drivers/net/ethernet/airoha/airoha_eth.h | 7 ++++---
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 41c1a0ffbdd8..aaf2a4717d12 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2482,16 +2482,20 @@ static int airoha_qdma_get_tx_ets_stats(struct net_device *netdev, int channel,
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_qdma *qdma = dev->qdma;
+ u32 cpu_tx_packets, fwd_tx_packets;
+ u64 tx_packets;
- u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
- u64 fwd_tx_packets = airoha_qdma_rr(qdma,
- REG_CNTR_VAL((channel << 1) + 1));
- u64 tx_packets = (cpu_tx_packets - dev->cpu_tx_packets) +
- (fwd_tx_packets - dev->fwd_tx_packets);
+ cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
+ fwd_tx_packets = airoha_qdma_rr(qdma,
+ REG_CNTR_VAL((channel << 1) + 1));
+ tx_packets = (u32)(cpu_tx_packets -
+ dev->qos_stats[channel].cpu_tx_packets);
+ tx_packets += (u32)(fwd_tx_packets -
+ dev->qos_stats[channel].fwd_tx_packets);
_bstats_update(opt->stats.bstats, 0, tx_packets);
- dev->cpu_tx_packets = cpu_tx_packets;
- dev->fwd_tx_packets = fwd_tx_packets;
+ dev->qos_stats[channel].cpu_tx_packets = cpu_tx_packets;
+ dev->qos_stats[channel].fwd_tx_packets = fwd_tx_packets;
return 0;
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index bf1c249255bd..bf44be9f0954 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -553,9 +553,10 @@ struct airoha_gdm_dev {
struct airoha_eth *eth;
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
- /* qos stats counters */
- u64 cpu_tx_packets;
- u64 fwd_tx_packets;
+ struct {
+ u32 cpu_tx_packets;
+ u32 fwd_tx_packets;
+ } qos_stats[AIROHA_NUM_QOS_CHANNELS];
u32 flags;
int nbq;
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v9 1/3] net: airoha: rename airoha_priv_flags to airoha_dev_flags
From: Lorenzo Bianconi @ 2026-07-20 22:03 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev
In-Reply-To: <20260721-airoha-ethtool-priv_flags-v9-0-9c15d8b71a56@kernel.org>
Rename the airoha_priv_flags enum to airoha_dev_flags and the
AIROHA_PRIV_F_WAN flag to AIROHA_DEV_F_WAN. The "priv_flags" naming
dates back to an earlier design that used ethtool private flags; since
this series switched to tc qdisc offload for LAN/WAN configuration,
align the naming to reflect that these are per-device flags rather than
ethtool private flags. No functional change.
Reviewed-by: Simon Horman <horms@kernel.org>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
drivers/net/ethernet/airoha/airoha_eth.h | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 59001fd4b6f7..41c1a0ffbdd8 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2039,7 +2039,7 @@ static int airoha_dev_init(struct net_device *netdev)
fallthrough;
case AIROHA_GDM2_IDX:
/* GDM2 is always used as wan */
- dev->flags |= AIROHA_PRIV_F_WAN;
+ dev->flags |= AIROHA_DEV_F_WAN;
break;
default:
break;
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f6d01a8e8da1..bf1c249255bd 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -543,8 +543,8 @@ struct airoha_qdma {
DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
};
-enum airoha_priv_flags {
- AIROHA_PRIV_F_WAN = BIT(0),
+enum airoha_dev_flags {
+ AIROHA_DEV_F_WAN = BIT(0),
};
struct airoha_gdm_dev {
@@ -667,7 +667,7 @@ static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid)
static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
- return !(dev->flags & AIROHA_PRIV_F_WAN);
+ return !(dev->flags & AIROHA_DEV_F_WAN);
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
--
2.55.0
^ permalink raw reply related
* [PATCH net-next v9 0/3] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand
From: Lorenzo Bianconi @ 2026-07-20 22:03 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Lorenzo Bianconi
Cc: Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
Add the capability to configure GDM3/GDM4 as WAN/LAN on demand when QoS
offload is created or destroyed.
Make dev->qdma an RCU pointer so the TX path can safely dereference it
without holding RTNL.
Introduce airoha_qdma_start() and airoha_qdma_stop() helpers.
---
Changes in v9:
- Rebase on top of next-next main branch
- Link to v8: https://lore.kernel.org/r/20260703-airoha-ethtool-priv_flags-v8-0-015ba5ac89ee@kernel.org
Changes in v8:
- Rebase on top of next-next to fix conflicts.
- Link to v7: https://lore.kernel.org/r/20260701-airoha-ethtool-priv_flags-v7-0-b4153bd44428@kernel.org
Changes in v7:
- Fix ETS stats accounting in patch 2/3
- Reset ETS stats accounting in airoha_dev_set_qdma().
- Link to v6: https://lore.kernel.org/r/20260629-airoha-ethtool-priv_flags-v6-0-86bc600d31bc@kernel.org
Changes in v6:
- Rebase on top of next-next
- Add patch 1/3: "rename airoha_priv_flags to airoha_dev_flags"
- Drop patch 2/3: "refactor QDMA start/stop into reusable helpers"
- Link to v5: https://lore.kernel.org/r/20260611-airoha-ethtool-priv_flags-v5-0-c11de08486d1@kernel.org
Changes in v5:
- Add patch 1/3: use int instead of atomic_t for qdma users counter
- Protect dev->flags with flow_offload_mutex mutex.
- Introduce AIROHA_PRIV_F_QOS in order to handle better WAN/LAN
switching.
- Link to v4: https://lore.kernel.org/r/20260610-airoha-ethtool-priv_flags-v4-0-60e89cf28fea@kernel.org
Changes in v4:
- Move back QDMA TX/RX DMA enable to airoha_dev_open()/airoha_dev_stop().
- Configure GDM3/4 as WAN if GDM2 is not available in ndo_init()
callback.
- Protect qdma pointer in airoha_gdm_dev struct using RCU.
- Rely on rtnl_dereference() to access qdma pointer in the control path.
- Add airoha_qdma_start() and airoha_qdma_stop() utility routines in
patch 1/2
- Link to v3: https://lore.kernel.org/r/20260608-airoha-ethtool-priv_flags-v3-1-3e8e3dc3f715@kernel.org
Changes in v3:
- Do not introduce ethtool private flags support to configure LAN/WAN
for GDM3/4 and rely on tc qdisc offload for it instead.
- Set GDM3/4 ports as LAN by default.
- Move QDMA TX/RX DMA enable from airoha_dev_open() to airoha_probe()
and the corresponding disable from airoha_dev_stop() to airoha_qdma_cleanup().
- Link to v2: https://lore.kernel.org/r/20260607-airoha-ethtool-priv_flags-v2-1-742c7aa1e182@kernel.org
Changes in v2:
- Rework airoha_dev_set_wan_flag routine
- Enable GDM_STRIP_CRC_MASK in airoha_disable_gdm2_loopback()
- Do not always reset REG_SRC_PORT_FC_MAP6 in
airoha_disable_gdm2_loopback() but use the same condition used in
airoha_enable_gdm2_loopback().
- Link to v1: https://lore.kernel.org/r/20260606-airoha-ethtool-priv_flags-v1-1-401b2c9fe9f1@kernel.org
---
Lorenzo Bianconi (3):
net: airoha: rename airoha_priv_flags to airoha_dev_flags
net: airoha: fix ETS QoS stats counter underflow and cross-channel corruption
net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback to QoS offload
drivers/net/ethernet/airoha/airoha_eth.c | 253 ++++++++++++++++++++++++++----
drivers/net/ethernet/airoha/airoha_eth.h | 26 ++-
drivers/net/ethernet/airoha/airoha_ppe.c | 9 +-
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
4 files changed, 246 insertions(+), 43 deletions(-)
---
base-commit: 298bb2b8903323f6ef2eab4819a2e477765f0ff1
change-id: 20260606-airoha-ethtool-priv_flags-b6aa70caa780
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply
* Re: [PATCH net-next v8 0/3] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand
From: Lorenzo Bianconi @ 2026-07-20 22:01 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
In-Reply-To: <20260720144816.4d32909b@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 328 bytes --]
> On Fri, 17 Jul 2026 13:20:18 +0200 Lorenzo Bianconi wrote:
> > I'm not entirely sure what the next steps should be for this series.
> > Thanks in advance.
>
> You have to repost, once a patch is 2 weeks old korg patchwork bots
> insist on marking it as archived.
ack, thx for pointing this out.
Regards,
Lorenzo
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH] ARM: dts: allwinner: a10: Fix PMU interrupt
From: Andre Przywara @ 2026-07-20 21:51 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland
Cc: devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
The Performance Monitoring Unit of the Cortex-A8 cores in the Allwinner
A10 SoC is connected to interrupt line 66, not 3. This is shown in the
manual (where interrupt 3 is assigned to UART2, also in our .dtsi), but
has also been confirmed by triggering an PMU overflow interrupt and
inspecting the IRQ controller status registers (from U-Boot).
Please note that "perf stat" does not use interrupts, this might explain
why this evaded the initial testing.
Fixes: 7e345d25c796 ("ARM: dts: sun4i-a10: Add PMU node")
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm/boot/dts/allwinner/sun4i-a10.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi b/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi
index 51a6464aab9a3..cabf619c2e217 100644
--- a/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/allwinner/sun4i-a10.dtsi
@@ -185,7 +185,7 @@ de: display-engine {
pmu {
compatible = "arm,cortex-a8-pmu";
- interrupts = <3>;
+ interrupts = <66>;
};
reserved-memory {
--
2.46.4
^ permalink raw reply related
* Re: [PATCH net] net: airoha: fix HTB class modification offload
From: Jakub Kicinski @ 2026-07-20 21:49 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Wayen Yan, netdev, horms, pabeni, edumazet, andrew+netdev,
angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
linux-mediatek
In-Reply-To: <akthM-O5eea52MN6@lore-desk>
On Mon, 6 Jul 2026 10:02:59 +0200 Lorenzo Bianconi wrote:
> > HTB core does not populate parent_classid for TC_HTB_NODE_MODIFY.
> > Airoha currently checks parent_classid against TC_HTB_CLASSID_ROOT
> > in a helper shared by both TC_HTB_LEAF_ALLOC_QUEUE and
> > TC_HTB_NODE_MODIFY. Since the modify path leaves parent_classid as
> > zero, the check always fails and changing parameters of an already
> > offloaded HTB class is rejected with -EINVAL.
> >
> > Move the root-parent check into the allocation path and validate
> > modify requests using the per-netdev QoS channel bitmap, consistent
> > with the delete and query paths.
> >
> > Fixes: ef1ca9271313 ("net: airoha: Add sched HTB offload support")
> > Signed-off-by: Wayen Yan <win847@gmail.com>
>
> Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
ditto, please repost
^ permalink raw reply
* Re: [PATCH net-next v8 0/3] airoha: add the capability to configure GDM3/GDM4 as WAN/LAN on demand
From: Jakub Kicinski @ 2026-07-20 21:48 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Alexander Lobakin, linux-arm-kernel, linux-mediatek,
netdev, Madhur Agrawal
In-Reply-To: <aloP8n-EVtAHnl-l@lore-desk>
On Fri, 17 Jul 2026 13:20:18 +0200 Lorenzo Bianconi wrote:
> I'm not entirely sure what the next steps should be for this series.
> Thanks in advance.
You have to repost, once a patch is 2 weeks old korg patchwork bots
insist on marking it as archived.
^ permalink raw reply
* [PATCH] arm64: dts: allwinner: a523: x96qpro+: overvolting eMMC
From: Andre Przywara @ 2026-07-20 21:38 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland
Cc: devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
On the X96QPro+ TV box, the eMMC wasn't working properly: the chip would
be detected, but most requests failed, leaving it unusable. Some
"solutions" were circulating downstream, one "popular" one is severely
limiting the clock frequency, with a drastic performance impact.
Instead slightly bumping the I/O voltage on PortC from the canonical
1.8V to 1.9V seemed to do the trick: the eMMC now works and reaches the
expected 120MB/s performance.
Adjust the respective regulator voltage in the .dts to enable eMMC
operation on that device.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
Hi,
I am not super happy with this patch, but it seems to be a pragmatic and
the least hacky solution I have seen.
Cheers,
Andre
arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts b/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts
index a96927fbdadd5..5636e95312218 100644
--- a/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts
+++ b/arch/arm64/boot/dts/allwinner/sun55i-h728-x96qpro+.dts
@@ -194,8 +194,8 @@ reg_bldo4: bldo4 {
reg_cldo1: cldo1 {
regulator-always-on;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <1900000>;
+ regulator-max-microvolt = <1900000>;
regulator-name = "vcc-codec-sd";
};
--
2.46.4
^ permalink raw reply related
* Re: [PATCH 0/4] media: rc: sunxi-cir: support the A523/H728/T527 IR receiver
From: Justin Suess @ 2026-07-20 21:33 UTC (permalink / raw)
To: Andre Przywara
Cc: Sean Young, Mauro Carvalho Chehab, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-media, devicetree, linux-arm-kernel,
linux-sunxi, Sashiko
In-Reply-To: <20260720225310.039aca2e@ryzen.lan>
On Mon, Jul 20, 2026 at 10:53:10PM +0200, Andre Przywara wrote:
> On Thu, 2 Jul 2026 17:47:46 -0400
> Justin Suess <utilityemal77@gmail.com> wrote:
>
> Hey Justin,
>
> > This series adds support for the CIR receiver found in the Allwinner
> > A523/T527/H728 family (sun55i). The only board in this family that I
>
> will you be able to send a v2 with the fixes we discussed in this
> thread? No driver changes, just the binding for the new specific
> compatible string, and the DT changes.
> I tested this on the X96QPro+, and also managed to enable IR operation
> on the Avaota board, where it uses the other IP instance (and another
> pin).
>
> Please let us know if you can send a new version this week still, to
> possibly allow a v7.3 merge. Happy to review that quickly then.
> If not, please drop me a note, I can then take care of that.
>
Gotcha should be no problem! Sorry I was working on some other patches
for a bit and was out of town and didn't have access to the hardware.
I'll get the board out of my closet and do some testing before I send...
Justin
> Cheers,
> Andre
>
^ permalink raw reply
* [PATCH v3 0/3] pmdomain: mediatek: Add MT6858 support
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov,
Krzysztof Kozlowski
Add power domain driver support for the MediaTek Dimensity 7100 (MT6858)
SoC. On this SoC, there are some new subsystem-specific bus protection
blocks, which are listed here in ascending address order:
- IMG_SUB0 at 0x1502f000
- CAM_SUB1 at 0x1a00c000
- CAM_SUB0 at 0x1a00d000
- IPE_SUB0 at 0x1b00e000
The VLPCFG bus configuration registers at 0x1c00c000 are also new.
Additionally, implement the SMC-based power sequence required for
enabling the modem power domain on this SoC.
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
Changes in v3:
- Fix error path to also skip sram_pdn for MODEM_SECURE_PWRSEQ
(discovered in Sashiko report)
- Link to v2: https://patch.msgid.link/20260715-mt6858-pmdomain-v2-0-6293e87fc093@jolla.com
Changes in v2:
- Rebase
- MTK_SCPD_MODEM_SECURE -> MTK_SCPD_MODEM_SECURE_PWRSEQ
- Convert SMC command enum to defines
- Based on the new SIMPLE_PWRSEQ changes, skip the sram_pdn stage for
MODEM_SECURE_PWRSEQ too since it wasn't intended to have an effect
- Clarify that the SMC call also handles bus protection
- Simply if/else code based on the assumption *_PWRSEQ are mutually
exclusive
- Add comment explaining that PWR_STA_2ND is skipped for modem
(mentioned in Sashiko report)
- Link to v1: https://patch.msgid.link/20260714-mt6858-pmdomain-v1-0-4f09bbb822e0@jolla.com
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
To: Ulf Hansson <ulfh@kernel.org>
Cc: Matthias Brugger <mbrugger@suse.com>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org
Cc: linux-pm@vger.kernel.org
---
Nikolai Burov (3):
dt-bindings: power: Add MediaTek MT6858 power domain controller
pmdomain: mediatek: Add support for secure modem power domain control
pmdomain: mediatek: Add support for MT6858 SoC
.../bindings/power/mediatek,power-controller.yaml | 21 +-
drivers/pmdomain/mediatek/mt6858-pm-domains.h | 466 +++++++++++++++++++++
drivers/pmdomain/mediatek/mtk-pm-domains.c | 55 ++-
drivers/pmdomain/mediatek/mtk-pm-domains.h | 6 +
include/dt-bindings/power/mediatek,mt6858-power.h | 23 +
include/linux/soc/mediatek/mtk_sip_svc.h | 3 +
6 files changed, 568 insertions(+), 6 deletions(-)
---
base-commit: 3fe08b9796f36ef437ab9328e7dd1e5ff2d66603
change-id: 20260712-mt6858-pmdomain-b1b456c96675
Best regards,
--
Nikolai Burov <nikolai.burov@jolla.com>
^ permalink raw reply
* Re: [PATCH] KVM: arm64: ptdump: Flush the last region
From: Wei-Lin Chang @ 2026-07-20 21:21 UTC (permalink / raw)
To: Mark Rutland
Cc: linux-arm-kernel, kvmarm, linux-kernel, Marc Zyngier,
Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
Dev Jain, Ryan Roberts, Sebastian Ene, Vincent Donnefort
In-Reply-To: <al4Wn4Wb6GrOAw70@J2N7QTR9R3.cambridge.arm.com>
On Mon, Jul 20, 2026 at 01:37:51PM +0100, Mark Rutland wrote:
> On Mon, Jul 20, 2026 at 11:58:44AM +0100, Wei-Lin Chang wrote:
> > On Mon, Jul 20, 2026 at 09:35:01AM +0100, Mark Rutland wrote:
> > > On Sat, Jul 18, 2026 at 12:12:33AM +0100, Wei-Lin Chang wrote:
> > > > @@ -155,11 +155,13 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
> > > > .seq = m,
> > > > };
> > > >
> > > > - write_lock(&kvm->mmu_lock);
> > > > + guard(write_lock)(&kvm->mmu_lock);
> > > > ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
> > > > - write_unlock(&kvm->mmu_lock);
> > > > + if (ret)
> > > > + return ret;
> > > > + note_page(&st->parser_state.ptdump, BIT(mmu->pgt->ia_bits), -1, 0);
> > >
> > > This can be:
> > >
> > > note_page_flush(&st->parser_state.ptdump);
> > >
> > > The level change alone should trigger the dump, so the address doesn't
> > > need to be at the end of the guest IPA space.
> > >
> > > Importantly, note_page_flush() will pass 0 as the address, which won't
> > > trigger the checks you try to suppress below.
> > >
> > > Please use note_page_flush() here, and drop the changes to
> > > arch/arm64/mm/ptdump.c.
> >
> > Sorry, I shouldn't have omitted this information, but I did try
> > note_page_flush(). And it gives something like this in the last row:
> >
> > 0x00000000ffc00000-0x0000000000000000 17592186040324M 2 R W px ux AF BLK
> >
> > The astronomical size is from (addr - st->start_address). As IA bits for
> > stage-2 are not close to 64, we'll have large sizes for the last row.
>
> Ok. That's a bug in the current implementation of note_page_flush(),
> then. The *intent* is that note_page_flush() is used to terminate
> output, and we should mak it work.
Thanks for the suggestion, I agree.
>
> Do we need to pass additional information, or do we have the necessary
> values in (or accessible from) struct ptdump_state?
There is struct ptdump_state.range[], and I think we can get the end
address of the ptdump from that.
>
> > That's one of the reasons I chose to call note_page() with
> > BIT(pgtable->ia_bits) as addr, to end the ptdump at the end of the guest
> > IPA space.
> >
> > Additionally, the last row is combining two ranges:
> >
> > 1. 0x00000000ffc00000-0x0000000100000000 4M 2 R W px ux AF BLK
> > 2. 0x0000000100000000-0x0000000000000000 BIG_SIZE - (empty prot)
> >
> > The attributes are wrong for the large range after
> > BIT(pgtable->ia_bits). This is because before dumping the last row, the
> > ptdump code is waiting to be notified of the end of the final region
> > with all those {R, W, px, ux, AF, BLK} attributes. Using
> > note_page_flush() essentially tells it the valid range ends at 1 << 64.
> > So actually using note_page() with BIT(pgtable->ia_bits) is required for
> > correctness.
> >
> > The kernel ptdump is not affected by this (at least from my quick test):
> >
> > 0xffffffffff6fe000-0xffffffffff800000 1032K PTE
> > 0xffffffffff800000-0x0000000000000000 8M PMD
>
> Yes, it's not affected because its final VA is 0xffffffffffffffff (i.e.
> 2^64i - 1), and so using 0 as the next address does the right thing
> modulo 64 bits.
>
> We use ptdump for non-kernel tables today (e.g. the EFI mm), so
> presumably they suffer the same problem?
Cool, I didn't know about efi_mm and its ptdump! After looking at it, I
think yes it also has this problem, since its a ttbr0 ptdump.
So I plan:
1. In note_page_flush, use ptdump_state.range[] to determine the end
address (the last range with start != end).
2. In KVM ptdump, initialize ptdump_state.range[], and call
note_page_flush() at the end of the walk.
For the extra marker name dumps, we can simply change the second
marker's start_address from BIT(pgtable->ia_bits) to ~0UL so it doesn't
trigger the marker name dump.
Hope these sound good!
Thanks,
Wei-Lin Chang
>
> Mark.
^ permalink raw reply
* Re: [PATCH] Documentation/devicetree/bindings: update Sudeep Holla's email address
From: Rob Herring (Arm) @ 2026-07-20 21:20 UTC (permalink / raw)
To: Laszlo Ersek
Cc: Lorenzo Pieralisi, Sudeep Holla, Krzysztof Kozlowski, arm-scmi,
Conor Dooley, linux-arm-kernel, devicetree, Jassi Brar,
Linus Walleij, Cristian Marussi, Liviu Dudau, linux-kernel
In-Reply-To: <20260720160235.36592-1-laszlo.ersek@arm.com>
On Mon, 20 Jul 2026 18:02:30 +0200, Laszlo Ersek wrote:
> MAINTAINERS disagrees with Documentation/devicetree/bindings on Sudeep's
> email address; fix the latter.
>
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Cristian Marussi <cristian.marussi@arm.com>
> Cc: Jassi Brar <jassisinghbrar@gmail.com>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Linus Walleij <linusw@kernel.org>
> Cc: Liviu Dudau <liviu.dudau@arm.com>
> Cc: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Sudeep Holla <sudeep.holla@kernel.org>
> Cc: arm-scmi@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> Fixes: 59e82a4237cf ("MAINTAINERS: Change Sudeep Holla's email address")
> Signed-off-by: Laszlo Ersek <laszlo.ersek@arm.com>
> ---
> Documentation/devicetree/bindings/arm/arm,juno-fpga-apb-regs.yaml | 2 +-
> Documentation/devicetree/bindings/arm/arm,vexpress-juno.yaml | 2 +-
> Documentation/devicetree/bindings/arm/arm,vexpress-scc.yaml | 2 +-
> Documentation/devicetree/bindings/dvfs/performance-domain.yaml | 2 +-
> Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 2 +-
> Documentation/devicetree/bindings/firmware/arm,scpi.yaml | 2 +-
> Documentation/devicetree/bindings/mailbox/arm,mhuv3.yaml | 2 +-
> 7 files changed, 7 insertions(+), 7 deletions(-)
>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH] arm64/mm: Check the requested PFN range during memoroy removal
From: Jonathan Cameron @ 2026-07-20 20:23 UTC (permalink / raw)
To: Richard Cheng
Cc: catalin.marinas, will, ryan.roberts, ardb, kevin.brodsky,
anshuman.khandual, yang, chaitanyas.prakash, linux-arm-kernel,
linux-kernel, linux-cxl, newtonl, kristinc, mochs, kaihengf,
kobak
In-Reply-To: <20260720020655.9607-1-icheng@nvidia.com>
On Mon, 20 Jul 2026 10:06:55 +0800
Richard Cheng <icheng@nvidia.com> wrote:
Hi Richard,
Please run a spell checker over your commit messages.
"memory" in the patch description for example.
Fix itself looks correct to me.
> prevent_memory_remove_notifier() advances pfn while checking whether the
> range contains early memory. After the loop, pfn points to end_pfn, but
> it is passed to can_unmap_without_split(). This checks the range
> immediately after the requested memory instead of the range being
> offlined.
>
> Consequently, valid requests can be rejected with shifted address range,
> while an unsafe request can be accepted when the following range is
> unmapped. This was observed with CXL DAX memory, where the final memory
> block was incorrectly allowed offline.
>
> Pass arg->start_pfn so the leaf-split check examines the requested
> range.
>
> Fixes: 95a58852b0e5 ("arm64/mm: Reject memory removal that splits a kernel leaf mapping")
> Signed-off-by: Richard Cheng <icheng@nvidia.com>
> ---
> The bug occurred on a machine with CXL Type-3 device.
> Branch: cxl-next
>
> Before 95a58852b0e5:
>
> """
> $ sudo echo offline > memory557056/state
> write error: Operation not permitted
> $ sudo dmesg
> ---[snip]---
> [440008000000 440010000000] splits a leaf entry in linear map
>
> $ sudo echo offline > memory558079/state
> $ cat memory558079/state
> offline
> """
>
> After:
> """
> $ sudo echo offline > memory557056/state
> write error: Operation not permitted
> [440000000000 440008000000] splits a leaf entry in linear map
>
> $ sudo echo offline > memory558079/state
> write error: Operation not permitted
> [441ff8000000 442000000000] splits a leaf entry in linear map
>
> My fix corrects the checked range and prevents the false acceptance.
>
> Best regards,
> Richard Cheng.
> ---
> arch/arm64/mm/mmu.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index a25d8beacc83..18a8b0d3714e 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -2194,7 +2194,7 @@ static int prevent_memory_remove_notifier(struct notifier_block *nb,
> }
> }
>
> - if (!can_unmap_without_split(pfn, arg->nr_pages))
> + if (!can_unmap_without_split(arg->start_pfn, arg->nr_pages))
> return NOTIFY_BAD;
>
> return NOTIFY_OK;
>
> base-commit: 5ca04f3ba91f1773bbd5da6d9c654ccc1ba7831d
^ permalink raw reply
* Re: [PATCH v2 4/6] media: nxp: imx8-isi: Add 16-bit raw Bayer format support
From: Laurent Pinchart @ 2026-07-20 21:15 UTC (permalink / raw)
To: guoniu.zhou
Cc: Mauro Carvalho Chehab, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Christian Hemp,
Stefan Riedmueller, Jacopo Mondi, Loic Poulain,
Bryan O'Donoghue, Dong Aisheng, Guoniu Zhou, linux-media, imx,
linux-arm-kernel, linux-kernel, Laurentiu Palcu
In-Reply-To: <20260720-isi-v2-4-45845bc5d4fa@oss.nxp.com>
Hello Guoniu, Laurentiu,
Thank you for the patch.
On Mon, Jul 20, 2026 at 11:34:06AM +0800, guoniu.zhou@oss.nxp.com wrote:
> From: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
>
> Add support for 16-bit raw Bayer formats (SBGGR16, SGBRG16, SGRBG16,
> SRGGB16) to both the pipeline subdev and video capture interface.
>
> These formats are commonly used by high-end image sensors that output
> 16-bit raw data, enabling the ISI to process and capture full dynamic
> range from such sensors.
It's not really high-end any more :-) Lots of sensors can output RAW16
today.
> Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
> ---
> Changes in v2:
> - Add Reviewed-by tag from Frank Li
> ---
> .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 24 +++++++++++++++
> .../media/platform/nxp/imx8-isi/imx8-isi-video.c | 36 ++++++++++++++++++++++
> 2 files changed, 60 insertions(+)
>
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
> index 2d0843c86534..e58925d71164 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
> @@ -179,6 +179,30 @@ static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = {
> .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
> | BIT(MXC_ISI_PIPE_PAD_SOURCE),
> .encoding = MXC_ISI_ENC_RAW,
While at it, could you please also add support for
MEDIA_BUS_FMT_Y16_1X16, for 16-bit monochrome sensors (here and in
mxc_isi_formats) ? With that,
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
> + .output = MEDIA_BUS_FMT_SBGGR16_1X16,
> + .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
> + | BIT(MXC_ISI_PIPE_PAD_SOURCE),
> + .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
> + .output = MEDIA_BUS_FMT_SGBRG16_1X16,
> + .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
> + | BIT(MXC_ISI_PIPE_PAD_SOURCE),
> + .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
> + .output = MEDIA_BUS_FMT_SGRBG16_1X16,
> + .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
> + | BIT(MXC_ISI_PIPE_PAD_SOURCE),
> + .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
> + .output = MEDIA_BUS_FMT_SRGGB16_1X16,
> + .pads = BIT(MXC_ISI_PIPE_PAD_SINK)
> + | BIT(MXC_ISI_PIPE_PAD_SOURCE),
> + .encoding = MXC_ISI_ENC_RAW,
> },
> /* JPEG */
> {
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> index fe4adfa3a1f0..5eb448f4c26f 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> @@ -356,6 +356,42 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
> .color_planes = 1,
> .depth = { 16 },
> .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
> + .fourcc = V4L2_PIX_FMT_SBGGR16,
> + .type = MXC_ISI_VIDEO_CAP,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 16 },
> + .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
> + .fourcc = V4L2_PIX_FMT_SGBRG16,
> + .type = MXC_ISI_VIDEO_CAP,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 16 },
> + .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
> + .fourcc = V4L2_PIX_FMT_SGRBG16,
> + .type = MXC_ISI_VIDEO_CAP,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 16 },
> + .encoding = MXC_ISI_ENC_RAW,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
> + .fourcc = V4L2_PIX_FMT_SRGGB16,
> + .type = MXC_ISI_VIDEO_CAP,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 16 },
> + .encoding = MXC_ISI_ENC_RAW,
> },
> /* JPEG */
> {
--
Regards,
Laurent Pinchart
^ permalink raw reply
* [PATCH 2/2] arm64: dts: imx8qm-ss-conn: Add HSIC usb@5b0e0000 node
From: Frank.Li @ 2026-07-20 21:10 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Xu Yang,
Frank Li
In-Reply-To: <20260720-qm_smmu-v1-0-3689fbd8b516@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Add usb@5b0e0000 and related node for i.MX8QM.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi | 34 +++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi b/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
index 1370a571153a5..c7063aaac4a34 100644
--- a/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
@@ -4,6 +4,40 @@
* Dong Aisheng <aisheng.dong@nxp.com>
*/
+&{/} {
+ usbphynop2: usbphynop2 {
+ compatible = "usb-nop-xceiv";
+ clocks = <&usb2_lpcg IMX_LPCG_CLK_7>;
+ clock-names = "main_clk";
+ power-domains = <&pd IMX_SC_R_USB_0_PHY>;
+ status = "disabled";
+ };
+};
+
+&conn_subsys {
+ usbh1: usb@5b0e0000 {
+ compatible = "fsl,imx7ulp-usb", "fsl,imx6ul-usb", "fsl,imx27-usb";
+ reg = <0x5b0e0000 0x200>;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
+ phy_type = "hsic";
+ dr_mode = "host";
+ clocks = <&usb2_lpcg IMX_LPCG_CLK_6>;
+ ahb-burst-config = <0x0>;
+ tx-burst-size-dword = <0x10>;
+ rx-burst-size-dword = <0x10>;
+ power-domains = <&pd IMX_SC_R_USB_1>;
+ fsl,usbphy = <&usbphynop2>;
+ fsl,usbmisc = <&usbmisc2 0>;
+ };
+
+ usbmisc2: usbmisc@5b0e0200 {
+ compatible = "fsl,imx7ulp-usbmisc", "fsl,imx7d-usbmisc", "fsl,imx6q-usbmisc";
+ #index-cells = <1>;
+ reg = <0x5b0e0200 0x200>;
+ };
+};
+
&usbphy1 {
compatible = "fsl,imx8qm-usbphy", "fsl,imx7ulp-usbphy";
};
--
2.43.0
^ permalink raw reply related
* [PATCH 1/2] arm64: dts: imx8qm-ss-conn: add usbotg3 iommu information
From: Frank.Li @ 2026-07-20 21:10 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Xu Yang,
Frank Li
In-Reply-To: <20260720-qm_smmu-v1-0-3689fbd8b516@nxp.com>
From: Frank Li <Frank.Li@nxp.com>
Add usbotg3 iommu information.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi b/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
index ccf9f510e0f88..1370a571153a5 100644
--- a/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi
@@ -32,3 +32,7 @@ &usdhc3 {
compatible = "fsl,imx8qm-usdhc", "fsl,imx8qxp-usdhc", "fsl,imx7d-usdhc";
iommus = <&smmu 0x11 0x7f80>;
};
+
+&usbotg3_cdns3 {
+ iommus = <&smmu 0x4 0x7f80>;
+};
--
2.43.0
^ permalink raw reply related
* [PATCH 0/2] arm64: dts: imx8qm: add smmu information for usb and add hsic usb port
From: Frank.Li @ 2026-07-20 21:10 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Xu Yang,
Frank Li
Add smmu information for usb and add hsic usb port.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Frank Li (2):
arm64: dts: imx8qm-ss-conn: add usbotg3 iommu information
arm64: dts: imx8qm-ss-conn: Add HSIC usb@5b0e0000 node
arch/arm64/boot/dts/freescale/imx8qm-ss-conn.dtsi | 38 +++++++++++++++++++++++
1 file changed, 38 insertions(+)
---
base-commit: 3fe08b9796f36ef437ab9328e7dd1e5ff2d66603
change-id: 20260720-qm_smmu-417e915cb713
Best regards,
--
Frank Li <Frank.Li@nxp.com>
^ permalink raw reply
* Re: [PATCH v2 3/6] media: nxp: imx8-isi: Fix per-stream reference counting for multiplexed streams
From: Laurent Pinchart @ 2026-07-20 21:03 UTC (permalink / raw)
To: Guoniu Zhou
Cc: Mauro Carvalho Chehab, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Christian Hemp,
Stefan Riedmueller, Jacopo Mondi, Loic Poulain,
Bryan O'Donoghue, Dong Aisheng, Guoniu Zhou, linux-media, imx,
linux-arm-kernel, linux-kernel, stable
In-Reply-To: <20260720-isi-v2-3-45845bc5d4fa@oss.nxp.com>
On Mon, Jul 20, 2026 at 11:34:05AM +0800, Guoniu Zhou wrote:
> The ISI crossbar fails to properly enable multiple streams from different
> virtual channels on the same input pad. Only the first stream gets enabled
> in hardware, subsequent streams are silently ignored.
>
> The driver uses a single enable_count per input to track the input state.
> When enable_count is non-zero, the code assumes the input is already active
> and skips calling v4l2_subdev_enable_streams() for additional streams:
>
> Call 1: enable_streams(stream 0)
> -> enable_count == 0, enable gasket and stream 0 in hardware
> -> enable_count = 1
>
> Call 2: enable_streams(stream 1)
> -> enable_count == 1, skip hardware enable (BUG!)
> -> enable_count = 2
> -> stream 1 never gets enabled
>
> Similarly on disable, when enable_count reaches zero, ALL streams are
> disabled regardless of which streams are actually still active.
>
> Fix this by tracking per-stream state using:
> - enabled_streams (u64 bitmask): tracks which streams are currently enabled
> - enabled_count[] (array): per-stream reference counter to support the same
> stream being enabled/disabled multiple times
>
> Now each stream is independently enabled/disabled in hardware based on the
> enabled_streams bitmask, while enabled_count[] provides reference counting
> for scenarios where the same stream is enabled multiple times, such as
> duplicate cases in the ISI stream.
>
> Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver")
> Cc: stable@vger.kernel.org
As far as I understand, only newer SoCs support multi-stream for ISI
inputs (please tell me if that's incorrect). Those SoCs were not
supported when the ISI driver was merged, so I don't think this should
be backported to stable kernels. The subject line should then read
"Implement per-stream ..." instead of "Fix per-stream ..." and the
commit message adapted accordingly.
> Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
> ---
> Changes in v2:
> - Use fixed-size array for enabled_count instead of dynamic allocation
> - Use BIT_ULL() macro for u64 bitmask operations
> - Use MXC_ISI_MAX_STREAMS (64) as loop boundary instead of num_sources
> - Remove mxc_isi_stream_counters_alloc/free functions
> ---
> .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 7 +-
> .../platform/nxp/imx8-isi/imx8-isi-crossbar.c | 76 ++++++++++++++++------
> 2 files changed, 63 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
> index 7547a6559d4c..9adbe2fe7cf8 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
> @@ -184,8 +184,13 @@ struct mxc_isi_dma_buffer {
> dma_addr_t dma;
> };
>
> +/* V4L2 subdev max stream ID is 63, need 64 counters (0-63) */
> +#define MXC_ISI_MAX_STREAMS 64
> +
> struct mxc_isi_input {
> - unsigned int enable_count;
> + u64 enabled_streams;
> + /* Per-stream reference counter */
> + unsigned int enabled_count[MXC_ISI_MAX_STREAMS];
You can save some memory here by replacing unsigned int with u8. The
counter can never go above the number of ISI outputs, as streams can be
enabled once only.
> };
>
> struct mxc_isi_crossbar {
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
> index 328d08a278ea..64576f6bd45c 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
> @@ -337,6 +337,8 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
> struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
> struct v4l2_subdev *remote_sd;
> struct mxc_isi_input *input;
> + u64 streams_to_enable;
> + unsigned long stream;
unsigned int is enough, this is a counter, not a bitmask.
> u64 sink_streams;
> u32 sink_pad;
> u32 remote_pad;
> @@ -350,30 +352,47 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
>
> input = &xbar->inputs[sink_pad];
>
> - /*
> - * TODO: Track per-stream enable counts to support multiplexed
> - * streams.
> - */
> - if (!input->enable_count) {
Let's keep a short comment here:
/* Enable the gasket when the first stream is enabled for this input. */
> + if (!input->enabled_streams) {
> ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd,
> remote_pad, sink_pad);
> if (ret)
> return ret;
> + }
>
> + /*
> + * Track per-stream enable counts to support multiplexed streams.
> + * Only enable streams that are not already enabled.
> + */
> + streams_to_enable = sink_streams & ~input->enabled_streams;
> +
> + if (streams_to_enable) {
> ret = v4l2_subdev_enable_streams(remote_sd, remote_pad,
> - sink_streams);
> + streams_to_enable);
> if (ret) {
> dev_err(xbar->isi->dev,
> "failed to enable streams 0x%llx on '%s':%u: %d\n",
> - sink_streams, remote_sd->name, remote_pad, ret);
> - mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> - return ret;
> + streams_to_enable, remote_sd->name, remote_pad, ret);
> + goto err_gasket_disable;
> }
> +
> + input->enabled_streams |= streams_to_enable;
> }
>
> - input->enable_count++;
> + /* Increment reference count for all requested streams */
> + for (stream = 0; stream < MXC_ISI_MAX_STREAMS; stream++) {
for (unsigned int stream = 0; stream < MXC_ISI_MAX_STREAMS; stream++) {
and drop the local variable above.
> + if (!(sink_streams & BIT_ULL(stream)))
> + continue;
> +
> + input->enabled_count[stream]++;
> + }
Each ISI pipe processes a single stream (the driver uses the
V4L2_SUBDEV_ROUTING_NO_N_TO_1 validation flag, and patch 2/6 ensures
that only stream 0 is accepted on the source side of the crossbar). This
means that that, after translating streams_mask to the sink side,
sink_streams should have a single bit set, and streams_to_enable will
have either 0 or 1 bit set.
If that explanation is correct (please let me know if you disagree),
then this loop can be replaced with
input->enabled_count[__ffs64(sink_streams)]++;
>
> return 0;
> +
> +err_gasket_disable:
> + if (!input->enabled_streams)
> + mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> +
> + return ret;
> }
>
> static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
> @@ -383,6 +402,8 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
> struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
> struct v4l2_subdev *remote_sd;
> struct mxc_isi_input *input;
> + u64 streams_to_disable = 0;
> + unsigned long stream;
unsigned int here too.
> u64 sink_streams;
> u32 sink_pad;
> u32 remote_pad;
> @@ -396,19 +417,36 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
>
> input = &xbar->inputs[sink_pad];
>
> - input->enable_count--;
> + /*
> + * Decrease the enable count for each stream. Only disable streams
> + * whose count reaches zero.
> + */
> + for (stream = 0; stream < MXC_ISI_MAX_STREAMS; stream++) {
for (unsigned int stream = 0; stream < MXC_ISI_MAX_STREAMS; stream++) {
> + if (!(sink_streams & BIT_ULL(stream)))
> + continue;
>
> - if (!input->enable_count) {
> - ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
> - sink_streams);
> - if (ret)
> - dev_err(xbar->isi->dev,
> - "failed to disable streams 0x%llx on '%s':%u: %d\n",
> - sink_streams, remote_sd->name, remote_pad, ret);
> + if (!(input->enabled_streams & BIT_ULL(stream)))
> + continue;
>
> - mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> + if (--input->enabled_count[stream] == 0)
> + streams_to_disable |= BIT_ULL(stream);
> }
Similarly here, the whole loop could be replaced by
if (--input->enabled_count[__ffs64(stream)] == 0)
streams_to_disable |= BIT_ULL(stream);
Another option is to store the information in the mxc_isi_pipe structure
in the form of two new fields:
unsigned int input;
unsigned int input_stream;
The fields would be set to the input index and the sink stream mask in
mxc_isi_crossbar_enable_streams():
struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sources];
...
pipe->input = sink_pad;
pipe->input_stream = sink_streams;
In this function you would then do (completely untested)
struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sources];
...
pipe->input = 0;
pipe->input_stream = 0;
/*
* Check if any other pipe receives the same input stream. If so we
* can't disable it yet, so return immediately.
*/
for (i = 0; i < xbar->isi->pdata->num_channels; ++i) {
struct mxc_isi_pipe *pipe = &xbar->isi->pipes[i];
if (pipe->input == sink_pad &&
pipe->input_stream == sink_streams)
return 0;
}
input->enabled_streams &= ~sink_streams;
The advantage of this is that we won't have to add a 64 entries array to
each input. I'm not too concerned about the memory usage (even if
reducing memory usage is always nice, more for the improvement in cache
locality than for the memory saving itself), but the 64 entries feel a
bit arbitrary.
What do you think is best ?
> + if (!streams_to_disable)
> + return 0;
> +
> + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
> + streams_to_disable);
> + if (ret)
> + dev_err(xbar->isi->dev,
> + "failed to disable streams 0x%llx on '%s':%u: %d\n",
> + streams_to_disable, remote_sd->name, remote_pad, ret);
> +
> + input->enabled_streams &= ~streams_to_disable;
> +
> + if (!input->enabled_streams)
> + mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> +
> return ret;
> }
>
--
Regards,
Laurent Pinchart
^ permalink raw reply
* Re: [PATCH 0/4] media: rc: sunxi-cir: support the A523/H728/T527 IR receiver
From: Andre Przywara @ 2026-07-20 20:53 UTC (permalink / raw)
To: Justin Suess
Cc: Sean Young, Mauro Carvalho Chehab, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-media, devicetree, linux-arm-kernel,
linux-sunxi, Sashiko
In-Reply-To: <20260702214750.3428694-1-utilityemal77@gmail.com>
On Thu, 2 Jul 2026 17:47:46 -0400
Justin Suess <utilityemal77@gmail.com> wrote:
Hey Justin,
> This series adds support for the CIR receiver found in the Allwinner
> A523/T527/H728 family (sun55i). The only board in this family that I
will you be able to send a v2 with the fixes we discussed in this
thread? No driver changes, just the binding for the new specific
compatible string, and the DT changes.
I tested this on the X96QPro+, and also managed to enable IR operation
on the Avaota board, where it uses the other IP instance (and another
pin).
Please let us know if you can send a new version this week still, to
possibly allow a v7.3 merge. Happy to review that quickly then.
If not, please drop me a note, I can then take care of that.
Cheers,
Andre
> am in possession of that has the IR receiver is the x96qpro+, so it is
> just enabled for that board. The Avaota A1 may have it, but I don't have
> hardware to test, so it's just enabled on the x96qpro+ for now, but
> left in the a523 DTSI for future use.
>
> The sun55i family carries a newer revision of the sunxi CIR IP that is
> not backwards-compatible with the prior hardware. So a new pulse
> capture mode field in the control register resets to 0 by default,
> which captures nothing, and the sample clock divider became selectable
> instead of the fixed module clock / 64.
>
> Solve this by adding the two new registers, enabling the setting of the
> pulse capture mode register and the selection of the clock speed.
>
> The new compatible is therefore added standalone, without the
> usual sun6i-a31-ir fallback (it won't work).
>
> Tested on an X96Q Pro+ (H728) TV box with the Google TV remote that
> came in the box (using NEC keymap). Was able to press buttons and
> get readings from lirc / ir-keytable.
>
> The series is based on tag v7.2-rc1 on mainline.
>
> Justin Suess (4):
> media: dt-bindings: allwinner,sun4i-a10-ir: add A523 compatible
> media: rc: sunxi-cir: add support for the A523
> arm64: dts: allwinner: a523: add IR receiver node
> arm64: dts: allwinner: a523: enable IR receiver on the X96Q Pro+
>
> .../media/allwinner,sun4i-a10-ir.yaml | 1 +
> .../arm64/boot/dts/allwinner/sun55i-a523.dtsi | 19 +++++
> .../dts/allwinner/sun55i-h728-x96qpro+.dts | 4 +
> drivers/media/rc/sunxi-cir.c | 76 +++++++++++++++----
> 4 files changed, 87 insertions(+), 13 deletions(-)
>
^ permalink raw reply
* [PATCH v3 3/3] pmdomain: mediatek: Add support for MT6858 SoC
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov
In-Reply-To: <20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com>
From: Nikolai Burov <nikolai.burov@jolla.com>
Add support for the power domains found in the MediaTek MT6858 SoC.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
drivers/pmdomain/mediatek/mt6858-pm-domains.h | 466 ++++++++++++++++++++++++++
drivers/pmdomain/mediatek/mtk-pm-domains.c | 5 +
drivers/pmdomain/mediatek/mtk-pm-domains.h | 5 +
3 files changed, 476 insertions(+)
diff --git a/drivers/pmdomain/mediatek/mt6858-pm-domains.h b/drivers/pmdomain/mediatek/mt6858-pm-domains.h
new file mode 100644
index 000000000000..177b2e86ab1e
--- /dev/null
+++ b/drivers/pmdomain/mediatek/mt6858-pm-domains.h
@@ -0,0 +1,466 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * KY Liu <ky.liu@mediatek.com>
+ * Copyright (c) 2026 Jolla Mobile Ltd
+ * Nikolai Burov <nikolai.burov@jolla.com>
+ */
+
+#ifndef __SOC_MEDIATEK_MT6858_PM_DOMAINS_H
+#define __SOC_MEDIATEK_MT6858_PM_DOMAINS_H
+
+#include "mtk-pm-domains.h"
+#include <dt-bindings/power/mediatek,mt6858-power.h>
+
+/* TOP_AXI registers */
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET 0x0c14
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR 0x0c18
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY 0x0c1c
+
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_DIS0 (BIT(0) | BIT(1) | BIT(18))
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IPE BIT(2)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IMG1 BIT(3)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VEN0 BIT(12)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VDE0 BIT(20)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CAM_MAIN (BIT(30) | BIT(31))
+
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET 0x0c24
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR 0x0c28
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY 0x0c2c
+
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IMG1 BIT(7)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IPE BIT(8)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CAM_MAIN (BIT(9) | BIT(10))
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VEN0 BIT(12)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VDE0 BIT(13)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA (GENMASK(3, 1) | BIT(6))
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA_2ND (BIT(0) | BIT(5) | GENMASK(15, 7))
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET 0x0c44
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR 0x0c48
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY 0x0c4c
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CONN BIT(8)
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_MM_INFRA BIT(16)
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET 0x0c54
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR 0x0c58
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY 0x0c5c
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_MM_INFRA BIT(11)
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CONN BIT(12)
+
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_SET 0x0c64
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_CLR 0x0c68
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_RDY 0x0c6c
+
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_MM_INFRA (BIT(20) | BIT(21))
+
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET 0x0c84
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR 0x0c88
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY 0x0c8c
+
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_AUDIO BIT(6)
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SSUSB BIT(7)
+
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET 0x0c94
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR 0x0c98
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY 0x0c9c
+
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN BIT(1)
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN_2ND BIT(0)
+
+/* {IMG,IPE,CAM}_SUBx registers */
+#define MT6858_SUBx_PROT_EN_SET 0x03c4
+#define MT6858_SUBx_PROT_EN_CLR 0x03c8
+#define MT6858_SUBx_PROT_EN_STA 0x03cc
+
+#define MT6858_IMG_SUB0_PROT_EN_SMI_ISP_IMG1 (BIT(0) | BIT(1))
+
+#define MT6858_IPE_SUB0_PROT_EN_SMI_ISP_IPE (BIT(0) | BIT(1))
+
+#define MT6858_CAM_SUB0_PROT_EN_SMI_CAM_MAIN BIT(0)
+#define MT6858_CAM_SUB0_PROT_EN_SMI_CAM_SUBB BIT(1)
+
+#define MT6858_CAM_SUB1_PROT_EN_SMI_CAM_MAIN BIT(0)
+#define MT6858_CAM_SUB1_PROT_EN_SMI_CAM_SUBA BIT(1)
+
+/* VLP_AXI registers */
+#define MT6858_VLP_AXI_PROT_EN_SET 0x0214
+#define MT6858_VLP_AXI_PROT_EN_CLR 0x0218
+#define MT6858_VLP_AXI_PROT_EN_STA 0x021c
+
+#define MT6858_VLP_AXI_PROT_EN_MM_PROC BIT(8)
+#define MT6858_VLP_AXI_PROT_EN_MM_PROC_2ND (BIT(9) | BIT(10))
+
+/* PWR_CON registers */
+#define MT6858_PWR_ACK BIT(30)
+#define MT6858_PWR_ACK_2ND BIT(31)
+
+static enum scpsys_bus_prot_block scpsys_bus_prot_blocks_mt6858[] = {
+ BUS_PROT_BLOCK_INFRA,
+ BUS_PROT_BLOCK_IMG_SUB0,
+ BUS_PROT_BLOCK_CAM_SUB1,
+ BUS_PROT_BLOCK_CAM_SUB0,
+ BUS_PROT_BLOCK_IPE_SUB0,
+ BUS_PROT_BLOCK_VLP,
+};
+
+static const struct scpsys_domain_data scpsys_domain_data_mt6858[] = {
+ [MT6858_POWER_DOMAIN_MD] = {
+ .name = "md",
+ /*
+ * Note: the PWR_ACK_2ND bit is not used for the modem domain.
+ * Skip it and fall back to checking the 1st bit twice.
+ */
+ .sta_mask = MT6858_PWR_ACK,
+ .ctl_offs = 0xe00,
+ .pwr_sta_offs = 0xe00,
+ .pwr_sta2nd_offs = 0xe00,
+ .ext_buck_iso_offs = 0xf20,
+ .ext_buck_iso_mask = GENMASK(1, 0),
+ .caps = MTK_SCPD_MODEM_SECURE_PWRSEQ | MTK_SCPD_EXT_BUCK_ISO |
+ MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_CONN] = {
+ .name = "conn",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe04,
+ .pwr_sta_offs = 0xe04,
+ .pwr_sta2nd_offs = 0xe04,
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CONN,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN_2ND,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CONN,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_AUDIO] = {
+ .name = "audio",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe18,
+ .pwr_sta_offs = 0xe18,
+ .pwr_sta2nd_offs = 0xe18,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_AUDIO,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_ISP_IMG1] = {
+ .name = "isp-img1",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe28,
+ .pwr_sta_offs = 0xe28,
+ .pwr_sta2nd_offs = 0xe28,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IMG1,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IMG1,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(IMG_SUB0,
+ MT6858_IMG_SUB0_PROT_EN_SMI_ISP_IMG1,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_ISP_IMG2] = {
+ .name = "isp-img2",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe2c,
+ .pwr_sta_offs = 0xe2c,
+ .pwr_sta2nd_offs = 0xe2c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_ISP_IPE] = {
+ .name = "isp-ipe",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe30,
+ .pwr_sta_offs = 0xe30,
+ .pwr_sta2nd_offs = 0xe30,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IPE,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IPE,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(IPE_SUB0,
+ MT6858_IPE_SUB0_PROT_EN_SMI_ISP_IPE,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_VDE0] = {
+ .name = "vde0",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe34,
+ .pwr_sta_offs = 0xe34,
+ .pwr_sta2nd_offs = 0xe34,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VDE0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VDE0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_VEN0] = {
+ .name = "ven0",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe3c,
+ .pwr_sta_offs = 0xe3c,
+ .pwr_sta2nd_offs = 0xe3c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VEN0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VEN0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_CAM_MAIN] = {
+ .name = "cam-main",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe44,
+ .pwr_sta_offs = 0xe44,
+ .pwr_sta2nd_offs = 0xe44,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CAM_MAIN,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CAM_MAIN,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(CAM_SUB0,
+ MT6858_CAM_SUB0_PROT_EN_SMI_CAM_MAIN,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ BUS_PROT_WR_IGN(CAM_SUB1,
+ MT6858_CAM_SUB1_PROT_EN_SMI_CAM_MAIN,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_CAM_SUBA] = {
+ .name = "cam-suba",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe4c,
+ .pwr_sta_offs = 0xe4c,
+ .pwr_sta2nd_offs = 0xe4c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(CAM_SUB1,
+ MT6858_CAM_SUB1_PROT_EN_SMI_CAM_SUBA,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_CAM_SUBB] = {
+ .name = "cam-subb",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe50,
+ .pwr_sta_offs = 0xe50,
+ .pwr_sta2nd_offs = 0xe50,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(CAM_SUB0,
+ MT6858_CAM_SUB0_PROT_EN_SMI_CAM_SUBB,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_DIS0] = {
+ .name = "dis0",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe6c,
+ .pwr_sta_offs = 0xe6c,
+ .pwr_sta2nd_offs = 0xe6c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_DIS0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_MM_INFRA] = {
+ .name = "mm-infra",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe74,
+ .pwr_sta_offs = 0xe74,
+ .pwr_sta2nd_offs = 0xe74,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA_2ND,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_MM_PROC_DORMANT] = {
+ .name = "mm-proc-dormant",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe78,
+ .pwr_sta_offs = 0xe78,
+ .pwr_sta2nd_offs = 0xe78,
+ .sram_pdn_bits = BIT(9),
+ .sram_pdn_ack_bits = BIT(13),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(VLP,
+ MT6858_VLP_AXI_PROT_EN_MM_PROC,
+ MT6858_VLP_AXI_PROT_EN_SET,
+ MT6858_VLP_AXI_PROT_EN_CLR,
+ MT6858_VLP_AXI_PROT_EN_STA),
+ BUS_PROT_WR_IGN(VLP,
+ MT6858_VLP_AXI_PROT_EN_MM_PROC_2ND,
+ MT6858_VLP_AXI_PROT_EN_SET,
+ MT6858_VLP_AXI_PROT_EN_CLR,
+ MT6858_VLP_AXI_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_SRAM_PDN_INVERTED,
+ },
+ [MT6858_POWER_DOMAIN_CSI_RX] = {
+ .name = "csi-rx",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe98,
+ .pwr_sta_offs = 0xe98,
+ .pwr_sta2nd_offs = 0xe98,
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_SSUSB] = {
+ .name = "ssusb",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xea4,
+ .pwr_sta_offs = 0xea4,
+ .pwr_sta2nd_offs = 0xea4,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SSUSB,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY),
+ },
+ },
+};
+
+static const struct scpsys_soc_data mt6858_scpsys_data = {
+ .domains_data = scpsys_domain_data_mt6858,
+ .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6858),
+ .bus_prot_blocks = scpsys_bus_prot_blocks_mt6858,
+ .num_bus_prot_blocks = ARRAY_SIZE(scpsys_bus_prot_blocks_mt6858),
+ .type = SCPSYS_MTCMOS_TYPE_DIRECT_CTL,
+};
+
+#endif /* __SOC_MEDIATEK_MT6858_PM_DOMAINS_H */
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index 5b4d860318a4..b43f8b4a6a01 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -20,6 +20,7 @@
#include "mt6735-pm-domains.h"
#include "mt6795-pm-domains.h"
+#include "mt6858-pm-domains.h"
#include "mt6893-pm-domains.h"
#include "mt8167-pm-domains.h"
#include "mt8173-pm-domains.h"
@@ -1288,6 +1289,10 @@ static const struct of_device_id scpsys_of_match[] = {
.compatible = "mediatek,mt6795-power-controller",
.data = &mt6795_scpsys_data,
},
+ {
+ .compatible = "mediatek,mt6858-power-controller",
+ .data = &mt6858_scpsys_data,
+ },
{
.compatible = "mediatek,mt6893-power-controller",
.data = &mt6893_scpsys_data,
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h
index 8690690335ad..45da83e9d649 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.h
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h
@@ -66,6 +66,11 @@ enum scpsys_bus_prot_block {
BUS_PROT_BLOCK_INFRA_NAO,
BUS_PROT_BLOCK_SMI,
BUS_PROT_BLOCK_SPM,
+ BUS_PROT_BLOCK_IMG_SUB0,
+ BUS_PROT_BLOCK_CAM_SUB1,
+ BUS_PROT_BLOCK_CAM_SUB0,
+ BUS_PROT_BLOCK_IPE_SUB0,
+ BUS_PROT_BLOCK_VLP,
BUS_PROT_BLOCK_COUNT,
};
--
2.54.0
^ permalink raw reply related
* [PATCH v3 1/3] dt-bindings: power: Add MediaTek MT6858 power domain controller
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov,
Krzysztof Kozlowski
In-Reply-To: <20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com>
From: Nikolai Burov <nikolai.burov@jolla.com>
Add a new compatible and document bindings for the power domain
controller of the MT6858 SoC.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
.../bindings/power/mediatek,power-controller.yaml | 21 +++++++++++++++++++-
include/dt-bindings/power/mediatek,mt6858-power.h | 23 ++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
index 070c6e5666dc..d03e4a925163 100644
--- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
+++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
@@ -25,6 +25,7 @@ properties:
enum:
- mediatek,mt6735-power-controller
- mediatek,mt6795-power-controller
+ - mediatek,mt6858-power-controller
- mediatek,mt6893-power-controller
- mediatek,mt8167-power-controller
- mediatek,mt8173-power-controller
@@ -56,7 +57,7 @@ properties:
faults while enabling or disabling a power domain.
For example, this may hold phandles to INFRACFG and SMI.
minItems: 1
- maxItems: 3
+ maxItems: 6
patternProperties:
"^power-domain@[0-9a-f]+$":
@@ -103,6 +104,7 @@ $defs:
description: |
Power domain index. Valid values are defined in:
"include/dt-bindings/power/mt6795-power.h" - for MT8167 type power domain.
+ "include/dt-bindings/power/mediatek,mt6858-power.h" - for MT6858 type power domain.
"include/dt-bindings/power/mediatek,mt6893-power.h" - for MT6893 type power domain.
"include/dt-bindings/power/mt8167-power.h" - for MT8167 type power domain.
"include/dt-bindings/power/mt8173-power.h" - for MT8173 type power domain.
@@ -156,6 +158,23 @@ required:
- compatible
allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt6858-power-controller
+ then:
+ properties:
+ access-controllers:
+ items:
+ - description: handle to INFRACFG register block
+ - description: handle to IMG_SUB0 register block
+ - description: handle to CAM_SUB1 register block
+ - description: handle to CAM_SUB0 register block
+ - description: handle to IPE_SUB0 register block
+ - description: handle to VLPCFG register block
+
- if:
properties:
compatible:
diff --git a/include/dt-bindings/power/mediatek,mt6858-power.h b/include/dt-bindings/power/mediatek,mt6858-power.h
new file mode 100644
index 000000000000..6ed9e82d4ad3
--- /dev/null
+++ b/include/dt-bindings/power/mediatek,mt6858-power.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_POWER_MT6858_POWER_H
+#define _DT_BINDINGS_POWER_MT6858_POWER_H
+
+#define MT6858_POWER_DOMAIN_MD 0
+#define MT6858_POWER_DOMAIN_CONN 1
+#define MT6858_POWER_DOMAIN_AUDIO 2
+#define MT6858_POWER_DOMAIN_MM_INFRA 3
+#define MT6858_POWER_DOMAIN_ISP_IMG1 4
+#define MT6858_POWER_DOMAIN_ISP_IMG2 5
+#define MT6858_POWER_DOMAIN_ISP_IPE 6
+#define MT6858_POWER_DOMAIN_VDE0 7
+#define MT6858_POWER_DOMAIN_VEN0 8
+#define MT6858_POWER_DOMAIN_CAM_MAIN 9
+#define MT6858_POWER_DOMAIN_CAM_SUBA 10
+#define MT6858_POWER_DOMAIN_CAM_SUBB 11
+#define MT6858_POWER_DOMAIN_DIS0 12
+#define MT6858_POWER_DOMAIN_MM_PROC_DORMANT 13
+#define MT6858_POWER_DOMAIN_CSI_RX 14
+#define MT6858_POWER_DOMAIN_SSUSB 15
+
+#endif /* _DT_BINDINGS_POWER_MT6858_POWER_H */
--
2.54.0
^ permalink raw reply related
* [PATCH v3 2/3] pmdomain: mediatek: Add support for secure modem power domain control
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov
In-Reply-To: <20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com>
From: Nikolai Burov <nikolai.burov@jolla.com>
On recent MediaTek SoCs such as MT6858, the kernel is required to use
a secure monitor call (SMC) to enable or disable the modem power domain.
The power domain control register can be read, but firmware prevents it
from being modified directly. Some other parts of the power sequence,
such as setting the ext_buck_iso register, still need to be performed on
the kernel side.
In preparation for modem support, add a flag to enable this new power
sequence for SoCs that need it. Power domains using this flag are not
expected to configure any bus protection registers, since these are
handled internally by the SMC call.
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
drivers/pmdomain/mediatek/mtk-pm-domains.c | 50 +++++++++++++++++++++++++++---
drivers/pmdomain/mediatek/mtk-pm-domains.h | 1 +
include/linux/soc/mediatek/mtk_sip_svc.h | 3 ++
3 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index 8309a4b46afb..5b4d860318a4 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -57,6 +57,10 @@
#define MTK_SIP_KERNEL_HWCCF_CONTROL MTK_SIP_SMC_CMD(0x540)
+/* Secure MTCMOS commands for modem subsystem */
+#define MTK_MD_MTCMOS_ENABLE 18
+#define MTK_MD_MTCMOS_DISABLE 19
+
struct scpsys_domain {
struct generic_pm_domain genpd;
const struct scpsys_domain_data *data;
@@ -668,6 +672,34 @@ static int scpsys_modem_pwrseq_off(struct scpsys_domain *pd)
return 0;
}
+static bool scpsys_modem_sec_poll(unsigned long cmd)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 1, 0, 0, 0, 0, 0, &res);
+
+ return res.a0 == 0;
+}
+
+static int scpsys_modem_sec_power_on(bool on)
+{
+ struct arm_smccc_res res;
+ unsigned long cmd = on ? MTK_MD_MTCMOS_ENABLE : MTK_MD_MTCMOS_DISABLE;
+ bool tmp;
+ int ret;
+
+ arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 == 0)
+ return 0;
+
+ ret = readx_poll_timeout(scpsys_modem_sec_poll, cmd, tmp, tmp,
+ MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int scpsys_power_on(struct generic_pm_domain *genpd)
{
struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
@@ -686,7 +718,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
regmap_clear_bits(scpsys->base, pd->data->ext_buck_iso_offs,
pd->data->ext_buck_iso_mask);
- if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ))
+ if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
+ ret = scpsys_modem_sec_power_on(true);
+ else if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ))
ret = scpsys_modem_pwrseq_on(pd);
else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
ret = scpsys_simple_pwrseq_on(pd);
@@ -717,7 +751,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
goto err_pwr_ack;
}
- if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
+ if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) &&
+ !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) {
ret = scpsys_sram_enable(pd);
if (ret < 0)
goto err_disable_subsys_clks;
@@ -739,7 +774,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
err_enable_bus_protect:
scpsys_bus_protect_enable(pd, 0);
err_disable_sram:
- if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
+ if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) &&
+ !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
scpsys_sram_disable(pd);
err_disable_subsys_clks:
if (!MTK_SCPD_CAPS(pd, MTK_SCPD_STRICT_BUS_PROTECTION))
@@ -761,7 +797,11 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd)
if (ret < 0)
return ret;
- if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
+ if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) {
+ ret = scpsys_modem_sec_power_on(false);
+ if (ret)
+ return ret;
+ } else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
ret = scpsys_sram_disable(pd);
if (ret < 0)
return ret;
@@ -781,7 +821,7 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd)
ret = scpsys_modem_pwrseq_off(pd);
else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
ret = scpsys_simple_pwrseq_off(pd);
- else
+ else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
ret = scpsys_ctl_pwrseq_off(pd);
if (ret < 0) {
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h
index 092403de66fa..8690690335ad 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.h
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h
@@ -18,6 +18,7 @@
#define MTK_SCPD_SKIP_RESET_B BIT(11)
#define MTK_SCPD_INFRA_PWR_CTL BIT(12)
#define MTK_SCPD_SIMPLE_PWRSEQ BIT(13)
+#define MTK_SCPD_MODEM_SECURE_PWRSEQ BIT(14)
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data ? \
(_scpd)->data->caps & (_x) : \
(_scpd)->hwv_data->caps & (_x))
diff --git a/include/linux/soc/mediatek/mtk_sip_svc.h b/include/linux/soc/mediatek/mtk_sip_svc.h
index abe24a73ee19..6c95a29b79fa 100644
--- a/include/linux/soc/mediatek/mtk_sip_svc.h
+++ b/include/linux/soc/mediatek/mtk_sip_svc.h
@@ -22,6 +22,9 @@
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, MTK_SIP_SMC_CONVENTION, \
ARM_SMCCC_OWNER_SIP, fn_id)
+/* Modem related SMC call */
+#define MTK_SIP_KERNEL_CCCI_CONTROL MTK_SIP_SMC_CMD(0x505)
+
/* DVFSRC SMC calls */
#define MTK_SIP_DVFSRC_VCOREFS_CONTROL MTK_SIP_SMC_CMD(0x506)
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox