net: ethernet: ti: am65-cpsw-nuss: enable packet timestamping support
authorGrygorii Strashko <grygorii.strashko@ti.com>
Fri, 1 May 2020 20:50:07 +0000 (23:50 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 May 2020 19:02:03 +0000 (12:02 -0700)
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>
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/am65-cpsw-ethtool.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h

index 2b7590c..69b6462 100644 (file)
@@ -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
index c3502aa..23661a6 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "am65-cpsw-nuss.h"
 #include "cpsw_ale.h"
+#include "am65-cpts.h"
 
 #define AM65_CPSW_REGDUMP_VER 0x1
 
@@ -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);
@@ -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,
 
index 2bf5673..bb39128 100644 (file)
@@ -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
@@ -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)
@@ -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);
 
@@ -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);
@@ -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);
 
@@ -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)
 {
@@ -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;
 
@@ -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;
@@ -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]);
index 41ae5b4..b1cddfd 100644 (file)
@@ -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
@@ -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 {
@@ -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 {