162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * cxgb4_ptp.c:Chelsio PTP support for T5/T6 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2003-2017 Chelsio Communications, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This software is available to you under a choice of one of two 762306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 1062306a36Sopenharmony_ci * OpenIB.org BSD license below: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1362306a36Sopenharmony_ci * without modification, are permitted provided that the following 1462306a36Sopenharmony_ci * conditions are met: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1762306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1862306a36Sopenharmony_ci * disclaimer. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2162306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2262306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2362306a36Sopenharmony_ci * provided with the distribution. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3262306a36Sopenharmony_ci * SOFTWARE. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Written by: Atul Gupta (atul.gupta@chelsio.com) 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/module.h> 3862306a36Sopenharmony_ci#include <linux/net_tstamp.h> 3962306a36Sopenharmony_ci#include <linux/skbuff.h> 4062306a36Sopenharmony_ci#include <linux/netdevice.h> 4162306a36Sopenharmony_ci#include <linux/pps_kernel.h> 4262306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 4362306a36Sopenharmony_ci#include <linux/ptp_classify.h> 4462306a36Sopenharmony_ci#include <linux/udp.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include "cxgb4.h" 4762306a36Sopenharmony_ci#include "t4_hw.h" 4862306a36Sopenharmony_ci#include "t4_regs.h" 4962306a36Sopenharmony_ci#include "t4_msg.h" 5062306a36Sopenharmony_ci#include "t4fw_api.h" 5162306a36Sopenharmony_ci#include "cxgb4_ptp.h" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * cxgb4_ptp_is_ptp_tx - determine whether TX packet is PTP or not 5562306a36Sopenharmony_ci * @skb: skb of outgoing ptp request 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cibool cxgb4_ptp_is_ptp_tx(struct sk_buff *skb) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct udphdr *uh; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci uh = udp_hdr(skb); 6362306a36Sopenharmony_ci return skb->len >= PTP_MIN_LENGTH && 6462306a36Sopenharmony_ci skb->len <= PTP_IN_TRANSMIT_PACKET_MAXNUM && 6562306a36Sopenharmony_ci likely(skb->protocol == htons(ETH_P_IP)) && 6662306a36Sopenharmony_ci ip_hdr(skb)->protocol == IPPROTO_UDP && 6762306a36Sopenharmony_ci uh->dest == htons(PTP_EVENT_PORT); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cibool is_ptp_enabled(struct sk_buff *skb, struct net_device *dev) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct port_info *pi; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci pi = netdev_priv(dev); 7562306a36Sopenharmony_ci return (pi->ptp_enable && cxgb4_xmit_with_hwtstamp(skb) && 7662306a36Sopenharmony_ci cxgb4_ptp_is_ptp_tx(skb)); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * cxgb4_ptp_is_ptp_rx - determine whether RX packet is PTP or not 8162306a36Sopenharmony_ci * @skb: skb of incoming ptp request 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cibool cxgb4_ptp_is_ptp_rx(struct sk_buff *skb) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct udphdr *uh = (struct udphdr *)(skb->data + ETH_HLEN + 8762306a36Sopenharmony_ci IPV4_HLEN(skb->data)); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return uh->dest == htons(PTP_EVENT_PORT) && 9062306a36Sopenharmony_ci uh->source == htons(PTP_EVENT_PORT); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/** 9462306a36Sopenharmony_ci * cxgb4_ptp_read_hwstamp - read timestamp for TX event PTP message 9562306a36Sopenharmony_ci * @adapter: board private structure 9662306a36Sopenharmony_ci * @pi: port private structure 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_civoid cxgb4_ptp_read_hwstamp(struct adapter *adapter, struct port_info *pi) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct skb_shared_hwtstamps *skb_ts = NULL; 10262306a36Sopenharmony_ci u64 tx_ts; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci skb_ts = skb_hwtstamps(adapter->ptp_tx_skb); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci tx_ts = t4_read_reg(adapter, 10762306a36Sopenharmony_ci T5_PORT_REG(pi->port_id, MAC_PORT_TX_TS_VAL_LO)); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci tx_ts |= (u64)t4_read_reg(adapter, 11062306a36Sopenharmony_ci T5_PORT_REG(pi->port_id, 11162306a36Sopenharmony_ci MAC_PORT_TX_TS_VAL_HI)) << 32; 11262306a36Sopenharmony_ci skb_ts->hwtstamp = ns_to_ktime(tx_ts); 11362306a36Sopenharmony_ci skb_tstamp_tx(adapter->ptp_tx_skb, skb_ts); 11462306a36Sopenharmony_ci dev_kfree_skb_any(adapter->ptp_tx_skb); 11562306a36Sopenharmony_ci spin_lock(&adapter->ptp_lock); 11662306a36Sopenharmony_ci adapter->ptp_tx_skb = NULL; 11762306a36Sopenharmony_ci spin_unlock(&adapter->ptp_lock); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * cxgb4_ptprx_timestamping - Enable Timestamp for RX PTP event message 12262306a36Sopenharmony_ci * @pi: port private structure 12362306a36Sopenharmony_ci * @port: pot number 12462306a36Sopenharmony_ci * @mode: RX mode 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ciint cxgb4_ptprx_timestamping(struct port_info *pi, u8 port, u16 mode) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 13062306a36Sopenharmony_ci struct fw_ptp_cmd c; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 13462306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 13562306a36Sopenharmony_ci FW_CMD_REQUEST_F | 13662306a36Sopenharmony_ci FW_CMD_WRITE_F | 13762306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(port)); 13862306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 13962306a36Sopenharmony_ci c.u.init.sc = FW_PTP_SC_RXTIME_STAMP; 14062306a36Sopenharmony_ci c.u.init.mode = cpu_to_be16(mode); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 14362306a36Sopenharmony_ci if (err < 0) 14462306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 14562306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 14662306a36Sopenharmony_ci return err; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ciint cxgb4_ptp_txtype(struct adapter *adapter, u8 port) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct fw_ptp_cmd c; 15262306a36Sopenharmony_ci int err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 15562306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 15662306a36Sopenharmony_ci FW_CMD_REQUEST_F | 15762306a36Sopenharmony_ci FW_CMD_WRITE_F | 15862306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(port)); 15962306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 16062306a36Sopenharmony_ci c.u.init.sc = FW_PTP_SC_TX_TYPE; 16162306a36Sopenharmony_ci c.u.init.mode = cpu_to_be16(PTP_TS_NONE); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 16462306a36Sopenharmony_ci if (err < 0) 16562306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 16662306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return err; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciint cxgb4_ptp_redirect_rx_packet(struct adapter *adapter, struct port_info *pi) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct sge *s = &adapter->sge; 17462306a36Sopenharmony_ci struct sge_eth_rxq *receive_q = &s->ethrxq[pi->first_qset]; 17562306a36Sopenharmony_ci struct fw_ptp_cmd c; 17662306a36Sopenharmony_ci int err; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 17962306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 18062306a36Sopenharmony_ci FW_CMD_REQUEST_F | 18162306a36Sopenharmony_ci FW_CMD_WRITE_F | 18262306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(pi->port_id)); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 18562306a36Sopenharmony_ci c.u.init.sc = FW_PTP_SC_RDRX_TYPE; 18662306a36Sopenharmony_ci c.u.init.txchan = pi->tx_chan; 18762306a36Sopenharmony_ci c.u.init.absid = cpu_to_be16(receive_q->rspq.abs_id); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 19062306a36Sopenharmony_ci if (err < 0) 19162306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 19262306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 19362306a36Sopenharmony_ci return err; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * cxgb4_ptp_adjfine - Adjust frequency of PHC cycle counter 19862306a36Sopenharmony_ci * @ptp: ptp clock structure 19962306a36Sopenharmony_ci * @scaled_ppm: Desired frequency in scaled parts per billion 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * Adjust the frequency of the PHC cycle counter by the indicated amount from 20262306a36Sopenharmony_ci * the base frequency. 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * Scaled parts per million is ppm with a 16-bit binary fractional field. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic int cxgb4_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct adapter *adapter = (struct adapter *)container_of(ptp, 20962306a36Sopenharmony_ci struct adapter, ptp_clock_info); 21062306a36Sopenharmony_ci s32 ppb = scaled_ppm_to_ppb(scaled_ppm); 21162306a36Sopenharmony_ci struct fw_ptp_cmd c; 21262306a36Sopenharmony_ci int err; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 21562306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 21662306a36Sopenharmony_ci FW_CMD_REQUEST_F | 21762306a36Sopenharmony_ci FW_CMD_WRITE_F | 21862306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(0)); 21962306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 22062306a36Sopenharmony_ci c.u.ts.sc = FW_PTP_SC_ADJ_FREQ; 22162306a36Sopenharmony_ci c.u.ts.sign = (ppb < 0) ? 1 : 0; 22262306a36Sopenharmony_ci if (ppb < 0) 22362306a36Sopenharmony_ci ppb = -ppb; 22462306a36Sopenharmony_ci c.u.ts.ppb = cpu_to_be32(ppb); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 22762306a36Sopenharmony_ci if (err < 0) 22862306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 22962306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return err; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/** 23562306a36Sopenharmony_ci * cxgb4_ptp_fineadjtime - Shift the time of the hardware clock 23662306a36Sopenharmony_ci * @adapter: board private structure 23762306a36Sopenharmony_ci * @delta: Desired change in nanoseconds 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * Adjust the timer by resetting the timecounter structure. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistatic int cxgb4_ptp_fineadjtime(struct adapter *adapter, s64 delta) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct fw_ptp_cmd c; 24462306a36Sopenharmony_ci int err; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 24762306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 24862306a36Sopenharmony_ci FW_CMD_REQUEST_F | 24962306a36Sopenharmony_ci FW_CMD_WRITE_F | 25062306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(0)); 25162306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 25262306a36Sopenharmony_ci c.u.ts.sc = FW_PTP_SC_ADJ_FTIME; 25362306a36Sopenharmony_ci c.u.ts.sign = (delta < 0) ? 1 : 0; 25462306a36Sopenharmony_ci if (delta < 0) 25562306a36Sopenharmony_ci delta = -delta; 25662306a36Sopenharmony_ci c.u.ts.tm = cpu_to_be64(delta); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 25962306a36Sopenharmony_ci if (err < 0) 26062306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 26162306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 26262306a36Sopenharmony_ci return err; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * cxgb4_ptp_adjtime - Shift the time of the hardware clock 26762306a36Sopenharmony_ci * @ptp: ptp clock structure 26862306a36Sopenharmony_ci * @delta: Desired change in nanoseconds 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Adjust the timer by resetting the timecounter structure. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic int cxgb4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct adapter *adapter = 27562306a36Sopenharmony_ci (struct adapter *)container_of(ptp, struct adapter, 27662306a36Sopenharmony_ci ptp_clock_info); 27762306a36Sopenharmony_ci struct fw_ptp_cmd c; 27862306a36Sopenharmony_ci s64 sign = 1; 27962306a36Sopenharmony_ci int err; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (delta < 0) 28262306a36Sopenharmony_ci sign = -1; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (delta * sign > PTP_CLOCK_MAX_ADJTIME) { 28562306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 28662306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 28762306a36Sopenharmony_ci FW_CMD_REQUEST_F | 28862306a36Sopenharmony_ci FW_CMD_WRITE_F | 28962306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(0)); 29062306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 29162306a36Sopenharmony_ci c.u.ts.sc = FW_PTP_SC_ADJ_TIME; 29262306a36Sopenharmony_ci c.u.ts.sign = (delta < 0) ? 1 : 0; 29362306a36Sopenharmony_ci if (delta < 0) 29462306a36Sopenharmony_ci delta = -delta; 29562306a36Sopenharmony_ci c.u.ts.tm = cpu_to_be64(delta); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 29862306a36Sopenharmony_ci if (err < 0) 29962306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 30062306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 30162306a36Sopenharmony_ci } else { 30262306a36Sopenharmony_ci err = cxgb4_ptp_fineadjtime(adapter, delta); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return err; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/** 30962306a36Sopenharmony_ci * cxgb4_ptp_gettime - Reads the current time from the hardware clock 31062306a36Sopenharmony_ci * @ptp: ptp clock structure 31162306a36Sopenharmony_ci * @ts: timespec structure to hold the current time value 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * Read the timecounter and return the correct value in ns after converting 31462306a36Sopenharmony_ci * it into a struct timespec. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_cistatic int cxgb4_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct adapter *adapter = container_of(ptp, struct adapter, 31962306a36Sopenharmony_ci ptp_clock_info); 32062306a36Sopenharmony_ci u64 ns; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ns = t4_read_reg(adapter, T5_PORT_REG(0, MAC_PORT_PTP_SUM_LO_A)); 32362306a36Sopenharmony_ci ns |= (u64)t4_read_reg(adapter, 32462306a36Sopenharmony_ci T5_PORT_REG(0, MAC_PORT_PTP_SUM_HI_A)) << 32; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* convert to timespec*/ 32762306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/** 33262306a36Sopenharmony_ci * cxgb4_ptp_settime - Set the current time on the hardware clock 33362306a36Sopenharmony_ci * @ptp: ptp clock structure 33462306a36Sopenharmony_ci * @ts: timespec containing the new time for the cycle counter 33562306a36Sopenharmony_ci * 33662306a36Sopenharmony_ci * Reset value to new base value instead of the kernel 33762306a36Sopenharmony_ci * wall timer value. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_cistatic int cxgb4_ptp_settime(struct ptp_clock_info *ptp, 34062306a36Sopenharmony_ci const struct timespec64 *ts) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct adapter *adapter = (struct adapter *)container_of(ptp, 34362306a36Sopenharmony_ci struct adapter, ptp_clock_info); 34462306a36Sopenharmony_ci struct fw_ptp_cmd c; 34562306a36Sopenharmony_ci u64 ns; 34662306a36Sopenharmony_ci int err; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 34962306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 35062306a36Sopenharmony_ci FW_CMD_REQUEST_F | 35162306a36Sopenharmony_ci FW_CMD_WRITE_F | 35262306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(0)); 35362306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 35462306a36Sopenharmony_ci c.u.ts.sc = FW_PTP_SC_SET_TIME; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ns = timespec64_to_ns(ts); 35762306a36Sopenharmony_ci c.u.ts.tm = cpu_to_be64(ns); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 36062306a36Sopenharmony_ci if (err < 0) 36162306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 36262306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return err; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic void cxgb4_init_ptp_timer(struct adapter *adapter) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct fw_ptp_cmd c; 37062306a36Sopenharmony_ci int err; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci memset(&c, 0, sizeof(c)); 37362306a36Sopenharmony_ci c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) | 37462306a36Sopenharmony_ci FW_CMD_REQUEST_F | 37562306a36Sopenharmony_ci FW_CMD_WRITE_F | 37662306a36Sopenharmony_ci FW_PTP_CMD_PORTID_V(0)); 37762306a36Sopenharmony_ci c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16)); 37862306a36Sopenharmony_ci c.u.scmd.sc = FW_PTP_SC_INIT_TIMER; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), NULL); 38162306a36Sopenharmony_ci if (err < 0) 38262306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 38362306a36Sopenharmony_ci "PTP: %s error %d\n", __func__, -err); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * cxgb4_ptp_enable - enable or disable an ancillary feature 38862306a36Sopenharmony_ci * @ptp: ptp clock structure 38962306a36Sopenharmony_ci * @request: Desired resource to enable or disable 39062306a36Sopenharmony_ci * @on: Caller passes one to enable or zero to disable 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * Enable (or disable) ancillary features of the PHC subsystem. 39362306a36Sopenharmony_ci * Currently, no ancillary features are supported. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic int cxgb4_ptp_enable(struct ptp_clock_info __always_unused *ptp, 39662306a36Sopenharmony_ci struct ptp_clock_request __always_unused *request, 39762306a36Sopenharmony_ci int __always_unused on) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci return -ENOTSUPP; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic const struct ptp_clock_info cxgb4_ptp_clock_info = { 40362306a36Sopenharmony_ci .owner = THIS_MODULE, 40462306a36Sopenharmony_ci .name = "cxgb4_clock", 40562306a36Sopenharmony_ci .max_adj = MAX_PTP_FREQ_ADJ, 40662306a36Sopenharmony_ci .n_alarm = 0, 40762306a36Sopenharmony_ci .n_ext_ts = 0, 40862306a36Sopenharmony_ci .n_per_out = 0, 40962306a36Sopenharmony_ci .pps = 0, 41062306a36Sopenharmony_ci .adjfine = cxgb4_ptp_adjfine, 41162306a36Sopenharmony_ci .adjtime = cxgb4_ptp_adjtime, 41262306a36Sopenharmony_ci .gettime64 = cxgb4_ptp_gettime, 41362306a36Sopenharmony_ci .settime64 = cxgb4_ptp_settime, 41462306a36Sopenharmony_ci .enable = cxgb4_ptp_enable, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/** 41862306a36Sopenharmony_ci * cxgb4_ptp_init - initialize PTP for devices which support it 41962306a36Sopenharmony_ci * @adapter: board private structure 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * This function performs the required steps for enabling PTP support. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_civoid cxgb4_ptp_init(struct adapter *adapter) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct timespec64 now; 42662306a36Sopenharmony_ci /* no need to create a clock device if we already have one */ 42762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(adapter->ptp_clock)) 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci adapter->ptp_tx_skb = NULL; 43162306a36Sopenharmony_ci adapter->ptp_clock_info = cxgb4_ptp_clock_info; 43262306a36Sopenharmony_ci spin_lock_init(&adapter->ptp_lock); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci adapter->ptp_clock = ptp_clock_register(&adapter->ptp_clock_info, 43562306a36Sopenharmony_ci &adapter->pdev->dev); 43662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(adapter->ptp_clock)) { 43762306a36Sopenharmony_ci adapter->ptp_clock = NULL; 43862306a36Sopenharmony_ci dev_err(adapter->pdev_dev, 43962306a36Sopenharmony_ci "PTP %s Clock registration has failed\n", __func__); 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci now = ktime_to_timespec64(ktime_get_real()); 44462306a36Sopenharmony_ci cxgb4_init_ptp_timer(adapter); 44562306a36Sopenharmony_ci if (cxgb4_ptp_settime(&adapter->ptp_clock_info, &now) < 0) { 44662306a36Sopenharmony_ci ptp_clock_unregister(adapter->ptp_clock); 44762306a36Sopenharmony_ci adapter->ptp_clock = NULL; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/** 45262306a36Sopenharmony_ci * cxgb4_ptp_stop - disable PTP device and stop the overflow check 45362306a36Sopenharmony_ci * @adapter: board private structure 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * Stop the PTP support. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_civoid cxgb4_ptp_stop(struct adapter *adapter) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci if (adapter->ptp_tx_skb) { 46062306a36Sopenharmony_ci dev_kfree_skb_any(adapter->ptp_tx_skb); 46162306a36Sopenharmony_ci adapter->ptp_tx_skb = NULL; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (adapter->ptp_clock) { 46562306a36Sopenharmony_ci ptp_clock_unregister(adapter->ptp_clock); 46662306a36Sopenharmony_ci adapter->ptp_clock = NULL; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci} 469