airoha: backport on-demand GDM3/GDM4 QoS configuration

Backport the change that configures GDM3/GDM4 as WAN and enables GDM2
loopback when HTB hardware offload is requested, instead of enabling
them during device initialization.

This lets GDM3/GDM4 operate as LAN ports until hardware QoS requires
the WAN path. The change also handles QDMA migration on running
interfaces when switching port roles.

Link: https://patch.msgid.link/20260725-airoha-ethtool-priv_flags-v12-3-5136a30b2157@kernel.org

Signed-off-by: Yalei Zang <yalei.zang@airoha.com>
Link: https://github.com/openwrt/openwrt/pull/25901
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Yalei Zang authored and Robert Marko committed 2026-10-16 15:51:00 +02:00
1 parent 99426e4f7d
commit d4083ed9ee
12 files changed
+890 -589

No files matched your search

@@ -0,0 +1,70 @@
From 0040ff44cd5b59f85ea2feac9ad6b6d9eed66833 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 25 Jul 2026 22:41:40 +0200
Subject: [PATCH 38/50] net: airoha: rename airoha_priv_flags to
airoha_dev_flags
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.
While at it, switch to test_bit()/set_bit()/clear_bit() APIs and
convert the flags field from u32 to unsigned long to make flags
manipulation atomic.
Reviewed-by: Simon Horman <horms@kernel.org>
Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260725-airoha-ethtool-priv_flags-v12-1-5136a30b2157@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 2 +-
drivers/net/ethernet/airoha/airoha_eth.h | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2078,7 +2078,7 @@ static int airoha_dev_init(struct net_de
fallthrough;
case AIROHA_GDM2_IDX:
/* GDM2 is always used as wan */
- dev->flags |= AIROHA_PRIV_F_WAN;
+ set_bit(AIROHA_DEV_F_WAN, &dev->flags);
break;
default:
break;
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -573,8 +573,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,
};
struct airoha_gdm_dev {
@@ -587,7 +587,7 @@ struct airoha_gdm_dev {
u64 cpu_tx_packets;
u64 fwd_tx_packets;
- u32 flags;
+ unsigned long flags;
int nbq;
struct airoha_hw_stats stats;
@@ -697,7 +697,7 @@ static inline u16 airoha_qdma_get_txq(st
static inline bool airoha_is_lan_gdm_dev(struct airoha_gdm_dev *dev)
{
- return !(dev->flags & AIROHA_PRIV_F_WAN);
+ return !test_bit(AIROHA_DEV_F_WAN, &dev->flags);
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
@@ -0,0 +1,84 @@
From 8b9a508819880c0c41d70f5b655bbd0baa231395 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 25 Jul 2026 22:41:41 +0200
Subject: [PATCH 39/50] net: airoha: fix ETS QoS stats counter underflow and
cross-channel corruption
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>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260725-airoha-ethtool-priv_flags-v12-2-5136a30b2157@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 18 +++++++++++-------
drivers/net/ethernet/airoha/airoha_eth.h | 7 ++++---
2 files changed, 15 insertions(+), 10 deletions(-)
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2521,16 +2521,20 @@ static int airoha_qdma_get_tx_ets_stats(
{
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;
}
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -583,9 +583,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];
unsigned long flags;
int nbq;
@@ -0,0 +1,616 @@
From 78a35725e533eef0afe83f6f96cffd69be9eaac7 Mon Sep 17 00:00:00 2001
From: Lorenzo Bianconi <lorenzo@kernel.org>
Date: Sat, 25 Jul 2026 22:41:42 +0200
Subject: [PATCH 40/50] net: airoha: defer GDM3/GDM4 WAN mode and GDM2 loopback
to QoS offload
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.
Introduce the AIROHA_DEV_F_TX_QOS flag to track whether a device has an
active TX HTB qdisc; set it in airoha_tc_setup_qdisc_htb() on successful
TC_HTB_CREATE and 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
TX 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.
When migrating between QDMA blocks, stop TX queues, swap the pointer via
rcu_assign_pointer(), then synchronize_rcu() to ensure no in-flight
ndo_start_xmit holds the old pointer.
Serialize netdev_tx_completed_queue() calls via per-TX-queue spinlocks
txq_lock[] so that both the old and new QDMA NAPI instances can complete
packets concurrently on the same netdev TX queues without racing on
dql_completed().
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 Agarwal <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>
Link: https://patch.msgid.link/20260725-airoha-ethtool-priv_flags-v12-3-5136a30b2157@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
drivers/net/ethernet/airoha/airoha_eth.c | 245 +++++++++++++++++++---
drivers/net/ethernet/airoha/airoha_eth.h | 18 +-
drivers/net/ethernet/airoha/airoha_ppe.c | 9 +-
drivers/net/ethernet/airoha/airoha_regs.h | 1 +
4 files changed, 243 insertions(+), 30 deletions(-)
--- 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
if (!dev)
continue;
- if (dev->qdma != qdma)
+ if (rcu_access_pointer(dev->qdma) != qdma)
continue;
netdev = netdev_from_priv(dev);
@@ -1038,10 +1038,16 @@ static int airoha_qdma_tx_napi_poll(stru
q->queued--;
if (skb) {
+ struct airoha_gdm_dev *dev = netdev_priv(skb->dev);
+ u16 qidx = skb_get_queue_mapping(skb);
struct netdev_queue *txq;
txq = skb_get_tx_queue(skb->dev, skb);
+
+ spin_lock_bh(&dev->txq_lock[qidx]);
netdev_tx_completed_queue(txq, 1, skb->len);
+ spin_unlock_bh(&dev->txq_lock[qidx]);
+
dev_kfree_skb_any(skb);
}
@@ -1905,8 +1911,8 @@ static int airoha_dev_open(struct net_de
{
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);
@@ -1914,6 +1920,7 @@ static int airoha_dev_open(struct net_de
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);
@@ -1937,7 +1944,6 @@ static int airoha_dev_stop(struct net_de
{
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);
@@ -1945,7 +1951,7 @@ static int airoha_dev_stop(struct net_de
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;
@@ -2028,6 +2034,53 @@ static int airoha_enable_gdm2_loopback(s
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)
{
@@ -2054,15 +2107,37 @@ airoha_get_wan_gdm_dev(struct airoha_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 = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
- netdev->irq = dev->qdma->irq_banks[0].irq;
+ qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
+ cur_qdma = airoha_qdma_deref(dev);
+
+ if (cur_qdma)
+ netif_tx_stop_all_queues(netdev);
+
+ rcu_assign_pointer(dev->qdma, qdma);
+ netdev->irq = qdma->irq_banks[0].irq;
+ synchronize_rcu();
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));
+
+ /* Seed qos_stats baselines with the new QDMA block's current
+ * counter values to avoid a spurious spike on the first stats
+ * query after init or 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));
+ }
+
+ if (cur_qdma)
+ netif_tx_wake_all_queues(netdev);
}
static int airoha_dev_init(struct net_device *netdev)
@@ -2217,9 +2292,9 @@ static netdev_tx_t airoha_dev_xmit(struc
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);
@@ -2228,6 +2303,8 @@ static netdev_tx_t airoha_dev_xmit(struc
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);
@@ -2277,6 +2354,8 @@ static netdev_tx_t airoha_dev_xmit(struc
netif_tx_stop_queue(txq);
q->txq_stopped = true;
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
+
return NETDEV_TX_BUSY;
}
@@ -2342,6 +2421,7 @@ static netdev_tx_t airoha_dev_xmit(struc
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
return NETDEV_TX_OK;
@@ -2354,6 +2434,7 @@ error_unlock:
error:
dev_kfree_skb_any(skb);
netdev->stats.tx_dropped++;
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
@@ -2433,17 +2514,19 @@ static int airoha_qdma_set_chan_tx_sched
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) |
@@ -2451,12 +2534,12 @@ static int airoha_qdma_set_chan_tx_sched
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));
@@ -2520,10 +2603,11 @@ static int airoha_qdma_get_tx_ets_stats(
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));
@@ -2809,16 +2893,18 @@ static int airoha_qdma_set_tx_rate_limit
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)
@@ -2854,11 +2940,12 @@ static int airoha_tc_htb_alloc_leaf_queu
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");
@@ -2894,7 +2981,7 @@ static int airoha_qdma_set_rx_meter(stru
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++) {
@@ -3069,10 +3156,11 @@ static void airoha_tc_remove_htb_queue(s
{
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);
@@ -3098,6 +3186,98 @@ static int airoha_tc_htb_delete_leaf_que
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);
+
+ clear_bit(AIROHA_DEV_F_WAN, &dev->flags);
+ 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 (test_bit(AIROHA_DEV_F_TX_QOS, &wan_dev->flags) ||
+ 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));
+ }
+
+ set_bit(AIROHA_DEV_F_WAN, &dev->flags);
+ 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:
+ clear_bit(AIROHA_DEV_F_WAN, &dev->flags);
+ 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);
@@ -3106,6 +3286,8 @@ static int airoha_tc_htb_destroy(struct
for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
airoha_tc_remove_htb_queue(netdev, q);
+ clear_bit(AIROHA_DEV_F_TX_QOS, &dev->flags);
+
return 0;
}
@@ -3125,24 +3307,33 @@ static int airoha_tc_get_htb_get_leaf_qu
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;
+
+ set_bit(AIROHA_DEV_F_TX_QOS, &dev->flags);
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;
}
@@ -3244,8 +3435,8 @@ static int airoha_alloc_gdm_device(struc
{
struct net_device *netdev;
struct airoha_gdm_dev *dev;
+ int err, i;
u8 index;
- int err;
netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
AIROHA_NUM_NETDEV_TX_RINGS,
@@ -3296,6 +3487,8 @@ static int airoha_alloc_gdm_device(struc
netdev->dev.of_node = of_node_get(np);
dev = netdev_priv(netdev);
u64_stats_init(&dev->stats.syncp);
+ for (i = 0; i < ARRAY_SIZE(dev->txq_lock); i++)
+ spin_lock_init(&dev->txq_lock[i]);
dev->port = port;
dev->eth = eth;
dev->nbq = nbq;
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -575,11 +575,12 @@ struct airoha_qdma {
enum airoha_dev_flags {
AIROHA_DEV_F_WAN,
+ AIROHA_DEV_F_TX_QOS,
};
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);
@@ -592,6 +593,11 @@ struct airoha_gdm_dev {
int nbq;
struct airoha_hw_stats stats;
+
+ /* Serialize netdev_tx_completed_queue() calls per TX queue during
+ * QDMA migration.
+ */
+ spinlock_t txq_lock[AIROHA_NUM_NETDEV_TX_RINGS];
};
struct airoha_gdm_port {
@@ -715,6 +721,16 @@ int airoha_get_fe_port(struct airoha_gdm
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);
--- 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(stru
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 - &eth->qdma[0];
u32 fe_cpu_port;
--- 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)
@@ -13,7 +13,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1617,6 +1617,10 @@ static int airoha_hw_init(struct platfor
@@ -1623,6 +1623,10 @@ static int airoha_hw_init(struct platfor
if (err)
return err;
@@ -28,10 +28,10 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* Enable split for MIB counters for GDM3 and GDM4 */
airoha_fe_set(eth, REG_FE_GDM_MIB_CFG(AIROHA_GDM3_IDX),
@@ -1918,7 +1920,8 @@ static int airoha_dev_open(struct net_de
if (err)
@@ -1925,7 +1927,8 @@ static int airoha_dev_open(struct net_de
return err;
qdma = airoha_qdma_deref(dev);
- if (netdev_uses_dsa(netdev))
+ /* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
+ if (netdev_uses_dsa(netdev) || port->id > 2)
@@ -15,7 +15,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3585,7 +3585,6 @@ static void airoha_remove(struct platfor
@@ -3782,7 +3782,6 @@ static void airoha_remove(struct platfor
}
static const char * const en7581_xsi_rsts_names[] = {
@@ -23,7 +23,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
"hsi0-mac",
"hsi1-mac",
"hsi-mac",
@@ -3672,7 +3671,6 @@ static int airoha_en7581_get_dev_from_sp
@@ -3869,7 +3868,6 @@ static int airoha_en7581_get_dev_from_sp
}
static const char * const an7583_xsi_rsts_names[] = {
@@ -42,7 +42,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
#include <linux/u64_stats_sync.h>
#include <net/dst_metadata.h>
#include <net/page_pool/helpers.h>
@@ -1699,7 +1700,7 @@ static void airoha_update_hw_stats(struc
@@ -1705,7 +1706,7 @@ static void airoha_update_hw_stats(struc
u32 val, i = 0;
u64 data;
@@ -51,7 +51,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
/* Read relevant MIB for GDM with multiple port attached */
if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
@@ -1893,7 +1894,7 @@ static void airoha_update_hw_stats(struc
@@ -1899,7 +1900,7 @@ static void airoha_update_hw_stats(struc
u64_stats_update_end(&dev->stats.syncp);
@@ -60,8 +60,8 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
static void airoha_dev_set_xmit_frame_size(struct net_device *netdev)
@@ -1915,6 +1916,14 @@ static int airoha_dev_open(struct net_de
u32 pse_port = FE_PSE_PORT_PPE1;
@@ -1921,6 +1922,14 @@ static int airoha_dev_open(struct net_de
struct airoha_qdma *qdma;
int err;
+ err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
@@ -75,17 +75,18 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
netif_tx_start_all_queues(netdev);
err = airoha_set_vip_for_gdm_port(dev, true);
if (err)
@@ -2032,6 +2041,9 @@ static int airoha_enable_gdm2_loopback(s
__field_prep(mask, AIROHA_GDM2_IDX));
}
@@ -1961,6 +1970,10 @@ static int airoha_dev_stop(struct net_de
airoha_set_gdm_port_fwd_cfg(dev->eth,
REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
+
+ phylink_stop(dev->phylink);
+ phylink_disconnect_phy(dev->phylink);
+
return 0;
}
@@ -2435,6 +2447,24 @@ airoha_ethtool_get_rmon_stats(struct net
@@ -2516,6 +2529,24 @@ airoha_ethtool_get_rmon_stats(struct net
} while (u64_stats_fetch_retry(&dev->stats.syncp, start));
}
@@ -110,7 +111,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
int channel, enum tx_sched_mode mode,
const u16 *weights, u8 n_weights)
@@ -3185,7 +3215,8 @@ static const struct ethtool_ops airoha_e
@@ -3380,7 +3411,8 @@ static const struct ethtool_ops airoha_e
.get_drvinfo = airoha_ethtool_get_drvinfo,
.get_eth_mac_stats = airoha_ethtool_get_mac_stats,
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
@@ -120,7 +121,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
.get_link = ethtool_op_get_link,
};
@@ -3241,6 +3272,155 @@ bool airoha_is_valid_gdm_dev(struct airo
@@ -3436,6 +3468,155 @@ bool airoha_is_valid_gdm_dev(struct airo
return false;
}
@@ -276,7 +277,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
static int airoha_alloc_gdm_device(struct airoha_eth *eth,
struct airoha_gdm_port *port,
int nbq, struct device_node *np)
@@ -3304,7 +3484,7 @@ static int airoha_alloc_gdm_device(struc
@@ -3501,7 +3682,7 @@ static int airoha_alloc_gdm_device(struc
dev->nbq = nbq;
port->devs[index] = dev;
@@ -285,7 +286,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
}
static int airoha_alloc_gdm_port(struct airoha_eth *eth,
@@ -3339,7 +3519,7 @@ static int airoha_alloc_gdm_port(struct
@@ -3536,7 +3717,7 @@ static int airoha_alloc_gdm_port(struct
return -ENOMEM;
port->id = id;
@@ -294,7 +295,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
eth->ports[p] = port;
err = airoha_metadata_dst_alloc(port);
@@ -3536,6 +3716,8 @@ error_napi_stop:
@@ -3733,6 +3914,8 @@ error_napi_stop:
netdev = netdev_from_priv(dev);
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
@@ -303,7 +304,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
of_node_put(netdev->dev.of_node);
}
airoha_metadata_dst_free(port);
@@ -3574,6 +3756,7 @@ static void airoha_remove(struct platfor
@@ -3771,6 +3954,7 @@ static void airoha_remove(struct platfor
netdev = netdev_from_priv(dev);
unregister_netdev(netdev);
@@ -313,17 +314,17 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
airoha_metadata_dst_free(port);
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -591,6 +591,9 @@ struct airoha_gdm_dev {
int nbq;
struct airoha_hw_stats stats;
@@ -598,6 +598,9 @@ struct airoha_gdm_dev {
* QDMA migration.
*/
spinlock_t txq_lock[AIROHA_NUM_NETDEV_TX_RINGS];
+
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
};
struct airoha_gdm_port {
@@ -598,8 +601,8 @@ struct airoha_gdm_port {
@@ -605,8 +608,8 @@ struct airoha_gdm_port {
int id;
int users;
@@ -25,7 +25,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#include "airoha_regs.h"
#include "airoha_eth.h"
@@ -331,7 +332,7 @@ static int airoha_ppe_foe_entry_prepare(
@@ -334,7 +335,7 @@ static int airoha_ppe_foe_entry_prepare(
struct airoha_foe_entry *hwe,
struct net_device *netdev, int type,
struct airoha_flow_data *data,
@@ -34,7 +34,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev);
@@ -366,7 +367,7 @@ static int airoha_ppe_foe_entry_prepare(
@@ -369,7 +370,7 @@ static int airoha_ppe_foe_entry_prepare(
} else {
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port;
@@ -43,7 +43,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (!airoha_is_valid_gdm_dev(eth, dev))
return -EINVAL;
@@ -386,11 +387,13 @@ static int airoha_ppe_foe_entry_prepare(
@@ -389,11 +390,13 @@ static int airoha_ppe_foe_entry_prepare(
*/
channel = dsa_port >= 0 ? dsa_port : port->id;
channel = channel % AIROHA_NUM_QOS_CHANNELS;
@@ -57,7 +57,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
AIROHA_FOE_IB2_PSE_QOS;
/* For downlink traffic consume SRAM memory for hw
* forwarding descriptors queue.
@@ -1081,7 +1084,7 @@ static int airoha_ppe_flow_offload_repla
@@ -1084,7 +1087,7 @@ static int airoha_ppe_flow_offload_repla
struct airoha_flow_data data = {};
struct net_device *odev = NULL;
struct flow_action_entry *act;
@@ -66,7 +66,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_foe_entry hwe;
int err, i, offload_type;
u16 addr_type = 0;
@@ -1114,6 +1117,13 @@ static int airoha_ppe_flow_offload_repla
@@ -1117,6 +1120,13 @@ static int airoha_ppe_flow_offload_repla
return -EOPNOTSUPP;
}
@@ -80,7 +80,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
switch (addr_type) {
case 0:
offload_type = PPE_PKT_TYPE_BRIDGE;
@@ -1179,7 +1189,7 @@ static int airoha_ppe_flow_offload_repla
@@ -1182,7 +1192,7 @@ static int airoha_ppe_flow_offload_repla
return -EINVAL;
err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type,
@@ -72,7 +72,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+
+static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
+{
+ struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+ struct airoha_eth *eth = qdma->eth;
+ int qdma_id = qdma - &eth->qdma[0];
+ int i, lro_queue_index = 0;
@@ -365,7 +365,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
WRITE_ONCE(desc->ctrl, 0);
WRITE_ONCE(desc->addr, 0);
WRITE_ONCE(desc->data, 0);
@@ -1908,13 +2135,52 @@ static void airoha_dev_set_xmit_frame_si
@@ -1914,13 +2141,56 @@ static void airoha_dev_set_xmit_frame_si
VLAN_ETH_HLEN + netdev->mtu));
}
@@ -387,8 +387,8 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+
+ if (!iter_dev || iter_dev == dev)
+ continue;
+
+ if (iter_dev->qdma != dev->qdma)
+ if (rcu_access_pointer(iter_dev->qdma) !=
+ rcu_access_pointer(dev->qdma))
+ continue;
+
+ netdev = netdev_from_priv(iter_dev);
@@ -404,11 +404,15 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_eth *eth = qdma->eth;
u32 pse_port = FE_PSE_PORT_PPE1;
struct airoha_qdma *qdma;
- int err;
+ int err, qdma_id = qdma - &eth->qdma[0];
+ struct airoha_eth *eth;
+ int err, qdma_id;
+
+ qdma = airoha_qdma_deref(dev);
+ eth = qdma->eth;
+ qdma_id = qdma - &eth->qdma[0];
+
+ /* HW LRO is shared by all netdevs using the same QDMA. */
+ if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
@@ -419,8 +423,11 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
err = phylink_of_phy_connect(dev->phylink, netdev->dev.of_node, 0);
if (err) {
@@ -1931,21 +2197,27 @@ static int airoha_dev_open(struct net_de
@@ -1935,24 +2205,29 @@ static int airoha_dev_open(struct net_de
if (err)
return err;
- qdma = airoha_qdma_deref(dev);
/* It seems GDM3 and GDM4 needs SPORT enabled to correctly work */
if (netdev_uses_dsa(netdev) || port->id > 2)
- airoha_fe_set(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
@@ -451,30 +458,26 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
return 0;
}
@@ -1964,6 +2236,12 @@ static int airoha_dev_stop(struct net_de
airoha_set_gdm_port_fwd_cfg(qdma->eth,
@@ -1960,6 +2235,7 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
+ struct airoha_qdma *qdma;
netif_tx_disable(netdev);
airoha_set_vip_for_gdm_port(dev, false);
@@ -1971,6 +2247,10 @@ static int airoha_dev_stop(struct net_de
REG_GDM_FWD_CFG(port->id),
FE_PSE_PORT_DROP);
+ qdma = airoha_qdma_deref(dev);
+ qdma->users--;
+ airoha_update_netdev_features(dev);
+
+ phylink_stop(dev->phylink);
+ phylink_disconnect_phy(dev->phylink);
+
return 0;
}
phylink_stop(dev->phylink);
phylink_disconnect_phy(dev->phylink);
@@ -2041,9 +2319,6 @@ static int airoha_enable_gdm2_loopback(s
__field_prep(mask, AIROHA_GDM2_IDX));
}
- phylink_stop(dev->phylink);
- phylink_disconnect_phy(dev->phylink);
-
return 0;
}
@@ -2232,6 +2507,36 @@ int airoha_get_fe_port(struct airoha_gdm
@@ -2308,6 +2588,38 @@ int airoha_get_fe_port(struct airoha_gdm
}
}
@@ -482,8 +485,10 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+ netdev_features_t features)
+{
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma;
+
+ if (dev->qdma->users > 1)
+ qdma = airoha_qdma_deref(dev);
+ if (qdma->users > 1)
+ features &= ~NETIF_F_GRO_HW;
+
+ return features;
@@ -494,7 +499,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+{
+ netdev_features_t diff = netdev->features ^ features;
+ struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
+ int qdma_id = qdma - &dev->eth->qdma[0];
+
+ if (!(diff & NETIF_F_GRO_HW))
@@ -511,7 +516,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *netdev)
{
@@ -3205,6 +3510,8 @@ static const struct net_device_ops airoh
@@ -3401,6 +3713,8 @@ static const struct net_device_ops airoh
.ndo_stop = airoha_dev_stop,
.ndo_change_mtu = airoha_dev_change_mtu,
.ndo_select_queue = airoha_dev_select_queue,
@@ -520,7 +525,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
.ndo_start_xmit = airoha_dev_xmit,
.ndo_get_stats64 = airoha_dev_get_stats64,
.ndo_set_mac_address = airoha_dev_set_macaddr,
@@ -3442,11 +3749,9 @@ static int airoha_alloc_gdm_device(struc
@@ -3638,11 +3952,9 @@ static int airoha_alloc_gdm_device(struc
netdev->ethtool_ops = &airoha_ethtool_ops;
netdev->max_mtu = AIROHA_MAX_MTU;
netdev->watchdog_timeo = 5 * HZ;
@@ -565,7 +570,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
struct airoha_irq_bank irq_banks[AIROHA_MAX_NUM_IRQ_BANKS];
@@ -713,6 +727,18 @@ static inline bool airoha_is_7583(struct
@@ -720,6 +734,18 @@ static inline bool airoha_is_7583(struct
return eth->soc->version == 0x7583;
}
@@ -607,7 +612,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n)
#define GDM_PAD_EN_MASK BIT(28)
#define GDM_DROP_CRC_ERR_MASK BIT(23)
@@ -898,9 +912,15 @@
@@ -899,9 +913,15 @@
#define QDMA_ETH_RXMSG_SPORT_MASK GENMASK(25, 21)
#define QDMA_ETH_RXMSG_CRSN_MASK GENMASK(20, 16)
#define QDMA_ETH_RXMSG_PPE_ENTRY_MASK GENMASK(15, 0)
@@ -19,35 +19,19 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -538,7 +538,7 @@ static bool airoha_fe_lro_is_enabled(str
@@ -2157,10 +2157,7 @@ static void airoha_update_netdev_feature
struct airoha_gdm_dev *iter_dev = port->devs[j];
struct net_device *netdev;
static void airoha_dev_lro_enable(struct airoha_gdm_dev *dev)
{
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
struct airoha_eth *eth = qdma->eth;
int qdma_id = qdma - &eth->qdma[0];
int i, lro_queue_index = 0;
@@ -1164,7 +1164,7 @@ static void airoha_qdma_wake_netdev_txqs
if (!dev)
continue;
- if (dev->qdma != qdma)
+ if (rcu_access_pointer(dev->qdma) != qdma)
continue;
netdev = netdev_from_priv(dev);
@@ -2154,7 +2154,8 @@ static void airoha_update_netdev_feature
if (!iter_dev || iter_dev == dev)
continue;
- if (iter_dev->qdma != dev->qdma)
+ if (rcu_access_pointer(iter_dev->qdma) !=
+ rcu_access_pointer(dev->qdma))
- if (!iter_dev || iter_dev == dev)
- continue;
- if (rcu_access_pointer(iter_dev->qdma) !=
- rcu_access_pointer(dev->qdma))
+ if (!iter_dev)
continue;
netdev = netdev_from_priv(iter_dev);
@@ -2166,14 +2167,27 @@ static void airoha_update_netdev_feature
@@ -2172,6 +2169,16 @@ static void airoha_update_netdev_feature
}
}
@@ -64,21 +48,7 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
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;
- struct airoha_eth *eth = qdma->eth;
+ struct airoha_eth *eth = dev->eth;
+ struct airoha_qdma *qdma;
u32 pse_port = FE_PSE_PORT_PPE1;
- int err, qdma_id = qdma - &eth->qdma[0];
+ int err, qdma_id;
+
+ qdma = airoha_qdma_deref(dev);
+ qdma_id = qdma - &eth->qdma[0];
/* HW LRO is shared by all netdevs using the same QDMA. */
if (qdma->users && airoha_fe_lro_is_enabled(eth, qdma_id)) {
@@ -2205,7 +2219,7 @@ static int airoha_dev_open(struct net_de
@@ -2215,7 +2222,7 @@ static int airoha_dev_open(struct net_de
airoha_dev_set_xmit_frame_size(netdev);
port->users++;
@@ -87,495 +57,55 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
if (!airoha_is_lan_gdm_dev(dev) &&
airoha_ppe_is_enabled(eth, 1))
@@ -2225,18 +2239,19 @@ static int airoha_dev_stop(struct net_de
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
struct airoha_gdm_port *port = dev->port;
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma;
netif_tx_disable(netdev);
airoha_set_vip_for_gdm_port(dev, false);
+ qdma = airoha_qdma_deref(dev);
if (--port->users)
airoha_ppe_set_xmit_frame_size(dev);
else
airoha_set_gdm_port_fwd_cfg(qdma->eth,
REG_GDM_FWD_CFG(port->id),
@@ -2248,7 +2255,7 @@ static int airoha_dev_stop(struct net_de
FE_PSE_PORT_DROP);
qdma = airoha_qdma_deref(dev);
- qdma->users--;
+ airoha_qdma_user_put(qdma);
airoha_update_netdev_features(dev);
phylink_stop(dev->phylink);
@@ -2322,6 +2337,53 @@ static int airoha_enable_gdm2_loopback(s
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)
{
@@ -2348,15 +2410,30 @@ airoha_get_wan_gdm_dev(struct airoha_eth
static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
{
@@ -2409,17 +2416,28 @@ static void airoha_dev_set_qdma(struct a
struct net_device *netdev = netdev_from_priv(dev);
+ struct airoha_qdma *cur_qdma, *qdma;
struct airoha_qdma *cur_qdma, *qdma;
struct airoha_eth *eth = dev->eth;
int ppe_id;
+ bool running = netif_running(netdev);
int i, ppe_id;
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
- dev->qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
- netdev->irq = dev->qdma->irq_banks[0].irq;
+ qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
+ cur_qdma = airoha_qdma_deref(dev);
+ if (netif_running(netdev))
+ airoha_qdma_user_get(qdma);
qdma = &eth->qdma[!airoha_is_lan_gdm_dev(dev)];
cur_qdma = airoha_qdma_deref(dev);
- if (cur_qdma)
+ if (cur_qdma) {
netif_tx_stop_all_queues(netdev);
+ if (running && cur_qdma != qdma) {
+ int id = cur_qdma - &eth->qdma[0];
+
+ rcu_assign_pointer(dev->qdma, qdma);
+ netdev->irq = qdma->irq_banks[0].irq;
+ if (netdev->features & NETIF_F_GRO_HW)
+ airoha_fe_lro_disable(eth, id);
+ airoha_qdma_user_put(cur_qdma);
+ }
+ }
rcu_assign_pointer(dev->qdma, qdma);
netdev->irq = qdma->irq_banks[0].irq;
+ if (running && cur_qdma != qdma)
+ airoha_qdma_user_get(qdma);
synchronize_rcu();
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));
@@ -2438,6 +2456,12 @@ static void airoha_dev_set_qdma(struct a
if (cur_qdma)
netif_tx_wake_all_queues(netdev);
+
+ if (!cur_qdma)
+ return;
+
+ synchronize_rcu();
+ netif_tx_wake_all_queues(netdev);
+
+ if (netif_running(netdev))
+ airoha_qdma_user_put(cur_qdma);
+ if (cur_qdma != qdma) {
+ airoha_update_netdev_features(dev);
+ if (running && (netdev->features & NETIF_F_GRO_HW))
+ airoha_dev_lro_enable(dev);
+ }
}
static int airoha_dev_init(struct net_device *netdev)
@@ -2511,8 +2588,10 @@ static netdev_features_t airoha_dev_fix_
netdev_features_t features)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
+ struct airoha_qdma *qdma;
- if (dev->qdma->users > 1)
+ qdma = airoha_qdma_deref(dev);
+ if (qdma->users > 1)
features &= ~NETIF_F_GRO_HW;
return features;
@@ -2523,7 +2602,7 @@ static int airoha_dev_set_features(struc
{
netdev_features_t diff = netdev->features ^ features;
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
int qdma_id = qdma - &dev->eth->qdma[0];
if (!(diff & NETIF_F_GRO_HW))
@@ -2541,9 +2620,9 @@ static netdev_tx_t airoha_dev_xmit(struc
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);
@@ -2552,6 +2631,8 @@ static netdev_tx_t airoha_dev_xmit(struc
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);
@@ -2601,6 +2682,8 @@ static netdev_tx_t airoha_dev_xmit(struc
netif_tx_stop_queue(txq);
q->txq_stopped = true;
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
+
return NETDEV_TX_BUSY;
}
@@ -2666,6 +2749,7 @@ static netdev_tx_t airoha_dev_xmit(struc
FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
spin_unlock_bh(&q->lock);
+ rcu_read_unlock();
return NETDEV_TX_OK;
@@ -2678,6 +2762,7 @@ error_unlock:
error:
dev_kfree_skb_any(skb);
netdev->stats.tx_dropped++;
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
@@ -2775,17 +2860,19 @@ static int airoha_qdma_set_chan_tx_sched
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) |
@@ -2793,12 +2880,12 @@ static int airoha_qdma_set_chan_tx_sched
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));
@@ -2862,7 +2949,7 @@ static int airoha_qdma_get_tx_ets_stats(
struct tc_ets_qopt_offload *opt)
{
struct airoha_gdm_dev *dev = netdev_priv(netdev);
- struct airoha_qdma *qdma = dev->qdma;
+ struct airoha_qdma *qdma = airoha_qdma_deref(dev);
u64 cpu_tx_packets = airoha_qdma_rr(qdma, REG_CNTR_VAL(channel << 1));
u64 fwd_tx_packets = airoha_qdma_rr(qdma,
@@ -3147,16 +3234,18 @@ static int airoha_qdma_set_tx_rate_limit
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)
@@ -3192,11 +3281,12 @@ static int airoha_tc_htb_alloc_leaf_queu
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");
@@ -3232,7 +3322,7 @@ static int airoha_qdma_set_rx_meter(stru
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++) {
@@ -3407,10 +3497,11 @@ static void airoha_tc_remove_htb_queue(s
{
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);
@@ -3436,6 +3527,95 @@ static int airoha_tc_htb_delete_leaf_que
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_PRIV_F_WAN;
+ airoha_dev_set_qdma(dev);
+
+ airoha_set_macaddr(dev, netdev->dev_addr);
+ 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_PRIV_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_PRIV_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;
+
+ 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_PRIV_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);
@@ -3444,6 +3624,8 @@ static int airoha_tc_htb_destroy(struct
for_each_set_bit(q, dev->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
airoha_tc_remove_htb_queue(netdev, q);
+ dev->flags &= ~AIROHA_PRIV_F_QOS;
+
return 0;
}
@@ -3463,24 +3645,33 @@ static int airoha_tc_get_htb_get_leaf_qu
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_PRIV_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;
}
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -401,6 +401,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)
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -589,11 +589,12 @@ struct airoha_qdma {
enum airoha_priv_flags {
AIROHA_PRIV_F_WAN = BIT(0),
+ AIROHA_PRIV_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);
@@ -743,6 +744,16 @@ int airoha_get_fe_port(struct airoha_gdm
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);
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -16,7 +16,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 = {
@@ -87,8 +90,8 @@ static u32 airoha_ppe_get_timestamp(stru
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 - &eth->qdma[0];
u32 fe_cpu_port;
@@ -16,22 +16,17 @@ Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -3544,6 +3544,7 @@ static void airoha_disable_qos_for_gdm34
@@ -3577,9 +3577,9 @@ static void airoha_disable_qos_for_gdm34
dev->flags &= ~AIROHA_PRIV_F_WAN;
clear_bit(AIROHA_DEV_F_WAN, &dev->flags);
airoha_dev_set_qdma(dev);
+ airoha_dev_set_xmit_frame_size(netdev);
airoha_set_macaddr(dev, netdev->dev_addr);
- airoha_ppe_set_xmit_frame_size(dev);
if (netif_running(netdev))
@@ -3592,6 +3593,7 @@ static int airoha_enable_qos_for_gdm34(s
if (err)
goto error_disable_loopback;
+ airoha_dev_set_xmit_frame_size(netdev);
if (netif_running(netdev)) {
u32 pse_port;
airoha_set_gdm_port_fwd_cfg(dev->eth,
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -105,16 +105,23 @@ void airoha_ppe_set_xmit_frame_size(stru
@@ -18,7 +18,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -2571,17 +2571,9 @@ static u32 airoha_get_dsa_tag(struct sk_
@@ -2599,17 +2599,9 @@ static u32 airoha_get_dsa_tag(struct sk_
int airoha_get_fe_port(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;