Commit c431047c authored by Po Liu's avatar Po Liu Committed by David S. Miller

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: default avatarPo Liu <Po.Liu@nxp.com>
Reviewed-by: default avatarClaudiu Manoil <claudiu.manoil@nxp.com>
Reviewed-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bec170e5
...@@ -53,10 +53,10 @@ config FSL_ENETC_HW_TIMESTAMPING ...@@ -53,10 +53,10 @@ config FSL_ENETC_HW_TIMESTAMPING
config FSL_ENETC_QOS config FSL_ENETC_QOS
bool "ENETC hardware Time-sensitive Network support" 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 help
There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
/802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set /802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
enable/disable from user space via Qos commands(tc). In the kernel 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 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, ...@@ -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); return enetc_setup_tc_mqprio(ndev, type_data);
case TC_SETUP_QDISC_TAPRIO: case TC_SETUP_QDISC_TAPRIO:
return enetc_setup_tc_taprio(ndev, type_data); return enetc_setup_tc_taprio(ndev, type_data);
case TC_SETUP_QDISC_CBS:
return enetc_setup_tc_cbs(ndev, type_data);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -255,7 +255,9 @@ int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd); ...@@ -255,7 +255,9 @@ int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
#ifdef CONFIG_FSL_ENETC_QOS #ifdef CONFIG_FSL_ENETC_QOS
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
void enetc_sched_speed_set(struct net_device *ndev); void enetc_sched_speed_set(struct net_device *ndev);
int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data);
#else #else
#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP #define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
#define enetc_sched_speed_set(ndev) (void)0 #define enetc_sched_speed_set(ndev) (void)0
#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
#endif #endif
...@@ -185,6 +185,8 @@ enum enetc_bdr_type {TX, RX}; ...@@ -185,6 +185,8 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PSICFGR0_SIVC(bmp) (((bmp) & 0xff) << 24) /* VLAN_TYPE */ #define ENETC_PSICFGR0_SIVC(bmp) (((bmp) & 0xff) << 24) /* VLAN_TYPE */
#define ENETC_PTCCBSR0(n) (0x1110 + (n) * 8) /* n = 0 to 7*/ #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_PTCCBSR1(n) (0x1114 + (n) * 8) /* n = 0 to 7*/
#define ENETC_RSSHASH_KEY_SIZE 40 #define ENETC_RSSHASH_KEY_SIZE 40
#define ENETC_PRSSK(n) (0x1410 + (n) * 4) /* n = [0..9] */ #define ENETC_PRSSK(n) (0x1410 + (n) * 4) /* n = [0..9] */
...@@ -603,6 +605,8 @@ struct enetc_cbd { ...@@ -603,6 +605,8 @@ struct enetc_cbd {
u8 status_flags; u8 status_flags;
}; };
#define ENETC_CLK 400000000ULL
/* port time gating control register */ /* port time gating control register */
#define ENETC_QBV_PTGCR_OFFSET 0x11a00 #define ENETC_QBV_PTGCR_OFFSET 0x11a00
#define ENETC_QBV_TGE BIT(31) #define ENETC_QBV_TGE BIT(31)
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "enetc.h" #include "enetc.h"
#include <net/pkt_sched.h> #include <net/pkt_sched.h>
#include <linux/math64.h>
static u16 enetc_get_max_gcl_len(struct enetc_hw *hw) 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) ...@@ -170,3 +171,130 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)
return err; 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment