Support reading an ATU snapshot in a single RMU request. Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.h | 10 +++++ drivers/net/dsa/mv88e6xxx/devlink.c | 67 ++++++++++++++++++++++--------- drivers/net/dsa/mv88e6xxx/rmu.c | 78 +++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/rmu.h | 35 +++++++++++++++++ 4 files changed, 171 insertions(+), 19 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index fdc87d02469b5..7bbb649330d55 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -205,6 +205,16 @@ struct mv88e6xxx_stu_entry { u8 state[DSA_MAX_PORTS]; }; +struct mv88e6xxx_devlink_atu_entry { + /* The FID is scattered over multiple registers. */ + u16 fid; + u16 atu_op; + u16 atu_data; + u16 atu_01; + u16 atu_23; + u16 atu_45; +}; + struct mv88e6xxx_bus_ops; struct mv88e6xxx_irq_ops; struct mv88e6xxx_gpio_ops; diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c index 6d7da381f9f38..5c2a59d1145b8 100644 --- a/drivers/net/dsa/mv88e6xxx/devlink.c +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -6,6 +6,7 @@ #include "global1.h" #include "global2.h" #include "port.h" +#include "rmu.h" static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash) { @@ -307,16 +308,6 @@ static int mv88e6xxx_region_global_snapshot(struct devlink *dl, * mv88e6xxx generations */ -struct mv88e6xxx_devlink_atu_entry { - /* The FID is scattered over multiple registers. */ - u16 fid; - u16 atu_op; - u16 atu_data; - u16 atu_01; - u16 atu_23; - u16 atu_45; -}; - static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip, int fid, struct mv88e6xxx_devlink_atu_entry *table, @@ -369,6 +360,33 @@ static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip, return 0; } +static int mv88e6xxx_region_atu_snapshot_rmu(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_devlink_atu_entry *table, + int *count) +{ + unsigned int num_macs = mv88e6xxx_num_macs(chip); + u16 cont_code = 0; + int err; + + do { + if (*count + MV88E6XXX_RMU_MAX_ATU_ENTRIES > num_macs) { + err = -ERANGE; + break; + } + + err = mv88e6xxx_rmu_dump_atu(chip, &table[*count], &cont_code); + if (err < 0) + break; + + *count += err; + } while (cont_code != 0); + + if (err < 0) + *count = 0; + + return err; +} + static int mv88e6xxx_region_atu_snapshot(struct devlink *dl, const struct devlink_region_ops *ops, struct netlink_ext_ack *extack, @@ -386,18 +404,29 @@ static int mv88e6xxx_region_atu_snapshot(struct devlink *dl, mv88e6xxx_reg_lock(chip); - while (1) { - fid = find_next_bit(chip->fid_bitmap, MV88E6XXX_N_FID, fid + 1); - if (fid == MV88E6XXX_N_FID) - break; + err = mv88e6xxx_region_atu_snapshot_rmu(chip, table, &count); + if (err >= 0) { + /* RMU succeeded, use its results */ + err = 0; + } else if (!mv88e6xxx_rmu_can_mdio_fallback(chip, err)) { + kfree(table); + goto out; + } else { + while (1) { + fid = find_next_bit(chip->fid_bitmap, MV88E6XXX_N_FID, + fid + 1); + if (fid == MV88E6XXX_N_FID) + break; - err = mv88e6xxx_region_atu_snapshot_fid(chip, fid, table, - &count); - if (err) { - kfree(table); - goto out; + err = mv88e6xxx_region_atu_snapshot_fid(chip, fid, + table, &count); + if (err) { + kfree(table); + goto out; + } } } + *data = (u8 *)table; out: mv88e6xxx_reg_unlock(chip); diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c index 5660372f41ad7..36422039030cd 100644 --- a/drivers/net/dsa/mv88e6xxx/rmu.c +++ b/drivers/net/dsa/mv88e6xxx/rmu.c @@ -106,6 +106,84 @@ static int mv88e6xxx_rmu_request(struct mv88e6xxx_chip *chip, resp, resp_len, timeout_ms); } +int mv88e6xxx_rmu_dump_atu(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_devlink_atu_entry *entries, + u16 *cont_code) +{ + __be16 req[] = { + MV88E6XXX_RMU_REQ_FORMAT_SOHO, + MV88E6XXX_RMU_REQ_PAD, + MV88E6XXX_RMU_REQ_CODE_ATU, + htons(*cont_code), + }; + struct mv88e6xxx_rmu_atu_resp resp; + int resp_len, count; + int ret, i; + + if (chip->rmu_state == MV88E6XXX_RMU_DISABLED) + return -EOPNOTSUPP; + + resp_len = sizeof(resp); + ret = mv88e6xxx_rmu_request(chip, req, sizeof(req), + &resp, resp_len, + MV88E6XXX_RMU_REQUEST_TIMEOUT_MS); + if (ret < 0) { + dev_dbg(chip->dev, "RMU: error for command DUMP_ATU %pe\n", + ERR_PTR(ret)); + return ret; + } + + /* minimum packet length */ + resp_len = sizeof(struct mv88e6xxx_rmu_header) + sizeof(__be16); /* cont_code */ + + if (ret < resp_len || (ret - resp_len) % sizeof(struct mv88e6xxx_rmu_atu_entry)) { + dev_err(chip->dev, "RMU: DUMP_ATU returned wrong length: rx %d expecting %zd\n", + ret, sizeof(resp)); + return -EPROTO; + } + + count = (ret - resp_len) / sizeof(struct mv88e6xxx_rmu_atu_entry); + + if (resp.rmu_header.code != MV88E6XXX_RMU_RESP_CODE_ATU) { + dev_err(chip->dev, "RMU: DUMP_ATU returned wrong code %d\n", + be16_to_cpu(resp.rmu_header.code)); + return -EPROTO; + } + + for (i = 0; i < count; i++) { + const struct mv88e6xxx_rmu_atu_entry *src = &resp.entries[i]; + struct mv88e6xxx_devlink_atu_entry *dst = &entries[i]; + u16 tmp; + + tmp = ntohs(src->state_trunk_dpv); + if (!MV88E6XXX_RMU_ATU_ENTRY_STATE_GET(tmp)) { + *cont_code = 0; + return i; + } + + dst->atu_data = MV88E6XXX_RMU_ATU_ENTRY_STATE_GET(tmp) & 0xf; + if (tmp & MV88E6XXX_RMU_ATU_TRUNK) + dst->atu_data |= MV88E6XXX_G1_ATU_DATA_TRUNK; + dst->atu_data |= (MV88E6XXX_RMU_ATU_DPV_GET(tmp) & + mv88e6xxx_port_mask(chip)) << 4; + + tmp = ntohs(src->pri_fid); + dst->fid = MV88E6XXX_RMU_ATU_FID_GET(tmp); + dst->atu_op = MV88E6XXX_G1_ATU_OP_GET_NEXT_DB; + + dst->atu_01 = ntohs(src->atu_01); + dst->atu_23 = ntohs(src->atu_23); + dst->atu_45 = ntohs(src->atu_45); + } + + if (i == MV88E6XXX_RMU_MAX_ATU_ENTRIES) + *cont_code = ntohs(resp.cont_code); + else + *cont_code = 0; + + return i; +} + int mv88e6xxx_rmu_stats(struct mv88e6xxx_chip *chip, int port, u64 *data, const struct mv88e6xxx_hw_stat *hw_stats, diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h index 0865e899cb340..3a288ff3b113f 100644 --- a/drivers/net/dsa/mv88e6xxx/rmu.h +++ b/drivers/net/dsa/mv88e6xxx/rmu.h @@ -17,6 +17,7 @@ #define MV88E6XXX_RMU_REQ_FORMAT_SOHO htons(0x0001) #define MV88E6XXX_RMU_REQ_PAD htons(0x0000) #define MV88E6XXX_RMU_REQ_CODE_GET_ID htons(0x0000) +#define MV88E6XXX_RMU_REQ_CODE_ATU htons(0x1000) #define MV88E6XXX_RMU_REQ_CODE_MIB htons(0x1020) #define MV88E6XXX_RMU_REQ_CODE_REG_RW htons(0x2000) #define MV88E6XXX_RMU_REQ_DATA htons(0x0000) @@ -50,6 +51,7 @@ #define MV88E6XXX_RMU_RESP_FORMAT_1 htons(0x0001) #define MV88E6XXX_RMU_RESP_FORMAT_2 htons(0x0002) #define MV88E6XXX_RMU_RESP_CODE_GOT_ID htons(0x0000) +#define MV88E6XXX_RMU_RESP_CODE_ATU htons(0x1000) #define MV88E6XXX_RMU_RESP_CODE_MIB htons(0x1020) #define MV88E6XXX_RMU_RESP_CODE_REG_RW htons(0x2000) @@ -67,6 +69,34 @@ struct mv88e6xxx_rmu_rw_resp { __be16 end1; } __packed; +struct mv88e6xxx_rmu_atu_entry { + __be16 state_trunk_dpv; + __be16 atu_01; + __be16 atu_23; + __be16 atu_45; + __be16 pri_fid; +} __packed; + +#define MV88E6XXX_RMU_MAX_ATU_ENTRIES 48 + +#define MV88E6XXX_RMU_ATU_ENTRY_STATE_MASK GENMASK(15, 12) +#define MV88E6XXX_RMU_ATU_ENTRY_STATE_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_ENTRY_STATE_MASK, p) +#define MV88E6XXX_RMU_ATU_TRUNK 0x0800 +#define MV88E6XXX_RMU_ATU_DPV_MASK GENMASK(10, 0) +#define MV88E6XXX_RMU_ATU_DPV_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_DPV_MASK, p) + +#define MV88E6XXX_RMU_ATU_PRI_MASK GENMASK(14, 12) +#define MV88E6XXX_RMU_ATU_PRI_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_PRI_MASK, p) + +#define MV88E6XXX_RMU_ATU_FID_MASK GENMASK(11, 0) +#define MV88E6XXX_RMU_ATU_FID_GET(p) FIELD_GET(MV88E6XXX_RMU_ATU_FID_MASK, p) + +struct mv88e6xxx_rmu_atu_resp { + struct mv88e6xxx_rmu_header rmu_header; + struct mv88e6xxx_rmu_atu_entry entries[MV88E6XXX_RMU_MAX_ATU_ENTRIES]; + __be16 cont_code; +} __packed; + /* number of RMU MIB statistics, by type, in octets */ #define MV88E6XXX_RMU_STATS_TYPE_DATA0_LEN 128 #define MV88E6XXX_RMU_STATS_TYPE_PORT_LEN 12 @@ -81,6 +111,11 @@ struct mv88e6xxx_rmu_mib_resp { __be32 data[MV88E6XXX_RMU_STATS_TYPE_MAX_LEN / 4]; } __packed; +struct mv88e6xxx_devlink_atu_entry; + +int mv88e6xxx_rmu_dump_atu(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_devlink_atu_entry *entries, + u16 *cont_code); int mv88e6xxx_rmu_stats(struct mv88e6xxx_chip *chip, int port, u64 *data, const struct mv88e6xxx_hw_stat *hw_stats, -- 2.43.0