Skip to content

Commit ef1ca92

Browse files
LorenzoBianconiPaolo Abeni
authored andcommitted
net: airoha: Add sched HTB offload support
Introduce support for HTB Qdisc offload available in the Airoha EN7581 ethernet controller. EN7581 can offload only one level of HTB leafs. Each HTB leaf represents a QoS channel supported by EN7581 SoC. The typical use-case is creating a HTB leaf for QoS channel to rate limit the egress traffic and attach an ETS Qdisc to each HTB leaf in order to enforce traffic prioritization. Signed-off-by: Lorenzo Bianconi <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent 20bf7d0 commit ef1ca92

File tree

1 file changed

+287
-1
lines changed

1 file changed

+287
-1
lines changed

drivers/net/ethernet/mediatek/airoha_eth.c

Lines changed: 287 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#define AIROHA_NUM_QOS_QUEUES 8
2929
#define AIROHA_NUM_TX_RING 32
3030
#define AIROHA_NUM_RX_RING 32
31+
#define AIROHA_NUM_NETDEV_TX_RINGS (AIROHA_NUM_TX_RING + \
32+
AIROHA_NUM_QOS_CHANNELS)
3133
#define AIROHA_FE_MC_MAX_VLAN_TABLE 64
3234
#define AIROHA_FE_MC_MAX_VLAN_PORT 16
3335
#define AIROHA_NUM_TX_IRQ 2
@@ -43,6 +45,9 @@
4345
#define PSE_RSV_PAGES 128
4446
#define PSE_QUEUE_RSV_PAGES 64
4547

48+
#define QDMA_METER_IDX(_n) ((_n) & 0xff)
49+
#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3)
50+
4651
/* FE */
4752
#define PSE_BASE 0x0100
4853
#define CSR_IFC_BASE 0x0200
@@ -583,6 +588,17 @@
583588
#define EGRESS_SLOW_TICK_RATIO_MASK GENMASK(29, 16)
584589
#define EGRESS_FAST_TICK_MASK GENMASK(15, 0)
585590

591+
#define TRTCM_PARAM_RW_MASK BIT(31)
592+
#define TRTCM_PARAM_RW_DONE_MASK BIT(30)
593+
#define TRTCM_PARAM_TYPE_MASK GENMASK(29, 28)
594+
#define TRTCM_METER_GROUP_MASK GENMASK(27, 26)
595+
#define TRTCM_PARAM_INDEX_MASK GENMASK(23, 17)
596+
#define TRTCM_PARAM_RATE_TYPE_MASK BIT(16)
597+
598+
#define REG_TRTCM_CFG_PARAM(_n) ((_n) + 0x4)
599+
#define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8)
600+
#define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc)
601+
586602
#define REG_TXWRR_MODE_CFG 0x1020
587603
#define TWRR_WEIGHT_SCALE_MASK BIT(31)
588604
#define TWRR_WEIGHT_BASE_MASK BIT(3)
@@ -759,6 +775,29 @@ enum tx_sched_mode {
759775
TC_SCH_WRR2,
760776
};
761777

778+
enum trtcm_param_type {
779+
TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */
780+
TRTCM_TOKEN_RATE_MODE,
781+
TRTCM_BUCKETSIZE_SHIFT_MODE,
782+
TRTCM_BUCKET_COUNTER_MODE,
783+
};
784+
785+
enum trtcm_mode_type {
786+
TRTCM_COMMIT_MODE,
787+
TRTCM_PEAK_MODE,
788+
};
789+
790+
enum trtcm_param {
791+
TRTCM_TICK_SEL = BIT(0),
792+
TRTCM_PKT_MODE = BIT(1),
793+
TRTCM_METER_MODE = BIT(2),
794+
};
795+
796+
#define MIN_TOKEN_SIZE 4096
797+
#define MAX_TOKEN_SIZE_OFFSET 17
798+
#define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6)
799+
#define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0)
800+
762801
struct airoha_queue_entry {
763802
union {
764803
void *buf;
@@ -850,6 +889,8 @@ struct airoha_gdm_port {
850889

851890
struct airoha_hw_stats stats;
852891

892+
DECLARE_BITMAP(qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS);
893+
853894
/* qos stats counters */
854895
u64 cpu_tx_packets;
855896
u64 fwd_tx_packets;
@@ -2817,6 +2858,243 @@ static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port,
28172858
}
28182859
}
28192860

2861+
static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel,
2862+
u32 addr, enum trtcm_param_type param,
2863+
enum trtcm_mode_type mode,
2864+
u32 *val_low, u32 *val_high)
2865+
{
2866+
u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel);
2867+
u32 val, config = FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) |
2868+
FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
2869+
FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
2870+
FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
2871+
2872+
airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
2873+
if (read_poll_timeout(airoha_qdma_rr, val,
2874+
val & TRTCM_PARAM_RW_DONE_MASK,
2875+
USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
2876+
qdma, REG_TRTCM_CFG_PARAM(addr)))
2877+
return -ETIMEDOUT;
2878+
2879+
*val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr));
2880+
if (val_high)
2881+
*val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr));
2882+
2883+
return 0;
2884+
}
2885+
2886+
static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,
2887+
u32 addr, enum trtcm_param_type param,
2888+
enum trtcm_mode_type mode, u32 val)
2889+
{
2890+
u32 idx = QDMA_METER_IDX(channel), group = QDMA_METER_GROUP(channel);
2891+
u32 config = TRTCM_PARAM_RW_MASK |
2892+
FIELD_PREP(TRTCM_PARAM_TYPE_MASK, param) |
2893+
FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
2894+
FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
2895+
FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
2896+
2897+
airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
2898+
airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
2899+
2900+
return read_poll_timeout(airoha_qdma_rr, val,
2901+
val & TRTCM_PARAM_RW_DONE_MASK,
2902+
USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
2903+
qdma, REG_TRTCM_CFG_PARAM(addr));
2904+
}
2905+
2906+
static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
2907+
u32 addr, enum trtcm_mode_type mode,
2908+
bool enable, u32 enable_mask)
2909+
{
2910+
u32 val;
2911+
2912+
if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
2913+
mode, &val, NULL))
2914+
return -EINVAL;
2915+
2916+
val = enable ? val | enable_mask : val & ~enable_mask;
2917+
2918+
return airoha_qdma_set_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
2919+
mode, val);
2920+
}
2921+
2922+
static int airoha_qdma_set_trtcm_token_bucket(struct airoha_qdma *qdma,
2923+
int channel, u32 addr,
2924+
enum trtcm_mode_type mode,
2925+
u32 rate_val, u32 bucket_size)
2926+
{
2927+
u32 val, config, tick, unit, rate, rate_frac;
2928+
int err;
2929+
2930+
if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
2931+
mode, &config, NULL))
2932+
return -EINVAL;
2933+
2934+
val = airoha_qdma_rr(qdma, addr);
2935+
tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val);
2936+
if (config & TRTCM_TICK_SEL)
2937+
tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val);
2938+
if (!tick)
2939+
return -EINVAL;
2940+
2941+
unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick;
2942+
if (!unit)
2943+
return -EINVAL;
2944+
2945+
rate = rate_val / unit;
2946+
rate_frac = rate_val % unit;
2947+
rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit;
2948+
rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) |
2949+
FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac);
2950+
2951+
err = airoha_qdma_set_trtcm_param(qdma, channel, addr,
2952+
TRTCM_TOKEN_RATE_MODE, mode, rate);
2953+
if (err)
2954+
return err;
2955+
2956+
val = max_t(u32, bucket_size, MIN_TOKEN_SIZE);
2957+
val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET);
2958+
2959+
return airoha_qdma_set_trtcm_param(qdma, channel, addr,
2960+
TRTCM_BUCKETSIZE_SHIFT_MODE,
2961+
mode, val);
2962+
}
2963+
2964+
static int airoha_qdma_set_tx_rate_limit(struct airoha_gdm_port *port,
2965+
int channel, u32 rate,
2966+
u32 bucket_size)
2967+
{
2968+
int i, err;
2969+
2970+
for (i = 0; i <= TRTCM_PEAK_MODE; i++) {
2971+
err = airoha_qdma_set_trtcm_config(port->qdma, channel,
2972+
REG_EGRESS_TRTCM_CFG, i,
2973+
!!rate, TRTCM_METER_MODE);
2974+
if (err)
2975+
return err;
2976+
2977+
err = airoha_qdma_set_trtcm_token_bucket(port->qdma, channel,
2978+
REG_EGRESS_TRTCM_CFG,
2979+
i, rate, bucket_size);
2980+
if (err)
2981+
return err;
2982+
}
2983+
2984+
return 0;
2985+
}
2986+
2987+
static int airoha_tc_htb_alloc_leaf_queue(struct airoha_gdm_port *port,
2988+
struct tc_htb_qopt_offload *opt)
2989+
{
2990+
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
2991+
u32 rate = div_u64(opt->rate, 1000) << 3; /* kbps */
2992+
struct net_device *dev = port->dev;
2993+
int num_tx_queues = dev->real_num_tx_queues;
2994+
int err;
2995+
2996+
if (opt->parent_classid != TC_HTB_CLASSID_ROOT) {
2997+
NL_SET_ERR_MSG_MOD(opt->extack, "invalid parent classid");
2998+
return -EINVAL;
2999+
}
3000+
3001+
err = airoha_qdma_set_tx_rate_limit(port, channel, rate, opt->quantum);
3002+
if (err) {
3003+
NL_SET_ERR_MSG_MOD(opt->extack,
3004+
"failed configuring htb offload");
3005+
return err;
3006+
}
3007+
3008+
if (opt->command == TC_HTB_NODE_MODIFY)
3009+
return 0;
3010+
3011+
err = netif_set_real_num_tx_queues(dev, num_tx_queues + 1);
3012+
if (err) {
3013+
airoha_qdma_set_tx_rate_limit(port, channel, 0, opt->quantum);
3014+
NL_SET_ERR_MSG_MOD(opt->extack,
3015+
"failed setting real_num_tx_queues");
3016+
return err;
3017+
}
3018+
3019+
set_bit(channel, port->qos_sq_bmap);
3020+
opt->qid = AIROHA_NUM_TX_RING + channel;
3021+
3022+
return 0;
3023+
}
3024+
3025+
static void airoha_tc_remove_htb_queue(struct airoha_gdm_port *port, int queue)
3026+
{
3027+
struct net_device *dev = port->dev;
3028+
3029+
netif_set_real_num_tx_queues(dev, dev->real_num_tx_queues - 1);
3030+
airoha_qdma_set_tx_rate_limit(port, queue + 1, 0, 0);
3031+
clear_bit(queue, port->qos_sq_bmap);
3032+
}
3033+
3034+
static int airoha_tc_htb_delete_leaf_queue(struct airoha_gdm_port *port,
3035+
struct tc_htb_qopt_offload *opt)
3036+
{
3037+
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
3038+
3039+
if (!test_bit(channel, port->qos_sq_bmap)) {
3040+
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
3041+
return -EINVAL;
3042+
}
3043+
3044+
airoha_tc_remove_htb_queue(port, channel);
3045+
3046+
return 0;
3047+
}
3048+
3049+
static int airoha_tc_htb_destroy(struct airoha_gdm_port *port)
3050+
{
3051+
int q;
3052+
3053+
for_each_set_bit(q, port->qos_sq_bmap, AIROHA_NUM_QOS_CHANNELS)
3054+
airoha_tc_remove_htb_queue(port, q);
3055+
3056+
return 0;
3057+
}
3058+
3059+
static int airoha_tc_get_htb_get_leaf_queue(struct airoha_gdm_port *port,
3060+
struct tc_htb_qopt_offload *opt)
3061+
{
3062+
u32 channel = TC_H_MIN(opt->classid) % AIROHA_NUM_QOS_CHANNELS;
3063+
3064+
if (!test_bit(channel, port->qos_sq_bmap)) {
3065+
NL_SET_ERR_MSG_MOD(opt->extack, "invalid queue id");
3066+
return -EINVAL;
3067+
}
3068+
3069+
opt->qid = channel;
3070+
3071+
return 0;
3072+
}
3073+
3074+
static int airoha_tc_setup_qdisc_htb(struct airoha_gdm_port *port,
3075+
struct tc_htb_qopt_offload *opt)
3076+
{
3077+
switch (opt->command) {
3078+
case TC_HTB_CREATE:
3079+
break;
3080+
case TC_HTB_DESTROY:
3081+
return airoha_tc_htb_destroy(port);
3082+
case TC_HTB_NODE_MODIFY:
3083+
case TC_HTB_LEAF_ALLOC_QUEUE:
3084+
return airoha_tc_htb_alloc_leaf_queue(port, opt);
3085+
case TC_HTB_LEAF_DEL:
3086+
case TC_HTB_LEAF_DEL_LAST:
3087+
case TC_HTB_LEAF_DEL_LAST_FORCE:
3088+
return airoha_tc_htb_delete_leaf_queue(port, opt);
3089+
case TC_HTB_LEAF_QUERY_QUEUE:
3090+
return airoha_tc_get_htb_get_leaf_queue(port, opt);
3091+
default:
3092+
return -EOPNOTSUPP;
3093+
}
3094+
3095+
return 0;
3096+
}
3097+
28203098
static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
28213099
void *type_data)
28223100
{
@@ -2825,6 +3103,8 @@ static int airoha_dev_tc_setup(struct net_device *dev, enum tc_setup_type type,
28253103
switch (type) {
28263104
case TC_SETUP_QDISC_ETS:
28273105
return airoha_tc_setup_qdisc_ets(port, type_data);
3106+
case TC_SETUP_QDISC_HTB:
3107+
return airoha_tc_setup_qdisc_htb(port, type_data);
28283108
default:
28293109
return -EOPNOTSUPP;
28303110
}
@@ -2875,7 +3155,8 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
28753155
}
28763156

28773157
dev = devm_alloc_etherdev_mqs(eth->dev, sizeof(*port),
2878-
AIROHA_NUM_TX_RING, AIROHA_NUM_RX_RING);
3158+
AIROHA_NUM_NETDEV_TX_RINGS,
3159+
AIROHA_NUM_RX_RING);
28793160
if (!dev) {
28803161
dev_err(eth->dev, "alloc_etherdev failed\n");
28813162
return -ENOMEM;
@@ -2895,6 +3176,11 @@ static int airoha_alloc_gdm_port(struct airoha_eth *eth, struct device_node *np)
28953176
dev->irq = qdma->irq;
28963177
SET_NETDEV_DEV(dev, eth->dev);
28973178

3179+
/* reserve hw queues for HTB offloading */
3180+
err = netif_set_real_num_tx_queues(dev, AIROHA_NUM_TX_RING);
3181+
if (err)
3182+
return err;
3183+
28983184
err = of_get_ethdev_address(np, dev);
28993185
if (err) {
29003186
if (err == -EPROBE_DEFER)

0 commit comments

Comments
 (0)