diff options
Diffstat (limited to 'drivers/net/dsa/microchip')
-rw-r--r-- | drivers/net/dsa/microchip/ksz8.h | 8 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz8795.c | 179 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477_i2c.c | 2 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 240 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.h | 18 |
5 files changed, 366 insertions, 81 deletions
diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index ea05abfbd51d..e68465fdf6b9 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -21,10 +21,6 @@ int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val); int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries); -int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, - struct alu_struct *alu); -void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, - struct alu_struct *alu); void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt); void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt); @@ -32,6 +28,10 @@ void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze); void ksz8_port_init_cnt(struct ksz_device *dev, int port); int ksz8_fdb_dump(struct ksz_device *dev, int port, dsa_fdb_dump_cb_t *cb, void *data); +int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, + u16 vid, struct dsa_db db); +int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, + u16 vid, struct dsa_db db); int ksz8_mdb_add(struct ksz_device *dev, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); int ksz8_mdb_del(struct ksz_device *dev, int port, diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index ffcad057d065..f56fca1b1a22 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -336,34 +336,48 @@ void ksz8_port_init_cnt(struct ksz_device *dev, int port) } } -static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) +static int ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) { const u16 *regs; u16 ctrl_addr; + int ret; regs = dev->info->regs; ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr; mutex_lock(&dev->alu_mutex); - ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); - ksz_read64(dev, regs[REG_IND_DATA_HI], data); + ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); + if (ret) + goto unlock_alu; + + ret = ksz_read64(dev, regs[REG_IND_DATA_HI], data); +unlock_alu: mutex_unlock(&dev->alu_mutex); + + return ret; } -static void ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) +static int ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) { const u16 *regs; u16 ctrl_addr; + int ret; regs = dev->info->regs; ctrl_addr = IND_ACC_TABLE(table) | addr; mutex_lock(&dev->alu_mutex); - ksz_write64(dev, regs[REG_IND_DATA_HI], data); - ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); + ret = ksz_write64(dev, regs[REG_IND_DATA_HI], data); + if (ret) + goto unlock_alu; + + ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); +unlock_alu: mutex_unlock(&dev->alu_mutex); + + return ret; } static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data) @@ -457,46 +471,54 @@ int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, return rc; } -int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, - struct alu_struct *alu) +static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, + struct alu_struct *alu, bool *valid) { u32 data_hi, data_lo; const u8 *shifts; const u32 *masks; u64 data; + int ret; shifts = dev->info->shifts; masks = dev->info->masks; - ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data); + ret = ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data); + if (ret) + return ret; + data_hi = data >> 32; data_lo = (u32)data; - if (data_hi & (masks[STATIC_MAC_TABLE_VALID] | - masks[STATIC_MAC_TABLE_OVERRIDE])) { - alu->mac[5] = (u8)data_lo; - alu->mac[4] = (u8)(data_lo >> 8); - alu->mac[3] = (u8)(data_lo >> 16); - alu->mac[2] = (u8)(data_lo >> 24); - alu->mac[1] = (u8)data_hi; - alu->mac[0] = (u8)(data_hi >> 8); - alu->port_forward = - (data_hi & masks[STATIC_MAC_TABLE_FWD_PORTS]) >> - shifts[STATIC_MAC_FWD_PORTS]; - alu->is_override = - (data_hi & masks[STATIC_MAC_TABLE_OVERRIDE]) ? 1 : 0; - data_hi >>= 1; - alu->is_static = true; - alu->is_use_fid = - (data_hi & masks[STATIC_MAC_TABLE_USE_FID]) ? 1 : 0; - alu->fid = (data_hi & masks[STATIC_MAC_TABLE_FID]) >> - shifts[STATIC_MAC_FID]; + + if (!(data_hi & (masks[STATIC_MAC_TABLE_VALID] | + masks[STATIC_MAC_TABLE_OVERRIDE]))) { + *valid = false; return 0; } - return -ENXIO; + + alu->mac[5] = (u8)data_lo; + alu->mac[4] = (u8)(data_lo >> 8); + alu->mac[3] = (u8)(data_lo >> 16); + alu->mac[2] = (u8)(data_lo >> 24); + alu->mac[1] = (u8)data_hi; + alu->mac[0] = (u8)(data_hi >> 8); + alu->port_forward = + (data_hi & masks[STATIC_MAC_TABLE_FWD_PORTS]) >> + shifts[STATIC_MAC_FWD_PORTS]; + alu->is_override = (data_hi & masks[STATIC_MAC_TABLE_OVERRIDE]) ? 1 : 0; + data_hi >>= 1; + alu->is_static = true; + alu->is_use_fid = (data_hi & masks[STATIC_MAC_TABLE_USE_FID]) ? 1 : 0; + alu->fid = (data_hi & masks[STATIC_MAC_TABLE_FID]) >> + shifts[STATIC_MAC_FID]; + + *valid = true; + + return 0; } -void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, - struct alu_struct *alu) +static int ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, + struct alu_struct *alu) { u32 data_hi, data_lo; const u8 *shifts; @@ -524,7 +546,8 @@ void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, data_hi &= ~masks[STATIC_MAC_TABLE_OVERRIDE]; data = (u64)data_hi << 32 | data_lo; - ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data); + + return ksz8_w_table(dev, TABLE_STATIC_MAC, addr, data); } static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid, @@ -977,24 +1000,29 @@ int ksz8_fdb_dump(struct ksz_device *dev, int port, return ret; } -int ksz8_mdb_add(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +static int ksz8_add_sta_mac(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid) { struct alu_struct alu; - int index; + int index, ret; int empty = 0; alu.port_forward = 0; for (index = 0; index < dev->info->num_statics; index++) { - if (!ksz8_r_sta_mac_table(dev, index, &alu)) { - /* Found one already in static MAC table. */ - if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && - alu.fid == mdb->vid) - break; - /* Remember the first empty entry. */ - } else if (!empty) { - empty = index + 1; + bool valid; + + ret = ksz8_r_sta_mac_table(dev, index, &alu, &valid); + if (ret) + return ret; + if (!valid) { + /* Remember the first empty entry. */ + if (!empty) + empty = index + 1; + continue; } + + if (!memcmp(alu.mac, addr, ETH_ALEN) && alu.fid == vid) + break; } /* no available entry */ @@ -1005,48 +1033,73 @@ int ksz8_mdb_add(struct ksz_device *dev, int port, if (index == dev->info->num_statics) { index = empty - 1; memset(&alu, 0, sizeof(alu)); - memcpy(alu.mac, mdb->addr, ETH_ALEN); + memcpy(alu.mac, addr, ETH_ALEN); alu.is_static = true; } alu.port_forward |= BIT(port); - if (mdb->vid) { + if (vid) { alu.is_use_fid = true; /* Need a way to map VID to FID. */ - alu.fid = mdb->vid; + alu.fid = vid; } - ksz8_w_sta_mac_table(dev, index, &alu); - return 0; + return ksz8_w_sta_mac_table(dev, index, &alu); } -int ksz8_mdb_del(struct ksz_device *dev, int port, - const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +static int ksz8_del_sta_mac(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid) { struct alu_struct alu; - int index; + int index, ret; for (index = 0; index < dev->info->num_statics; index++) { - if (!ksz8_r_sta_mac_table(dev, index, &alu)) { - /* Found one already in static MAC table. */ - if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && - alu.fid == mdb->vid) - break; - } + bool valid; + + ret = ksz8_r_sta_mac_table(dev, index, &alu, &valid); + if (ret) + return ret; + if (!valid) + continue; + + if (!memcmp(alu.mac, addr, ETH_ALEN) && alu.fid == vid) + break; } /* no available entry */ if (index == dev->info->num_statics) - goto exit; + return 0; /* clear port */ alu.port_forward &= ~BIT(port); if (!alu.port_forward) alu.is_static = false; - ksz8_w_sta_mac_table(dev, index, &alu); -exit: - return 0; + return ksz8_w_sta_mac_table(dev, index, &alu); +} + +int ksz8_mdb_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +{ + return ksz8_add_sta_mac(dev, port, mdb->addr, mdb->vid); +} + +int ksz8_mdb_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +{ + return ksz8_del_sta_mac(dev, port, mdb->addr, mdb->vid); +} + +int ksz8_fdb_add(struct ksz_device *dev, int port, const unsigned char *addr, + u16 vid, struct dsa_db db) +{ + return ksz8_add_sta_mac(dev, port, addr, vid); +} + +int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr, + u16 vid, struct dsa_db db) +{ + return ksz8_del_sta_mac(dev, port, addr, vid); } int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, @@ -1346,9 +1399,7 @@ int ksz8_enable_stp_addr(struct ksz_device *dev) alu.is_override = true; alu.port_forward = dev->info->cpu_ports; - ksz8_w_sta_mac_table(dev, 0, &alu); - - return 0; + return ksz8_w_sta_mac_table(dev, 0, &alu); } int ksz8_setup(struct dsa_switch *ds) diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index e315f669ec06..97a317263a2f 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); static struct i2c_driver ksz9477_i2c_driver = { .driver = { .name = "ksz9477-switch", - .of_match_table = of_match_ptr(ksz9477_dt_ids), + .of_match_table = ksz9477_dt_ids, }, .probe_new = ksz9477_i2c_probe, .remove = ksz9477_i2c_remove, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 74c56d05ab0b..a4428be5f483 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -32,10 +32,6 @@ #include "ksz9477.h" #include "lan937x.h" -#define KSZ_CBS_ENABLE ((MTI_SCHEDULE_STRICT_PRIO << MTI_SCHEDULE_MODE_S) | \ - (MTI_SHAPING_SRP << MTI_SHAPING_S)) -#define KSZ_CBS_DISABLE ((MTI_SCHEDULE_WRR << MTI_SCHEDULE_MODE_S) |\ - (MTI_SHAPING_OFF << MTI_SHAPING_S)) #define MIB_COUNTER_NUM 0x20 struct ksz_stats_raw { @@ -204,6 +200,8 @@ static const struct ksz_dev_ops ksz8_dev_ops = { .freeze_mib = ksz8_freeze_mib, .port_init_cnt = ksz8_port_init_cnt, .fdb_dump = ksz8_fdb_dump, + .fdb_add = ksz8_fdb_add, + .fdb_del = ksz8_fdb_del, .mdb_add = ksz8_mdb_add, .mdb_del = ksz8_mdb_del, .vlan_filtering = ksz8_port_vlan_filtering, @@ -1089,6 +1087,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 3, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1228,6 +1227,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 4, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1352,6 +1352,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 3, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1379,6 +1380,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 3, .num_tx_queues = 4, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &ksz9477_dev_ops, .phy_errata_9477 = true, .mib_names = ksz9477_mib_names, @@ -1411,6 +1413,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1437,6 +1440,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1463,6 +1467,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1493,6 +1498,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -1523,6 +1529,7 @@ const struct ksz_chip_data ksz_switch_chips[] = { .port_nirqs = 6, .num_tx_queues = 8, .tc_cbs_supported = true, + .tc_ets_supported = true, .ops = &lan937x_dev_ops, .mib_names = ksz9477_mib_names, .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), @@ -3091,6 +3098,14 @@ static int cinc_cal(s32 idle_slope, s32 send_slope, u32 *bw) return 0; } +static int ksz_setup_tc_mode(struct ksz_device *dev, int port, u8 scheduler, + u8 shaper) +{ + return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, + FIELD_PREP(MTI_SCHEDULE_MODE_M, scheduler) | + FIELD_PREP(MTI_SHAPING_M, shaper)); +} + static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, struct tc_cbs_qopt_offload *qopt) { @@ -3110,8 +3125,8 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, return ret; if (!qopt->enable) - return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, - KSZ_CBS_DISABLE); + return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_WRR, + MTI_SHAPING_OFF); /* High Credit */ ret = ksz_pwrite16(dev, port, REG_PORT_MTI_HI_WATER_MARK, @@ -3136,8 +3151,215 @@ static int ksz_setup_tc_cbs(struct dsa_switch *ds, int port, return ret; } - return ksz_pwrite8(dev, port, REG_PORT_MTI_QUEUE_CTRL_0, - KSZ_CBS_ENABLE); + return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_STRICT_PRIO, + MTI_SHAPING_SRP); +} + +static int ksz_disable_egress_rate_limit(struct ksz_device *dev, int port) +{ + int queue, ret; + + /* Configuration will not take effect until the last Port Queue X + * Egress Limit Control Register is written. + */ + for (queue = 0; queue < dev->info->num_tx_queues; queue++) { + ret = ksz_pwrite8(dev, port, KSZ9477_REG_PORT_OUT_RATE_0 + queue, + KSZ9477_OUT_RATE_NO_LIMIT); + if (ret) + return ret; + } + + return 0; +} + +static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p, + int band) +{ + /* Compared to queues, bands prioritize packets differently. In strict + * priority mode, the lowest priority is assigned to Queue 0 while the + * highest priority is given to Band 0. + */ + return p->bands - 1 - band; +} + +static int ksz_queue_set_strict(struct ksz_device *dev, int port, int queue) +{ + int ret; + + ret = ksz_pwrite32(dev, port, REG_PORT_MTI_QUEUE_INDEX__4, queue); + if (ret) + return ret; + + return ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_STRICT_PRIO, + MTI_SHAPING_OFF); +} + +static int ksz_queue_set_wrr(struct ksz_device *dev, int port, int queue, + int weight) +{ + int ret; + + ret = ksz_pwrite32(dev, port, REG_PORT_MTI_QUEUE_INDEX__4, queue); + if (ret) + return ret; + + ret = ksz_setup_tc_mode(dev, port, MTI_SCHEDULE_WRR, + MTI_SHAPING_OFF); + if (ret) + return ret; + + return ksz_pwrite8(dev, port, KSZ9477_PORT_MTI_QUEUE_CTRL_1, weight); +} + +static int ksz_tc_ets_add(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p) +{ + int ret, band, tc_prio; + u32 queue_map = 0; + + /* In order to ensure proper prioritization, it is necessary to set the + * rate limit for the related queue to zero. Otherwise strict priority + * or WRR mode will not work. This is a hardware limitation. + */ + ret = ksz_disable_egress_rate_limit(dev, port); + if (ret) + return ret; + + /* Configure queue scheduling mode for all bands. Currently only strict + * prio mode is supported. + */ + for (band = 0; band < p->bands; band++) { + int queue = ksz_ets_band_to_queue(p, band); + + ret = ksz_queue_set_strict(dev, port, queue); + if (ret) + return ret; + } + + /* Configure the mapping between traffic classes and queues. Note: + * priomap variable support 16 traffic classes, but the chip can handle + * only 8 classes. + */ + for (tc_prio = 0; tc_prio < ARRAY_SIZE(p->priomap); tc_prio++) { + int queue; + + if (tc_prio > KSZ9477_MAX_TC_PRIO) + break; + + queue = ksz_ets_band_to_queue(p, p->priomap[tc_prio]); + queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S); + } + + return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} + +static int ksz_tc_ets_del(struct ksz_device *dev, int port) +{ + int ret, queue, tc_prio, s; + u32 queue_map = 0; + + /* To restore the default chip configuration, set all queues to use the + * WRR scheduler with a weight of 1. + */ + for (queue = 0; queue < dev->info->num_tx_queues; queue++) { + ret = ksz_queue_set_wrr(dev, port, queue, + KSZ9477_DEFAULT_WRR_WEIGHT); + if (ret) + return ret; + } + + switch (dev->info->num_tx_queues) { + case 2: + s = 2; + break; + case 4: + s = 1; + break; + case 8: + s = 0; + break; + default: + return -EINVAL; + } + + /* Revert the queue mapping for TC-priority to its default setting on + * the chip. + */ + for (tc_prio = 0; tc_prio <= KSZ9477_MAX_TC_PRIO; tc_prio++) { + int queue; + + queue = tc_prio >> s; + queue_map |= queue << (tc_prio * KSZ9477_PORT_TC_MAP_S); + } + + return ksz_pwrite32(dev, port, KSZ9477_PORT_MRI_TC_MAP__4, queue_map); +} + +static int ksz_tc_ets_validate(struct ksz_device *dev, int port, + struct tc_ets_qopt_offload_replace_params *p) +{ + int band; + + /* Since it is not feasible to share one port among multiple qdisc, + * the user must configure all available queues appropriately. + */ + if (p->bands != dev->info->num_tx_queues) { + dev_err(dev->dev, "Not supported amount of bands. It should be %d\n", + dev->info->num_tx_queues); + return -EOPNOTSUPP; + } + + for (band = 0; band < p->bands; ++band) { + /* The KSZ switches utilize a weighted round robin configuration + * where a certain number of packets can be transmitted from a + * queue before the next queue is serviced. For more information + * on this, refer to section 5.2.8.4 of the KSZ8565R + * documentation on the Port Transmit Queue Control 1 Register. + * However, the current ETS Qdisc implementation (as of February + * 2023) assigns a weight to each queue based on the number of + * bytes or extrapolated bandwidth in percentages. Since this + * differs from the KSZ switches' method and we don't want to + * fake support by converting bytes to packets, it is better to + * return an error instead. + */ + if (p->quanta[band]) { + dev_err(dev->dev, "Quanta/weights configuration is not supported.\n"); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port, + struct tc_ets_qopt_offload *qopt) +{ + struct ksz_device *dev = ds->priv; + int ret; + + if (!dev->info->tc_ets_supported) + return -EOPNOTSUPP; + + if (qopt->parent != TC_H_ROOT) { + dev_err(dev->dev, "Parent should be \"root\"\n"); + return -EOPNOTSUPP; + } + + switch (qopt->command) { + case TC_ETS_REPLACE: + ret = ksz_tc_ets_validate(dev, port, &qopt->replace_params); + if (ret) + return ret; + + return ksz_tc_ets_add(dev, port, &qopt->replace_params); + case TC_ETS_DESTROY: + return ksz_tc_ets_del(dev, port); + case TC_ETS_STATS: + case TC_ETS_GRAFT: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; } static int ksz_setup_tc(struct dsa_switch *ds, int port, @@ -3146,6 +3368,8 @@ static int ksz_setup_tc(struct dsa_switch *ds, int port, switch (type) { case TC_SETUP_QDISC_CBS: return ksz_setup_tc_cbs(ds, port, type_data); + case TC_SETUP_QDISC_ETS: + return ksz_tc_setup_qdisc_ets(ds, port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index d2d5761d58e9..8abecaf6089e 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -51,6 +51,7 @@ struct ksz_chip_data { u8 port_nirqs; u8 num_tx_queues; bool tc_cbs_supported; + bool tc_ets_supported; const struct ksz_dev_ops *ops; bool phy_errata_9477; bool ksz87xx_eee_link_erratum; @@ -649,21 +650,30 @@ static inline int is_lan937x(struct ksz_device *dev) #define KSZ8_LEGAL_PACKET_SIZE 1518 #define KSZ9477_MAX_FRAME_SIZE 9000 +#define KSZ9477_REG_PORT_OUT_RATE_0 0x0420 +#define KSZ9477_OUT_RATE_NO_LIMIT 0 + +#define KSZ9477_PORT_MRI_TC_MAP__4 0x0808 + +#define KSZ9477_PORT_TC_MAP_S 4 +#define KSZ9477_MAX_TC_PRIO 7 + /* CBS related registers */ #define REG_PORT_MTI_QUEUE_INDEX__4 0x0900 #define REG_PORT_MTI_QUEUE_CTRL_0 0x0914 -#define MTI_SCHEDULE_MODE_M 0x3 -#define MTI_SCHEDULE_MODE_S 6 +#define MTI_SCHEDULE_MODE_M GENMASK(7, 6) #define MTI_SCHEDULE_STRICT_PRIO 0 #define MTI_SCHEDULE_WRR 2 -#define MTI_SHAPING_M 0x3 -#define MTI_SHAPING_S 4 +#define MTI_SHAPING_M GENMASK(5, 4) #define MTI_SHAPING_OFF 0 #define MTI_SHAPING_SRP 1 #define MTI_SHAPING_TIME_AWARE 2 +#define KSZ9477_PORT_MTI_QUEUE_CTRL_1 0x0915 +#define KSZ9477_DEFAULT_WRR_WEIGHT 1 + #define REG_PORT_MTI_HI_WATER_MARK 0x0916 #define REG_PORT_MTI_LO_WATER_MARK 0x0918 |