Commit c7ad3657 authored by David S. Miller's avatar David S. Miller

Merge branch 'am65-cpsw-add-taprio-EST-offload-support'

Murali Karicheri says:

====================
am65-cpsw: add taprio/EST offload support

AM65 CPSW h/w supports Enhanced Scheduled Traffic (EST – defined
in P802.1Qbv/D2.2 that later got included in IEEE 802.1Q-2018)
configuration. EST allows express queue traffic to be scheduled
(placed) on the wire at specific repeatable time intervals. In
Linux kernel, EST configuration is done through tc command and
the taprio scheduler in the net core implements a software only
scheduler (SCH_TAPRIO). If the NIC is capable of EST configuration,
user indicate "flag 2" in the command which is then parsed by
taprio scheduler in net core and indicate that the command is to
be offloaded to h/w. taprio then offloads the command to the
driver by calling ndo_setup_tc() ndo ops. This patch implements
ndo_setup_tc() as well as other changes required to offload EST
configuration to CPSW h/w

For more details please refer patch 2/2.

This series is based on original work done by Ivan Khoronzhuk
<ivan.khoronzhuk@linaro.org> to add taprio offload support to
AM65 CPSW 2G.

1. Example configuration 3 Gates

ifconfig eth0 down
ethtool -L eth0 tx 3

ethtool --set-priv-flags eth0 p0-rx-ptype-rrobin off

ifconfig eth0 192.168.2.20

tc qdisc replace dev eth0 parent root handle 100 taprio \
    num_tc 3 \
    map 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 \
    queues 1@0 1@1 1@2 \
    base-time 0000 \
    sched-entry S 4 125000 \
    sched-entry S 2 125000 \
    sched-entry S 1 250000 \
    flags 2

2. Example configuration 8 Gates

ifconfig eth0 down
ethtool -L eth0 tx 8

ethtool --set-priv-flags eth0 p0-rx-ptype-rrobin off

ifconfig eth0 192.168.2.20

tc qdisc replace dev eth0 parent root handle 100 taprio \
    num_tc 8 \
    map 0 1 2 3 4 5 6 7 0 0 0 0 0 0 0 0 \
    queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \
    base-time 0000 \
    sched-entry S 80 125000 \
    sched-entry S 40 125000 \
    sched-entry S 20 125000 \
    sched-entry S 10 125000 \
    sched-entry S 08 125000 \
    sched-entry S 04 125000 \
    sched-entry S 02 125000 \
    sched-entry S 01 125000 \
    flags 2

Classify frames to particular priority using skbedit so that they land at
a specific queue in cpsw h/w which is Gated by the EST gate which opens based
on the sched-entry.

tc qdisc add dev eth0 clsact

In the below for example an iperf3 session with destination port 5007
will go through Q7.

tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5007 0xffff action skbedit priority 7

tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5006 0xffff action skbedit priority 6
tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5005 0xffff action skbedit priority 5
tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5004 0xffff action skbedit priority 4
tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5003 0xffff action skbedit priority 3
tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5002 0xffff action skbedit priority 2
tc filter add dev eth0 egress protocol ip prio 1 u32 match ip dport 5001 0xffff action skbedit priority 1

iperf3 -c 192.168.2.10 -u -l1470 -b32M -t1 -p 5007

Testing was done by capturing frames at the PC using wireshark and checking for
the bust interval or cycle time of UDP frames with a specific port number.
Verified that the distance between first frame of a burst (cycle-time) is 1
milli second and burst duration is within 125 usec based on the received packet
timestamp shown in wireshark packet display.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 86b6ba17 8127224c
...@@ -123,6 +123,15 @@ config TI_K3_AM65_CPTS ...@@ -123,6 +123,15 @@ config TI_K3_AM65_CPTS
protocol, Ethernet Enhanced Scheduled Traffic Operations (CPTS_ESTFn) protocol, Ethernet Enhanced Scheduled Traffic Operations (CPTS_ESTFn)
and PCIe Subsystem Precision Time Measurement (PTM). and PCIe Subsystem Precision Time Measurement (PTM).
config TI_AM65_CPSW_TAS
bool "Enable TAS offload in AM65 CPSW"
depends on TI_K3_AM65_CPSW_NUSS && NET_SCH_TAPRIO && TI_K3_AM65_CPTS
help
Say y here to support Time Aware Shaper(TAS) offload in AM65 CPSW.
AM65 CPSW hardware supports Enhanced Scheduled Traffic (EST)
defined in IEEE 802.1Q 2018. The EST scheduler runs on CPTS and the
TAS/EST schedule is updated in the Fetch RAM memory of the CPSW.
config TI_KEYSTONE_NETCP config TI_KEYSTONE_NETCP
tristate "TI Keystone NETCP Core Support" tristate "TI Keystone NETCP Core Support"
select TI_DAVINCI_MDIO select TI_DAVINCI_MDIO
......
...@@ -25,5 +25,5 @@ obj-$(CONFIG_TI_KEYSTONE_NETCP_ETHSS) += keystone_netcp_ethss.o ...@@ -25,5 +25,5 @@ obj-$(CONFIG_TI_KEYSTONE_NETCP_ETHSS) += keystone_netcp_ethss.o
keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o cpsw_ale.o keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o cpsw_ale.o
obj-$(CONFIG_TI_K3_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o obj-$(CONFIG_TI_K3_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o
ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o cpsw_ale.o k3-cppi-desc-pool.o ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o cpsw_ale.o k3-cppi-desc-pool.o am65-cpsw-qos.o
obj-$(CONFIG_TI_K3_AM65_CPTS) += am65-cpts.o obj-$(CONFIG_TI_K3_AM65_CPTS) += am65-cpts.o
...@@ -730,9 +730,17 @@ static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev) ...@@ -730,9 +730,17 @@ static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev)
static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags) static int am65_cpsw_set_ethtool_priv_flags(struct net_device *ndev, u32 flags)
{ {
struct am65_cpsw_common *common = am65_ndev_to_common(ndev); struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
int rrobin;
common->pf_p0_rx_ptype_rrobin = rrobin = !!(flags & AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN);
!!(flags & AM65_CPSW_PRIV_P0_RX_PTYPE_RROBIN);
if (common->est_enabled && rrobin) {
netdev_err(ndev,
"p0-rx-ptype-rrobin flag conflicts with QOS\n");
return -EINVAL;
}
common->pf_p0_rx_ptype_rrobin = rrobin;
am65_cpsw_nuss_set_p0_ptype(common); am65_cpsw_nuss_set_p0_ptype(common);
return 0; return 0;
......
...@@ -37,12 +37,14 @@ ...@@ -37,12 +37,14 @@
#define AM65_CPSW_XGMII_BASE 0x2100 #define AM65_CPSW_XGMII_BASE 0x2100
#define AM65_CPSW_CPSW_NU_BASE 0x20000 #define AM65_CPSW_CPSW_NU_BASE 0x20000
#define AM65_CPSW_NU_PORTS_BASE 0x1000 #define AM65_CPSW_NU_PORTS_BASE 0x1000
#define AM65_CPSW_NU_FRAM_BASE 0x12000
#define AM65_CPSW_NU_STATS_BASE 0x1a000 #define AM65_CPSW_NU_STATS_BASE 0x1a000
#define AM65_CPSW_NU_ALE_BASE 0x1e000 #define AM65_CPSW_NU_ALE_BASE 0x1e000
#define AM65_CPSW_NU_CPTS_BASE 0x1d000 #define AM65_CPSW_NU_CPTS_BASE 0x1d000
#define AM65_CPSW_NU_PORTS_OFFSET 0x1000 #define AM65_CPSW_NU_PORTS_OFFSET 0x1000
#define AM65_CPSW_NU_STATS_PORT_OFFSET 0x200 #define AM65_CPSW_NU_STATS_PORT_OFFSET 0x200
#define AM65_CPSW_NU_FRAM_PORT_OFFSET 0x200
#define AM65_CPSW_MAX_PORTS 8 #define AM65_CPSW_MAX_PORTS 8
...@@ -188,9 +190,11 @@ void am65_cpsw_nuss_adjust_link(struct net_device *ndev) ...@@ -188,9 +190,11 @@ void am65_cpsw_nuss_adjust_link(struct net_device *ndev)
cpsw_ale_control_set(common->ale, port->port_id, cpsw_ale_control_set(common->ale, port->port_id,
ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); ALE_PORT_STATE, ALE_PORT_STATE_FORWARD);
am65_cpsw_qos_link_up(ndev, phy->speed);
netif_tx_wake_all_queues(ndev); netif_tx_wake_all_queues(ndev);
} else { } else {
int tmo; int tmo;
/* disable forwarding */ /* disable forwarding */
cpsw_ale_control_set(common->ale, port->port_id, cpsw_ale_control_set(common->ale, port->port_id,
ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); ALE_PORT_STATE, ALE_PORT_STATE_DISABLE);
...@@ -204,6 +208,7 @@ void am65_cpsw_nuss_adjust_link(struct net_device *ndev) ...@@ -204,6 +208,7 @@ void am65_cpsw_nuss_adjust_link(struct net_device *ndev)
cpsw_sl_ctl_reset(port->slave.mac_sl); cpsw_sl_ctl_reset(port->slave.mac_sl);
am65_cpsw_qos_link_down(ndev);
netif_tx_stop_all_queues(ndev); netif_tx_stop_all_queues(ndev);
} }
...@@ -1378,6 +1383,7 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops_2g = { ...@@ -1378,6 +1383,7 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops_2g = {
.ndo_vlan_rx_kill_vid = am65_cpsw_nuss_ndo_slave_kill_vid, .ndo_vlan_rx_kill_vid = am65_cpsw_nuss_ndo_slave_kill_vid,
.ndo_do_ioctl = am65_cpsw_nuss_ndo_slave_ioctl, .ndo_do_ioctl = am65_cpsw_nuss_ndo_slave_ioctl,
.ndo_set_features = am65_cpsw_nuss_ndo_slave_set_features, .ndo_set_features = am65_cpsw_nuss_ndo_slave_set_features,
.ndo_setup_tc = am65_cpsw_qos_ndo_setup_tc,
}; };
static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port) static void am65_cpsw_nuss_slave_disable_unused(struct am65_cpsw_port *port)
...@@ -1739,6 +1745,9 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common) ...@@ -1739,6 +1745,9 @@ static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common)
port->stat_base = common->cpsw_base + AM65_CPSW_NU_STATS_BASE + port->stat_base = common->cpsw_base + AM65_CPSW_NU_STATS_BASE +
(AM65_CPSW_NU_STATS_PORT_OFFSET * port_id); (AM65_CPSW_NU_STATS_PORT_OFFSET * port_id);
port->name = of_get_property(port_np, "label", NULL); port->name = of_get_property(port_np, "label", NULL);
port->fetch_ram_base =
common->cpsw_base + AM65_CPSW_NU_FRAM_BASE +
(AM65_CPSW_NU_FRAM_PORT_OFFSET * (port_id - 1));
port->disabled = !of_device_is_available(port_np); port->disabled = !of_device_is_available(port_np);
if (port->disabled) if (port->disabled)
......
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include "am65-cpsw-qos.h"
struct am65_cpts; struct am65_cpts;
...@@ -38,10 +40,12 @@ struct am65_cpsw_port { ...@@ -38,10 +40,12 @@ struct am65_cpsw_port {
u32 port_id; u32 port_id;
void __iomem *port_base; void __iomem *port_base;
void __iomem *stat_base; void __iomem *stat_base;
void __iomem *fetch_ram_base;
bool disabled; bool disabled;
struct am65_cpsw_slave_data slave; struct am65_cpsw_slave_data slave;
bool tx_ts_enabled; bool tx_ts_enabled;
bool rx_ts_enabled; bool rx_ts_enabled;
struct am65_cpsw_qos qos;
}; };
struct am65_cpsw_host { struct am65_cpsw_host {
...@@ -104,6 +108,7 @@ struct am65_cpsw_common { ...@@ -104,6 +108,7 @@ struct am65_cpsw_common {
u32 cpsw_ver; u32 cpsw_ver;
bool pf_p0_rx_ptype_rrobin; bool pf_p0_rx_ptype_rrobin;
struct am65_cpts *cpts; struct am65_cpts *cpts;
int est_enabled;
}; };
struct am65_cpsw_ndev_stats { struct am65_cpsw_ndev_stats {
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
*/
#ifndef AM65_CPSW_QOS_H_
#define AM65_CPSW_QOS_H_
#include <linux/netdevice.h>
#include <net/pkt_sched.h>
struct am65_cpsw_est {
int buf;
/* has to be the last one */
struct tc_taprio_qopt_offload taprio;
};
struct am65_cpsw_qos {
struct am65_cpsw_est *est_admin;
struct am65_cpsw_est *est_oper;
ktime_t link_down_time;
int link_speed;
};
int am65_cpsw_qos_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type,
void *type_data);
void am65_cpsw_qos_link_up(struct net_device *ndev, int link_speed);
void am65_cpsw_qos_link_down(struct net_device *ndev);
#endif /* AM65_CPSW_QOS_H_ */
...@@ -450,6 +450,19 @@ static int am65_cpts_ptp_gettimex(struct ptp_clock_info *ptp, ...@@ -450,6 +450,19 @@ static int am65_cpts_ptp_gettimex(struct ptp_clock_info *ptp,
return 0; return 0;
} }
u64 am65_cpts_ns_gettime(struct am65_cpts *cpts)
{
u64 ns;
/* reuse ptp_clk_lock as it serialize ts push */
mutex_lock(&cpts->ptp_clk_lock);
ns = am65_cpts_gettime(cpts, NULL);
mutex_unlock(&cpts->ptp_clk_lock);
return ns;
}
EXPORT_SYMBOL_GPL(am65_cpts_ns_gettime);
static int am65_cpts_ptp_settime(struct ptp_clock_info *ptp, static int am65_cpts_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts) const struct timespec64 *ts)
{ {
...@@ -494,6 +507,41 @@ static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on) ...@@ -494,6 +507,41 @@ static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on)
return 0; return 0;
} }
int am65_cpts_estf_enable(struct am65_cpts *cpts, int idx,
struct am65_cpts_estf_cfg *cfg)
{
u64 cycles;
u32 val;
cycles = cfg->ns_period * cpts->refclk_freq;
cycles = DIV_ROUND_UP(cycles, NSEC_PER_SEC);
if (cycles > U32_MAX)
return -EINVAL;
/* according to TRM should be zeroed */
am65_cpts_write32(cpts, 0, estf[idx].length);
val = upper_32_bits(cfg->ns_start);
am65_cpts_write32(cpts, val, estf[idx].comp_hi);
val = lower_32_bits(cfg->ns_start);
am65_cpts_write32(cpts, val, estf[idx].comp_lo);
val = lower_32_bits(cycles);
am65_cpts_write32(cpts, val, estf[idx].length);
dev_dbg(cpts->dev, "%s: ESTF:%u enabled\n", __func__, idx);
return 0;
}
EXPORT_SYMBOL_GPL(am65_cpts_estf_enable);
void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx)
{
am65_cpts_write32(cpts, 0, estf[idx].length);
dev_dbg(cpts->dev, "%s: ESTF:%u disabled\n", __func__, idx);
}
EXPORT_SYMBOL_GPL(am65_cpts_estf_disable);
static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts, static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts,
struct ptp_perout_request *req, int on) struct ptp_perout_request *req, int on)
{ {
......
...@@ -12,6 +12,11 @@ ...@@ -12,6 +12,11 @@
struct am65_cpts; struct am65_cpts;
struct am65_cpts_estf_cfg {
u64 ns_period;
u64 ns_start;
};
#if IS_ENABLED(CONFIG_TI_K3_AM65_CPTS) #if IS_ENABLED(CONFIG_TI_K3_AM65_CPTS)
struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs, struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
struct device_node *node); struct device_node *node);
...@@ -19,6 +24,10 @@ int am65_cpts_phc_index(struct am65_cpts *cpts); ...@@ -19,6 +24,10 @@ int am65_cpts_phc_index(struct am65_cpts *cpts);
void am65_cpts_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb); void am65_cpts_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb);
void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb); void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb);
void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en); void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en);
u64 am65_cpts_ns_gettime(struct am65_cpts *cpts);
int am65_cpts_estf_enable(struct am65_cpts *cpts, int idx,
struct am65_cpts_estf_cfg *cfg);
void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx);
#else #else
static inline struct am65_cpts *am65_cpts_create(struct device *dev, static inline struct am65_cpts *am65_cpts_create(struct device *dev,
void __iomem *regs, void __iomem *regs,
...@@ -45,6 +54,21 @@ static inline void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts, ...@@ -45,6 +54,21 @@ static inline void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts,
static inline void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en) static inline void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en)
{ {
} }
static s64 am65_cpts_ns_gettime(struct am65_cpts *cpts)
{
return 0;
}
static int am65_cpts_estf_enable(struct am65_cpts *cpts,
int idx, struct am65_cpts_estf_cfg *cfg)
{
return 0;
}
static void am65_cpts_estf_disable(struct am65_cpts *cpts, int idx)
{
}
#endif #endif
#endif /* K3_CPTS_H_ */ #endif /* K3_CPTS_H_ */
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