diff options
author | Atul Gupta <atul.gupta@chelsio.com> | 2017-07-04 16:46:20 +0530 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-07-05 09:21:54 +0100 |
commit | a456950445a075f5c28a331474dc71e4133ccd3b (patch) | |
tree | 5e15c378866512fb0d355e71316c67ae0fceb93e /drivers | |
parent | 97d731d81053e896ead376fd726ed8aee5932bdd (diff) |
cxgb4: time stamping interface for PTP
Supports hardware and software time stamping via the
Linux SO_TIMESTAMPING socket option.
Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: Atul Gupta <atul.gupta@chelsio.com>
Signed-off-by: Ganesh Goudar <ganeshgr@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 76 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c | 194 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.h | 74 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/sge.c | 173 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4_msg.h | 28 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h | 50 |
9 files changed, 595 insertions, 13 deletions
diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile index c6b71f656992..817212702f0a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/Makefile +++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_CHELSIO_T4) += cxgb4.o -cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o +cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4_uld.o sched.o cxgb4_filter.o cxgb4_tc_u32.o cxgb4_ptp.o cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 451c138ff6ea..dd6e5a35c910 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -48,6 +48,8 @@ #include <linux/vmalloc.h> #include <linux/etherdevice.h> #include <linux/net_tstamp.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/ptp_classify.h> #include <asm/io.h> #include "t4_chip_type.h" #include "cxgb4_uld.h" @@ -510,6 +512,7 @@ struct port_info { #endif /* CONFIG_CHELSIO_T4_FCOE */ bool rxtstamp; /* Enable TS */ struct hwtstamp_config tstamp_config; + bool ptp_enable; struct sched_table *sched_tbl; }; @@ -705,6 +708,7 @@ struct sge_uld_txq_info { struct sge { struct sge_eth_txq ethtxq[MAX_ETH_QSETS]; + struct sge_eth_txq ptptxq; struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES]; struct sge_eth_rxq ethrxq[MAX_ETH_QSETS]; @@ -869,6 +873,11 @@ struct adapter { * used for all 4 filters. */ + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + struct sk_buff *ptp_tx_skb; + /* ptp lock */ + spinlock_t ptp_lock; spinlock_t stats_lock; spinlock_t win0_lock ____cacheline_aligned_in_smp; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index f41507e040da..584aa606d1f7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -79,6 +79,7 @@ #include "l2t.h" #include "sched.h" #include "cxgb4_tc_u32.h" +#include "cxgb4_ptp.h" char cxgb4_driver_name[] = KBUILD_MODNAME; @@ -872,6 +873,14 @@ static int setup_sge_queues(struct adapter *adap) goto freeout; } + if (!is_t4(adap->params.chip)) { + err = t4_sge_alloc_eth_txq(adap, &s->ptptxq, adap->port[0], + netdev_get_tx_queue(adap->port[0], 0) + , s->fw_evtq.cntxt_id); + if (err) + goto freeout; + } + t4_write_reg(adap, is_t4(adap->params.chip) ? MPS_TRC_RSS_CONTROL_A : MPS_T5_TRC_RSS_CONTROL_A, @@ -2438,6 +2447,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) unsigned int mbox; int ret = 0, prtad, devad; struct port_info *pi = netdev_priv(dev); + struct adapter *adapter = pi->adapter; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&req->ifr_data; switch (cmd) { @@ -2475,18 +2485,69 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) sizeof(pi->tstamp_config))) return -EFAULT; - switch (pi->tstamp_config.rx_filter) { - case HWTSTAMP_FILTER_NONE: + if (!is_t4(adapter->params.chip)) { + switch (pi->tstamp_config.tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + switch (pi->tstamp_config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + pi->rxtstamp = false; + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + cxgb4_ptprx_timestamping(pi, pi->port_id, + PTP_TS_L4); + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + cxgb4_ptprx_timestamping(pi, pi->port_id, + PTP_TS_L2_L4); + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + pi->rxtstamp = true; + break; + default: + pi->tstamp_config.rx_filter = + HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + if ((pi->tstamp_config.tx_type == HWTSTAMP_TX_OFF) && + (pi->tstamp_config.rx_filter == + HWTSTAMP_FILTER_NONE)) { + if (cxgb4_ptp_txtype(adapter, pi->port_id) >= 0) + pi->ptp_enable = false; + } + + if (pi->tstamp_config.rx_filter != + HWTSTAMP_FILTER_NONE) { + if (cxgb4_ptp_redirect_rx_packet(adapter, + pi) >= 0) + pi->ptp_enable = true; + } + } else { + /* For T4 Adapters */ + switch (pi->tstamp_config.rx_filter) { + case HWTSTAMP_FILTER_NONE: pi->rxtstamp = false; break; - case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_ALL: pi->rxtstamp = true; break; - default: - pi->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + default: + pi->tstamp_config.rx_filter = + HWTSTAMP_FILTER_NONE; return -ERANGE; + } } - return copy_to_user(req->ifr_data, &pi->tstamp_config, sizeof(pi->tstamp_config)) ? -EFAULT : 0; @@ -4240,6 +4301,9 @@ static void cfg_queues(struct adapter *adap) for (i = 0; i < ARRAY_SIZE(s->ctrlq); i++) s->ctrlq[i].q.size = 512; + if (!is_t4(adap->params.chip)) + s->ptptxq.q.size = 8; + init_rspq(adap, &s->fw_evtq, 0, 1, 1024, 64); init_rspq(adap, &s->intrq, 0, 1, 512, 64); } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c new file mode 100644 index 000000000000..0efcbad2d2d6 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c @@ -0,0 +1,194 @@ +/* + * cxgb4_ptp.c:Chelsio PTP support for T5/T6 + * + * Copyright (c) 2003-2017 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Written by: Atul Gupta (atul.gupta@chelsio.com) + */ + +#include <linux/module.h> +#include <linux/net_tstamp.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/pps_kernel.h> +#include <linux/ptp_clock_kernel.h> +#include <linux/ptp_classify.h> +#include <linux/udp.h> + +#include "cxgb4.h" +#include "t4_hw.h" +#include "t4_regs.h" +#include "t4_msg.h" +#include "t4fw_api.h" +#include "cxgb4_ptp.h" + +/** + * cxgb4_ptp_is_ptp_tx - determine whether TX packet is PTP or not + * @skb: skb of outgoing ptp request + * + */ +bool cxgb4_ptp_is_ptp_tx(struct sk_buff *skb) +{ + struct udphdr *uh; + + uh = udp_hdr(skb); + return skb->len >= PTP_MIN_LENGTH && + skb->len <= PTP_IN_TRANSMIT_PACKET_MAXNUM && + likely(skb->protocol == htons(ETH_P_IP)) && + ip_hdr(skb)->protocol == IPPROTO_UDP && + uh->dest == htons(PTP_EVENT_PORT); +} + +bool is_ptp_enabled(struct sk_buff *skb, struct net_device *dev) +{ + struct port_info *pi; + + pi = netdev_priv(dev); + return (pi->ptp_enable && cxgb4_xmit_with_hwtstamp(skb) && + cxgb4_ptp_is_ptp_tx(skb)); +} + +/** + * cxgb4_ptp_is_ptp_rx - determine whether RX packet is PTP or not + * @skb: skb of incoming ptp request + * + */ +bool cxgb4_ptp_is_ptp_rx(struct sk_buff *skb) +{ + struct udphdr *uh = (struct udphdr *)(skb->data + ETH_HLEN + + IPV4_HLEN(skb->data)); + + return uh->dest == htons(PTP_EVENT_PORT) && + uh->source == htons(PTP_EVENT_PORT); +} + +/** + * cxgb4_ptp_read_hwstamp - read timestamp for TX event PTP message + * @adapter: board private structure + * @pi: port private structure + * + */ +void cxgb4_ptp_read_hwstamp(struct adapter *adapter, struct port_info *pi) +{ + struct skb_shared_hwtstamps *skb_ts = NULL; + u64 tx_ts; + + skb_ts = skb_hwtstamps(adapter->ptp_tx_skb); + + tx_ts = t4_read_reg(adapter, + T5_PORT_REG(pi->port_id, MAC_PORT_TX_TS_VAL_LO)); + + tx_ts |= (u64)t4_read_reg(adapter, + T5_PORT_REG(pi->port_id, + MAC_PORT_TX_TS_VAL_HI)) << 32; + skb_ts->hwtstamp = ns_to_ktime(tx_ts); + skb_tstamp_tx(adapter->ptp_tx_skb, skb_ts); + dev_kfree_skb_any(adapter->ptp_tx_skb); + spin_lock(&adapter->ptp_lock); + adapter->ptp_tx_skb = NULL; + spin_unlock(&adapter->ptp_lock); +} + +/** + * cxgb4_ptprx_timestamping - Enable Timestamp for RX PTP event message + * @pi: port private structure + * @port: pot number + * @mode: RX mode + * + */ +int cxgb4_ptprx_timestamping(struct port_info *pi, u8 port, u16 mode) +{ + struct adapter *adapter = pi->adapter; + struct fw_ptp_cmd c; + int err; + + memset(&c, 0, sizeof(c)); + c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_WRITE_F | + FW_PTP_CMD_PORTID_V(port)); + c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); + c.u.init.sc = FW_PTP_SC_RXTIME_STAMP; + c.u.init.mode = cpu_to_be16(mode); + + err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); + if (err < 0) + dev_err(adapter->pdev_dev, + "PTP: %s error %d\n", __func__, -err); + return err; +} + +int cxgb4_ptp_txtype(struct adapter *adapter, u8 port) +{ + struct fw_ptp_cmd c; + int err; + + memset(&c, 0, sizeof(c)); + c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_WRITE_F | + FW_PTP_CMD_PORTID_V(port)); + c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); + c.u.init.sc = FW_PTP_SC_TX_TYPE; + c.u.init.mode = cpu_to_be16(PTP_TS_NONE); + + err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); + if (err < 0) + dev_err(adapter->pdev_dev, + "PTP: %s error %d\n", __func__, -err); + + return err; +} + +int cxgb4_ptp_redirect_rx_packet(struct adapter *adapter, struct port_info *pi) +{ + struct sge *s = &adapter->sge; + struct sge_eth_rxq *receive_q = &s->ethrxq[pi->first_qset]; + struct fw_ptp_cmd c; + int err; + + memset(&c, 0, sizeof(c)); + c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_WRITE_F | + FW_PTP_CMD_PORTID_V(pi->port_id)); + + c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); + c.u.init.sc = FW_PTP_SC_RDRX_TYPE; + c.u.init.txchan = pi->tx_chan; + c.u.init.absid = cpu_to_be16(receive_q->rspq.abs_id); + + err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); + if (err < 0) + dev_err(adapter->pdev_dev, + "PTP: %s error %d\n", __func__, -err); + return err; +} diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.h new file mode 100644 index 000000000000..cccfae84bb84 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.h @@ -0,0 +1,74 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2017 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __CXGB4_PTP_H__ +#define __CXGB4_PTP_H__ + +/* Maximum parts-per-billion adjustment that is acceptable */ +#define MAX_PTP_FREQ_ADJ 1000000 +#define PTP_CLOCK_MAX_ADJTIME 10000000 /* 10 ms */ + +#define PTP_MIN_LENGTH 63 +#define PTP_IN_TRANSMIT_PACKET_MAXNUM 240 +#define PTP_EVENT_PORT 319 + +enum ptp_rx_filter_mode { + PTP_TS_NONE = 0, + PTP_TS_L2, + PTP_TS_L4, + PTP_TS_L2_L4 +}; + +struct port_info; + +static inline bool cxgb4_xmit_with_hwtstamp(struct sk_buff *skb) +{ + return skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP; +} + +static inline void cxgb4_xmit_hwtstamp_pending(struct sk_buff *skb) +{ + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; +} + +void cxgb4_ptp_init(struct adapter *adap); +void cxgb4_ptp_stop(struct adapter *adap); +bool cxgb4_ptp_is_ptp_tx(struct sk_buff *skb); +bool cxgb4_ptp_is_ptp_rx(struct sk_buff *skb); +int cxgb4_ptprx_timestamping(struct port_info *pi, u8 port, u16 mode); +int cxgb4_ptp_redirect_rx_packet(struct adapter *adap, struct port_info *pi); +int cxgb4_ptp_txtype(struct adapter *adap, u8 port_id); +void cxgb4_ptp_read_hwstamp(struct adapter *adap, struct port_info *pi); +bool is_ptp_enabled(struct sk_buff *skb, struct net_device *dev); +#endif /* __CXGB4_PTP_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index f05f0d400324..ede12209f20b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -52,6 +52,7 @@ #include "t4_values.h" #include "t4_msg.h" #include "t4fw_api.h" +#include "cxgb4_ptp.h" /* * Rx buffer size. We use largish buffers if possible but settle for single @@ -1162,7 +1163,7 @@ cxgb_fcoe_offload(struct sk_buff *skb, struct adapter *adap, */ netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev) { - u32 wr_mid, ctrl0; + u32 wr_mid, ctrl0, op; u64 cntrl, *end; int qidx, credits; unsigned int flits, ndesc; @@ -1175,6 +1176,7 @@ netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev) dma_addr_t addr[MAX_SKB_FRAGS + 1]; bool immediate = false; int len, max_pkt_len; + bool ptp_enabled = is_ptp_enabled(skb, dev); #ifdef CONFIG_CHELSIO_T4_FCOE int err; #endif /* CONFIG_CHELSIO_T4_FCOE */ @@ -1198,15 +1200,31 @@ out_free: dev_kfree_skb_any(skb); pi = netdev_priv(dev); adap = pi->adapter; qidx = skb_get_queue_mapping(skb); - q = &adap->sge.ethtxq[qidx + pi->first_qset]; + if (ptp_enabled) { + spin_lock(&adap->ptp_lock); + if (!(adap->ptp_tx_skb)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + adap->ptp_tx_skb = skb_get(skb); + } else { + spin_unlock(&adap->ptp_lock); + goto out_free; + } + q = &adap->sge.ptptxq; + } else { + q = &adap->sge.ethtxq[qidx + pi->first_qset]; + } + skb_tx_timestamp(skb); reclaim_completed_tx(adap, &q->q, true); cntrl = TXPKT_L4CSUM_DIS_F | TXPKT_IPCSUM_DIS_F; #ifdef CONFIG_CHELSIO_T4_FCOE err = cxgb_fcoe_offload(skb, adap, pi, &cntrl); - if (unlikely(err == -ENOTSUPP)) + if (unlikely(err == -ENOTSUPP)) { + if (ptp_enabled) + spin_unlock(&adap->ptp_lock); goto out_free; + } #endif /* CONFIG_CHELSIO_T4_FCOE */ flits = calc_tx_flits(skb); @@ -1218,6 +1236,8 @@ out_free: dev_kfree_skb_any(skb); dev_err(adap->pdev_dev, "%s: Tx ring %u full while queue awake!\n", dev->name, qidx); + if (ptp_enabled) + spin_unlock(&adap->ptp_lock); return NETDEV_TX_BUSY; } @@ -1227,6 +1247,8 @@ out_free: dev_kfree_skb_any(skb); if (!immediate && unlikely(map_skb(adap->pdev_dev, skb, addr) < 0)) { q->mapping_err++; + if (ptp_enabled) + spin_unlock(&adap->ptp_lock); goto out_free; } @@ -1279,7 +1301,11 @@ out_free: dev_kfree_skb_any(skb); q->tx_cso += ssi->gso_segs; } else { len += sizeof(*cpl); - wr->op_immdlen = htonl(FW_WR_OP_V(FW_ETH_TX_PKT_WR) | + if (ptp_enabled) + op = FW_PTP_TX_PKT_WR; + else + op = FW_ETH_TX_PKT_WR; + wr->op_immdlen = htonl(FW_WR_OP_V(op) | FW_WR_IMMDLEN_V(len)); cpl = (void *)(wr + 1); if (skb->ip_summed == CHECKSUM_PARTIAL) { @@ -1301,6 +1327,8 @@ out_free: dev_kfree_skb_any(skb); ctrl0 = TXPKT_OPCODE_V(CPL_TX_PKT_XT) | TXPKT_INTF_V(pi->tx_chan) | TXPKT_PF_V(adap->pf); + if (ptp_enabled) + ctrl0 |= TXPKT_TSTAMP_F; #ifdef CONFIG_CHELSIO_T4_DCB if (is_t4(adap->params.chip)) ctrl0 |= TXPKT_OVLAN_IDX_V(q->dcb_prio); @@ -1332,6 +1360,8 @@ out_free: dev_kfree_skb_any(skb); txq_advance(&q->q, ndesc); ring_tx_db(adap, &q->q, ndesc); + if (ptp_enabled) + spin_unlock(&adap->ptp_lock); return NETDEV_TX_OK; } @@ -2023,6 +2053,92 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, rxq->stats.rx_cso++; } +enum { + RX_NON_PTP_PKT = 0, + RX_PTP_PKT_SUC = 1, + RX_PTP_PKT_ERR = 2 +}; + +/** + * t4_systim_to_hwstamp - read hardware time stamp + * @adap: the adapter + * @skb: the packet + * + * Read Time Stamp from MPS packet and insert in skb which + * is forwarded to PTP application + */ +static noinline int t4_systim_to_hwstamp(struct adapter *adapter, + struct sk_buff *skb) +{ + struct skb_shared_hwtstamps *hwtstamps; + struct cpl_rx_mps_pkt *cpl = NULL; + unsigned char *data; + int offset; + + cpl = (struct cpl_rx_mps_pkt *)skb->data; + if (!(CPL_RX_MPS_PKT_TYPE_G(ntohl(cpl->op_to_r1_hi)) & + X_CPL_RX_MPS_PKT_TYPE_PTP)) + return RX_PTP_PKT_ERR; + + data = skb->data + sizeof(*cpl); + skb_pull(skb, 2 * sizeof(u64) + sizeof(struct cpl_rx_mps_pkt)); + offset = ETH_HLEN + IPV4_HLEN(skb->data) + UDP_HLEN; + if (skb->len < offset + OFF_PTP_SEQUENCE_ID + sizeof(short)) + return RX_PTP_PKT_ERR; + + hwtstamps = skb_hwtstamps(skb); + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ns_to_ktime(be64_to_cpu(*((u64 *)data))); + + return RX_PTP_PKT_SUC; +} + +/** + * t4_rx_hststamp - Recv PTP Event Message + * @adap: the adapter + * @rsp: the response queue descriptor holding the RX_PKT message + * @skb: the packet + * + * PTP enabled and MPS packet, read HW timestamp + */ +static int t4_rx_hststamp(struct adapter *adapter, const __be64 *rsp, + struct sge_eth_rxq *rxq, struct sk_buff *skb) +{ + int ret; + + if (unlikely((*(u8 *)rsp == CPL_RX_MPS_PKT) && + !is_t4(adapter->params.chip))) { + ret = t4_systim_to_hwstamp(adapter, skb); + if (ret == RX_PTP_PKT_ERR) { + kfree_skb(skb); + rxq->stats.rx_drops++; + } + return ret; + } + return RX_NON_PTP_PKT; +} + +/** + * t4_tx_hststamp - Loopback PTP Transmit Event Message + * @adap: the adapter + * @skb: the packet + * @dev: the ingress net device + * + * Read hardware timestamp for the loopback PTP Tx event message + */ +static int t4_tx_hststamp(struct adapter *adapter, struct sk_buff *skb, + struct net_device *dev) +{ + struct port_info *pi = netdev_priv(dev); + + if (!is_t4(adapter->params.chip) && adapter->ptp_tx_skb) { + cxgb4_ptp_read_hwstamp(adapter, pi); + kfree_skb(skb); + return 0; + } + return 1; +} + /** * t4_ethrx_handler - process an ingress ethernet packet * @q: the response queue that received the packet @@ -2038,11 +2154,13 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, struct sk_buff *skb; const struct cpl_rx_pkt *pkt; struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq); + struct adapter *adapter = q->adap; struct sge *s = &q->adap->sge; int cpl_trace_pkt = is_t4(q->adap->params.chip) ? CPL_TRACE_PKT : CPL_TRACE_PKT_T5; u16 err_vec; struct port_info *pi; + int ret = 0; if (unlikely(*(u8 *)rsp == cpl_trace_pkt)) return handle_trace_pkt(q->adap, si); @@ -2068,8 +2186,25 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, rxq->stats.rx_drops++; return 0; } + pi = netdev_priv(q->netdev); + + /* Handle PTP Event Rx packet */ + if (unlikely(pi->ptp_enable)) { + ret = t4_rx_hststamp(adapter, rsp, rxq, skb); + if (ret == RX_PTP_PKT_ERR) + return 0; + } + if (likely(!ret)) + __skb_pull(skb, s->pktshift); /* remove ethernet header pad */ + + /* Handle the PTP Event Tx Loopback packet */ + if (unlikely(pi->ptp_enable && !ret && + (pkt->l2info & htonl(RXF_UDP_F)) && + cxgb4_ptp_is_ptp_rx(skb))) { + if (!t4_tx_hststamp(adapter, skb, q->netdev)) + return 0; + } - __skb_pull(skb, s->pktshift); /* remove ethernet header padding */ skb->protocol = eth_type_trans(skb, q->netdev); skb_record_rx_queue(skb, q->idx); if (skb->dev->features & NETIF_F_RXHASH) @@ -2078,7 +2213,6 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, rxq->stats.pkts++; - pi = netdev_priv(skb->dev); if (pi->rxtstamp) cxgb4_sgetim_to_hwtstamp(q->adap, skb_hwtstamps(skb), si->sgetstamp); @@ -2502,6 +2636,20 @@ static void sge_tx_timer_cb(unsigned long data) tasklet_schedule(&txq->qresume_tsk); } + if (!is_t4(adap->params.chip)) { + struct sge_eth_txq *q = &s->ptptxq; + int avail; + + spin_lock(&adap->ptp_lock); + avail = reclaimable(&q->q); + + if (avail) { + free_tx_desc(adap, &q->q, avail, false); + q->q.in_use -= avail; + } + spin_unlock(&adap->ptp_lock); + } + budget = MAX_TIMER_TX_RECLAIM; i = s->ethtxq_rover; do { @@ -3068,6 +3216,19 @@ void t4_free_sge_resources(struct adapter *adap) if (adap->sge.intrq.desc) free_rspq_fl(adap, &adap->sge.intrq, NULL); + if (!is_t4(adap->params.chip)) { + etq = &adap->sge.ptptxq; + if (etq->q.desc) { + t4_eth_eq_free(adap, adap->mbox, adap->pf, 0, + etq->q.cntxt_id); + spin_lock_bh(&adap->ptp_lock); + free_tx_desc(adap, &etq->q, etq->q.in_use, true); + spin_unlock_bh(&adap->ptp_lock); + kfree(etq->q.sdesc); + free_txq(adap, &etq->q); + } + } + /* clear the reverse egress queue map */ memset(adap->sge.egr_map, 0, adap->sge.egr_sz * sizeof(*adap->sge.egr_map)); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index 8098c93cd16e..b0ff78da8aa2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -92,6 +92,7 @@ enum { CPL_RDMA_TERMINATE = 0xA2, CPL_RDMA_WRITE = 0xA4, CPL_SGE_EGR_UPDATE = 0xA5, + CPL_RX_MPS_PKT = 0xAF, CPL_TRACE_PKT = 0xB0, CPL_ISCSI_DATA = 0xB2, @@ -807,6 +808,10 @@ struct cpl_tx_pkt { #define TXPKT_INS_OVLAN_V(x) ((x) << TXPKT_INS_OVLAN_S) #define TXPKT_INS_OVLAN_F TXPKT_INS_OVLAN_V(1U) +#define TXPKT_TSTAMP_S 23 +#define TXPKT_TSTAMP_V(x) ((x) << TXPKT_TSTAMP_S) +#define TXPKT_TSTAMP_F TXPKT_TSTAMP_V(1ULL) + #define TXPKT_OPCODE_S 24 #define TXPKT_OPCODE_V(x) ((x) << TXPKT_OPCODE_S) @@ -1875,4 +1880,27 @@ struct cpl_rx_phys_dsgl { (((x) >> CPL_RX_PHYS_DSGL_NOOFSGENTR_S) & \ CPL_RX_PHYS_DSGL_NOOFSGENTR_M) +struct cpl_rx_mps_pkt { + __be32 op_to_r1_hi; + __be32 r1_lo_length; +}; + +#define CPL_RX_MPS_PKT_OP_S 24 +#define CPL_RX_MPS_PKT_OP_M 0xff +#define CPL_RX_MPS_PKT_OP_V(x) ((x) << CPL_RX_MPS_PKT_OP_S) +#define CPL_RX_MPS_PKT_OP_G(x) \ + (((x) >> CPL_RX_MPS_PKT_OP_S) & CPL_RX_MPS_PKT_OP_M) + +#define CPL_RX_MPS_PKT_TYPE_S 20 +#define CPL_RX_MPS_PKT_TYPE_M 0xf +#define CPL_RX_MPS_PKT_TYPE_V(x) ((x) << CPL_RX_MPS_PKT_TYPE_S) +#define CPL_RX_MPS_PKT_TYPE_G(x) \ + (((x) >> CPL_RX_MPS_PKT_TYPE_S) & CPL_RX_MPS_PKT_TYPE_M) + +enum { + X_CPL_RX_MPS_PKT_TYPE_PAUSE = 1 << 0, + X_CPL_RX_MPS_PKT_TYPE_PPP = 1 << 1, + X_CPL_RX_MPS_PKT_TYPE_QFC = 1 << 2, + X_CPL_RX_MPS_PKT_TYPE_PTP = 1 << 3 +}; #endif /* __T4_MSG_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 3884336ce23c..dac90837842b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -1799,6 +1799,8 @@ #define MPS_PORT_STAT_RX_PORT_LESS_64B_H 0x614 #define MAC_PORT_MAGIC_MACID_LO 0x824 #define MAC_PORT_MAGIC_MACID_HI 0x828 +#define MAC_PORT_TX_TS_VAL_LO 0x928 +#define MAC_PORT_TX_TS_VAL_HI 0x92c #define MAC_PORT_EPIO_DATA0_A 0x8c0 #define MAC_PORT_EPIO_DATA1_A 0x8c4 diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index f2ffc1f6b8be..0ebed64d62d3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -103,6 +103,7 @@ enum fw_wr_opcodes { FW_RI_FR_NSMR_TPTE_WR = 0x20, FW_RI_INV_LSTAG_WR = 0x1a, FW_ISCSI_TX_DATA_WR = 0x45, + FW_PTP_TX_PKT_WR = 0x46, FW_CRYPTO_LOOKASIDE_WR = 0X6d, FW_LASTC2E_WR = 0x70 }; @@ -685,6 +686,7 @@ enum fw_cmd_opcodes { FW_SCHED_CMD = 0x24, FW_DEVLOG_CMD = 0x25, FW_CLIP_CMD = 0x28, + FW_PTP_CMD = 0x3e, FW_LASTC2E_CMD = 0x40, FW_ERROR_CMD = 0x80, FW_DEBUG_CMD = 0x81, @@ -2802,6 +2804,54 @@ struct fw_port_lb_stats_cmd { } u; }; +enum fw_ptp_subop { + /* none */ + FW_PTP_SC_INIT_TIMER = 0x00, + FW_PTP_SC_TX_TYPE = 0x01, + /* init */ + FW_PTP_SC_RXTIME_STAMP = 0x08, + FW_PTP_SC_RDRX_TYPE = 0x09, + /* ts */ + FW_PTP_SC_ADJ_FREQ = 0x10, + FW_PTP_SC_ADJ_TIME = 0x11, + FW_PTP_SC_ADJ_FTIME = 0x12, + FW_PTP_SC_WALL_CLOCK = 0x13, + FW_PTP_SC_GET_TIME = 0x14, + FW_PTP_SC_SET_TIME = 0x15, +}; + +struct fw_ptp_cmd { + __be32 op_to_portid; + __be32 retval_len16; + union fw_ptp { + struct fw_ptp_sc { + __u8 sc; + __u8 r3[7]; + } scmd; + struct fw_ptp_init { + __u8 sc; + __u8 txchan; + __be16 absid; + __be16 mode; + __be16 r3; + } init; + struct fw_ptp_ts { + __u8 sc; + __u8 sign; + __be16 r3; + __be32 ppb; + __be64 tm; + } ts; + } u; + __be64 r3; +}; + +#define FW_PTP_CMD_PORTID_S 0 +#define FW_PTP_CMD_PORTID_M 0xf +#define FW_PTP_CMD_PORTID_V(x) ((x) << FW_PTP_CMD_PORTID_S) +#define FW_PTP_CMD_PORTID_G(x) \ + (((x) >> FW_PTP_CMD_PORTID_S) & FW_PTP_CMD_PORTID_M) + struct fw_rss_ind_tbl_cmd { __be32 op_to_viid; __be32 retval_len16; |