From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 884628DB1 for ; Fri, 2 Oct 2015 17:23:38 +0200 (CEST) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by fmsmga102.fm.intel.com with ESMTP; 02 Oct 2015 08:23:38 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.17,623,1437462000"; d="scan'208";a="818014954" Received: from unknown ([10.217.248.93]) by orsmga002.jf.intel.com with SMTP; 02 Oct 2015 08:23:35 -0700 Received: by (sSMTP sendmail emulation); Fri, 02 Oct 2015 17:22:32 +0200 From: Daniel Mrzyglod To: dev@dpdk.org Date: Fri, 2 Oct 2015 17:20:07 +0200 Message-Id: <1443799208-9408-3-git-send-email-danielx.t.mrzyglod@intel.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1443799208-9408-1-git-send-email-danielx.t.mrzyglod@intel.com> References: <1443799208-9408-1-git-send-email-danielx.t.mrzyglod@intel.com> Subject: [dpdk-dev] [PATCH 2/3] ixgbe: add additional ieee1588 support functions X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 02 Oct 2015 15:23:39 -0000 Add additional functions to support the existing IEEE1588 functionality and to enable getting, setting and adjusting the device time. Signed-off-by: Daniel Mrzyglod --- drivers/net/ixgbe/ixgbe_ethdev.c | 250 +++++++++++++++++++++++++++++++++++++-- drivers/net/ixgbe/ixgbe_ethdev.h | 24 ++++ 2 files changed, 263 insertions(+), 11 deletions(-) diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c index ec2918c..d0c575f 100644 --- a/drivers/net/ixgbe/ixgbe_ethdev.c +++ b/drivers/net/ixgbe/ixgbe_ethdev.c @@ -126,10 +126,12 @@ #define IXGBE_HKEY_MAX_INDEX 10 /* Additional timesync values. */ -#define IXGBE_TIMINCA_16NS_SHIFT 24 -#define IXGBE_TIMINCA_INCVALUE 16000000 -#define IXGBE_TIMINCA_INIT ((0x02 << IXGBE_TIMINCA_16NS_SHIFT) \ - | IXGBE_TIMINCA_INCVALUE) +#define NSEC_PER_SEC 1000000000L +#define IXGBE_INCVAL_10GB 0x66666666 +#define IXGBE_INCVAL_SHIFT_10GB 28 +#define IXGBE_INCVAL_SHIFT_82599 7 +#define IXGBE_INCPER_SHIFT_82599 24 +#define IXGBE_CYCLECOUTER_MASK 0xffffffffffffffff static int eth_ixgbe_dev_init(struct rte_eth_dev *eth_dev); static int eth_ixgbe_dev_uninit(struct rte_eth_dev *eth_dev); @@ -325,6 +327,11 @@ static int ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev, uint32_t flags); static int ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev, struct timespec *timestamp); +static int ixgbe_timesync_adjust(struct rte_eth_dev *dev, int64_t delta); +static int ixgbe_timesync_gettime(struct rte_eth_dev *dev, + struct timespec *timestamp); +static int ixgbe_timesync_settime(struct rte_eth_dev *dev, + struct timespec *timestamp); /* * Define VF Stats MACRO for Non "cleared on read" register @@ -465,6 +472,9 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .get_eeprom_length = ixgbe_get_eeprom_length, .get_eeprom = ixgbe_get_eeprom, .set_eeprom = ixgbe_set_eeprom, + .timesync_adjust = ixgbe_timesync_adjust, + .timesync_gettime = ixgbe_timesync_gettime, + .timesync_settime = ixgbe_timesync_settime, }; /* @@ -5241,20 +5251,223 @@ ixgbe_dev_set_mc_addr_list(struct rte_eth_dev *dev, ixgbe_dev_addr_list_itr, TRUE); } +static inline uint64_t +timespec_to_ns(const struct timespec *ts) +{ + return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; +} + +static struct timespec +ns_to_timespec(int64_t nsec) +{ + struct timespec ts = {0, 0}; + int32_t rem; + + if (nsec == 0) + return ts; + rem = nsec % NSEC_PER_SEC; + ts.tv_sec = nsec / NSEC_PER_SEC; + + if (unlikely(rem < 0)) { + ts.tv_sec--; + rem += NSEC_PER_SEC; + } + + ts.tv_nsec = rem; + + return ts; +} + +static inline uint64_t +cyclecounter_cycles_to_ns(const struct cyclecounter *cc, + uint64_t cycles, uint64_t mask, uint64_t *frac) +{ + uint64_t ns = cycles; + + ns = (ns * cc->mult) + *frac; + *frac = ns & mask; + return ns >> cc->shift; +} + +static uint64_t +cyclecounter_cycles_to_ns_backwards(const struct cyclecounter *cc, + uint64_t cycles, uint64_t mask __rte_unused, uint64_t frac) +{ + uint64_t ns = (uint64_t) cycles; + + ns = ((ns * cc->mult) - frac) >> cc->shift; + + return ns; +} + +static uint64_t +timecounter_cycles_to_ns_time(struct timecounter *tc, uint64_t cycle_tstamp) +{ + uint64_t delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; + uint64_t nsec = tc->nsec, frac = tc->frac; + + + /* Cycle counts that are corectly converted as they + * are between -1/2 max cycle count and +1/2max cycle count + * */ + if (delta > tc->cc->mask / 2) { + delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; + nsec -= cyclecounter_cycles_to_ns_backwards(tc->cc, delta, tc->mask, frac); + } else { + nsec += cyclecounter_cycles_to_ns(tc->cc, delta, tc->mask, &frac); + } + + return nsec; +} + +static uint64_t +ixgbe_read_timesync_cyclecounter(struct rte_eth_dev *dev) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + uint64_t systim_cycles = 0; + + systim_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIML); + systim_cycles |= (uint64_t)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32; + + return systim_cycles; +} + +static uint64_t +timecounter_read_ns_delta(struct rte_eth_dev *dev) +{ + uint64_t cycle_now, cycle_delta; + uint64_t ns_offset; + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + + /* read cycle counter: */ + cycle_now = adapter->tc.cc->read(dev); + + /* calculate the delta since the last timecounter_read_delta(): */ + cycle_delta = (cycle_now - adapter->tc.cycle_last) & adapter->tc.cc->mask; + + /* convert to nanoseconds: */ + ns_offset = cyclecounter_cycles_to_ns(adapter->tc.cc, cycle_delta, + adapter->tc.mask, &adapter->tc.frac); + + /* update time stamp of timecounter_read_delta() call: */ + adapter->tc.cycle_last = cycle_now; + + return ns_offset; +} + +static uint64_t +timecounter_read(struct rte_eth_dev *dev) +{ + uint64_t nsec; + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + + /* increment time by nanoseconds since last call */ + nsec = timecounter_read_ns_delta(dev); + nsec += adapter->tc.nsec; + adapter->tc.nsec = nsec; + + return nsec; +} + + +static void +timecounter_init(struct rte_eth_dev *dev, + uint64_t start_time) +{ + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + adapter->tc.cc = &adapter->cc; + adapter->tc.cycle_last = adapter->tc.cc->read(dev); + adapter->tc.nsec = start_time; + adapter->tc.mask = (1ULL << adapter->tc.cc->shift) - 1; + adapter->tc.frac = 0; +} + +static void +ixgbe_start_cyclecounter(struct rte_eth_dev *dev) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + uint32_t incval = 0; + uint32_t shift = 0; + + incval = IXGBE_INCVAL_10GB >> IXGBE_INCVAL_SHIFT_82599; + shift = IXGBE_INCVAL_SHIFT_10GB - IXGBE_INCVAL_SHIFT_82599; + + IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, + (1 << IXGBE_INCPER_SHIFT_82599) | + incval); + + memset(&adapter->cc, 0, sizeof(struct cyclecounter)); + adapter->cc.read = ixgbe_read_timesync_cyclecounter; + adapter->cc.mask = IXGBE_CYCLECOUTER_MASK; + adapter->cc.shift = shift; + adapter->cc.mult = 1; +} + +static int +ixgbe_timesync_adjust(struct rte_eth_dev *dev, int64_t delta) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + + adapter->tc.nsec += delta; + IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0); + IXGBE_WRITE_FLUSH(hw); + + return 0; +} + +static int +ixgbe_timesync_settime(struct rte_eth_dev *dev, struct timespec *ts) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + uint64_t ns; + ns = timespec_to_ns(ts); + + /* reset the timecounter */ + timecounter_init(dev, ns); + /* it's setup after every change */ + IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0); + IXGBE_WRITE_FLUSH(hw); + + return 0; +} + +static int +ixgbe_timesync_gettime(struct rte_eth_dev *dev, struct timespec *ts) +{ + uint64_t ns; + + ns = timecounter_read(dev); + *ts = ns_to_timespec(ns); + + return 0; +} + static int ixgbe_timesync_enable(struct rte_eth_dev *dev) { struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); uint32_t tsync_ctl; uint32_t tsauxc; + uint64_t ns; + struct timespec zerotime = {0, 0}; /* Enable system time for platforms where it isn't on by default. */ tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC); tsauxc &= ~IXGBE_TSAUXC_DISABLE_SYSTIME; - IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc); - /* Start incrementing the register used to timestamp PTP packets. */ - IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, IXGBE_TIMINCA_INIT); + /* Set 0.0 epoch time to initialize timecounter*/ + ns = timespec_to_ns(&zerotime); + ixgbe_start_cyclecounter(dev); + timecounter_init(dev, ns); + + IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc); /* Enable L2 filtering of IEEE1588/802.1AS Ethernet frame types. */ IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588), @@ -5272,6 +5485,9 @@ ixgbe_timesync_enable(struct rte_eth_dev *dev) tsync_ctl |= IXGBE_TSYNCTXCTL_ENABLED; IXGBE_WRITE_REG(hw, IXGBE_TSYNCTXCTL, tsync_ctl); + /* After writing to registers should be flush */ + IXGBE_WRITE_FLUSH(hw); + return 0; } @@ -5306,9 +5522,13 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev, uint32_t flags __rte_unused) { struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + uint32_t tsync_rxctl; uint32_t rx_stmpl; uint32_t rx_stmph; + uint64_t regival = 0; tsync_rxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL); if ((tsync_rxctl & IXGBE_TSYNCRXCTL_VALID) == 0) @@ -5316,9 +5536,11 @@ ixgbe_timesync_read_rx_timestamp(struct rte_eth_dev *dev, rx_stmpl = IXGBE_READ_REG(hw, IXGBE_RXSTMPL); rx_stmph = IXGBE_READ_REG(hw, IXGBE_RXSTMPH); + timecounter_read(dev); - timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl); - timestamp->tv_nsec = 0; + regival = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl); + regival = timecounter_cycles_to_ns_time(&adapter->tc, regival); + *timestamp = ns_to_timespec(regival); return 0; } @@ -5328,9 +5550,13 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev, struct timespec *timestamp) { struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct ixgbe_adapter *adapter = + (struct ixgbe_adapter *)dev->data->dev_private; + uint32_t tsync_txctl; uint32_t tx_stmpl; uint32_t tx_stmph; + uint64_t regival = 0; tsync_txctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL); if ((tsync_txctl & IXGBE_TSYNCTXCTL_VALID) == 0) @@ -5338,9 +5564,11 @@ ixgbe_timesync_read_tx_timestamp(struct rte_eth_dev *dev, tx_stmpl = IXGBE_READ_REG(hw, IXGBE_TXSTMPL); tx_stmph = IXGBE_READ_REG(hw, IXGBE_TXSTMPH); + timecounter_read(dev); - timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl); - timestamp->tv_nsec = 0; + regival = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl); + regival = timecounter_cycles_to_ns_time(&adapter->tc, regival); + *timestamp = ns_to_timespec(regival); return 0; } diff --git a/drivers/net/ixgbe/ixgbe_ethdev.h b/drivers/net/ixgbe/ixgbe_ethdev.h index c3d4f4f..6708e08 100644 --- a/drivers/net/ixgbe/ixgbe_ethdev.h +++ b/drivers/net/ixgbe/ixgbe_ethdev.h @@ -252,6 +252,27 @@ struct ixgbe_filter_info { }; /* + * Structure for cyclecounter IEEE1588 functionality + */ +struct cyclecounter { + uint64_t (*read)(struct rte_eth_dev *dev); + uint64_t mask; + uint32_t mult; + uint32_t shift; +}; + +/* + * Structure for structure that keep UnixEpoch time + */ +struct timecounter { + struct cyclecounter *cc; + uint64_t cycle_last; + uint64_t nsec; + uint64_t mask; + uint64_t frac; +}; + +/* * Structure to store private data for each driver instance (for each port). */ struct ixgbe_adapter { @@ -273,6 +294,9 @@ struct ixgbe_adapter { bool rx_bulk_alloc_allowed; bool rx_vec_allowed; + + struct cyclecounter cc; + struct timecounter tc; }; #define IXGBE_DEV_PRIVATE_TO_HW(adapter)\ -- 2.1.0