162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2017 - 2021 Pensando Systems, Inc */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/netdevice.h> 562306a36Sopenharmony_ci#include <linux/etherdevice.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "ionic.h" 862306a36Sopenharmony_ci#include "ionic_bus.h" 962306a36Sopenharmony_ci#include "ionic_lif.h" 1062306a36Sopenharmony_ci#include "ionic_ethtool.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int ionic_hwstamp_tx_mode(int config_tx_type) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci switch (config_tx_type) { 1562306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 1662306a36Sopenharmony_ci return IONIC_TXSTAMP_OFF; 1762306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 1862306a36Sopenharmony_ci return IONIC_TXSTAMP_ON; 1962306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 2062306a36Sopenharmony_ci return IONIC_TXSTAMP_ONESTEP_SYNC; 2162306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 2262306a36Sopenharmony_ci return IONIC_TXSTAMP_ONESTEP_P2P; 2362306a36Sopenharmony_ci default: 2462306a36Sopenharmony_ci return -ERANGE; 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic u64 ionic_hwstamp_rx_filt(int config_rx_filter) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci switch (config_rx_filter) { 3162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 3262306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP1_ALL; 3362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 3462306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP1_SYNC; 3562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 3662306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP1_SYNC | IONIC_PKT_CLS_PTP1_DREQ; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 3962306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_L4_ALL; 4062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 4162306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_L4_SYNC; 4262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 4362306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_L4_SYNC | IONIC_PKT_CLS_PTP2_L4_DREQ; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 4662306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_L2_ALL; 4762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 4862306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_L2_SYNC; 4962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 5062306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_L2_SYNC | IONIC_PKT_CLS_PTP2_L2_DREQ; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 5362306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_ALL; 5462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 5562306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_SYNC; 5662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 5762306a36Sopenharmony_ci return IONIC_PKT_CLS_PTP2_SYNC | IONIC_PKT_CLS_PTP2_DREQ; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 6062306a36Sopenharmony_ci return IONIC_PKT_CLS_NTP_ALL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci default: 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif, 6862306a36Sopenharmony_ci struct hwtstamp_config *new_ts) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 7162306a36Sopenharmony_ci struct hwtstamp_config *config; 7262306a36Sopenharmony_ci struct hwtstamp_config ts; 7362306a36Sopenharmony_ci int tx_mode = 0; 7462306a36Sopenharmony_ci u64 rx_filt = 0; 7562306a36Sopenharmony_ci int err, err2; 7662306a36Sopenharmony_ci bool rx_all; 7762306a36Sopenharmony_ci __le64 mask; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (!lif->phc || !lif->phc->ptp) 8062306a36Sopenharmony_ci return -EOPNOTSUPP; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci mutex_lock(&lif->phc->config_lock); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (new_ts) { 8562306a36Sopenharmony_ci config = new_ts; 8662306a36Sopenharmony_ci } else { 8762306a36Sopenharmony_ci /* If called with new_ts == NULL, replay the previous request 8862306a36Sopenharmony_ci * primarily for recovery after a FW_RESET. 8962306a36Sopenharmony_ci * We saved the previous configuration request info, so copy 9062306a36Sopenharmony_ci * the previous request for reference, clear the current state 9162306a36Sopenharmony_ci * to match the device's reset state, and run with it. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci config = &ts; 9462306a36Sopenharmony_ci memcpy(config, &lif->phc->ts_config, sizeof(*config)); 9562306a36Sopenharmony_ci memset(&lif->phc->ts_config, 0, sizeof(lif->phc->ts_config)); 9662306a36Sopenharmony_ci lif->phc->ts_config_tx_mode = 0; 9762306a36Sopenharmony_ci lif->phc->ts_config_rx_filt = 0; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci tx_mode = ionic_hwstamp_tx_mode(config->tx_type); 10162306a36Sopenharmony_ci if (tx_mode < 0) { 10262306a36Sopenharmony_ci err = tx_mode; 10362306a36Sopenharmony_ci goto err_queues; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mask = cpu_to_le64(BIT_ULL(tx_mode)); 10762306a36Sopenharmony_ci if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) { 10862306a36Sopenharmony_ci err = -ERANGE; 10962306a36Sopenharmony_ci goto err_queues; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci rx_filt = ionic_hwstamp_rx_filt(config->rx_filter); 11362306a36Sopenharmony_ci rx_all = config->rx_filter != HWTSTAMP_FILTER_NONE && !rx_filt; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci mask = cpu_to_le64(rx_filt); 11662306a36Sopenharmony_ci if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) != mask) { 11762306a36Sopenharmony_ci rx_filt = 0; 11862306a36Sopenharmony_ci rx_all = true; 11962306a36Sopenharmony_ci config->rx_filter = HWTSTAMP_FILTER_ALL; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n", 12362306a36Sopenharmony_ci __func__, config->rx_filter, rx_filt, rx_all); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (tx_mode) { 12662306a36Sopenharmony_ci err = ionic_lif_create_hwstamp_txq(lif); 12762306a36Sopenharmony_ci if (err) 12862306a36Sopenharmony_ci goto err_queues; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (rx_filt) { 13262306a36Sopenharmony_ci err = ionic_lif_create_hwstamp_rxq(lif); 13362306a36Sopenharmony_ci if (err) 13462306a36Sopenharmony_ci goto err_queues; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (tx_mode != lif->phc->ts_config_tx_mode) { 13862306a36Sopenharmony_ci err = ionic_lif_set_hwstamp_txmode(lif, tx_mode); 13962306a36Sopenharmony_ci if (err) 14062306a36Sopenharmony_ci goto err_txmode; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (rx_filt != lif->phc->ts_config_rx_filt) { 14462306a36Sopenharmony_ci err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt); 14562306a36Sopenharmony_ci if (err) 14662306a36Sopenharmony_ci goto err_rxfilt; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) { 15062306a36Sopenharmony_ci err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all); 15162306a36Sopenharmony_ci if (err) 15262306a36Sopenharmony_ci goto err_rxall; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci memcpy(&lif->phc->ts_config, config, sizeof(*config)); 15662306a36Sopenharmony_ci lif->phc->ts_config_rx_filt = rx_filt; 15762306a36Sopenharmony_ci lif->phc->ts_config_tx_mode = tx_mode; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci mutex_unlock(&lif->phc->config_lock); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cierr_rxall: 16462306a36Sopenharmony_ci if (rx_filt != lif->phc->ts_config_rx_filt) { 16562306a36Sopenharmony_ci rx_filt = lif->phc->ts_config_rx_filt; 16662306a36Sopenharmony_ci err2 = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt); 16762306a36Sopenharmony_ci if (err2) 16862306a36Sopenharmony_ci dev_err(ionic->dev, 16962306a36Sopenharmony_ci "Failed to revert rx timestamp filter: %d\n", err2); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_cierr_rxfilt: 17262306a36Sopenharmony_ci if (tx_mode != lif->phc->ts_config_tx_mode) { 17362306a36Sopenharmony_ci tx_mode = lif->phc->ts_config_tx_mode; 17462306a36Sopenharmony_ci err2 = ionic_lif_set_hwstamp_txmode(lif, tx_mode); 17562306a36Sopenharmony_ci if (err2) 17662306a36Sopenharmony_ci dev_err(ionic->dev, 17762306a36Sopenharmony_ci "Failed to revert tx timestamp mode: %d\n", err2); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_cierr_txmode: 18062306a36Sopenharmony_ci /* special queues remain allocated, just unused */ 18162306a36Sopenharmony_cierr_queues: 18262306a36Sopenharmony_ci mutex_unlock(&lif->phc->config_lock); 18362306a36Sopenharmony_ci return err; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ciint ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct hwtstamp_config config; 18962306a36Sopenharmony_ci int err; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!lif->phc || !lif->phc->ptp) 19262306a36Sopenharmony_ci return -EOPNOTSUPP; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 19562306a36Sopenharmony_ci return -EFAULT; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 19862306a36Sopenharmony_ci err = ionic_lif_hwstamp_set_ts_config(lif, &config); 19962306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 20062306a36Sopenharmony_ci if (err) { 20162306a36Sopenharmony_ci netdev_info(lif->netdev, "hwstamp set failed: %d\n", err); 20262306a36Sopenharmony_ci return err; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) 20662306a36Sopenharmony_ci return -EFAULT; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_civoid ionic_lif_hwstamp_replay(struct ionic_lif *lif) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int err; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!lif->phc || !lif->phc->ptp) 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci mutex_lock(&lif->queue_lock); 21962306a36Sopenharmony_ci err = ionic_lif_hwstamp_set_ts_config(lif, NULL); 22062306a36Sopenharmony_ci mutex_unlock(&lif->queue_lock); 22162306a36Sopenharmony_ci if (err) 22262306a36Sopenharmony_ci netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_civoid ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci int err; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (!lif->phc || !lif->phc->ptp) 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci mutex_lock(&lif->phc->config_lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (lif->phc->ts_config_tx_mode) { 23562306a36Sopenharmony_ci err = ionic_lif_create_hwstamp_txq(lif); 23662306a36Sopenharmony_ci if (err) 23762306a36Sopenharmony_ci netdev_info(lif->netdev, "hwstamp recreate txq failed: %d\n", err); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (lif->phc->ts_config_rx_filt) { 24162306a36Sopenharmony_ci err = ionic_lif_create_hwstamp_rxq(lif); 24262306a36Sopenharmony_ci if (err) 24362306a36Sopenharmony_ci netdev_info(lif->netdev, "hwstamp recreate rxq failed: %d\n", err); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mutex_unlock(&lif->phc->config_lock); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct hwtstamp_config config; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!lif->phc || !lif->phc->ptp) 25462306a36Sopenharmony_ci return -EOPNOTSUPP; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci mutex_lock(&lif->phc->config_lock); 25762306a36Sopenharmony_ci memcpy(&config, &lif->phc->ts_config, sizeof(config)); 25862306a36Sopenharmony_ci mutex_unlock(&lif->phc->config_lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) 26162306a36Sopenharmony_ci return -EFAULT; 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic u64 ionic_hwstamp_read(struct ionic *ionic, 26662306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u32 tick_high_before, tick_high, tick_low; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* read and discard low part to defeat hw staging of high part */ 27162306a36Sopenharmony_ci ioread32(&ionic->idev.hwstamp_regs->tick_low); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci tick_high_before = ioread32(&ionic->idev.hwstamp_regs->tick_high); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ptp_read_system_prets(sts); 27662306a36Sopenharmony_ci tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low); 27762306a36Sopenharmony_ci ptp_read_system_postts(sts); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci tick_high = ioread32(&ionic->idev.hwstamp_regs->tick_high); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* If tick_high changed, re-read tick_low once more. Assume tick_high 28262306a36Sopenharmony_ci * cannot change again so soon as in the span of re-reading tick_low. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if (tick_high != tick_high_before) { 28562306a36Sopenharmony_ci ptp_read_system_prets(sts); 28662306a36Sopenharmony_ci tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low); 28762306a36Sopenharmony_ci ptp_read_system_postts(sts); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return (u64)tick_low | ((u64)tick_high << 32); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic u64 ionic_cc_read(const struct cyclecounter *cc) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct ionic_phc *phc = container_of(cc, struct ionic_phc, cc); 29662306a36Sopenharmony_ci struct ionic *ionic = phc->lif->ionic; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return ionic_hwstamp_read(ionic, NULL); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci ctx->work = COMPLETION_INITIALIZER_ONSTACK(ctx->work); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ctx->cmd.lif_setphc.opcode = IONIC_CMD_LIF_SETPHC; 30662306a36Sopenharmony_ci ctx->cmd.lif_setphc.lif_index = cpu_to_le16(phc->lif->index); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ctx->cmd.lif_setphc.tick = cpu_to_le64(phc->tc.cycle_last); 30962306a36Sopenharmony_ci ctx->cmd.lif_setphc.nsec = cpu_to_le64(phc->tc.nsec); 31062306a36Sopenharmony_ci ctx->cmd.lif_setphc.frac = cpu_to_le64(phc->tc.frac); 31162306a36Sopenharmony_ci ctx->cmd.lif_setphc.mult = cpu_to_le32(phc->cc.mult); 31262306a36Sopenharmony_ci ctx->cmd.lif_setphc.shift = cpu_to_le32(phc->cc.shift); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return ionic_adminq_post(phc->lif, ctx); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); 32062306a36Sopenharmony_ci struct ionic_admin_ctx ctx = {}; 32162306a36Sopenharmony_ci unsigned long irqflags; 32262306a36Sopenharmony_ci s64 adj; 32362306a36Sopenharmony_ci int err; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Reject phc adjustments during device upgrade */ 32662306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) 32762306a36Sopenharmony_ci return -EBUSY; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Adjustment value scaled by 2^16 million */ 33062306a36Sopenharmony_ci adj = (s64)scaled_ppm * phc->init_cc_mult; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Adjustment value to scale */ 33362306a36Sopenharmony_ci adj /= (s64)SCALED_PPM; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Final adjusted multiplier */ 33662306a36Sopenharmony_ci adj += phc->init_cc_mult; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci spin_lock_irqsave(&phc->lock, irqflags); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* update the point-in-time basis to now, before adjusting the rate */ 34162306a36Sopenharmony_ci timecounter_read(&phc->tc); 34262306a36Sopenharmony_ci phc->cc.mult = adj; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Setphc commands are posted in-order, sequenced by phc->lock. We 34562306a36Sopenharmony_ci * need to drop the lock before waiting for the command to complete. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci err = ionic_setphc_cmd(phc, &ctx); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci spin_unlock_irqrestore(&phc->lock, irqflags); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return ionic_adminq_wait(phc->lif, &ctx, err, true); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); 35762306a36Sopenharmony_ci struct ionic_admin_ctx ctx = {}; 35862306a36Sopenharmony_ci unsigned long irqflags; 35962306a36Sopenharmony_ci int err; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Reject phc adjustments during device upgrade */ 36262306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) 36362306a36Sopenharmony_ci return -EBUSY; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci spin_lock_irqsave(&phc->lock, irqflags); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci timecounter_adjtime(&phc->tc, delta); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Setphc commands are posted in-order, sequenced by phc->lock. We 37062306a36Sopenharmony_ci * need to drop the lock before waiting for the command to complete. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci err = ionic_setphc_cmd(phc, &ctx); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci spin_unlock_irqrestore(&phc->lock, irqflags); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return ionic_adminq_wait(phc->lif, &ctx, err, true); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int ionic_phc_settime64(struct ptp_clock_info *info, 38062306a36Sopenharmony_ci const struct timespec64 *ts) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); 38362306a36Sopenharmony_ci struct ionic_admin_ctx ctx = {}; 38462306a36Sopenharmony_ci unsigned long irqflags; 38562306a36Sopenharmony_ci int err; 38662306a36Sopenharmony_ci u64 ns; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Reject phc adjustments during device upgrade */ 38962306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) 39062306a36Sopenharmony_ci return -EBUSY; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ns = timespec64_to_ns(ts); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci spin_lock_irqsave(&phc->lock, irqflags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci timecounter_init(&phc->tc, &phc->cc, ns); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Setphc commands are posted in-order, sequenced by phc->lock. We 39962306a36Sopenharmony_ci * need to drop the lock before waiting for the command to complete. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci err = ionic_setphc_cmd(phc, &ctx); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci spin_unlock_irqrestore(&phc->lock, irqflags); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return ionic_adminq_wait(phc->lif, &ctx, err, true); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int ionic_phc_gettimex64(struct ptp_clock_info *info, 40962306a36Sopenharmony_ci struct timespec64 *ts, 41062306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); 41362306a36Sopenharmony_ci struct ionic *ionic = phc->lif->ionic; 41462306a36Sopenharmony_ci unsigned long irqflags; 41562306a36Sopenharmony_ci u64 tick, ns; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Do not attempt to read device time during upgrade */ 41862306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) 41962306a36Sopenharmony_ci return -EBUSY; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci spin_lock_irqsave(&phc->lock, irqflags); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci tick = ionic_hwstamp_read(ionic, sts); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ns = timecounter_cyc2time(&phc->tc, tick); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci spin_unlock_irqrestore(&phc->lock, irqflags); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic long ionic_phc_aux_work(struct ptp_clock_info *info) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); 43762306a36Sopenharmony_ci struct ionic_admin_ctx ctx = {}; 43862306a36Sopenharmony_ci unsigned long irqflags; 43962306a36Sopenharmony_ci int err; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Do not update phc during device upgrade, but keep polling to resume 44262306a36Sopenharmony_ci * after upgrade. Since we don't update the point in time basis, there 44362306a36Sopenharmony_ci * is no expectation that we are maintaining the phc time during the 44462306a36Sopenharmony_ci * upgrade. After upgrade, it will need to be readjusted back to the 44562306a36Sopenharmony_ci * correct time by the ptp daemon. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) 44862306a36Sopenharmony_ci return phc->aux_work_delay; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci spin_lock_irqsave(&phc->lock, irqflags); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* update point-in-time basis to now */ 45362306a36Sopenharmony_ci timecounter_read(&phc->tc); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Setphc commands are posted in-order, sequenced by phc->lock. We 45662306a36Sopenharmony_ci * need to drop the lock before waiting for the command to complete. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci err = ionic_setphc_cmd(phc, &ctx); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci spin_unlock_irqrestore(&phc->lock, irqflags); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ionic_adminq_wait(phc->lif, &ctx, err, true); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return phc->aux_work_delay; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ciktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 tick) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci unsigned long irqflags; 47062306a36Sopenharmony_ci u64 ns; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!lif->phc) 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock_irqsave(&lif->phc->lock, irqflags); 47662306a36Sopenharmony_ci ns = timecounter_cyc2time(&lif->phc->tc, tick); 47762306a36Sopenharmony_ci spin_unlock_irqrestore(&lif->phc->lock, irqflags); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ns_to_ktime(ns); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic const struct ptp_clock_info ionic_ptp_info = { 48362306a36Sopenharmony_ci .owner = THIS_MODULE, 48462306a36Sopenharmony_ci .name = "ionic_ptp", 48562306a36Sopenharmony_ci .adjfine = ionic_phc_adjfine, 48662306a36Sopenharmony_ci .adjtime = ionic_phc_adjtime, 48762306a36Sopenharmony_ci .gettimex64 = ionic_phc_gettimex64, 48862306a36Sopenharmony_ci .settime64 = ionic_phc_settime64, 48962306a36Sopenharmony_ci .do_aux_work = ionic_phc_aux_work, 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_civoid ionic_lif_register_phc(struct ionic_lif *lif) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci if (!lif->phc || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) 49562306a36Sopenharmony_ci return; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci lif->phc->ptp = ptp_clock_register(&lif->phc->ptp_info, lif->ionic->dev); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (IS_ERR(lif->phc->ptp)) { 50062306a36Sopenharmony_ci dev_warn(lif->ionic->dev, "Cannot register phc device: %ld\n", 50162306a36Sopenharmony_ci PTR_ERR(lif->phc->ptp)); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci lif->phc->ptp = NULL; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (lif->phc->ptp) 50762306a36Sopenharmony_ci ptp_schedule_worker(lif->phc->ptp, lif->phc->aux_work_delay); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_civoid ionic_lif_unregister_phc(struct ionic_lif *lif) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci if (!lif->phc || !lif->phc->ptp) 51362306a36Sopenharmony_ci return; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ptp_clock_unregister(lif->phc->ptp); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci lif->phc->ptp = NULL; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_civoid ionic_lif_alloc_phc(struct ionic_lif *lif) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct ionic *ionic = lif->ionic; 52362306a36Sopenharmony_ci struct ionic_phc *phc; 52462306a36Sopenharmony_ci u64 delay, diff, mult; 52562306a36Sopenharmony_ci u64 frac = 0; 52662306a36Sopenharmony_ci u64 features; 52762306a36Sopenharmony_ci u32 shift; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!ionic->idev.hwstamp_regs) 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci features = le64_to_cpu(ionic->ident.lif.eth.config.features); 53362306a36Sopenharmony_ci if (!(features & IONIC_ETH_HW_TIMESTAMP)) 53462306a36Sopenharmony_ci return; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci phc = devm_kzalloc(ionic->dev, sizeof(*phc), GFP_KERNEL); 53762306a36Sopenharmony_ci if (!phc) 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci phc->lif = lif; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci phc->cc.read = ionic_cc_read; 54362306a36Sopenharmony_ci phc->cc.mask = le64_to_cpu(ionic->ident.dev.hwstamp_mask); 54462306a36Sopenharmony_ci phc->cc.mult = le32_to_cpu(ionic->ident.dev.hwstamp_mult); 54562306a36Sopenharmony_ci phc->cc.shift = le32_to_cpu(ionic->ident.dev.hwstamp_shift); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!phc->cc.mult) { 54862306a36Sopenharmony_ci dev_err(lif->ionic->dev, 54962306a36Sopenharmony_ci "Invalid device PHC mask multiplier %u, disabling HW timestamp support\n", 55062306a36Sopenharmony_ci phc->cc.mult); 55162306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, phc); 55262306a36Sopenharmony_ci lif->phc = NULL; 55362306a36Sopenharmony_ci return; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci dev_dbg(lif->ionic->dev, "Device PHC mask %#llx mult %u shift %u\n", 55762306a36Sopenharmony_ci phc->cc.mask, phc->cc.mult, phc->cc.shift); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci spin_lock_init(&phc->lock); 56062306a36Sopenharmony_ci mutex_init(&phc->config_lock); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* max ticks is limited by the multiplier, or by the update period. */ 56362306a36Sopenharmony_ci if (phc->cc.shift + 2 + ilog2(IONIC_PHC_UPDATE_NS) >= 64) { 56462306a36Sopenharmony_ci /* max ticks that do not overflow when multiplied by max 56562306a36Sopenharmony_ci * adjusted multiplier (twice the initial multiplier) 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci diff = U64_MAX / phc->cc.mult / 2; 56862306a36Sopenharmony_ci } else { 56962306a36Sopenharmony_ci /* approx ticks at four times the update period */ 57062306a36Sopenharmony_ci diff = (u64)IONIC_PHC_UPDATE_NS << (phc->cc.shift + 2); 57162306a36Sopenharmony_ci diff = DIV_ROUND_UP(diff, phc->cc.mult); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* transform to bitmask */ 57562306a36Sopenharmony_ci diff |= diff >> 1; 57662306a36Sopenharmony_ci diff |= diff >> 2; 57762306a36Sopenharmony_ci diff |= diff >> 4; 57862306a36Sopenharmony_ci diff |= diff >> 8; 57962306a36Sopenharmony_ci diff |= diff >> 16; 58062306a36Sopenharmony_ci diff |= diff >> 32; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* constrain to the hardware bitmask */ 58362306a36Sopenharmony_ci diff &= phc->cc.mask; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* the wrap period is now defined by diff 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * we will update the time basis at about 1/4 the wrap period, so 58862306a36Sopenharmony_ci * should not see a difference of more than +/- diff/4. 58962306a36Sopenharmony_ci * 59062306a36Sopenharmony_ci * this is sufficient not see a difference of more than +/- diff/2, as 59162306a36Sopenharmony_ci * required by timecounter_cyc2time, to detect an old time stamp. 59262306a36Sopenharmony_ci * 59362306a36Sopenharmony_ci * adjust the initial multiplier, being careful to avoid overflow: 59462306a36Sopenharmony_ci * - do not overflow 63 bits: init_cc_mult * SCALED_PPM 59562306a36Sopenharmony_ci * - do not overflow 64 bits: max_mult * (diff / 2) 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * we want to increase the initial multiplier as much as possible, to 59862306a36Sopenharmony_ci * allow for more precise adjustment in ionic_phc_adjfine. 59962306a36Sopenharmony_ci * 60062306a36Sopenharmony_ci * only adjust the multiplier if we can double it or more. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci mult = U64_MAX / 2 / max(diff / 2, SCALED_PPM); 60362306a36Sopenharmony_ci shift = mult / phc->cc.mult; 60462306a36Sopenharmony_ci if (shift >= 2) { 60562306a36Sopenharmony_ci /* initial multiplier will be 2^n of hardware cc.mult */ 60662306a36Sopenharmony_ci shift = fls(shift); 60762306a36Sopenharmony_ci /* increase cc.mult and cc.shift by the same 2^n and n. */ 60862306a36Sopenharmony_ci phc->cc.mult <<= shift; 60962306a36Sopenharmony_ci phc->cc.shift += shift; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci dev_dbg(lif->ionic->dev, "Initial PHC mask %#llx mult %u shift %u\n", 61362306a36Sopenharmony_ci phc->cc.mask, phc->cc.mult, phc->cc.shift); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* frequency adjustments are relative to the initial multiplier */ 61662306a36Sopenharmony_ci phc->init_cc_mult = phc->cc.mult; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci timecounter_init(&phc->tc, &phc->cc, ktime_get_real_ns()); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Update cycle_last at 1/4 the wrap period, or IONIC_PHC_UPDATE_NS */ 62162306a36Sopenharmony_ci delay = min_t(u64, IONIC_PHC_UPDATE_NS, 62262306a36Sopenharmony_ci cyclecounter_cyc2ns(&phc->cc, diff / 4, 0, &frac)); 62362306a36Sopenharmony_ci dev_dbg(lif->ionic->dev, "Work delay %llu ms\n", delay / NSEC_PER_MSEC); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci phc->aux_work_delay = nsecs_to_jiffies(delay); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci phc->ptp_info = ionic_ptp_info; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* We have allowed to adjust the multiplier up to +/- 1 part per 1. 63062306a36Sopenharmony_ci * Here expressed as NORMAL_PPB (1 billion parts per billion). 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci phc->ptp_info.max_adj = NORMAL_PPB; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci lif->phc = phc; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_civoid ionic_lif_free_phc(struct ionic_lif *lif) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci if (!lif->phc) 64062306a36Sopenharmony_ci return; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci mutex_destroy(&lif->phc->config_lock); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci devm_kfree(lif->ionic->dev, lif->phc); 64562306a36Sopenharmony_ci lif->phc = NULL; 64662306a36Sopenharmony_ci} 647