// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2017 - 2021 Pensando Systems, Inc */ #include <linux/netdevice.h>#include <linux/etherdevice.h>#include <uapi/rdma/ib_user_verbs.h> #include "ionic.h"#include "ionic_bus.h"#include "ionic_lif.h"#include "ionic_ethtool.h" static int ionic_hwstamp_tx_mode(int config_tx_type){ switch (config_tx_type) { case HWTSTAMP_TX_OFF: return IONIC_TXSTAMP_OFF; case HWTSTAMP_TX_ON: return IONIC_TXSTAMP_ON; case HWTSTAMP_TX_ONESTEP_SYNC: return IONIC_TXSTAMP_ONESTEP_SYNC; case HWTSTAMP_TX_ONESTEP_P2P: return IONIC_TXSTAMP_ONESTEP_P2P; default: return -ERANGE; }} static u64 ionic_hwstamp_rx_filt(int config_rx_filter){ switch (config_rx_filter) { case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: return IONIC_PKT_CLS_PTP1_ALL; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: return IONIC_PKT_CLS_PTP1_SYNC; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: return IONIC_PKT_CLS_PTP1_SYNC | IONIC_PKT_CLS_PTP1_DREQ; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: return IONIC_PKT_CLS_PTP2_L4_ALL; case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: return IONIC_PKT_CLS_PTP2_L4_SYNC; case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: return IONIC_PKT_CLS_PTP2_L4_SYNC | IONIC_PKT_CLS_PTP2_L4_DREQ; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: return IONIC_PKT_CLS_PTP2_L2_ALL; case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: return IONIC_PKT_CLS_PTP2_L2_SYNC; case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: return IONIC_PKT_CLS_PTP2_L2_SYNC | IONIC_PKT_CLS_PTP2_L2_DREQ; case HWTSTAMP_FILTER_PTP_V2_EVENT: return IONIC_PKT_CLS_PTP2_ALL; case HWTSTAMP_FILTER_PTP_V2_SYNC: return IONIC_PKT_CLS_PTP2_SYNC; case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: return IONIC_PKT_CLS_PTP2_SYNC | IONIC_PKT_CLS_PTP2_DREQ; case HWTSTAMP_FILTER_NTP_ALL: return IONIC_PKT_CLS_NTP_ALL; default: return 0; }} static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif, struct kernel_hwtstamp_config *new_ts, struct netlink_ext_ack *extack){ struct kernel_hwtstamp_config *config; struct kernel_hwtstamp_config ts = {}; struct ionic *ionic = lif->ionic; int tx_mode = 0; u64 rx_filt = 0; int err, err2; bool rx_all; __le64 mask; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->phc->config_lock); if (new_ts) { config = new_ts; } else { /* If called with new_ts == NULL, replay the previous request * primarily for recovery after a FW_RESET. * We saved the previous configuration request info, so copy * the previous request for reference, clear the current state * to match the device's reset state, and run with it. */ config = &ts; memcpy(config, &lif->phc->ts_config, sizeof(*config)); memset(&lif->phc->ts_config, 0, sizeof(lif->phc->ts_config)); lif->phc->ts_config_tx_mode = 0; lif->phc->ts_config_rx_filt = 0; } tx_mode = ionic_hwstamp_tx_mode(config->tx_type); if (tx_mode < 0) { NL_SET_ERR_MSG_MOD(extack, "TX time stamping mode isn't supported"); err = tx_mode; goto err_queues; } mask = cpu_to_le64(BIT_ULL(tx_mode)); if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) { NL_SET_ERR_MSG_MOD(extack, "TX time stamping mode isn't supported"); err = -ERANGE; goto err_queues; } rx_filt = ionic_hwstamp_rx_filt(config->rx_filter); rx_all = config->rx_filter != HWTSTAMP_FILTER_NONE && !rx_filt; mask = cpu_to_le64(rx_filt); if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) != mask) { rx_filt = 0; rx_all = true; config->rx_filter = HWTSTAMP_FILTER_ALL; } dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n", __func__, config->rx_filter, rx_filt, rx_all); if (tx_mode) { err = ionic_lif_create_hwstamp_txq(lif); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error creating TX timestamp queue"); goto err_queues; } } if (rx_filt) { err = ionic_lif_create_hwstamp_rxq(lif); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error creating RX timestamp queue"); goto err_queues; } } if (tx_mode != lif->phc->ts_config_tx_mode) { err = ionic_lif_set_hwstamp_txmode(lif, tx_mode); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error enabling TX timestamp mode"); goto err_txmode; } } if (rx_filt != lif->phc->ts_config_rx_filt) { err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error enabling RX timestamp mode"); goto err_rxfilt; } } if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) { err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error enabling RX timestamp mode"); goto err_rxall; } } memcpy(&lif->phc->ts_config, config, sizeof(*config)); lif->phc->ts_config_rx_filt = rx_filt; lif->phc->ts_config_tx_mode = tx_mode; mutex_unlock(&lif->phc->config_lock); return 0; err_rxall: if (rx_filt != lif->phc->ts_config_rx_filt) { rx_filt = lif->phc->ts_config_rx_filt; err2 = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt); if (err2) dev_err(ionic->dev, "Failed to revert rx timestamp filter: %d\n", err2); }err_rxfilt: if (tx_mode != lif->phc->ts_config_tx_mode) { tx_mode = lif->phc->ts_config_tx_mode; err2 = ionic_lif_set_hwstamp_txmode(lif, tx_mode); if (err2) dev_err(ionic->dev, "Failed to revert tx timestamp mode: %d\n", err2); }err_txmode: /* special queues remain allocated, just unused */err_queues: mutex_unlock(&lif->phc->config_lock); return err;} int ionic_hwstamp_set(struct net_device *netdev, struct kernel_hwtstamp_config *config, struct netlink_ext_ack *extack){ struct ionic_lif *lif = netdev_priv(netdev); int err; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->queue_lock); err = ionic_lif_hwstamp_set_ts_config(lif, config, extack); mutex_unlock(&lif->queue_lock); if (err) { netdev_info(lif->netdev, "hwstamp set failed: %d\n", err); return err; } return 0;} void ionic_lif_hwstamp_replay(struct ionic_lif *lif){ int err; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return; mutex_lock(&lif->queue_lock); err = ionic_lif_hwstamp_set_ts_config(lif, NULL, NULL); mutex_unlock(&lif->queue_lock); if (err) netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err);} void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif){ int err; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return; mutex_lock(&lif->phc->config_lock); if (lif->phc->ts_config_tx_mode) { err = ionic_lif_create_hwstamp_txq(lif); if (err) netdev_info(lif->netdev, "hwstamp recreate txq failed: %d\n", err); } if (lif->phc->ts_config_rx_filt) { err = ionic_lif_create_hwstamp_rxq(lif); if (err) netdev_info(lif->netdev, "hwstamp recreate rxq failed: %d\n", err); } mutex_unlock(&lif->phc->config_lock);} int ionic_hwstamp_get(struct net_device *netdev, struct kernel_hwtstamp_config *config){ struct ionic_lif *lif = netdev_priv(netdev); if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->phc->config_lock); memcpy(config, &lif->phc->ts_config, sizeof(*config)); mutex_unlock(&lif->phc->config_lock); return 0;} static u64 ionic_hwstamp_read(struct ionic *ionic, struct ptp_system_timestamp *sts){ u32 tick_high_before, tick_high, tick_low; /* read and discard low part to defeat hw staging of high part */ ioread32(&ionic->idev.hwstamp_regs->tick_low); tick_high_before = ioread32(&ionic->idev.hwstamp_regs->tick_high); ptp_read_system_prets(sts); tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low); ptp_read_system_postts(sts); tick_high = ioread32(&ionic->idev.hwstamp_regs->tick_high); /* If tick_high changed, re-read tick_low once more. Assume tick_high * cannot change again so soon as in the span of re-reading tick_low. */ if (tick_high != tick_high_before) { ptp_read_system_prets(sts); tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low); ptp_read_system_postts(sts); } return (u64)tick_low | ((u64)tick_high << 32);} static u64 ionic_cc_read(struct cyclecounter *cc){ struct ionic_phc *phc = container_of(cc, struct ionic_phc, cc); struct ionic *ionic = phc->lif->ionic; return ionic_hwstamp_read(ionic, NULL);} static int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx){ ctx->work = COMPLETION_INITIALIZER_ONSTACK(ctx->work); ctx->cmd.lif_setphc.opcode = IONIC_CMD_LIF_SETPHC; ctx->cmd.lif_setphc.lif_index = cpu_to_le16(phc->lif->index); ctx->cmd.lif_setphc.tick = cpu_to_le64(phc->tc.cycle_last); ctx->cmd.lif_setphc.nsec = cpu_to_le64(phc->tc.nsec); ctx->cmd.lif_setphc.frac = cpu_to_le64(phc->tc.frac); ctx->cmd.lif_setphc.mult = cpu_to_le32(phc->cc.mult); ctx->cmd.lif_setphc.shift = cpu_to_le32(phc->cc.shift); return ionic_adminq_post(phc->lif, ctx);} static void ionic_phc_state_page_update(struct ionic_phc *phc){ struct ib_uverbs_clock_info *state = phc->state_page; u32 sign; /* read current sign */ sign = smp_load_acquire(&state->sign) & ~1; /* make sign odd for updating */ smp_store_mb(state->sign, sign | 1); state->cycles = phc->tc.cycle_last; state->nsec = phc->tc.nsec; state->frac = phc->tc.frac; state->mult = phc->cc.mult; /* make sign the next even number for update completed */ smp_store_release(&state->sign, sign + 2);} static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm){ struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; s64 adj; int err; /* Reject phc adjustments during device upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; /* Adjustment value scaled by 2^16 million */ adj = (s64)scaled_ppm * phc->init_cc_mult; /* Adjustment value to scale */ adj /= (s64)SCALED_PPM; /* Final adjusted multiplier */ adj += phc->init_cc_mult; spin_lock_irqsave(&phc->lock, irqflags); /* update the point-in-time basis to now, before adjusting the rate */ timecounter_read(&phc->tc); phc->cc.mult = adj; ionic_phc_state_page_update(phc); /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ err = ionic_setphc_cmd(phc, &ctx); spin_unlock_irqrestore(&phc->lock, irqflags); return ionic_adminq_wait(phc->lif, &ctx, err, true);} static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta){ struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; int err; /* Reject phc adjustments during device upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; spin_lock_irqsave(&phc->lock, irqflags); timecounter_adjtime(&phc->tc, delta); ionic_phc_state_page_update(phc); /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ err = ionic_setphc_cmd(phc, &ctx); spin_unlock_irqrestore(&phc->lock, irqflags); return ionic_adminq_wait(phc->lif, &ctx, err, true);} static int ionic_phc_settime64(struct ptp_clock_info *info, const struct timespec64 *ts){ struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; int err; u64 ns; /* Reject phc adjustments during device upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; ns = timespec64_to_ns(ts); spin_lock_irqsave(&phc->lock, irqflags); timecounter_init(&phc->tc, &phc->cc, ns); ionic_phc_state_page_update(phc); /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ err = ionic_setphc_cmd(phc, &ctx); spin_unlock_irqrestore(&phc->lock, irqflags); return ionic_adminq_wait(phc->lif, &ctx, err, true);} static int ionic_phc_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts, struct ptp_system_timestamp *sts){ struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic *ionic = phc->lif->ionic; unsigned long irqflags; u64 tick, ns; /* Do not attempt to read device time during upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; spin_lock_irqsave(&phc->lock, irqflags); tick = ionic_hwstamp_read(ionic, sts); ns = timecounter_cyc2time(&phc->tc, tick); spin_unlock_irqrestore(&phc->lock, irqflags); *ts = ns_to_timespec64(ns); return 0;} static long ionic_phc_aux_work(struct ptp_clock_info *info){ struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; int err; /* Do not update phc during device upgrade, but keep polling to resume * after upgrade. Since we don't update the point in time basis, there * is no expectation that we are maintaining the phc time during the * upgrade. After upgrade, it will need to be readjusted back to the * correct time by the ptp daemon. */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return phc->aux_work_delay; spin_lock_irqsave(&phc->lock, irqflags); /* update point-in-time basis to now */ timecounter_read(&phc->tc);Lines 1–500 of 710. Load more
// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017 - 2021 Pensando Systems, Inc */ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <uapi/rdma/ib_user_verbs.h> #include "ionic.h" #include "ionic_bus.h" #include "ionic_lif.h" #include "ionic_ethtool.h" static int ionic_hwstamp_tx_mode(int config_tx_type) { switch (config_tx_type) { case HWTSTAMP_TX_OFF: return IONIC_TXSTAMP_OFF; case HWTSTAMP_TX_ON: return IONIC_TXSTAMP_ON; case HWTSTAMP_TX_ONESTEP_SYNC: return IONIC_TXSTAMP_ONESTEP_SYNC; case HWTSTAMP_TX_ONESTEP_P2P: return IONIC_TXSTAMP_ONESTEP_P2P; default: return -ERANGE; } } static u64 ionic_hwstamp_rx_filt(int config_rx_filter) { switch (config_rx_filter) { case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: return IONIC_PKT_CLS_PTP1_ALL; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: return IONIC_PKT_CLS_PTP1_SYNC; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: return IONIC_PKT_CLS_PTP1_SYNC | IONIC_PKT_CLS_PTP1_DREQ; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: return IONIC_PKT_CLS_PTP2_L4_ALL; case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: return IONIC_PKT_CLS_PTP2_L4_SYNC; case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: return IONIC_PKT_CLS_PTP2_L4_SYNC | IONIC_PKT_CLS_PTP2_L4_DREQ; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: return IONIC_PKT_CLS_PTP2_L2_ALL; case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: return IONIC_PKT_CLS_PTP2_L2_SYNC; case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: return IONIC_PKT_CLS_PTP2_L2_SYNC | IONIC_PKT_CLS_PTP2_L2_DREQ; case HWTSTAMP_FILTER_PTP_V2_EVENT: return IONIC_PKT_CLS_PTP2_ALL; case HWTSTAMP_FILTER_PTP_V2_SYNC: return IONIC_PKT_CLS_PTP2_SYNC; case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: return IONIC_PKT_CLS_PTP2_SYNC | IONIC_PKT_CLS_PTP2_DREQ; case HWTSTAMP_FILTER_NTP_ALL: return IONIC_PKT_CLS_NTP_ALL; default: return 0; } } static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif, struct kernel_hwtstamp_config *new_ts, struct netlink_ext_ack *extack) { struct kernel_hwtstamp_config *config; struct kernel_hwtstamp_config ts = {}; struct ionic *ionic = lif->ionic; int tx_mode = 0; u64 rx_filt = 0; int err, err2; bool rx_all; __le64 mask; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->phc->config_lock); if (new_ts) { config = new_ts; } else { /* If called with new_ts == NULL, replay the previous request * primarily for recovery after a FW_RESET. * We saved the previous configuration request info, so copy * the previous request for reference, clear the current state * to match the device's reset state, and run with it. */ config = &ts; memcpy(config, &lif->phc->ts_config, sizeof(*config)); memset(&lif->phc->ts_config, 0, sizeof(lif->phc->ts_config)); lif->phc->ts_config_tx_mode = 0; lif->phc->ts_config_rx_filt = 0; } tx_mode = ionic_hwstamp_tx_mode(config->tx_type); if (tx_mode < 0) { NL_SET_ERR_MSG_MOD(extack, "TX time stamping mode isn't supported"); err = tx_mode; goto err_queues; } mask = cpu_to_le64(BIT_ULL(tx_mode)); if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) { NL_SET_ERR_MSG_MOD(extack, "TX time stamping mode isn't supported"); err = -ERANGE; goto err_queues; } rx_filt = ionic_hwstamp_rx_filt(config->rx_filter); rx_all = config->rx_filter != HWTSTAMP_FILTER_NONE && !rx_filt; mask = cpu_to_le64(rx_filt); if ((ionic->ident.lif.eth.hwstamp_rx_filters & mask) != mask) { rx_filt = 0; rx_all = true; config->rx_filter = HWTSTAMP_FILTER_ALL; } dev_dbg(ionic->dev, "%s: config_rx_filter %d rx_filt %#llx rx_all %d\n", __func__, config->rx_filter, rx_filt, rx_all); if (tx_mode) { err = ionic_lif_create_hwstamp_txq(lif); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error creating TX timestamp queue"); goto err_queues; } } if (rx_filt) { err = ionic_lif_create_hwstamp_rxq(lif); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error creating RX timestamp queue"); goto err_queues; } } if (tx_mode != lif->phc->ts_config_tx_mode) { err = ionic_lif_set_hwstamp_txmode(lif, tx_mode); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error enabling TX timestamp mode"); goto err_txmode; } } if (rx_filt != lif->phc->ts_config_rx_filt) { err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error enabling RX timestamp mode"); goto err_rxfilt; } } if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) { err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all); if (err) { NL_SET_ERR_MSG_MOD(extack, "Error enabling RX timestamp mode"); goto err_rxall; } } memcpy(&lif->phc->ts_config, config, sizeof(*config)); lif->phc->ts_config_rx_filt = rx_filt; lif->phc->ts_config_tx_mode = tx_mode; mutex_unlock(&lif->phc->config_lock); return 0; err_rxall: if (rx_filt != lif->phc->ts_config_rx_filt) { rx_filt = lif->phc->ts_config_rx_filt; err2 = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt); if (err2) dev_err(ionic->dev, "Failed to revert rx timestamp filter: %d\n", err2); } err_rxfilt: if (tx_mode != lif->phc->ts_config_tx_mode) { tx_mode = lif->phc->ts_config_tx_mode; err2 = ionic_lif_set_hwstamp_txmode(lif, tx_mode); if (err2) dev_err(ionic->dev, "Failed to revert tx timestamp mode: %d\n", err2); } err_txmode: /* special queues remain allocated, just unused */ err_queues: mutex_unlock(&lif->phc->config_lock); return err; } int ionic_hwstamp_set(struct net_device *netdev, struct kernel_hwtstamp_config *config, struct netlink_ext_ack *extack) { struct ionic_lif *lif = netdev_priv(netdev); int err; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->queue_lock); err = ionic_lif_hwstamp_set_ts_config(lif, config, extack); mutex_unlock(&lif->queue_lock); if (err) { netdev_info(lif->netdev, "hwstamp set failed: %d\n", err); return err; } return 0; } void ionic_lif_hwstamp_replay(struct ionic_lif *lif) { int err; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return; mutex_lock(&lif->queue_lock); err = ionic_lif_hwstamp_set_ts_config(lif, NULL, NULL); mutex_unlock(&lif->queue_lock); if (err) netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err); } void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif) { int err; if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return; mutex_lock(&lif->phc->config_lock); if (lif->phc->ts_config_tx_mode) { err = ionic_lif_create_hwstamp_txq(lif); if (err) netdev_info(lif->netdev, "hwstamp recreate txq failed: %d\n", err); } if (lif->phc->ts_config_rx_filt) { err = ionic_lif_create_hwstamp_rxq(lif); if (err) netdev_info(lif->netdev, "hwstamp recreate rxq failed: %d\n", err); } mutex_unlock(&lif->phc->config_lock); } int ionic_hwstamp_get(struct net_device *netdev, struct kernel_hwtstamp_config *config) { struct ionic_lif *lif = netdev_priv(netdev); if (!lif->phc || !lif->phc->ptp || !(lif->hw_features & IONIC_ETH_HW_TIMESTAMP)) return -EOPNOTSUPP; mutex_lock(&lif->phc->config_lock); memcpy(config, &lif->phc->ts_config, sizeof(*config)); mutex_unlock(&lif->phc->config_lock); return 0; } static u64 ionic_hwstamp_read(struct ionic *ionic, struct ptp_system_timestamp *sts) { u32 tick_high_before, tick_high, tick_low; /* read and discard low part to defeat hw staging of high part */ ioread32(&ionic->idev.hwstamp_regs->tick_low); tick_high_before = ioread32(&ionic->idev.hwstamp_regs->tick_high); ptp_read_system_prets(sts); tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low); ptp_read_system_postts(sts); tick_high = ioread32(&ionic->idev.hwstamp_regs->tick_high); /* If tick_high changed, re-read tick_low once more. Assume tick_high * cannot change again so soon as in the span of re-reading tick_low. */ if (tick_high != tick_high_before) { ptp_read_system_prets(sts); tick_low = ioread32(&ionic->idev.hwstamp_regs->tick_low); ptp_read_system_postts(sts); } return (u64)tick_low | ((u64)tick_high << 32); } static u64 ionic_cc_read(struct cyclecounter *cc) { struct ionic_phc *phc = container_of(cc, struct ionic_phc, cc); struct ionic *ionic = phc->lif->ionic; return ionic_hwstamp_read(ionic, NULL); } static int ionic_setphc_cmd(struct ionic_phc *phc, struct ionic_admin_ctx *ctx) { ctx->work = COMPLETION_INITIALIZER_ONSTACK(ctx->work); ctx->cmd.lif_setphc.opcode = IONIC_CMD_LIF_SETPHC; ctx->cmd.lif_setphc.lif_index = cpu_to_le16(phc->lif->index); ctx->cmd.lif_setphc.tick = cpu_to_le64(phc->tc.cycle_last); ctx->cmd.lif_setphc.nsec = cpu_to_le64(phc->tc.nsec); ctx->cmd.lif_setphc.frac = cpu_to_le64(phc->tc.frac); ctx->cmd.lif_setphc.mult = cpu_to_le32(phc->cc.mult); ctx->cmd.lif_setphc.shift = cpu_to_le32(phc->cc.shift); return ionic_adminq_post(phc->lif, ctx); } static void ionic_phc_state_page_update(struct ionic_phc *phc) { struct ib_uverbs_clock_info *state = phc->state_page; u32 sign; /* read current sign */ sign = smp_load_acquire(&state->sign) & ~1; /* make sign odd for updating */ smp_store_mb(state->sign, sign | 1); state->cycles = phc->tc.cycle_last; state->nsec = phc->tc.nsec; state->frac = phc->tc.frac; state->mult = phc->cc.mult; /* make sign the next even number for update completed */ smp_store_release(&state->sign, sign + 2); } static int ionic_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm) { struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; s64 adj; int err; /* Reject phc adjustments during device upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; /* Adjustment value scaled by 2^16 million */ adj = (s64)scaled_ppm * phc->init_cc_mult; /* Adjustment value to scale */ adj /= (s64)SCALED_PPM; /* Final adjusted multiplier */ adj += phc->init_cc_mult; spin_lock_irqsave(&phc->lock, irqflags); /* update the point-in-time basis to now, before adjusting the rate */ timecounter_read(&phc->tc); phc->cc.mult = adj; ionic_phc_state_page_update(phc); /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ err = ionic_setphc_cmd(phc, &ctx); spin_unlock_irqrestore(&phc->lock, irqflags); return ionic_adminq_wait(phc->lif, &ctx, err, true); } static int ionic_phc_adjtime(struct ptp_clock_info *info, s64 delta) { struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; int err; /* Reject phc adjustments during device upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; spin_lock_irqsave(&phc->lock, irqflags); timecounter_adjtime(&phc->tc, delta); ionic_phc_state_page_update(phc); /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ err = ionic_setphc_cmd(phc, &ctx); spin_unlock_irqrestore(&phc->lock, irqflags); return ionic_adminq_wait(phc->lif, &ctx, err, true); } static int ionic_phc_settime64(struct ptp_clock_info *info, const struct timespec64 *ts) { struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; int err; u64 ns; /* Reject phc adjustments during device upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; ns = timespec64_to_ns(ts); spin_lock_irqsave(&phc->lock, irqflags); timecounter_init(&phc->tc, &phc->cc, ns); ionic_phc_state_page_update(phc); /* Setphc commands are posted in-order, sequenced by phc->lock. We * need to drop the lock before waiting for the command to complete. */ err = ionic_setphc_cmd(phc, &ctx); spin_unlock_irqrestore(&phc->lock, irqflags); return ionic_adminq_wait(phc->lif, &ctx, err, true); } static int ionic_phc_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts, struct ptp_system_timestamp *sts) { struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic *ionic = phc->lif->ionic; unsigned long irqflags; u64 tick, ns; /* Do not attempt to read device time during upgrade */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return -EBUSY; spin_lock_irqsave(&phc->lock, irqflags); tick = ionic_hwstamp_read(ionic, sts); ns = timecounter_cyc2time(&phc->tc, tick); spin_unlock_irqrestore(&phc->lock, irqflags); *ts = ns_to_timespec64(ns); return 0; } static long ionic_phc_aux_work(struct ptp_clock_info *info) { struct ionic_phc *phc = container_of(info, struct ionic_phc, ptp_info); struct ionic_admin_ctx ctx = {}; unsigned long irqflags; int err; /* Do not update phc during device upgrade, but keep polling to resume * after upgrade. Since we don't update the point in time basis, there * is no expectation that we are maintaining the phc time during the * upgrade. After upgrade, it will need to be readjusted back to the * correct time by the ptp daemon. */ if (test_bit(IONIC_LIF_F_FW_RESET, phc->lif->state)) return phc->aux_work_delay; spin_lock_irqsave(&phc->lock, irqflags); /* update point-in-time basis to now */ timecounter_read(&phc->tc);