enetc: add support Credit Based Shaper(CBS) for hardware offload

The ENETC hardware support the Credit Based Shaper(CBS) which part
of the IEEE-802.1Qav. The CBS driver was loaded by the sch_cbs
interface when set in the QOS in the kernel.

Here is an example command to set 20Mbits bandwidth in 1Gbits port
for taffic class 7:

tc qdisc add dev eth0 root handle 1: mqprio \
	   num_tc 8 map 0 1 2 3 4 5 6 7 hw 1

tc qdisc replace dev eth0 parent 1:8 cbs \
	   locredit -1470 hicredit 30 \
	   sendslope -980000 idleslope 20000 offload 1

Signed-off-by: Po Liu <Po.Liu@nxp.com>
Reviewed-by: Claudiu Manoil <claudiu.manoil@nxp.com>
Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Po Liu 2019-11-25 05:56:56 +00:00 committed by David S. Miller
parent bec170e559
commit c431047c4e
5 changed files with 138 additions and 2 deletions

View file

@ -53,10 +53,10 @@ config FSL_ENETC_HW_TIMESTAMPING
config FSL_ENETC_QOS
bool "ENETC hardware Time-sensitive Network support"
depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO
depends on (FSL_ENETC || FSL_ENETC_VF) && (NET_SCH_TAPRIO || NET_SCH_CBS)
help
There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
/802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
enable/disable from user space via Qos commands(tc). In the kernel
side, it can be loaded by Qos driver. Currently, it is only support
taprio(802.1Qbv).
taprio(802.1Qbv) and Credit Based Shaper(802.1Qbu).

View file

@ -1496,6 +1496,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
return enetc_setup_tc_mqprio(ndev, type_data);
case TC_SETUP_QDISC_TAPRIO:
return enetc_setup_tc_taprio(ndev, type_data);
case TC_SETUP_QDISC_CBS:
return enetc_setup_tc_cbs(ndev, type_data);
default:
return -EOPNOTSUPP;
}

View file

@ -255,7 +255,9 @@ int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
#ifdef CONFIG_FSL_ENETC_QOS
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
void enetc_sched_speed_set(struct net_device *ndev);
int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data);
#else
#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
#define enetc_sched_speed_set(ndev) (void)0
#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
#endif

View file

@ -185,6 +185,8 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PSICFGR0_SIVC(bmp) (((bmp) & 0xff) << 24) /* VLAN_TYPE */
#define ENETC_PTCCBSR0(n) (0x1110 + (n) * 8) /* n = 0 to 7*/
#define ENETC_CBSE BIT(31)
#define ENETC_CBS_BW_MASK GENMASK(6, 0)
#define ENETC_PTCCBSR1(n) (0x1114 + (n) * 8) /* n = 0 to 7*/
#define ENETC_RSSHASH_KEY_SIZE 40
#define ENETC_PRSSK(n) (0x1410 + (n) * 4) /* n = [0..9] */
@ -603,6 +605,8 @@ struct enetc_cbd {
u8 status_flags;
};
#define ENETC_CLK 400000000ULL
/* port time gating control register */
#define ENETC_QBV_PTGCR_OFFSET 0x11a00
#define ENETC_QBV_TGE BIT(31)

View file

@ -4,6 +4,7 @@
#include "enetc.h"
#include <net/pkt_sched.h>
#include <linux/math64.h>
static u16 enetc_get_max_gcl_len(struct enetc_hw *hw)
{
@ -170,3 +171,130 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)
return err;
}
static u32 enetc_get_cbs_enable(struct enetc_hw *hw, u8 tc)
{
return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBSE;
}
static u8 enetc_get_cbs_bw(struct enetc_hw *hw, u8 tc)
{
return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBS_BW_MASK;
}
int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct tc_cbs_qopt_offload *cbs = type_data;
u32 port_transmit_rate = priv->speed;
u8 tc_nums = netdev_get_num_tc(ndev);
struct enetc_si *si = priv->si;
u32 hi_credit_bit, hi_credit_reg;
u32 max_interference_size;
u32 port_frame_max_size;
u32 tc_max_sized_frame;
u8 tc = cbs->queue;
u8 prio_top, prio_next;
int bw_sum = 0;
u8 bw;
prio_top = netdev_get_prio_tc_map(ndev, tc_nums - 1);
prio_next = netdev_get_prio_tc_map(ndev, tc_nums - 2);
/* Support highest prio and second prio tc in cbs mode */
if (tc != prio_top && tc != prio_next)
return -EOPNOTSUPP;
if (!cbs->enable) {
/* Make sure the other TC that are numerically
* lower than this TC have been disabled.
*/
if (tc == prio_top &&
enetc_get_cbs_enable(&si->hw, prio_next)) {
dev_err(&ndev->dev,
"Disable TC%d before disable TC%d\n",
prio_next, tc);
return -EINVAL;
}
enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), 0);
enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), 0);
return 0;
}
if (cbs->idleslope - cbs->sendslope != port_transmit_rate * 1000L ||
cbs->idleslope < 0 || cbs->sendslope > 0)
return -EOPNOTSUPP;
port_frame_max_size = ndev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;
bw = cbs->idleslope / (port_transmit_rate * 10UL);
/* Make sure the other TC that are numerically
* higher than this TC have been enabled.
*/
if (tc == prio_next) {
if (!enetc_get_cbs_enable(&si->hw, prio_top)) {
dev_err(&ndev->dev,
"Enable TC%d first before enable TC%d\n",
prio_top, prio_next);
return -EINVAL;
}
bw_sum += enetc_get_cbs_bw(&si->hw, prio_top);
}
if (bw_sum + bw >= 100) {
dev_err(&ndev->dev,
"The sum of all CBS Bandwidth can't exceed 100\n");
return -EINVAL;
}
tc_max_sized_frame = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(tc));
/* For top prio TC, the max_interfrence_size is maxSizedFrame.
*
* For next prio TC, the max_interfrence_size is calculated as below:
*
* max_interference_size = M0 + Ma + Ra * M0 / (R0 - Ra)
*
* - RA: idleSlope for AVB Class A
* - R0: port transmit rate
* - M0: maximum sized frame for the port
* - MA: maximum sized frame for AVB Class A
*/
if (tc == prio_top) {
max_interference_size = port_frame_max_size * 8;
} else {
u32 m0, ma, r0, ra;
m0 = port_frame_max_size * 8;
ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8;
ra = enetc_get_cbs_bw(&si->hw, prio_top) *
port_transmit_rate * 10000ULL;
r0 = port_transmit_rate * 1000000ULL;
max_interference_size = m0 + ma +
(u32)div_u64((u64)ra * m0, r0 - ra);
}
/* hiCredit bits calculate by:
*
* maxSizedFrame * (idleSlope/portTxRate)
*/
hi_credit_bit = max_interference_size * bw / 100;
/* hiCredit bits to hiCredit register need to calculated as:
*
* (enetClockFrequency / portTransmitRate) * 100
*/
hi_credit_reg = (u32)div_u64((ENETC_CLK * 100ULL) * hi_credit_bit,
port_transmit_rate * 1000000ULL);
enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), hi_credit_reg);
/* Set bw register and enable this traffic class */
enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE);
return 0;
}