Merge branch 'net-airoha-add-an7583-ethernet-controller-support'

Lorenzo Bianconi says:

====================
net: airoha: Add AN7583 ethernet controller support

Introduce support for AN7583 ethernet controller to airoha-eth dirver.
The main differences between EN7581 and AN7583 is the latter runs a
single PPE module while EN7581 runs two of them. Moreover PPE SRAM in
AN7583 SoC is reduced to 8K (while SRAM is 16K on EN7581).

v2: https://lore.kernel.org/r/20251016-an7583-eth-support-v2-0-ea6e7e9acbdb@kernel.org
v1: https://lore.kernel.org/r/20251015-an7583-eth-support-v1-0-064855f05923@kernel.org
====================

Link: https://patch.msgid.link/20251017-an7583-eth-support-v3-0-f28319666667@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-10-21 13:07:07 +02:00
commit d1d7998df9
6 changed files with 439 additions and 183 deletions

View File

@ -17,6 +17,7 @@ properties:
compatible:
enum:
- airoha,en7581-eth
- airoha,an7583-eth
reg:
items:
@ -44,6 +45,7 @@ properties:
- description: PDMA irq
resets:
minItems: 7
maxItems: 8
reset-names:
@ -54,8 +56,9 @@ properties:
- const: xsi-mac
- const: hsi0-mac
- const: hsi1-mac
- const: hsi-mac
- enum: [ hsi-mac, xfp-mac ]
- const: xfp-mac
minItems: 7
memory-region:
items:
@ -81,6 +84,36 @@ properties:
interface to implement hardware flow offloading programming Packet
Processor Engine (PPE) flow table.
allOf:
- $ref: ethernet-controller.yaml#
- if:
properties:
compatible:
contains:
enum:
- airoha,en7581-eth
then:
properties:
resets:
minItems: 8
reset-names:
minItems: 8
- if:
properties:
compatible:
contains:
enum:
- airoha,an7583-eth
then:
properties:
resets:
maxItems: 7
reset-names:
maxItems: 7
patternProperties:
"^ethernet@[1-4]$":
type: object

View File

@ -297,8 +297,11 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
int q;
all_rsv = airoha_fe_get_pse_all_rsv(eth);
/* hw misses PPE2 oq rsv */
all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2];
if (airoha_ppe_is_enabled(eth, 1)) {
/* hw misses PPE2 oq rsv */
all_rsv += PSE_RSV_PAGES *
pse_port_num_queues[FE_PSE_PORT_PPE2];
}
airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv);
/* CMD1 */
@ -335,13 +338,17 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++)
airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q,
PSE_QUEUE_RSV_PAGES);
/* PPE2 */
for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) {
if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2)
airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q,
PSE_QUEUE_RSV_PAGES);
else
airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0);
if (airoha_ppe_is_enabled(eth, 1)) {
/* PPE2 */
for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) {
if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2)
airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2,
q,
PSE_QUEUE_RSV_PAGES);
else
airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2,
q, 0);
}
}
/* GMD4 */
for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++)
@ -524,25 +531,6 @@ static int airoha_fe_init(struct airoha_eth *eth)
/* disable IFC by default */
airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK);
airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0),
FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) |
FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1));
airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1),
FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) |
FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2));
/* enable 1:N vlan action, init vlan table */
airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
@ -1387,8 +1375,7 @@ static int airoha_hw_init(struct platform_device *pdev,
int err, i;
/* disable xsi */
err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts),
eth->xsi_rsts);
err = reset_control_bulk_assert(eth->soc->num_xsi_rsts, eth->xsi_rsts);
if (err)
return err;
@ -1695,19 +1682,23 @@ static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
return 0;
}
static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
{
u32 pse_port = port->id == 3 ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4;
struct airoha_eth *eth = port->qdma->eth;
u32 chan = port->id == 3 ? 4 : 0;
u32 val, pse_port, chan, nbq;
int src_port;
/* Forward the traffic to the proper GDM port */
pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: FE_PSE_PORT_GDM4;
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port);
airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC);
/* Enable GDM2 loopback */
airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff);
airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff);
chan = port->id == AIROHA_GDM3_IDX ? airoha_is_7581(eth) ? 4 : 3 : 0;
airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2),
LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK,
FIELD_PREP(LPBK_CHAN_MASK, chan) |
@ -1722,36 +1713,36 @@ static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2));
if (port->id == 3) {
/* FIXME: handle XSI_PCE1_PORT */
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT));
airoha_fe_rmw(eth,
REG_SP_DFT_CPORT(HSGMII_LAN_PCIE0_SRCPORT >> 3),
SP_CPORT_PCIE0_MASK,
FIELD_PREP(SP_CPORT_PCIE0_MASK,
FE_PSE_PORT_CDM2));
} else {
/* FIXME: handle XSI_USB_PORT */
/* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */
nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
src_port = eth->soc->ops.get_src_port_id(port, nbq);
if (src_port < 0)
return src_port;
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, src_port));
val = src_port & SP_CPORT_DFT_MASK;
airoha_fe_rmw(eth,
REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
SP_CPORT_MASK(val),
FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val)));
if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth))
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
FC_ID_OF_SRC_PORT24_MASK,
FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2));
airoha_fe_rmw(eth, REG_FE_WAN_PORT,
WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
FIELD_PREP(WAN0_MASK, HSGMII_LAN_ETH_SRCPORT));
airoha_fe_rmw(eth,
REG_SP_DFT_CPORT(HSGMII_LAN_ETH_SRCPORT >> 3),
SP_CPORT_ETH_MASK,
FIELD_PREP(SP_CPORT_ETH_MASK, FE_PSE_PORT_CDM2));
}
return 0;
}
static int airoha_dev_init(struct net_device *dev)
{
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_eth *eth = port->qdma->eth;
u32 pse_port;
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
u32 pse_port, fe_cpu_port;
u8 ppe_id;
airoha_set_macaddr(port, dev->dev_addr);
@ -1759,18 +1750,37 @@ static int airoha_dev_init(struct net_device *dev)
case 3:
case 4:
/* If GDM2 is active we can't enable loopback */
if (!eth->ports[1])
airhoha_set_gdm2_loopback(port);
if (!eth->ports[1]) {
int err;
err = airhoha_set_gdm2_loopback(port);
if (err)
return err;
}
fallthrough;
case 2:
pse_port = FE_PSE_PORT_PPE2;
break;
default:
if (airoha_ppe_is_enabled(eth, 1)) {
/* For PPE2 always use secondary cpu port. */
fe_cpu_port = FE_PSE_PORT_CDM2;
pse_port = FE_PSE_PORT_PPE2;
break;
}
fallthrough;
default: {
u8 qdma_id = qdma - &eth->qdma[0];
/* For PPE1 select cpu port according to the running QDMA. */
fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1;
pse_port = FE_PSE_PORT_PPE1;
break;
}
}
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port);
ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0;
airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id),
DFT_CPORT_MASK(port->id),
fe_cpu_port << __ffs(DFT_CPORT_MASK(port->id)));
return 0;
}
@ -1887,6 +1897,22 @@ static bool airoha_dev_tx_queue_busy(struct airoha_queue *q, u32 nr_frags)
return index >= tail;
}
static int airoha_get_fe_port(struct airoha_gdm_port *port)
{
struct airoha_qdma *qdma = port->qdma;
struct airoha_eth *eth = qdma->eth;
switch (eth->soc->version) {
case 0x7583:
return port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
: port->id;
case 0x7581:
default:
return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4
: port->id;
}
}
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@ -1927,7 +1953,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
}
}
fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
fport = airoha_get_fe_port(port);
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@ -2922,6 +2948,7 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth,
static int airoha_probe(struct platform_device *pdev)
{
struct reset_control_bulk_data *xsi_rsts;
struct device_node *np;
struct airoha_eth *eth;
int i, err;
@ -2930,6 +2957,10 @@ static int airoha_probe(struct platform_device *pdev)
if (!eth)
return -ENOMEM;
eth->soc = of_device_get_match_data(&pdev->dev);
if (!eth->soc)
return -EINVAL;
eth->dev = &pdev->dev;
err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32));
@ -2954,13 +2985,18 @@ static int airoha_probe(struct platform_device *pdev)
return err;
}
eth->xsi_rsts[0].id = "xsi-mac";
eth->xsi_rsts[1].id = "hsi0-mac";
eth->xsi_rsts[2].id = "hsi1-mac";
eth->xsi_rsts[3].id = "hsi-mac";
eth->xsi_rsts[4].id = "xfp-mac";
xsi_rsts = devm_kzalloc(eth->dev,
eth->soc->num_xsi_rsts * sizeof(*xsi_rsts),
GFP_KERNEL);
if (err)
return err;
eth->xsi_rsts = xsi_rsts;
for (i = 0; i < eth->soc->num_xsi_rsts; i++)
eth->xsi_rsts[i].id = eth->soc->xsi_rsts_names[i];
err = devm_reset_control_bulk_get_exclusive(eth->dev,
ARRAY_SIZE(eth->xsi_rsts),
eth->soc->num_xsi_rsts,
eth->xsi_rsts);
if (err) {
dev_err(eth->dev, "failed to get bulk xsi reset lines\n");
@ -3048,8 +3084,90 @@ static void airoha_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
}
static const char * const en7581_xsi_rsts_names[] = {
"xsi-mac",
"hsi0-mac",
"hsi1-mac",
"hsi-mac",
"xfp-mac",
};
static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq)
{
switch (port->id) {
case 3:
/* 7581 SoC supports PCIe serdes on GDM3 port */
if (nbq == 4)
return HSGMII_LAN_7581_PCIE0_SRCPORT;
if (nbq == 5)
return HSGMII_LAN_7581_PCIE1_SRCPORT;
break;
case 4:
/* 7581 SoC supports eth and usb serdes on GDM4 port */
if (!nbq)
return HSGMII_LAN_7581_ETH_SRCPORT;
if (nbq == 1)
return HSGMII_LAN_7581_USB_SRCPORT;
break;
default:
break;
}
return -EINVAL;
}
static const char * const an7583_xsi_rsts_names[] = {
"xsi-mac",
"hsi0-mac",
"hsi1-mac",
"xfp-mac",
};
static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq)
{
switch (port->id) {
case 3:
/* 7583 SoC supports eth serdes on GDM3 port */
if (!nbq)
return HSGMII_LAN_7583_ETH_SRCPORT;
break;
case 4:
/* 7583 SoC supports PCIe and USB serdes on GDM4 port */
if (!nbq)
return HSGMII_LAN_7583_PCIE_SRCPORT;
if (nbq == 1)
return HSGMII_LAN_7583_USB_SRCPORT;
break;
default:
break;
}
return -EINVAL;
}
static const struct airoha_eth_soc_data en7581_soc_data = {
.version = 0x7581,
.xsi_rsts_names = en7581_xsi_rsts_names,
.num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names),
.num_ppe = 2,
.ops = {
.get_src_port_id = airoha_en7581_get_src_port_id,
},
};
static const struct airoha_eth_soc_data an7583_soc_data = {
.version = 0x7583,
.xsi_rsts_names = an7583_xsi_rsts_names,
.num_xsi_rsts = ARRAY_SIZE(an7583_xsi_rsts_names),
.num_ppe = 1,
.ops = {
.get_src_port_id = airoha_an7583_get_src_port_id,
},
};
static const struct of_device_id of_airoha_match[] = {
{ .compatible = "airoha,en7581-eth" },
{ .compatible = "airoha,en7581-eth", .data = &en7581_soc_data },
{ .compatible = "airoha,an7583-eth", .data = &an7583_soc_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_airoha_match);

View File

@ -21,7 +21,6 @@
#define AIROHA_MAX_NUM_IRQ_BANKS 4
#define AIROHA_MAX_DSA_PORTS 7
#define AIROHA_MAX_NUM_RSTS 3
#define AIROHA_MAX_NUM_XSI_RSTS 5
#define AIROHA_MAX_MTU 9216
#define AIROHA_MAX_PACKET_SIZE 2048
#define AIROHA_NUM_QOS_CHANNELS 4
@ -48,20 +47,9 @@
#define QDMA_METER_IDX(_n) ((_n) & 0xff)
#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3)
#define PPE_NUM 2
#define PPE1_SRAM_NUM_ENTRIES (8 * 1024)
#define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES)
#ifdef CONFIG_NET_AIROHA_FLOW_STATS
#define PPE1_STATS_NUM_ENTRIES (4 * 1024)
#else
#define PPE1_STATS_NUM_ENTRIES 0
#endif /* CONFIG_NET_AIROHA_FLOW_STATS */
#define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES)
#define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES)
#define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES)
#define PPE_SRAM_NUM_ENTRIES (8 * 1024)
#define PPE_STATS_NUM_ENTRIES (4 * 1024)
#define PPE_DRAM_NUM_ENTRIES (16 * 1024)
#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES)
#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1)
#define PPE_ENTRY_SIZE 80
#define PPE_RAM_NUM_ENTRIES_SHIFT(_n) (__ffs((_n) >> 10))
@ -79,10 +67,16 @@ enum {
};
enum {
HSGMII_LAN_PCIE0_SRCPORT = 0x16,
HSGMII_LAN_PCIE1_SRCPORT,
HSGMII_LAN_ETH_SRCPORT,
HSGMII_LAN_USB_SRCPORT,
HSGMII_LAN_7581_PCIE0_SRCPORT = 0x16,
HSGMII_LAN_7581_PCIE1_SRCPORT,
HSGMII_LAN_7581_ETH_SRCPORT,
HSGMII_LAN_7581_USB_SRCPORT,
};
enum {
HSGMII_LAN_7583_ETH_SRCPORT = 0x16,
HSGMII_LAN_7583_PCIE_SRCPORT = 0x18,
HSGMII_LAN_7583_USB_SRCPORT,
};
enum {
@ -111,6 +105,13 @@ enum {
CRSN_25 = 0x19,
};
enum airoha_gdm_index {
AIROHA_GDM1_IDX = 1,
AIROHA_GDM2_IDX = 2,
AIROHA_GDM3_IDX = 3,
AIROHA_GDM4_IDX = 4,
};
enum {
FE_PSE_PORT_CDM1,
FE_PSE_PORT_GDM1,
@ -554,7 +555,7 @@ struct airoha_ppe {
struct rhashtable l2_flows;
struct hlist_head *foe_flow;
u16 foe_check_time[PPE_NUM_ENTRIES];
u16 *foe_check_time;
struct airoha_foe_stats *foe_stats;
dma_addr_t foe_stats_dma;
@ -562,9 +563,21 @@ struct airoha_ppe {
struct dentry *debugfs_dir;
};
struct airoha_eth_soc_data {
u16 version;
const char * const *xsi_rsts_names;
int num_xsi_rsts;
int num_ppe;
struct {
int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq);
} ops;
};
struct airoha_eth {
struct device *dev;
const struct airoha_eth_soc_data *soc;
unsigned long state;
void __iomem *fe_regs;
@ -574,7 +587,7 @@ struct airoha_eth {
struct rhashtable flow_table;
struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
struct reset_control_bulk_data *xsi_rsts;
struct net_device *napi_dev;
@ -617,15 +630,27 @@ static inline bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port)
return port->id == 1;
}
static inline bool airoha_is_7581(struct airoha_eth *eth)
{
return eth->soc->version == 0x7581;
}
static inline bool airoha_is_7583(struct airoha_eth *eth)
{
return eth->soc->version == 0x7583;
}
bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
struct airoha_gdm_port *port);
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan);
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_port *port);
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);
void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,

View File

@ -32,9 +32,50 @@ static const struct rhashtable_params airoha_l2_flow_table_params = {
.automatic_shrinking = true,
};
static bool airoha_ppe2_is_enabled(struct airoha_eth *eth)
static int airoha_ppe_get_num_stats_entries(struct airoha_ppe *ppe)
{
return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK;
if (!IS_ENABLED(CONFIG_NET_AIROHA_FLOW_STATS))
return -EOPNOTSUPP;
if (airoha_is_7583(ppe->eth))
return -EOPNOTSUPP;
return PPE_STATS_NUM_ENTRIES;
}
static int airoha_ppe_get_total_num_stats_entries(struct airoha_ppe *ppe)
{
int num_stats = airoha_ppe_get_num_stats_entries(ppe);
if (num_stats > 0) {
struct airoha_eth *eth = ppe->eth;
num_stats = num_stats * eth->soc->num_ppe;
}
return num_stats;
}
static u32 airoha_ppe_get_total_sram_num_entries(struct airoha_ppe *ppe)
{
struct airoha_eth *eth = ppe->eth;
return PPE_SRAM_NUM_ENTRIES * eth->soc->num_ppe;
}
u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe)
{
u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
return sram_num_entries + PPE_DRAM_NUM_ENTRIES;
}
bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index)
{
if (index >= eth->soc->num_ppe)
return false;
return airoha_fe_rr(eth, REG_PPE_GLO_CFG(index)) & PPE_GLO_CFG_EN_MASK;
}
static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
@ -46,14 +87,22 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
{
u32 sram_tb_size, sram_num_entries, dram_num_entries;
u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries;
u32 sram_tb_size, dram_num_entries;
struct airoha_eth *eth = ppe->eth;
int i;
int i, sram_num_stats_entries;
sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry);
sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
sram_tb_size = sram_num_entries * sizeof(struct airoha_foe_entry);
dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES);
for (i = 0; i < PPE_NUM; i++) {
sram_num_stats_entries = airoha_ppe_get_num_stats_entries(ppe);
if (sram_num_stats_entries > 0)
sram_ppe_num_data_entries -= sram_num_stats_entries;
sram_ppe_num_data_entries =
PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries);
for (i = 0; i < eth->soc->num_ppe; i++) {
int p;
airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
@ -85,10 +134,16 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
airoha_fe_rmw(eth, REG_PPE_TB_CFG(i),
PPE_TB_CFG_SEARCH_MISS_MASK |
PPE_SRAM_TB_NUM_ENTRY_MASK |
PPE_DRAM_TB_NUM_ENTRY_MASK |
PPE_TB_CFG_KEEPALIVE_MASK |
PPE_TB_ENTRY_SIZE_MASK,
FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) |
FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0));
FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0) |
FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
sram_ppe_num_data_entries) |
FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
dram_num_entries));
airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
@ -101,35 +156,6 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
FIELD_PREP(FP1_EGRESS_MTU_MASK,
AIROHA_MAX_MTU));
}
if (airoha_ppe2_is_enabled(eth)) {
sram_num_entries =
PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES);
airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
PPE_SRAM_TB_NUM_ENTRY_MASK |
PPE_DRAM_TB_NUM_ENTRY_MASK,
FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
sram_num_entries) |
FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
dram_num_entries));
airoha_fe_rmw(eth, REG_PPE_TB_CFG(1),
PPE_SRAM_TB_NUM_ENTRY_MASK |
PPE_DRAM_TB_NUM_ENTRY_MASK,
FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
sram_num_entries) |
FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
dram_num_entries));
} else {
sram_num_entries =
PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES);
airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
PPE_SRAM_TB_NUM_ENTRY_MASK |
PPE_DRAM_TB_NUM_ENTRY_MASK,
FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
sram_num_entries) |
FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
dram_num_entries));
}
}
static void airoha_ppe_flow_mangle_eth(const struct flow_action_entry *act, void *eth)
@ -428,9 +454,11 @@ static int airoha_ppe_foe_entry_set_ipv6_tuple(struct airoha_foe_entry *hwe,
return 0;
}
static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
static u32 airoha_ppe_foe_get_entry_hash(struct airoha_ppe *ppe,
struct airoha_foe_entry *hwe)
{
int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
u32 ppe_hash_mask = airoha_ppe_get_total_num_entries(ppe) - 1;
u32 hash, hv1, hv2, hv3;
switch (type) {
@ -468,25 +496,31 @@ static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
case PPE_PKT_TYPE_IPV6_6RD:
default:
WARN_ON_ONCE(1);
return PPE_HASH_MASK;
return ppe_hash_mask;
}
hash = (hv1 & hv2) | ((~hv1) & hv3);
hash = (hash >> 24) | ((hash & 0xffffff) << 8);
hash ^= hv1 ^ hv2 ^ hv3;
hash ^= hash >> 16;
hash &= PPE_NUM_ENTRIES - 1;
hash &= ppe_hash_mask;
return hash;
}
static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash)
static int airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe,
u32 hash, u32 *index)
{
if (!airoha_ppe2_is_enabled(ppe->eth))
return hash;
int ppe_num_stats_entries;
return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES
: hash;
ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
if (ppe_num_stats_entries < 0)
return ppe_num_stats_entries;
*index = hash >= ppe_num_stats_entries ? hash - PPE_STATS_NUM_ENTRIES
: hash;
return 0;
}
static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe,
@ -500,9 +534,13 @@ static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe,
static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe,
struct airoha_npu *npu)
{
int i;
int i, ppe_num_stats_entries;
for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++)
ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
if (ppe_num_stats_entries < 0)
return;
for (i = 0; i < ppe_num_stats_entries; i++)
airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i);
}
@ -513,10 +551,17 @@ static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe,
{
int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
u32 index, pse_port, val, *data, *ib2, *meter;
int ppe_num_stats_entries;
u8 nbq;
index = airoha_ppe_foe_get_flow_stats_index(ppe, hash);
if (index >= PPE_STATS_NUM_ENTRIES)
ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
if (ppe_num_stats_entries < 0)
return;
if (airoha_ppe_foe_get_flow_stats_index(ppe, hash, &index))
return;
if (index >= ppe_num_stats_entries)
return;
if (type == PPE_PKT_TYPE_BRIDGE) {
@ -557,17 +602,17 @@ static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe,
static struct airoha_foe_entry *
airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash)
{
u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
lockdep_assert_held(&ppe_lock);
if (hash < PPE_SRAM_NUM_ENTRIES) {
if (hash < sram_num_entries) {
u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry);
bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES;
struct airoha_eth *eth = ppe->eth;
bool ppe2;
u32 val;
int i;
ppe2 = airoha_ppe2_is_enabled(ppe->eth) &&
hash >= PPE1_SRAM_NUM_ENTRIES;
airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2),
FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) |
PPE_SRAM_CTRL_REQ_MASK);
@ -577,7 +622,8 @@ airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash)
REG_PPE_RAM_CTRL(ppe2)))
return NULL;
for (i = 0; i < sizeof(struct airoha_foe_entry) / 4; i++)
for (i = 0; i < sizeof(struct airoha_foe_entry) / sizeof(*hwe);
i++)
hwe[i] = airoha_fe_rr(eth,
REG_PPE_RAM_ENTRY(ppe2, i));
}
@ -614,10 +660,32 @@ static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e,
return !memcmp(&e->data.d, &hwe->d, len - sizeof(hwe->ib1));
}
static int airoha_ppe_foe_commit_sram_entry(struct airoha_ppe *ppe, u32 hash)
{
struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES;
u32 *ptr = (u32 *)hwe, val;
int i;
for (i = 0; i < sizeof(*hwe) / sizeof(*ptr); i++)
airoha_fe_wr(ppe->eth, REG_PPE_RAM_ENTRY(ppe2, i), ptr[i]);
wmb();
airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2),
FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) |
PPE_SRAM_CTRL_WR_MASK | PPE_SRAM_CTRL_REQ_MASK);
return read_poll_timeout_atomic(airoha_fe_rr, val,
val & PPE_SRAM_CTRL_ACK_MASK,
10, 100, false, ppe->eth,
REG_PPE_RAM_CTRL(ppe2));
}
static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
struct airoha_foe_entry *e,
u32 hash, bool rx_wlan)
{
u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
u32 ts = airoha_ppe_get_timestamp(ppe);
struct airoha_eth *eth = ppe->eth;
@ -642,14 +710,8 @@ static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
if (!rx_wlan)
airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash);
if (hash < PPE_SRAM_NUM_ENTRIES) {
dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe);
bool ppe2 = airoha_ppe2_is_enabled(eth) &&
hash >= PPE1_SRAM_NUM_ENTRIES;
err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe),
hash, ppe2);
}
if (hash < sram_num_entries)
err = airoha_ppe_foe_commit_sram_entry(ppe, hash);
unlock:
rcu_read_unlock();
@ -772,7 +834,7 @@ static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe,
if (state == AIROHA_FOE_STATE_BIND)
goto unlock;
index = airoha_ppe_foe_get_entry_hash(hwe);
index = airoha_ppe_foe_get_entry_hash(ppe, hwe);
hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) {
if (e->type == FLOW_TYPE_L2_SUBFLOW) {
state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1);
@ -832,7 +894,7 @@ static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe,
if (type == PPE_PKT_TYPE_BRIDGE)
return airoha_ppe_foe_l2_flow_commit_entry(ppe, e);
hash = airoha_ppe_foe_get_entry_hash(&e->data);
hash = airoha_ppe_foe_get_entry_hash(ppe, &e->data);
e->type = FLOW_TYPE_L4;
e->hash = 0xffff;
@ -1158,11 +1220,19 @@ static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth,
void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,
struct airoha_foe_stats64 *stats)
{
u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash);
struct airoha_eth *eth = ppe->eth;
int ppe_num_stats_entries;
struct airoha_npu *npu;
u32 index;
if (index >= PPE_STATS_NUM_ENTRIES)
ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
if (ppe_num_stats_entries < 0)
return;
if (airoha_ppe_foe_get_flow_stats_index(ppe, hash, &index))
return;
if (index >= ppe_num_stats_entries)
return;
rcu_read_lock();
@ -1225,20 +1295,22 @@ static int airoha_ppe_flow_offload_cmd(struct airoha_eth *eth,
return -EOPNOTSUPP;
}
static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe,
struct airoha_npu *npu)
static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe)
{
int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES;
u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
struct airoha_foe_entry *hwe = ppe->foe;
int i, err = 0;
if (airoha_ppe2_is_enabled(ppe->eth))
sram_num_entries = sram_num_entries / 2;
for (i = 0; i < sram_num_entries; i++) {
int err;
for (i = 0; i < sram_num_entries; i++)
memset(&hwe[i], 0, sizeof(*hwe));
err = airoha_ppe_foe_commit_sram_entry(ppe, i);
if (err)
break;
}
return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma,
PPE_SRAM_NUM_ENTRIES);
return err;
}
static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth)
@ -1257,7 +1329,7 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth)
{
struct airoha_npu *npu = airoha_ppe_npu_get(eth);
struct airoha_ppe *ppe = eth->ppe;
int err;
int err, ppe_num_stats_entries;
if (IS_ERR(npu))
return PTR_ERR(npu);
@ -1266,18 +1338,15 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth)
if (err)
goto error_npu_put;
if (PPE_STATS_NUM_ENTRIES) {
ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
if (ppe_num_stats_entries > 0) {
err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma,
PPE_STATS_NUM_ENTRIES);
ppe_num_stats_entries);
if (err)
goto error_npu_put;
}
airoha_ppe_hw_init(ppe);
err = airoha_ppe_flush_sram_entries(ppe, npu);
if (err)
goto error_npu_put;
airoha_ppe_foe_flow_stats_reset(ppe, npu);
rcu_assign_pointer(eth->npu, npu);
@ -1313,9 +1382,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan)
{
struct airoha_ppe *ppe = dev->priv;
u32 ppe_hash_mask = airoha_ppe_get_total_num_entries(ppe) - 1;
u16 now, diff;
if (hash > PPE_HASH_MASK)
if (hash > ppe_hash_mask)
return;
now = (u16)jiffies;
@ -1405,8 +1475,9 @@ EXPORT_SYMBOL_GPL(airoha_ppe_put_dev);
int airoha_ppe_init(struct airoha_eth *eth)
{
int foe_size, err, ppe_num_stats_entries;
u32 ppe_num_entries;
struct airoha_ppe *ppe;
int foe_size, err;
ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL);
if (!ppe)
@ -1415,24 +1486,25 @@ int airoha_ppe_init(struct airoha_eth *eth)
ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb;
ppe->dev.ops.check_skb = airoha_ppe_check_skb;
ppe->dev.priv = ppe;
ppe->eth = eth;
eth->ppe = ppe;
foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry);
ppe_num_entries = airoha_ppe_get_total_num_entries(ppe);
foe_size = ppe_num_entries * sizeof(struct airoha_foe_entry);
ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma,
GFP_KERNEL);
if (!ppe->foe)
return -ENOMEM;
ppe->eth = eth;
eth->ppe = ppe;
ppe->foe_flow = devm_kzalloc(eth->dev,
PPE_NUM_ENTRIES * sizeof(*ppe->foe_flow),
ppe_num_entries * sizeof(*ppe->foe_flow),
GFP_KERNEL);
if (!ppe->foe_flow)
return -ENOMEM;
foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats);
if (foe_size) {
ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
if (ppe_num_stats_entries > 0) {
foe_size = ppe_num_stats_entries * sizeof(*ppe->foe_stats);
ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size,
&ppe->foe_stats_dma,
GFP_KERNEL);
@ -1440,6 +1512,15 @@ int airoha_ppe_init(struct airoha_eth *eth)
return -ENOMEM;
}
ppe->foe_check_time = devm_kzalloc(eth->dev, ppe_num_entries,
GFP_KERNEL);
if (!ppe->foe_check_time)
return -ENOMEM;
err = airoha_ppe_flush_sram_entries(ppe);
if (err)
return err;
err = rhashtable_init(&eth->flow_table, &airoha_flow_table_params);
if (err)
return err;

View File

@ -53,9 +53,10 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
[AIROHA_FOE_STATE_FIN] = "FIN",
};
struct airoha_ppe *ppe = m->private;
u32 ppe_num_entries = airoha_ppe_get_total_num_entries(ppe);
int i;
for (i = 0; i < PPE_NUM_ENTRIES; i++) {
for (i = 0; i < ppe_num_entries; i++) {
const char *state_str, *type_str = "UNKNOWN";
void *src_addr = NULL, *dest_addr = NULL;
u16 *src_port = NULL, *dest_port = NULL;

View File

@ -383,10 +383,8 @@
#define REG_MC_VLAN_DATA 0x2108
#define REG_SP_DFT_CPORT(_n) (0x20e0 + ((_n) << 2))
#define SP_CPORT_PCIE1_MASK GENMASK(31, 28)
#define SP_CPORT_PCIE0_MASK GENMASK(27, 24)
#define SP_CPORT_USB_MASK GENMASK(7, 4)
#define SP_CPORT_ETH_MASK GENMASK(7, 4)
#define SP_CPORT_DFT_MASK GENMASK(2, 0)
#define SP_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2))
#define REG_SRC_PORT_FC_MAP6 0x2298
#define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24)