diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h index 991af1cd902..569921b2e1f 100644 --- a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h +++ b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe.h @@ -19,6 +19,10 @@ #define QCA_PPE_MAX_BRIDGES 8 /* --- Global --- */ +#define PPE_SWITCH_ID 0x0 +#define PPE_SWITCH_ID_REV GENMASK(7, 0) +#define PPE_SWITCH_ID_DEV GENMASK(15, 8) + #define PPE_CLK_GATING_CTRL 0x8 #define PPE_QM_CLK_GATE_EN BIT(4) @@ -586,6 +590,11 @@ #define PPE_PORT_METER_W2(p) (PPE_POLICER_BASE + 0xc000 + (p) * 0x10 + 0x8) #define PPE_PORT_METER_W3(p) (PPE_POLICER_BASE + 0xc000 + (p) * 0x10 + 0xc) +/* The meter's token buckets, committed and excess, one entry per port and two + * words wide, the second of which commits it. + */ +#define PPE_PORT_METER_CRDT(p) (PPE_POLICER_BASE + 0xd000 + (p) * 0x10) + #define PPE_PORT_TX_DROP_CNT(p) (PPE_POLICER_BASE + 0x7d000 + (p) * 0x10) #define PPE_VP_TX_DROP_CNT(v) (PPE_POLICER_BASE + 0x7e000 + (v) * 0x10) diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c index 578ea330aa0..c82f171605f 100644 --- a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c +++ b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_main.c @@ -659,6 +659,41 @@ static int ppe_devlink_setup(struct dsa_switch *ds) return ret; } +/* The part names itself in the first register of the global block. qca-ssdk's + * named accessor for it is compiled out; what proves the field split is its init + * path reading the same word raw (ssdk_init.c chip_ver_get): device 0x15 is this + * switch generation, revision 0 IPQ807x and 1 IPQ6018. + */ +static int qca_ppe_devlink_info_get(struct dsa_switch *ds, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct qca_ppe_priv *priv = ds_to_priv(ds); + u32 id, dev, rev; + char buf[8]; + int ret; + + ret = regmap_read(priv->regmap, PPE_SWITCH_ID, &id); + if (ret) + return ret; + + dev = FIELD_GET(PPE_SWITCH_ID_DEV, id); + rev = FIELD_GET(PPE_SWITCH_ID_REV, id); + + snprintf(buf, sizeof(buf), "0x%02x", dev); + ret = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, + buf); + if (ret) + return ret; + + snprintf(buf, sizeof(buf), "0x%02x", rev); + + return devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, + buf); +} + static int qca_ppe_setup(struct dsa_switch *ds) { struct qca_ppe_priv *priv = ds_to_priv(ds); @@ -2449,6 +2484,7 @@ static const struct dsa_switch_ops qca_ppe_ops = { .get_stats64 = qca_ppe_get_stats64, .get_regs_len = qca_ppe_get_regs_len, .get_regs = qca_ppe_get_regs, + .devlink_info_get = qca_ppe_devlink_info_get, .devlink_sb_pool_get = qca_ppe_devlink_sb_pool_get, .devlink_sb_pool_set = qca_ppe_devlink_sb_pool_set, }; diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c index 8a8a0f80de5..d33cc77623d 100644 --- a/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c +++ b/target/linux/qualcommax/files/drivers/net/ethernet/qualcomm/qca_ppe_scheduler.c @@ -1488,6 +1488,14 @@ static int ppe_port_policer_set(struct qca_ppe_priv *priv, int port, regmap_write(priv->regmap, PPE_PORT_METER_W2(port), 0); regmap_write(priv->regmap, PPE_PORT_METER_W3(port), 0); + /* Credit outlives the rate that filled it: what a re-armed meter would + * spend on its first frames was bought at a rate the port has lost. + */ + if (!rate_bps) { + regmap_write(priv->regmap, PPE_PORT_METER_CRDT(port), 0); + regmap_write(priv->regmap, PPE_PORT_METER_CRDT(port) + 0x4, 0); + } + return 0; }