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:
parent
bec170e559
commit
c431047c4e
|
@ -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).
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue