Skip to content

Commit

Permalink
net: ethernet: ti: am65-cpsw-nuss: enable packet timestamping support
Browse files Browse the repository at this point in the history
The MCU CPSW Common Platform Time Sync (CPTS) provides possibility to
timestamp TX PTP packets and all RX packets.

This enables corresponding support in TI AM65x/J721E MCU CPSW driver.

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
grygoriyS authored and davem330 committed May 4, 2020
1 parent f6bd595 commit b1f66a5
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 2 deletions.
1 change: 1 addition & 0 deletions drivers/net/ethernet/ti/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ config TI_K3_AM65_CPSW_NUSS
depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
select TI_DAVINCI_MDIO
imply PHY_TI_GMII_SEL
imply TI_AM65_CPTS
help
This driver supports TI K3 AM654/J721E CPSW2G Ethernet SubSystem.
The two-port Gigabit Ethernet MAC (MCU_CPSW0) subsystem provides
Expand Down
24 changes: 23 additions & 1 deletion drivers/net/ethernet/ti/am65-cpsw-ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "am65-cpsw-nuss.h"
#include "cpsw_ale.h"
#include "am65-cpts.h"

#define AM65_CPSW_REGDUMP_VER 0x1

Expand Down Expand Up @@ -694,6 +695,27 @@ static void am65_cpsw_get_ethtool_stats(struct net_device *ndev,
hw_stats[i].offset);
}

static int am65_cpsw_get_ethtool_ts_info(struct net_device *ndev,
struct ethtool_ts_info *info)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);

if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
return ethtool_op_get_ts_info(ndev, info);

info->so_timestamping =
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->phc_index = am65_cpts_phc_index(common->cpts);
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
return 0;
}

static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
Expand Down Expand Up @@ -730,7 +752,7 @@ const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
.get_sset_count = am65_cpsw_get_sset_count,
.get_strings = am65_cpsw_get_strings,
.get_ethtool_stats = am65_cpsw_get_ethtool_stats,
.get_ts_info = ethtool_op_get_ts_info,
.get_ts_info = am65_cpsw_get_ethtool_ts_info,
.get_priv_flags = am65_cpsw_get_ethtool_priv_flags,
.set_priv_flags = am65_cpsw_set_ethtool_priv_flags,

Expand Down
172 changes: 172 additions & 0 deletions drivers/net/ethernet/ti/am65-cpsw-nuss.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "cpsw_sl.h"
#include "am65-cpsw-nuss.h"
#include "k3-cppi-desc-pool.h"
#include "am65-cpts.h"

#define AM65_CPSW_SS_BASE 0x0
#define AM65_CPSW_SGMII_BASE 0x100
Expand Down Expand Up @@ -668,6 +669,18 @@ static void am65_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma)
dev_kfree_skb_any(skb);
}

static void am65_cpsw_nuss_rx_ts(struct sk_buff *skb, u32 *psdata)
{
struct skb_shared_hwtstamps *ssh;
u64 ns;

ns = ((u64)psdata[1] << 32) | psdata[0];

ssh = skb_hwtstamps(skb);
memset(ssh, 0, sizeof(*ssh));
ssh->hwtstamp = ns_to_ktime(ns);
}

/* RX psdata[2] word format - checksum information */
#define AM65_CPSW_RX_PSD_CSUM_ADD GENMASK(15, 0)
#define AM65_CPSW_RX_PSD_CSUM_ERR BIT(16)
Expand Down Expand Up @@ -745,6 +758,9 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common,
skb->dev = ndev;

psdata = cppi5_hdesc_get_psdata(desc_rx);
/* add RX timestamp */
if (port->rx_ts_enabled)
am65_cpsw_nuss_rx_ts(skb, psdata);
csum_info = psdata[2];
dev_dbg(dev, "%s rx csum_info:%#x\n", __func__, csum_info);

Expand Down Expand Up @@ -904,6 +920,8 @@ static int am65_cpsw_nuss_tx_compl_packets(struct am65_cpsw_common *common,

ndev = skb->dev;

am65_cpts_tx_timestamp(common->cpts, skb);

ndev_priv = netdev_priv(ndev);
stats = this_cpu_ptr(ndev_priv->stats);
u64_stats_update_begin(&stats->syncp);
Expand Down Expand Up @@ -995,6 +1013,10 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
/* padding enabled in hw */
pkt_len = skb_headlen(skb);

/* SKB TX timestamp */
if (port->tx_ts_enabled)
am65_cpts_prep_tx_timestamp(common->cpts, skb);

q_idx = skb_get_queue_mapping(skb);
dev_dbg(dev, "%s skb_queue:%d\n", __func__, q_idx);

Expand Down Expand Up @@ -1158,6 +1180,111 @@ static int am65_cpsw_nuss_ndo_slave_set_mac_address(struct net_device *ndev,
return 0;
}

static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
struct ifreq *ifr)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
u32 ts_ctrl, seq_id, ts_ctrl_ltype2, ts_vlan_ltype;
struct hwtstamp_config cfg;

if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
return -EOPNOTSUPP;

if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;

/* TX HW timestamp */
switch (cfg.tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
break;
default:
return -ERANGE;
}

switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
port->rx_ts_enabled = false;
break;
case HWTSTAMP_FILTER_ALL:
case HWTSTAMP_FILTER_SOME:
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
port->rx_ts_enabled = true;
cfg.rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}

port->tx_ts_enabled = (cfg.tx_type == HWTSTAMP_TX_ON);

/* cfg TX timestamp */
seq_id = (AM65_CPSW_TS_SEQ_ID_OFFSET <<
AM65_CPSW_PN_TS_SEQ_ID_OFFSET_SHIFT) | ETH_P_1588;

ts_vlan_ltype = ETH_P_8021Q;

ts_ctrl_ltype2 = ETH_P_1588 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_107 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_129 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_130 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_131 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_132 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_319 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_320 |
AM65_CPSW_PN_TS_CTL_LTYPE2_TS_TTL_NONZERO;

ts_ctrl = AM65_CPSW_TS_EVENT_MSG_TYPE_BITS <<
AM65_CPSW_PN_TS_CTL_MSG_TYPE_EN_SHIFT;

if (port->tx_ts_enabled)
ts_ctrl |= AM65_CPSW_TS_TX_ANX_ALL_EN |
AM65_CPSW_PN_TS_CTL_TX_VLAN_LT1_EN;

writel(seq_id, port->port_base + AM65_CPSW_PORTN_REG_TS_SEQ_LTYPE_REG);
writel(ts_vlan_ltype, port->port_base +
AM65_CPSW_PORTN_REG_TS_VLAN_LTYPE_REG);
writel(ts_ctrl_ltype2, port->port_base +
AM65_CPSW_PORTN_REG_TS_CTL_LTYPE2);
writel(ts_ctrl, port->port_base + AM65_CPSW_PORTN_REG_TS_CTL);

/* en/dis RX timestamp */
am65_cpts_rx_enable(common->cpts, port->rx_ts_enabled);

return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
}

static int am65_cpsw_nuss_hwtstamp_get(struct net_device *ndev,
struct ifreq *ifr)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
struct hwtstamp_config cfg;

if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
return -EOPNOTSUPP;

cfg.flags = 0;
cfg.tx_type = port->tx_ts_enabled ?
HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
cfg.rx_filter = port->rx_ts_enabled ?
HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;

return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
}

static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
struct ifreq *req, int cmd)
{
Expand All @@ -1166,6 +1293,13 @@ static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
if (!netif_running(ndev))
return -EINVAL;

switch (cmd) {
case SIOCSHWTSTAMP:
return am65_cpsw_nuss_hwtstamp_set(ndev, req);
case SIOCGHWTSTAMP:
return am65_cpsw_nuss_hwtstamp_get(ndev, req);
}

if (!port->slave.phy)
return -EOPNOTSUPP;

Expand Down Expand Up @@ -1531,6 +1665,40 @@ static int am65_cpsw_am654_get_efuse_macid(struct device_node *of_node,
return 0;
}

static int am65_cpsw_init_cpts(struct am65_cpsw_common *common)
{
struct device *dev = common->dev;
struct device_node *node;
struct am65_cpts *cpts;
void __iomem *reg_base;

if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
return 0;

node = of_get_child_by_name(dev->of_node, "cpts");
if (!node) {
dev_err(dev, "%s cpts not found\n", __func__);
return -ENOENT;
}

reg_base = common->cpsw_base + AM65_CPSW_NU_CPTS_BASE;
cpts = am65_cpts_create(dev, reg_base, node);
if (IS_ERR(cpts)) {
int ret = PTR_ERR(cpts);

if (ret == -EOPNOTSUPP) {
dev_info(dev, "cpts disabled\n");
return 0;
}

dev_err(dev, "cpts create err %d\n", ret);
return ret;
}
common->cpts = cpts;

return 0;
}

static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common)
{
struct device_node *node, *port_np;
Expand Down Expand Up @@ -1899,6 +2067,10 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
goto err_of_clear;
}

ret = am65_cpsw_init_cpts(common);
if (ret)
goto err_of_clear;

/* init ports */
for (i = 0; i < common->port_num; i++)
am65_cpsw_nuss_slave_disable_unused(&common->ports[i]);
Expand Down
6 changes: 5 additions & 1 deletion drivers/net/ethernet/ti/am65-cpsw-nuss.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <linux/module.h>
#include <linux/netdevice.h>

struct am65_cpts;

#define HOST_PORT_NUM 0

#define AM65_CPSW_MAX_TX_QUEUES 8
Expand Down Expand Up @@ -37,6 +39,8 @@ struct am65_cpsw_port {
void __iomem *stat_base;
bool disabled;
struct am65_cpsw_slave_data slave;
bool tx_ts_enabled;
bool rx_ts_enabled;
};

struct am65_cpsw_host {
Expand Down Expand Up @@ -96,8 +100,8 @@ struct am65_cpsw_common {

u32 nuss_ver;
u32 cpsw_ver;

bool pf_p0_rx_ptype_rrobin;
struct am65_cpts *cpts;
};

struct am65_cpsw_ndev_stats {
Expand Down

0 comments on commit b1f66a5

Please sign in to comment.