162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci/* QLogic qede NIC Driver 362306a36Sopenharmony_ci * Copyright (c) 2015-2017 QLogic Corporation 462306a36Sopenharmony_ci * Copyright (c) 2019-2020 Marvell International Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "qede_ptp.h" 862306a36Sopenharmony_ci#define QEDE_PTP_TX_TIMEOUT (2 * HZ) 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct qede_ptp { 1162306a36Sopenharmony_ci const struct qed_eth_ptp_ops *ops; 1262306a36Sopenharmony_ci struct ptp_clock_info clock_info; 1362306a36Sopenharmony_ci struct cyclecounter cc; 1462306a36Sopenharmony_ci struct timecounter tc; 1562306a36Sopenharmony_ci struct ptp_clock *clock; 1662306a36Sopenharmony_ci struct work_struct work; 1762306a36Sopenharmony_ci unsigned long ptp_tx_start; 1862306a36Sopenharmony_ci struct qede_dev *edev; 1962306a36Sopenharmony_ci struct sk_buff *tx_skb; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci /* ptp spinlock is used for protecting the cycle/time counter fields 2262306a36Sopenharmony_ci * and, also for serializing the qed PTP API invocations. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci spinlock_t lock; 2562306a36Sopenharmony_ci bool hw_ts_ioctl_called; 2662306a36Sopenharmony_ci u16 tx_type; 2762306a36Sopenharmony_ci u16 rx_filter; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * qede_ptp_adjfine() - Adjust the frequency of the PTP cycle counter. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * @info: The PTP clock info structure. 3462306a36Sopenharmony_ci * @scaled_ppm: Scaled parts per million adjustment from base. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * Scaled parts per million is ppm with a 16-bit binary fractional field. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Return: Zero on success, negative errno otherwise. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic int qede_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct qede_ptp *ptp = container_of(info, struct qede_ptp, clock_info); 4362306a36Sopenharmony_ci s32 ppb = scaled_ppm_to_ppb(scaled_ppm); 4462306a36Sopenharmony_ci struct qede_dev *edev = ptp->edev; 4562306a36Sopenharmony_ci int rc; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci __qede_lock(edev); 4862306a36Sopenharmony_ci if (edev->state == QEDE_STATE_OPEN) { 4962306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 5062306a36Sopenharmony_ci rc = ptp->ops->adjfreq(edev->cdev, ppb); 5162306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 5262306a36Sopenharmony_ci } else { 5362306a36Sopenharmony_ci DP_ERR(edev, "PTP adjfine called while interface is down\n"); 5462306a36Sopenharmony_ci rc = -EFAULT; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci __qede_unlock(edev); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return rc; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int qede_ptp_adjtime(struct ptp_clock_info *info, s64 delta) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct qede_dev *edev; 6462306a36Sopenharmony_ci struct qede_ptp *ptp; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci ptp = container_of(info, struct qede_ptp, clock_info); 6762306a36Sopenharmony_ci edev = ptp->edev; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP adjtime called, delta = %llx\n", 7062306a36Sopenharmony_ci delta); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 7362306a36Sopenharmony_ci timecounter_adjtime(&ptp->tc, delta); 7462306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int qede_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct qede_dev *edev; 8262306a36Sopenharmony_ci struct qede_ptp *ptp; 8362306a36Sopenharmony_ci u64 ns; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ptp = container_of(info, struct qede_ptp, clock_info); 8662306a36Sopenharmony_ci edev = ptp->edev; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 8962306a36Sopenharmony_ci ns = timecounter_read(&ptp->tc); 9062306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP gettime called, ns = %llu\n", ns); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int qede_ptp_settime(struct ptp_clock_info *info, 10062306a36Sopenharmony_ci const struct timespec64 *ts) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct qede_dev *edev; 10362306a36Sopenharmony_ci struct qede_ptp *ptp; 10462306a36Sopenharmony_ci u64 ns; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ptp = container_of(info, struct qede_ptp, clock_info); 10762306a36Sopenharmony_ci edev = ptp->edev; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ns = timespec64_to_ns(ts); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP settime called, ns = %llu\n", ns); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Re-init the timecounter */ 11462306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 11562306a36Sopenharmony_ci timecounter_init(&ptp->tc, &ptp->cc, ns); 11662306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Enable (or disable) ancillary features of the phc subsystem */ 12262306a36Sopenharmony_cistatic int qede_ptp_ancillary_feature_enable(struct ptp_clock_info *info, 12362306a36Sopenharmony_ci struct ptp_clock_request *rq, 12462306a36Sopenharmony_ci int on) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct qede_dev *edev; 12762306a36Sopenharmony_ci struct qede_ptp *ptp; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ptp = container_of(info, struct qede_ptp, clock_info); 13062306a36Sopenharmony_ci edev = ptp->edev; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci DP_ERR(edev, "PHC ancillary features are not supported\n"); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return -ENOTSUPP; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void qede_ptp_task(struct work_struct *work) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 14062306a36Sopenharmony_ci struct qede_dev *edev; 14162306a36Sopenharmony_ci struct qede_ptp *ptp; 14262306a36Sopenharmony_ci u64 timestamp, ns; 14362306a36Sopenharmony_ci bool timedout; 14462306a36Sopenharmony_ci int rc; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ptp = container_of(work, struct qede_ptp, work); 14762306a36Sopenharmony_ci edev = ptp->edev; 14862306a36Sopenharmony_ci timedout = time_is_before_jiffies(ptp->ptp_tx_start + 14962306a36Sopenharmony_ci QEDE_PTP_TX_TIMEOUT); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Read Tx timestamp registers */ 15262306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 15362306a36Sopenharmony_ci rc = ptp->ops->read_tx_ts(edev->cdev, ×tamp); 15462306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 15562306a36Sopenharmony_ci if (rc) { 15662306a36Sopenharmony_ci if (unlikely(timedout)) { 15762306a36Sopenharmony_ci DP_INFO(edev, "Tx timestamp is not recorded\n"); 15862306a36Sopenharmony_ci dev_kfree_skb_any(ptp->tx_skb); 15962306a36Sopenharmony_ci ptp->tx_skb = NULL; 16062306a36Sopenharmony_ci clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, 16162306a36Sopenharmony_ci &edev->flags); 16262306a36Sopenharmony_ci edev->ptp_skip_txts++; 16362306a36Sopenharmony_ci } else { 16462306a36Sopenharmony_ci /* Reschedule to keep checking for a valid TS value */ 16562306a36Sopenharmony_ci schedule_work(&ptp->work); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci ns = timecounter_cyc2time(&ptp->tc, timestamp); 17162306a36Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 17262306a36Sopenharmony_ci shhwtstamps.hwtstamp = ns_to_ktime(ns); 17362306a36Sopenharmony_ci skb_tstamp_tx(ptp->tx_skb, &shhwtstamps); 17462306a36Sopenharmony_ci dev_kfree_skb_any(ptp->tx_skb); 17562306a36Sopenharmony_ci ptp->tx_skb = NULL; 17662306a36Sopenharmony_ci clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, 17962306a36Sopenharmony_ci "Tx timestamp, timestamp cycles = %llu, ns = %llu\n", 18062306a36Sopenharmony_ci timestamp, ns); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* Read the PHC. This API is invoked with ptp_lock held. */ 18462306a36Sopenharmony_cistatic u64 qede_ptp_read_cc(const struct cyclecounter *cc) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct qede_dev *edev; 18762306a36Sopenharmony_ci struct qede_ptp *ptp; 18862306a36Sopenharmony_ci u64 phc_cycles; 18962306a36Sopenharmony_ci int rc; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ptp = container_of(cc, struct qede_ptp, cc); 19262306a36Sopenharmony_ci edev = ptp->edev; 19362306a36Sopenharmony_ci rc = ptp->ops->read_cc(edev->cdev, &phc_cycles); 19462306a36Sopenharmony_ci if (rc) 19562306a36Sopenharmony_ci WARN_ONCE(1, "PHC read err %d\n", rc); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, "PHC read cycles = %llu\n", phc_cycles); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return phc_cycles; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int qede_ptp_cfg_filters(struct qede_dev *edev) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci enum qed_ptp_hwtstamp_tx_type tx_type = QED_PTP_HWTSTAMP_TX_ON; 20562306a36Sopenharmony_ci enum qed_ptp_filter_type rx_filter = QED_PTP_FILTER_NONE; 20662306a36Sopenharmony_ci struct qede_ptp *ptp = edev->ptp; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!ptp) 20962306a36Sopenharmony_ci return -EIO; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!ptp->hw_ts_ioctl_called) { 21262306a36Sopenharmony_ci DP_INFO(edev, "TS IOCTL not called\n"); 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci switch (ptp->tx_type) { 21762306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 21862306a36Sopenharmony_ci set_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags); 21962306a36Sopenharmony_ci tx_type = QED_PTP_HWTSTAMP_TX_ON; 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 22362306a36Sopenharmony_ci clear_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags); 22462306a36Sopenharmony_ci tx_type = QED_PTP_HWTSTAMP_TX_OFF; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 22862306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 22962306a36Sopenharmony_ci DP_ERR(edev, "One-step timestamping is not supported\n"); 23062306a36Sopenharmony_ci return -ERANGE; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 23462306a36Sopenharmony_ci switch (ptp->rx_filter) { 23562306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 23662306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_NONE; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 23962306a36Sopenharmony_ci case HWTSTAMP_FILTER_SOME: 24062306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 24162306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_NONE; 24262306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_ALL; 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 24562306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; 24662306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V1_L4_EVENT; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 24962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 25062306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; 25162306a36Sopenharmony_ci /* Initialize PTP detection for UDP/IPv4 events */ 25262306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V1_L4_GEN; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 25562306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; 25662306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V2_L4_EVENT; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 25962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 26062306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; 26162306a36Sopenharmony_ci /* Initialize PTP detection for UDP/IPv4 or UDP/IPv6 events */ 26262306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V2_L4_GEN; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 26562306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; 26662306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V2_L2_EVENT; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 26962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 27062306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; 27162306a36Sopenharmony_ci /* Initialize PTP detection L2 events */ 27262306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V2_L2_GEN; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 27562306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 27662306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V2_EVENT; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 27962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 28062306a36Sopenharmony_ci ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 28162306a36Sopenharmony_ci /* Initialize PTP detection L2, UDP/IPv4 or UDP/IPv6 events */ 28262306a36Sopenharmony_ci rx_filter = QED_PTP_FILTER_V2_GEN; 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ptp->ops->cfg_filters(edev->cdev, rx_filter, tx_type); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciint qede_ptp_hw_ts(struct qede_dev *edev, struct ifreq *ifr) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct hwtstamp_config config; 29662306a36Sopenharmony_ci struct qede_ptp *ptp; 29762306a36Sopenharmony_ci int rc; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ptp = edev->ptp; 30062306a36Sopenharmony_ci if (!ptp) 30162306a36Sopenharmony_ci return -EIO; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 30462306a36Sopenharmony_ci return -EFAULT; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, 30762306a36Sopenharmony_ci "HWTSTAMP IOCTL: Requested tx_type = %d, requested rx_filters = %d\n", 30862306a36Sopenharmony_ci config.tx_type, config.rx_filter); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ptp->hw_ts_ioctl_called = 1; 31162306a36Sopenharmony_ci ptp->tx_type = config.tx_type; 31262306a36Sopenharmony_ci ptp->rx_filter = config.rx_filter; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci rc = qede_ptp_cfg_filters(edev); 31562306a36Sopenharmony_ci if (rc) 31662306a36Sopenharmony_ci return rc; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci config.rx_filter = ptp->rx_filter; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, 32162306a36Sopenharmony_ci sizeof(config)) ? -EFAULT : 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciint qede_ptp_get_ts_info(struct qede_dev *edev, struct ethtool_ts_info *info) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct qede_ptp *ptp = edev->ptp; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!ptp) { 32962306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 33062306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 33162306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 33262306a36Sopenharmony_ci info->phc_index = -1; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 33862306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 33962306a36Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE | 34062306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 34162306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 34262306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (ptp->clock) 34562306a36Sopenharmony_ci info->phc_index = ptp_clock_index(ptp->clock); 34662306a36Sopenharmony_ci else 34762306a36Sopenharmony_ci info->phc_index = -1; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 35062306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | 35162306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | 35262306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | 35362306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 35462306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | 35562306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | 35662306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | 35762306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | 35862306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | 35962306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | 36062306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) | 36162306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_civoid qede_ptp_disable(struct qede_dev *edev) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct qede_ptp *ptp; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ptp = edev->ptp; 37362306a36Sopenharmony_ci if (!ptp) 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (ptp->clock) { 37762306a36Sopenharmony_ci ptp_clock_unregister(ptp->clock); 37862306a36Sopenharmony_ci ptp->clock = NULL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Cancel PTP work queue. Should be done after the Tx queues are 38262306a36Sopenharmony_ci * drained to prevent additional scheduling. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci cancel_work_sync(&ptp->work); 38562306a36Sopenharmony_ci if (ptp->tx_skb) { 38662306a36Sopenharmony_ci dev_kfree_skb_any(ptp->tx_skb); 38762306a36Sopenharmony_ci ptp->tx_skb = NULL; 38862306a36Sopenharmony_ci clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Disable PTP in HW */ 39262306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 39362306a36Sopenharmony_ci ptp->ops->disable(edev->cdev); 39462306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci kfree(ptp); 39762306a36Sopenharmony_ci edev->ptp = NULL; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int qede_ptp_init(struct qede_dev *edev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct qede_ptp *ptp; 40362306a36Sopenharmony_ci int rc; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ptp = edev->ptp; 40662306a36Sopenharmony_ci if (!ptp) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci spin_lock_init(&ptp->lock); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Configure PTP in HW */ 41262306a36Sopenharmony_ci rc = ptp->ops->enable(edev->cdev); 41362306a36Sopenharmony_ci if (rc) { 41462306a36Sopenharmony_ci DP_INFO(edev, "PTP HW enable failed\n"); 41562306a36Sopenharmony_ci return rc; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* Init work queue for Tx timestamping */ 41962306a36Sopenharmony_ci INIT_WORK(&ptp->work, qede_ptp_task); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Init cyclecounter and timecounter */ 42262306a36Sopenharmony_ci memset(&ptp->cc, 0, sizeof(ptp->cc)); 42362306a36Sopenharmony_ci ptp->cc.read = qede_ptp_read_cc; 42462306a36Sopenharmony_ci ptp->cc.mask = CYCLECOUNTER_MASK(64); 42562306a36Sopenharmony_ci ptp->cc.shift = 0; 42662306a36Sopenharmony_ci ptp->cc.mult = 1; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci timecounter_init(&ptp->tc, &ptp->cc, ktime_to_ns(ktime_get_real())); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciint qede_ptp_enable(struct qede_dev *edev) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct qede_ptp *ptp; 43662306a36Sopenharmony_ci int rc; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ptp = kzalloc(sizeof(*ptp), GFP_KERNEL); 43962306a36Sopenharmony_ci if (!ptp) { 44062306a36Sopenharmony_ci DP_INFO(edev, "Failed to allocate struct for PTP\n"); 44162306a36Sopenharmony_ci return -ENOMEM; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ptp->edev = edev; 44562306a36Sopenharmony_ci ptp->ops = edev->ops->ptp; 44662306a36Sopenharmony_ci if (!ptp->ops) { 44762306a36Sopenharmony_ci DP_INFO(edev, "PTP enable failed\n"); 44862306a36Sopenharmony_ci rc = -EIO; 44962306a36Sopenharmony_ci goto err1; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci edev->ptp = ptp; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci rc = qede_ptp_init(edev); 45562306a36Sopenharmony_ci if (rc) 45662306a36Sopenharmony_ci goto err1; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci qede_ptp_cfg_filters(edev); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Fill the ptp_clock_info struct and register PTP clock */ 46162306a36Sopenharmony_ci ptp->clock_info.owner = THIS_MODULE; 46262306a36Sopenharmony_ci snprintf(ptp->clock_info.name, 16, "%s", edev->ndev->name); 46362306a36Sopenharmony_ci ptp->clock_info.max_adj = QED_MAX_PHC_DRIFT_PPB; 46462306a36Sopenharmony_ci ptp->clock_info.n_alarm = 0; 46562306a36Sopenharmony_ci ptp->clock_info.n_ext_ts = 0; 46662306a36Sopenharmony_ci ptp->clock_info.n_per_out = 0; 46762306a36Sopenharmony_ci ptp->clock_info.pps = 0; 46862306a36Sopenharmony_ci ptp->clock_info.adjfine = qede_ptp_adjfine; 46962306a36Sopenharmony_ci ptp->clock_info.adjtime = qede_ptp_adjtime; 47062306a36Sopenharmony_ci ptp->clock_info.gettime64 = qede_ptp_gettime; 47162306a36Sopenharmony_ci ptp->clock_info.settime64 = qede_ptp_settime; 47262306a36Sopenharmony_ci ptp->clock_info.enable = qede_ptp_ancillary_feature_enable; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ptp->clock = ptp_clock_register(&ptp->clock_info, &edev->pdev->dev); 47562306a36Sopenharmony_ci if (IS_ERR(ptp->clock)) { 47662306a36Sopenharmony_ci DP_ERR(edev, "PTP clock registration failed\n"); 47762306a36Sopenharmony_ci qede_ptp_disable(edev); 47862306a36Sopenharmony_ci rc = -EINVAL; 47962306a36Sopenharmony_ci goto err2; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cierr1: 48562306a36Sopenharmony_ci kfree(ptp); 48662306a36Sopenharmony_cierr2: 48762306a36Sopenharmony_ci edev->ptp = NULL; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return rc; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_civoid qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct qede_ptp *ptp; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ptp = edev->ptp; 49762306a36Sopenharmony_ci if (!ptp) 49862306a36Sopenharmony_ci return; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (test_and_set_bit_lock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, 50162306a36Sopenharmony_ci &edev->flags)) { 50262306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, "Timestamping in progress\n"); 50362306a36Sopenharmony_ci edev->ptp_skip_txts++; 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (unlikely(!test_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags))) { 50862306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, 50962306a36Sopenharmony_ci "Tx timestamping was not enabled, this pkt will not be timestamped\n"); 51062306a36Sopenharmony_ci clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags); 51162306a36Sopenharmony_ci edev->ptp_skip_txts++; 51262306a36Sopenharmony_ci } else if (unlikely(ptp->tx_skb)) { 51362306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, 51462306a36Sopenharmony_ci "Device supports a single outstanding pkt to ts, It will not be ts\n"); 51562306a36Sopenharmony_ci clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags); 51662306a36Sopenharmony_ci edev->ptp_skip_txts++; 51762306a36Sopenharmony_ci } else { 51862306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 51962306a36Sopenharmony_ci /* schedule check for Tx timestamp */ 52062306a36Sopenharmony_ci ptp->tx_skb = skb_get(skb); 52162306a36Sopenharmony_ci ptp->ptp_tx_start = jiffies; 52262306a36Sopenharmony_ci schedule_work(&ptp->work); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_civoid qede_ptp_rx_ts(struct qede_dev *edev, struct sk_buff *skb) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct qede_ptp *ptp; 52962306a36Sopenharmony_ci u64 timestamp, ns; 53062306a36Sopenharmony_ci int rc; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ptp = edev->ptp; 53362306a36Sopenharmony_ci if (!ptp) 53462306a36Sopenharmony_ci return; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci spin_lock_bh(&ptp->lock); 53762306a36Sopenharmony_ci rc = ptp->ops->read_rx_ts(edev->cdev, ×tamp); 53862306a36Sopenharmony_ci if (rc) { 53962306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 54062306a36Sopenharmony_ci DP_INFO(edev, "Invalid Rx timestamp\n"); 54162306a36Sopenharmony_ci return; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ns = timecounter_cyc2time(&ptp->tc, timestamp); 54562306a36Sopenharmony_ci spin_unlock_bh(&ptp->lock); 54662306a36Sopenharmony_ci skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(ns); 54762306a36Sopenharmony_ci DP_VERBOSE(edev, QED_MSG_DEBUG, 54862306a36Sopenharmony_ci "Rx timestamp, timestamp cycles = %llu, ns = %llu\n", 54962306a36Sopenharmony_ci timestamp, ns); 55062306a36Sopenharmony_ci} 551