mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Merge branch 'net-airoha-support-multiple-net_devices-connected-to-the-same-gdm-port'
Lorenzo Bianconi says:
====================
net: airoha: Support multiple net_devices connected to the same GDM port
EN7581 or AN7583 SoCs support connecting multiple external SerDes (e.g.
Ethernet or USB SerDes) to GDM3 or GDM4 ports via a hw arbiter that
manages the traffic in a TDM manner. As a result multiple net_devices can
connect to the same GDM{3,4} port and there is a theoretical "1:n"
relation between GDM ports and net_devices.
┌─────────────────────────────────┐
│ │ ┌──────┐
│ P1 GDM1 ├────►MT7530│
│ │ └──────┘
│ │ ETH0 (DSA conduit)
│ │
│ PSE/FE │
│ │
│ │
│ │ ┌─────┐
│ P0 CDM1 ├────►QDMA0│
│ P4 P9 GDM4 │ └─────┘
└──┬─────────────────────────┬────┘
│ │
┌──▼──┐ ┌────▼────┐
│ PPE │ │ ARB │
└─────┘ └─┬─────┬─┘
│ │
┌──▼──┐┌─▼───┐
│ ETH ││ USB │
└─────┘└─────┘
ETH1 ETH2
This series introduces support for multiple net_devices connected to the
same Frame Engine (FE) GDM port (GDM3 or GDM4) via an external hw
arbiter. Please note GDM1 or GDM2 does not support the connection with
the external arbiter.
====================
Link: https://patch.msgid.link/20260603-airoha-eth-multi-serdes-v9-0-5d476bc2f426@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
ecc4aa6f41
|
|
@ -130,6 +130,42 @@ patternProperties:
|
|||
maximum: 4
|
||||
description: GMAC port identifier
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
reg:
|
||||
contains:
|
||||
items:
|
||||
- enum:
|
||||
- 3
|
||||
- 4
|
||||
then:
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^ethernet@[0-5]$":
|
||||
type: object
|
||||
unevaluatedProperties: false
|
||||
$ref: ethernet-controller.yaml#
|
||||
description: External ethernet port ID available on the GDM port
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,eth-port
|
||||
|
||||
reg:
|
||||
maximum: 5
|
||||
description: External ethernet port identifier
|
||||
|
||||
required:
|
||||
- reg
|
||||
- compatible
|
||||
|
||||
required:
|
||||
- reg
|
||||
- compatible
|
||||
|
|
@ -191,9 +227,27 @@ examples:
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
mac: ethernet@1 {
|
||||
ethernet@1 {
|
||||
compatible = "airoha,eth-mac";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
ethernet@4 {
|
||||
compatible = "airoha,eth-mac";
|
||||
reg = <4>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet@0 {
|
||||
compatible = "airoha,eth-port";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
ethernet@1 {
|
||||
compatible = "airoha,eth-port";
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,20 +71,76 @@ static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank,
|
|||
airoha_qdma_set_irqmask(irq_bank, index, mask, 0);
|
||||
}
|
||||
|
||||
static void airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
|
||||
static int airoha_set_macaddr(struct airoha_gdm_dev *dev, const u8 *addr)
|
||||
{
|
||||
u8 ref_addr[ETH_ALEN] __aligned(2);
|
||||
struct airoha_eth *eth = dev->eth;
|
||||
u32 val, reg;
|
||||
u32 reg, val, lmin, lmax;
|
||||
int i;
|
||||
|
||||
eth_zero_addr(ref_addr);
|
||||
lmin = (addr[3] << 16) | (addr[4] << 8) | addr[5];
|
||||
lmax = lmin;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
struct airoha_gdm_dev *iter_dev;
|
||||
struct net_device *netdev;
|
||||
|
||||
iter_dev = port->devs[j];
|
||||
if (!iter_dev || iter_dev == dev)
|
||||
continue;
|
||||
|
||||
if (airoha_is_lan_gdm_dev(iter_dev) !=
|
||||
airoha_is_lan_gdm_dev(dev))
|
||||
continue;
|
||||
|
||||
netdev = netdev_from_priv(iter_dev);
|
||||
if (netdev->reg_state != NETREG_REGISTERED)
|
||||
continue;
|
||||
|
||||
ether_addr_copy(ref_addr, netdev->dev_addr);
|
||||
val = (netdev->dev_addr[3] << 16) |
|
||||
(netdev->dev_addr[4] << 8) | netdev->dev_addr[5];
|
||||
if (val < lmin)
|
||||
lmin = val;
|
||||
if (val > lmax)
|
||||
lmax = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_zero_ether_addr(ref_addr) && memcmp(ref_addr, addr, 3)) {
|
||||
/* According to the HW design, hw mac address MSBs must be
|
||||
* the same for each net_device with the same LAN/WAN
|
||||
* configuration.
|
||||
*/
|
||||
struct net_device *netdev = netdev_from_priv(dev);
|
||||
|
||||
dev_warn(eth->dev,
|
||||
"%s: wrong mac addr, MSBs must be %02x:%02x:%02x\n",
|
||||
netdev->name, ref_addr[0], ref_addr[1],
|
||||
ref_addr[2]);
|
||||
dev_warn(eth->dev, "FE hw forwarding won't work properly\n");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = airoha_is_lan_gdm_dev(dev) ? REG_FE_LAN_MAC_H : REG_FE_WAN_MAC_H;
|
||||
val = (addr[0] << 16) | (addr[1] << 8) | addr[2];
|
||||
airoha_fe_wr(eth, reg, val);
|
||||
|
||||
val = (addr[3] << 16) | (addr[4] << 8) | addr[5];
|
||||
airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val);
|
||||
airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val);
|
||||
airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), lmin);
|
||||
airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), lmax);
|
||||
|
||||
airoha_ppe_init_upd_mem(dev);
|
||||
airoha_ppe_init_upd_mem(dev, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr,
|
||||
|
|
@ -106,7 +162,7 @@ static int airoha_set_vip_for_gdm_port(struct airoha_gdm_dev *dev, bool enable)
|
|||
struct airoha_eth *eth = dev->eth;
|
||||
u32 vip_port;
|
||||
|
||||
vip_port = eth->soc->ops.get_vip_port(port, port->nbq);
|
||||
vip_port = eth->soc->ops.get_vip_port(port, dev->nbq);
|
||||
if (enable) {
|
||||
airoha_fe_set(eth, REG_FE_VIP_PORT_EN, vip_port);
|
||||
airoha_fe_set(eth, REG_FE_IFC_PORT_EN, vip_port);
|
||||
|
|
@ -566,24 +622,26 @@ static int airoha_qdma_fill_rx_queue(struct airoha_queue *q)
|
|||
return nframes;
|
||||
}
|
||||
|
||||
static int airoha_qdma_get_gdm_port(struct airoha_eth *eth,
|
||||
struct airoha_qdma_desc *desc)
|
||||
static struct airoha_gdm_dev *
|
||||
airoha_qdma_get_gdm_dev(struct airoha_eth *eth, struct airoha_qdma_desc *desc)
|
||||
{
|
||||
u32 port, sport, msg1 = le32_to_cpu(READ_ONCE(desc->msg1));
|
||||
struct airoha_gdm_port *port;
|
||||
u16 p, d;
|
||||
|
||||
sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1);
|
||||
switch (sport) {
|
||||
case 0x10 ... 0x14:
|
||||
port = 0;
|
||||
break;
|
||||
case 0x2 ... 0x4:
|
||||
port = sport - 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (eth->soc->ops.get_dev_from_sport(desc, &p, &d))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return port >= ARRAY_SIZE(eth->ports) ? -EINVAL : port;
|
||||
if (p >= ARRAY_SIZE(eth->ports))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
port = eth->ports[p];
|
||||
if (!port)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (d >= ARRAY_SIZE(port->devs))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return port->devs[d] ? port->devs[d] : ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
|
||||
|
|
@ -598,9 +656,9 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
|
|||
struct airoha_queue_entry *e = &q->entry[q->tail];
|
||||
struct airoha_qdma_desc *desc = &q->desc[q->tail];
|
||||
u32 hash, reason, msg1, desc_ctrl;
|
||||
struct airoha_gdm_port *port;
|
||||
struct airoha_gdm_dev *dev;
|
||||
struct net_device *netdev;
|
||||
int data_len, len, p;
|
||||
int data_len, len;
|
||||
struct page *page;
|
||||
|
||||
desc_ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
|
||||
|
|
@ -621,15 +679,11 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
|
|||
if (!len || data_len < len)
|
||||
goto free_frag;
|
||||
|
||||
p = airoha_qdma_get_gdm_port(eth, desc);
|
||||
if (p < 0 || !eth->ports[p])
|
||||
dev = airoha_qdma_get_gdm_dev(eth, desc);
|
||||
if (IS_ERR(dev))
|
||||
goto free_frag;
|
||||
|
||||
port = eth->ports[p];
|
||||
if (!port->dev)
|
||||
goto free_frag;
|
||||
|
||||
netdev = port->dev->dev;
|
||||
netdev = netdev_from_priv(dev);
|
||||
if (!q->skb) { /* first buffer */
|
||||
q->skb = napi_build_skb(e->buf - AIROHA_RX_HEADROOM,
|
||||
q->buf_size);
|
||||
|
|
@ -659,6 +713,8 @@ static int airoha_qdma_rx_process(struct airoha_queue *q, int budget)
|
|||
continue;
|
||||
|
||||
if (netdev_uses_dsa(netdev)) {
|
||||
struct airoha_gdm_port *port = dev->port;
|
||||
|
||||
/* PPE module requires untagged packets to work
|
||||
* properly and it provides DSA port index via the
|
||||
* DMA descriptor. Report DSA tag to the DSA stack
|
||||
|
|
@ -852,24 +908,29 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
struct airoha_gdm_dev *dev;
|
||||
int j;
|
||||
int d;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
dev = port->dev;
|
||||
if (!dev)
|
||||
continue;
|
||||
for (d = 0; d < ARRAY_SIZE(port->devs); d++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[d];
|
||||
struct net_device *netdev;
|
||||
int j;
|
||||
|
||||
if (dev->qdma != qdma)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < dev->dev->num_tx_queues; j++) {
|
||||
if (airoha_qdma_get_txq(qdma, j) != qid)
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
netif_wake_subqueue(dev->dev, j);
|
||||
if (dev->qdma != qdma)
|
||||
continue;
|
||||
|
||||
netdev = netdev_from_priv(dev);
|
||||
for (j = 0; j < netdev->num_tx_queues; j++) {
|
||||
if (airoha_qdma_get_txq(qdma, j) != qid)
|
||||
continue;
|
||||
|
||||
netif_wake_subqueue(netdev, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
q->txq_stopped = false;
|
||||
|
|
@ -1715,8 +1776,8 @@ static int airoha_dev_open(struct net_device *netdev)
|
|||
int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN;
|
||||
struct airoha_gdm_dev *dev = netdev_priv(netdev);
|
||||
struct airoha_gdm_port *port = dev->port;
|
||||
u32 cur_len, pse_port = FE_PSE_PORT_PPE1;
|
||||
struct airoha_qdma *qdma = dev->qdma;
|
||||
u32 pse_port = FE_PSE_PORT_PPE1;
|
||||
|
||||
netif_tx_start_all_queues(netdev);
|
||||
err = airoha_set_vip_for_gdm_port(dev, true);
|
||||
|
|
@ -1730,27 +1791,59 @@ static int airoha_dev_open(struct net_device *netdev)
|
|||
airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id),
|
||||
GDM_STAG_EN_MASK);
|
||||
|
||||
airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
|
||||
GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
|
||||
FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
|
||||
FIELD_PREP(GDM_LONG_LEN_MASK, len));
|
||||
cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id),
|
||||
GDM_LONG_LEN_MASK);
|
||||
if (!port->users || len > cur_len) {
|
||||
/* Opening a sibling net_device with a larger MTU updates the
|
||||
* MTU of already running devices. This is required to allow
|
||||
* multiple net_devices with different MTUs to share the same
|
||||
* GDM port.
|
||||
*/
|
||||
airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id),
|
||||
GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
|
||||
FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
|
||||
FIELD_PREP(GDM_LONG_LEN_MASK, len));
|
||||
}
|
||||
port->users++;
|
||||
|
||||
airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG,
|
||||
GLOBAL_CFG_TX_DMA_EN_MASK |
|
||||
GLOBAL_CFG_RX_DMA_EN_MASK);
|
||||
atomic_inc(&qdma->users);
|
||||
|
||||
if (port->id == AIROHA_GDM2_IDX &&
|
||||
airoha_ppe_is_enabled(qdma->eth, 1)) {
|
||||
/* For PPE2 always use secondary cpu port. */
|
||||
if (!airoha_is_lan_gdm_dev(dev) &&
|
||||
airoha_ppe_is_enabled(qdma->eth, 1))
|
||||
pse_port = FE_PSE_PORT_PPE2;
|
||||
}
|
||||
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
|
||||
pse_port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void airoha_set_port_mtu(struct airoha_eth *eth,
|
||||
struct airoha_gdm_port *port)
|
||||
{
|
||||
u32 len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[i];
|
||||
struct net_device *netdev;
|
||||
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
netdev = netdev_from_priv(dev);
|
||||
if (netif_running(netdev))
|
||||
len = max_t(u32, len, netdev->mtu);
|
||||
}
|
||||
len += ETH_HLEN + ETH_FCS_LEN;
|
||||
|
||||
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
|
||||
GDM_LONG_LEN_MASK,
|
||||
FIELD_PREP(GDM_LONG_LEN_MASK, len));
|
||||
}
|
||||
|
||||
static int airoha_dev_stop(struct net_device *netdev)
|
||||
{
|
||||
struct airoha_gdm_dev *dev = netdev_priv(netdev);
|
||||
|
|
@ -1763,8 +1856,12 @@ static int airoha_dev_stop(struct net_device *netdev)
|
|||
for (i = 0; i < netdev->num_tx_queues; i++)
|
||||
netdev_tx_reset_subqueue(netdev, i);
|
||||
|
||||
airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
|
||||
FE_PSE_PORT_DROP);
|
||||
if (--port->users)
|
||||
airoha_set_port_mtu(dev->eth, port);
|
||||
else
|
||||
airoha_set_gdm_port_fwd_cfg(qdma->eth,
|
||||
REG_GDM_FWD_CFG(port->id),
|
||||
FE_PSE_PORT_DROP);
|
||||
|
||||
if (atomic_dec_and_test(&qdma->users)) {
|
||||
airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
|
||||
|
|
@ -1785,13 +1882,18 @@ static int airoha_dev_stop(struct net_device *netdev)
|
|||
static int airoha_dev_set_macaddr(struct net_device *netdev, void *p)
|
||||
{
|
||||
struct airoha_gdm_dev *dev = netdev_priv(netdev);
|
||||
struct sockaddr *addr = p;
|
||||
int err;
|
||||
|
||||
err = eth_mac_addr(netdev, p);
|
||||
err = eth_prepare_mac_addr_change(netdev, p);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
airoha_set_macaddr(dev, netdev->dev_addr);
|
||||
err = airoha_set_macaddr(dev, addr->sa_data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
eth_commit_mac_addr_change(netdev, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1832,7 +1934,7 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
|
|||
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
|
||||
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
|
||||
|
||||
src_port = eth->soc->ops.get_sport(port, port->nbq);
|
||||
src_port = eth->soc->ops.get_sport(port, dev->nbq);
|
||||
if (src_port < 0)
|
||||
return src_port;
|
||||
|
||||
|
|
@ -1849,7 +1951,7 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
|
|||
airoha_ppe_set_cpu_port(dev, i, AIROHA_GDM2_IDX);
|
||||
|
||||
if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) {
|
||||
u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq);
|
||||
u32 mask = FC_ID_OF_SRC_PORT_MASK(dev->nbq);
|
||||
|
||||
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6, mask,
|
||||
__field_prep(mask, AIROHA_GDM2_IDX));
|
||||
|
|
@ -1858,36 +1960,81 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct airoha_gdm_dev *
|
||||
airoha_get_wan_gdm_dev(struct airoha_eth *eth)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[j];
|
||||
|
||||
if (dev && !airoha_is_lan_gdm_dev(dev))
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void airoha_dev_set_qdma(struct airoha_gdm_dev *dev)
|
||||
{
|
||||
struct net_device *netdev = netdev_from_priv(dev);
|
||||
struct airoha_eth *eth = dev->eth;
|
||||
int 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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static int airoha_dev_init(struct net_device *netdev)
|
||||
{
|
||||
struct airoha_gdm_dev *dev = netdev_priv(netdev);
|
||||
struct airoha_gdm_port *port = dev->port;
|
||||
struct airoha_eth *eth = dev->eth;
|
||||
int i;
|
||||
|
||||
/* QDMA0 is used for lan ports while QDMA1 is used for WAN ports */
|
||||
dev->qdma = ð->qdma[!airoha_is_lan_gdm_dev(dev)];
|
||||
dev->dev->irq = dev->qdma->irq_banks[0].irq;
|
||||
airoha_set_macaddr(dev, netdev->dev_addr);
|
||||
|
||||
switch (port->id) {
|
||||
case AIROHA_GDM3_IDX:
|
||||
case AIROHA_GDM4_IDX:
|
||||
/* If GDM2 is active we can't enable loopback */
|
||||
if (!eth->ports[1]) {
|
||||
int err;
|
||||
case AIROHA_GDM4_IDX: {
|
||||
struct airoha_eth *eth = dev->eth;
|
||||
|
||||
err = airoha_enable_gdm2_loopback(dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/* GDM2 supports a single net_device */
|
||||
if (eth->ports[1] && eth->ports[1]->devs[0])
|
||||
break;
|
||||
|
||||
if (airoha_get_wan_gdm_dev(eth))
|
||||
break;
|
||||
|
||||
fallthrough;
|
||||
}
|
||||
case AIROHA_GDM2_IDX:
|
||||
/* GDM2 is always used as wan */
|
||||
dev->flags |= AIROHA_PRIV_F_WAN;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < eth->soc->num_ppe; i++)
|
||||
airoha_ppe_set_cpu_port(dev, i, airoha_get_fe_port(dev));
|
||||
airoha_dev_set_qdma(dev);
|
||||
airoha_set_macaddr(dev, netdev->dev_addr);
|
||||
|
||||
if (!airoha_is_lan_gdm_dev(dev) &&
|
||||
(port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)) {
|
||||
int err;
|
||||
|
||||
err = airoha_enable_gdm2_loopback(dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1919,13 +2066,10 @@ static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
|
|||
{
|
||||
struct airoha_gdm_dev *dev = netdev_priv(netdev);
|
||||
struct airoha_gdm_port *port = dev->port;
|
||||
u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
|
||||
struct airoha_eth *eth = dev->eth;
|
||||
|
||||
airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id),
|
||||
GDM_LONG_LEN_MASK,
|
||||
FIELD_PREP(GDM_LONG_LEN_MASK, len));
|
||||
WRITE_ONCE(netdev->mtu, mtu);
|
||||
if (port->users)
|
||||
airoha_set_port_mtu(dev->eth, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2053,7 +2197,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
|
|||
}
|
||||
|
||||
fport = airoha_get_fe_port(dev);
|
||||
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
|
||||
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_NBOQ_MASK, dev->nbq) |
|
||||
FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
|
||||
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
|
||||
|
||||
q = &qdma->q_tx[qid];
|
||||
|
|
@ -2989,12 +3134,15 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
if (port->dev == dev)
|
||||
return true;
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
if (port->devs[j] == dev)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -3002,10 +3150,11 @@ bool airoha_is_valid_gdm_dev(struct airoha_eth *eth,
|
|||
|
||||
static int airoha_alloc_gdm_device(struct airoha_eth *eth,
|
||||
struct airoha_gdm_port *port,
|
||||
struct device_node *np)
|
||||
int nbq, struct device_node *np)
|
||||
{
|
||||
struct airoha_gdm_dev *dev;
|
||||
struct net_device *netdev;
|
||||
struct airoha_gdm_dev *dev;
|
||||
u8 index;
|
||||
int err;
|
||||
|
||||
netdev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*dev),
|
||||
|
|
@ -3025,7 +3174,6 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
|
|||
NETIF_F_HW_TC;
|
||||
netdev->features |= netdev->hw_features;
|
||||
netdev->vlan_features = netdev->hw_features;
|
||||
netdev->dev.of_node = np;
|
||||
SET_NETDEV_DEV(netdev, eth->dev);
|
||||
|
||||
/* reserve hw queues for HTB offloading */
|
||||
|
|
@ -3043,11 +3191,24 @@ static int airoha_alloc_gdm_device(struct airoha_eth *eth,
|
|||
netdev->dev_addr);
|
||||
}
|
||||
|
||||
/* Allowed nbq for EN7581 on GDM3 port are 4 and 5 for PCIE0
|
||||
* and PCIE1 respectively.
|
||||
*/
|
||||
index = nbq;
|
||||
if (index && airoha_is_7581(eth) && port->id == AIROHA_GDM3_IDX)
|
||||
index -= 4;
|
||||
|
||||
if (index >= ARRAY_SIZE(port->devs) || port->devs[index]) {
|
||||
dev_err(eth->dev, "invalid nbq id: %d\n", nbq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
netdev->dev.of_node = of_node_get(np);
|
||||
dev = netdev_priv(netdev);
|
||||
dev->dev = netdev;
|
||||
dev->port = port;
|
||||
port->dev = dev;
|
||||
dev->eth = eth;
|
||||
dev->nbq = nbq;
|
||||
port->devs[index] = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3057,7 +3218,8 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
|
|||
{
|
||||
const __be32 *id_ptr = of_get_property(np, "reg", NULL);
|
||||
struct airoha_gdm_port *port;
|
||||
int err, p;
|
||||
struct device_node *node;
|
||||
int err, nbq, p, d = 0;
|
||||
u32 id;
|
||||
|
||||
if (!id_ptr) {
|
||||
|
|
@ -3085,15 +3247,51 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
|
|||
u64_stats_init(&port->stats.syncp);
|
||||
spin_lock_init(&port->stats.lock);
|
||||
port->id = id;
|
||||
/* XXX: Read nbq from DTS */
|
||||
port->nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
|
||||
eth->ports[p] = port;
|
||||
|
||||
err = airoha_metadata_dst_alloc(port);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return airoha_alloc_gdm_device(eth, port, np);
|
||||
/* Default nbq value to ensure backward compatibility */
|
||||
nbq = id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
|
||||
|
||||
for_each_child_of_node(np, node) {
|
||||
/* Multiple external serdes connected to the FE GDM port via an
|
||||
* external arbiter.
|
||||
*/
|
||||
const __be32 *nbq_ptr;
|
||||
|
||||
if (!of_device_is_compatible(node, "airoha,eth-port"))
|
||||
continue;
|
||||
|
||||
d++;
|
||||
if (!of_device_is_available(node))
|
||||
continue;
|
||||
|
||||
nbq_ptr = of_get_property(node, "reg", NULL);
|
||||
if (!nbq_ptr) {
|
||||
dev_err(eth->dev, "missing nbq id\n");
|
||||
of_node_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Verify the provided nbq parameter is valid */
|
||||
nbq = be32_to_cpup(nbq_ptr);
|
||||
err = eth->soc->ops.get_sport(port, nbq);
|
||||
if (err < 0) {
|
||||
of_node_put(node);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = airoha_alloc_gdm_device(eth, port, nbq, node);
|
||||
if (err) {
|
||||
of_node_put(node);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return !d ? airoha_alloc_gdm_device(eth, port, nbq, np) : 0;
|
||||
}
|
||||
|
||||
static int airoha_register_gdm_devices(struct airoha_eth *eth)
|
||||
|
|
@ -3102,14 +3300,22 @@ static int airoha_register_gdm_devices(struct airoha_eth *eth)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
int err;
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
err = register_netdev(port->dev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[j];
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
err = register_netdev(netdev_from_priv(dev));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
set_bit(DEV_STATE_REGISTERED, ð->state);
|
||||
|
|
@ -3216,14 +3422,23 @@ static int airoha_probe(struct platform_device *pdev)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
struct airoha_gdm_dev *dev;
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
dev = port->dev;
|
||||
if (dev && dev->dev->reg_state == NETREG_REGISTERED)
|
||||
unregister_netdev(dev->dev);
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[j];
|
||||
struct net_device *netdev;
|
||||
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
netdev = netdev_from_priv(dev);
|
||||
if (netdev->reg_state == NETREG_REGISTERED)
|
||||
unregister_netdev(netdev);
|
||||
of_node_put(netdev->dev.of_node);
|
||||
}
|
||||
airoha_metadata_dst_free(port);
|
||||
}
|
||||
airoha_hw_cleanup(eth);
|
||||
|
|
@ -3244,14 +3459,22 @@ static void airoha_remove(struct platform_device *pdev)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
struct airoha_gdm_dev *dev;
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
dev = port->dev;
|
||||
if (dev)
|
||||
unregister_netdev(dev->dev);
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[j];
|
||||
struct net_device *netdev;
|
||||
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
netdev = netdev_from_priv(dev);
|
||||
unregister_netdev(netdev);
|
||||
of_node_put(netdev->dev.of_node);
|
||||
}
|
||||
airoha_metadata_dst_free(port);
|
||||
}
|
||||
airoha_hw_cleanup(eth);
|
||||
|
|
@ -3314,6 +3537,39 @@ static u32 airoha_en7581_get_vip_port(struct airoha_gdm_port *port, int nbq)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_en7581_get_dev_from_sport(struct airoha_qdma_desc *desc,
|
||||
u16 *port, u16 *dev)
|
||||
{
|
||||
u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK,
|
||||
le32_to_cpu(READ_ONCE(desc->msg1)));
|
||||
|
||||
*dev = 0;
|
||||
switch (sport) {
|
||||
case 0x10 ... 0x14:
|
||||
*port = 0; /* GDM1 */
|
||||
break;
|
||||
case 0x2 ... 0x4:
|
||||
*port = sport - 1;
|
||||
break;
|
||||
case HSGMII_LAN_7581_PCIE1_SRCPORT:
|
||||
*dev = 1;
|
||||
fallthrough;
|
||||
case HSGMII_LAN_7581_PCIE0_SRCPORT:
|
||||
*port = 2; /* GDM3 */
|
||||
break;
|
||||
case HSGMII_LAN_7581_USB_SRCPORT:
|
||||
*dev = 1;
|
||||
fallthrough;
|
||||
case HSGMII_LAN_7581_ETH_SRCPORT:
|
||||
*port = 3; /* GDM4 */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const an7583_xsi_rsts_names[] = {
|
||||
"xsi-mac",
|
||||
"hsi0-mac",
|
||||
|
|
@ -3363,6 +3619,36 @@ static u32 airoha_an7583_get_vip_port(struct airoha_gdm_port *port, int nbq)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int airoha_an7583_get_dev_from_sport(struct airoha_qdma_desc *desc,
|
||||
u16 *port, u16 *dev)
|
||||
{
|
||||
u32 sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK,
|
||||
le32_to_cpu(READ_ONCE(desc->msg1)));
|
||||
|
||||
*dev = 0;
|
||||
switch (sport) {
|
||||
case 0x10 ... 0x14:
|
||||
*port = 0; /* GDM1 */
|
||||
break;
|
||||
case 0x2 ... 0x4:
|
||||
*port = sport - 1;
|
||||
break;
|
||||
case HSGMII_LAN_7583_ETH_SRCPORT:
|
||||
*port = 2; /* GDM3 */
|
||||
break;
|
||||
case HSGMII_LAN_7583_USB_SRCPORT:
|
||||
*dev = 1;
|
||||
fallthrough;
|
||||
case HSGMII_LAN_7583_PCIE_SRCPORT:
|
||||
*port = 3; /* GDM4 */
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct airoha_eth_soc_data en7581_soc_data = {
|
||||
.version = 0x7581,
|
||||
.xsi_rsts_names = en7581_xsi_rsts_names,
|
||||
|
|
@ -3371,6 +3657,7 @@ static const struct airoha_eth_soc_data en7581_soc_data = {
|
|||
.ops = {
|
||||
.get_sport = airoha_en7581_get_sport,
|
||||
.get_vip_port = airoha_en7581_get_vip_port,
|
||||
.get_dev_from_sport = airoha_en7581_get_dev_from_sport,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -3382,6 +3669,7 @@ static const struct airoha_eth_soc_data an7583_soc_data = {
|
|||
.ops = {
|
||||
.get_sport = airoha_an7583_get_sport,
|
||||
.get_vip_port = airoha_an7583_get_vip_port,
|
||||
.get_dev_from_sport = airoha_an7583_get_dev_from_sport,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <net/dsa.h>
|
||||
|
||||
#define AIROHA_MAX_NUM_GDM_PORTS 4
|
||||
#define AIROHA_MAX_NUM_GDM_DEVS 2
|
||||
#define AIROHA_MAX_NUM_QDMA 2
|
||||
#define AIROHA_MAX_NUM_IRQ_BANKS 4
|
||||
#define AIROHA_MAX_DSA_PORTS 7
|
||||
|
|
@ -537,22 +538,28 @@ struct airoha_qdma {
|
|||
DECLARE_BITMAP(qos_channel_map, AIROHA_NUM_QOS_CHANNELS);
|
||||
};
|
||||
|
||||
enum airoha_priv_flags {
|
||||
AIROHA_PRIV_F_WAN = BIT(0),
|
||||
};
|
||||
|
||||
struct airoha_gdm_dev {
|
||||
struct airoha_gdm_port *port;
|
||||
struct airoha_qdma *qdma;
|
||||
struct net_device *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;
|
||||
|
||||
u32 flags;
|
||||
int nbq;
|
||||
};
|
||||
|
||||
struct airoha_gdm_port {
|
||||
struct airoha_gdm_dev *dev;
|
||||
struct airoha_gdm_dev *devs[AIROHA_MAX_NUM_GDM_DEVS];
|
||||
int id;
|
||||
int nbq;
|
||||
int users;
|
||||
|
||||
struct airoha_hw_stats stats;
|
||||
|
||||
|
|
@ -588,6 +595,8 @@ struct airoha_eth_soc_data {
|
|||
struct {
|
||||
int (*get_sport)(struct airoha_gdm_port *port, int nbq);
|
||||
u32 (*get_vip_port)(struct airoha_gdm_port *port, int nbq);
|
||||
int (*get_dev_from_sport)(struct airoha_qdma_desc *desc,
|
||||
u16 *port, u16 *dev);
|
||||
} ops;
|
||||
};
|
||||
|
||||
|
|
@ -650,13 +659,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)
|
||||
{
|
||||
struct airoha_gdm_port *port = dev->port;
|
||||
|
||||
/* GDM1 port on EN7581 SoC is connected to the lan dsa switch.
|
||||
* GDM{2,3,4} can be used as wan port connected to an external
|
||||
* phy module.
|
||||
*/
|
||||
return port->id == 1;
|
||||
return !(dev->flags & AIROHA_PRIV_F_WAN);
|
||||
}
|
||||
|
||||
static inline bool airoha_is_7581(struct airoha_eth *eth)
|
||||
|
|
@ -680,7 +683,7 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
|
|||
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
|
||||
int airoha_ppe_init(struct airoha_eth *eth);
|
||||
void airoha_ppe_deinit(struct airoha_eth *eth);
|
||||
void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev);
|
||||
void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr);
|
||||
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
|
||||
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
||||
u32 hash);
|
||||
|
|
|
|||
|
|
@ -167,9 +167,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
|
|||
airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i),
|
||||
PPE_FLOW_CFG_IP6_6RD_MASK);
|
||||
|
||||
for (p = 0; p < ARRAY_SIZE(eth->ports); p++) {
|
||||
struct airoha_gdm_port *port = eth->ports[p];
|
||||
|
||||
for (p = 0; p < ARRAY_SIZE(eth->ports); p++)
|
||||
airoha_fe_rmw(eth, REG_PPE_MTU(i, p),
|
||||
FP0_EGRESS_MTU_MASK |
|
||||
FP1_EGRESS_MTU_MASK,
|
||||
|
|
@ -177,11 +175,27 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
|
|||
AIROHA_MAX_MTU) |
|
||||
FIELD_PREP(FP1_EGRESS_MTU_MASK,
|
||||
AIROHA_MAX_MTU));
|
||||
if (!port)
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eth->ports); i++) {
|
||||
struct airoha_gdm_port *port = eth->ports[i];
|
||||
int j;
|
||||
|
||||
if (!port)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(port->devs); j++) {
|
||||
struct airoha_gdm_dev *dev = port->devs[j];
|
||||
int ppe_id;
|
||||
u8 fport;
|
||||
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
airoha_ppe_set_cpu_port(port->dev, i,
|
||||
airoha_get_fe_port(port->dev));
|
||||
ppe_id = !airoha_is_lan_gdm_dev(dev) &&
|
||||
airoha_ppe_is_enabled(eth, 1);
|
||||
fport = airoha_get_fe_port(dev);
|
||||
airoha_ppe_set_cpu_port(dev, ppe_id, fport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -341,7 +355,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
|
|||
return -EINVAL;
|
||||
|
||||
port = dev->port;
|
||||
if (dsa_port >= 0 || eth->ports[1])
|
||||
if (dsa_port >= 0 || airoha_is_lan_gdm_dev(dev))
|
||||
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
|
||||
: port->id;
|
||||
else
|
||||
|
|
@ -1473,12 +1487,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
|
|||
airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan);
|
||||
}
|
||||
|
||||
void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev)
|
||||
void airoha_ppe_init_upd_mem(struct airoha_gdm_dev *dev, const u8 *addr)
|
||||
{
|
||||
struct airoha_gdm_port *port = dev->port;
|
||||
struct net_device *netdev = dev->dev;
|
||||
struct airoha_eth *eth = dev->eth;
|
||||
const u8 *addr = netdev->dev_addr;
|
||||
u32 val;
|
||||
|
||||
val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user