/* reassociate interrupts */
        fm10k_mbx_request_irq(interface);
 
+       /* reset clock */
+       fm10k_ts_reset(interface);
+
        if (netif_running(netdev))
                fm10k_open(netdev);
 
        /* tasks only run when interface is up */
        fm10k_watchdog_subtask(interface);
        fm10k_check_hang_subtask(interface);
+       fm10k_ts_tx_subtask(interface);
 
        /* release lock on service events to allow scheduling next event */
        fm10k_service_event_complete(interface);
        return 0;
 }
 
+static s32 fm10k_1588_msg_vf(struct fm10k_hw *hw, u32 **results,
+                            struct fm10k_mbx_info *mbx)
+{
+       struct fm10k_intfc *interface;
+       u64 timestamp;
+       s32 err;
+
+       err = fm10k_tlv_attr_get_u64(results[FM10K_1588_MSG_TIMESTAMP],
+                                    ×tamp);
+       if (err)
+               return err;
+
+       interface = container_of(hw, struct fm10k_intfc, hw);
+
+       fm10k_ts_tx_hwtstamp(interface, 0, timestamp);
+
+       return 0;
+}
+
 /* generic error handler for mailbox issues */
 static s32 fm10k_mbx_error(struct fm10k_hw *hw, u32 **results,
                           struct fm10k_mbx_info *mbx)
        FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
        FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_mbx_mac_addr),
        FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_msg_lport_state_vf),
+       FM10K_VF_MSG_1588_HANDLER(fm10k_1588_msg_vf),
        FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error),
 };
 
        return 0;
 }
 
+static s32 fm10k_1588_msg_pf(struct fm10k_hw *hw, u32 **results,
+                            struct fm10k_mbx_info *mbx)
+{
+       struct fm10k_swapi_1588_timestamp timestamp;
+       struct fm10k_iov_data *iov_data;
+       struct fm10k_intfc *interface;
+       u16 sglort, vf_idx;
+       s32 err;
+
+       err = fm10k_tlv_attr_get_le_struct(
+                               results[FM10K_PF_ATTR_ID_1588_TIMESTAMP],
+                               ×tamp, sizeof(timestamp));
+       if (err)
+               return err;
+
+       interface = container_of(hw, struct fm10k_intfc, hw);
+
+       if (timestamp.dglort) {
+               fm10k_ts_tx_hwtstamp(interface, timestamp.dglort,
+                                    le64_to_cpu(timestamp.egress));
+               return 0;
+       }
+
+       /* either dglort or sglort must be set */
+       if (!timestamp.sglort)
+               return FM10K_ERR_PARAM;
+
+       /* verify GLORT is at least one of the ones we own */
+       sglort = le16_to_cpu(timestamp.sglort);
+       if (!fm10k_glort_valid_pf(hw, sglort))
+               return FM10K_ERR_PARAM;
+
+       if (sglort == interface->glort) {
+               fm10k_ts_tx_hwtstamp(interface, 0,
+                                    le64_to_cpu(timestamp.ingress));
+               return 0;
+       }
+
+       /* if there is no iov_data then there is no mailboxes to process */
+       if (!ACCESS_ONCE(interface->iov_data))
+               return FM10K_ERR_PARAM;
+
+       rcu_read_lock();
+
+       /* notify VF if this timestamp belongs to it */
+       iov_data = interface->iov_data;
+       vf_idx = (hw->mac.dglort_map & FM10K_DGLORTMAP_NONE) - sglort;
+
+       if (!iov_data || vf_idx >= iov_data->num_vfs) {
+               err = FM10K_ERR_PARAM;
+               goto err_unlock;
+       }
+
+       err = hw->iov.ops.report_timestamp(hw, &iov_data->vf_info[vf_idx],
+                                          le64_to_cpu(timestamp.ingress));
+
+err_unlock:
+       rcu_read_unlock();
+
+       return err;
+}
+
 static const struct fm10k_msg_data pf_mbx_data[] = {
        FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf),
        FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf),
        FM10K_PF_MSG_ERR_HANDLER(LPORT_CREATE, fm10k_msg_err_pf),
        FM10K_PF_MSG_ERR_HANDLER(LPORT_DELETE, fm10k_msg_err_pf),
        FM10K_PF_MSG_UPDATE_PVID_HANDLER(fm10k_update_pvid),
+       FM10K_PF_MSG_1588_TIMESTAMP_HANDLER(fm10k_1588_msg_pf),
        FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error),
 };
 
                return -EIO;
        }
 
+       /* assign BAR 4 resources for use with PTP */
+       if (fm10k_read_reg(hw, FM10K_CTRL) & FM10K_CTRL_BAR4_ALLOWED)
+               interface->sw_addr = ioremap(pci_resource_start(pdev, 4),
+                                            pci_resource_len(pdev, 4));
+       hw->sw_addr = interface->sw_addr;
+
        /* Only the PF can support VXLAN and NVGRE offloads */
        if (hw->mac.type != fm10k_mac_pf) {
                netdev->hw_enc_features = 0;
                    (unsigned long)interface);
        INIT_WORK(&interface->service_task, fm10k_service_task);
 
+       /* Intitialize timestamp data */
+       fm10k_ts_init(interface);
+
        /* set default ring sizes */
        interface->tx_ring_count = FM10K_DEFAULT_TXD;
        interface->rx_ring_count = FM10K_DEFAULT_RXD;
        /* stop all the transmit queues from transmitting until link is up */
        netif_tx_stop_all_queues(netdev);
 
+       /* Register PTP interface */
+       fm10k_ptp_register(interface);
+
        /* print bus type/speed/width info */
        dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n",
                 (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" :
 err_mbx_interrupt:
        fm10k_clear_queueing_scheme(interface);
 err_sw_init:
+       if (interface->sw_addr)
+               iounmap(interface->sw_addr);
        iounmap(interface->uc_addr);
 err_ioremap:
        free_netdev(netdev);
        if (netdev->reg_state == NETREG_REGISTERED)
                unregister_netdev(netdev);
 
+       /* cleanup timestamp handling */
+       fm10k_ptp_unregister(interface);
+
        /* release VFs */
        fm10k_iov_disable(pdev);
 
        /* remove any debugfs interfaces */
        fm10k_dbg_intfc_exit(interface);
 
+       if (interface->sw_addr)
+               iounmap(interface->sw_addr);
        iounmap(interface->uc_addr);
 
        free_netdev(netdev);
        /* reset statistics starting values */
        hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
 
+       /* reset clock */
+       fm10k_ts_reset(interface);
+
        rtnl_lock();
 
        err = fm10k_init_queueing_scheme(interface);
        /* reassociate interrupts */
        fm10k_mbx_request_irq(interface);
 
+       /* reset clock */
+       fm10k_ts_reset(interface);
+
        if (netif_running(netdev))
                err = fm10k_open(netdev);
 
 
--- /dev/null
+/* Intel Ethernet Switch Host Interface Driver
+ * Copyright(c) 2013 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
+
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "fm10k.h"
+
+#define FM10K_TS_TX_TIMEOUT            (HZ * 15)
+
+void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
+                              struct skb_shared_hwtstamps *hwtstamp,
+                              u64 systime)
+{
+       unsigned long flags;
+
+       read_lock_irqsave(&interface->systime_lock, flags);
+       systime += interface->ptp_adjust;
+       read_unlock_irqrestore(&interface->systime_lock, flags);
+
+       hwtstamp->hwtstamp = ns_to_ktime(systime);
+}
+
+static struct sk_buff *fm10k_ts_tx_skb(struct fm10k_intfc *interface,
+                                      __le16 dglort)
+{
+       struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+       struct sk_buff *skb;
+
+       skb_queue_walk(list, skb) {
+               if (FM10K_CB(skb)->fi.w.dglort == dglort)
+                       return skb;
+       }
+
+       return NULL;
+}
+
+void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb)
+{
+       struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+       struct sk_buff *clone;
+       unsigned long flags;
+       __le16 dglort;
+
+       /* create clone for us to return on the Tx path */
+       clone = skb_clone_sk(skb);
+       if (!clone)
+               return;
+
+       FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT;
+       dglort = FM10K_CB(clone)->fi.w.dglort;
+
+       spin_lock_irqsave(&list->lock, flags);
+
+       /* attempt to locate any buffers with the same dglort,
+        * if none are present then insert skb in tail of list
+        */
+       skb = fm10k_ts_tx_skb(interface, FM10K_CB(clone)->fi.w.dglort);
+       if (!skb)
+               __skb_queue_tail(list, clone);
+
+       spin_unlock_irqrestore(&list->lock, flags);
+
+       /* if list is already has one then we just free the clone */
+       if (skb)
+               kfree_skb(skb);
+       else
+               skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
+}
+
+void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
+                         u64 systime)
+{
+       struct skb_shared_hwtstamps shhwtstamps;
+       struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&list->lock, flags);
+
+       /* attempt to locate and pull the sk_buff out of the list */
+       skb = fm10k_ts_tx_skb(interface, dglort);
+       if (skb)
+               __skb_unlink(skb, list);
+
+       spin_unlock_irqrestore(&list->lock, flags);
+
+       /* if not found do nothing */
+       if (!skb)
+               return;
+
+       /* timestamp the sk_buff and return it to the socket */
+       fm10k_systime_to_hwtstamp(interface, &shhwtstamps, systime);
+       skb_complete_tx_timestamp(skb, &shhwtstamps);
+}
+
+void fm10k_ts_tx_subtask(struct fm10k_intfc *interface)
+{
+       struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+
+       /* If we're down or resetting, just bail */
+       if (test_bit(__FM10K_DOWN, &interface->state) ||
+           test_bit(__FM10K_RESETTING, &interface->state))
+               return;
+
+       spin_lock_irqsave(&list->lock, flags);
+
+       /* walk though the list and flush any expired timestamp packets */
+       skb_queue_walk_safe(list, skb, tmp) {
+               if (!time_is_after_jiffies(FM10K_CB(skb)->ts_tx_timeout))
+                       continue;
+               __skb_unlink(skb, list);
+               kfree_skb(skb);
+               interface->tx_hwtstamp_timeouts++;
+       }
+
+       spin_unlock_irqrestore(&list->lock, flags);
+}
+
+static u64 fm10k_systime_read(struct fm10k_intfc *interface)
+{
+       struct fm10k_hw *hw = &interface->hw;
+
+       return hw->mac.ops.read_systime(hw);
+}
+
+void fm10k_ts_reset(struct fm10k_intfc *interface)
+{
+       s64 ns = ktime_to_ns(ktime_get_real());
+       unsigned long flags;
+
+       /* reinitialize the clock */
+       write_lock_irqsave(&interface->systime_lock, flags);
+       interface->ptp_adjust = fm10k_systime_read(interface) - ns;
+       write_unlock_irqrestore(&interface->systime_lock, flags);
+}
+
+void fm10k_ts_init(struct fm10k_intfc *interface)
+{
+       /* Initialize lock protecting systime access */
+       rwlock_init(&interface->systime_lock);
+
+       /* Initialize skb queue for pending timestamp requests */
+       skb_queue_head_init(&interface->ts_tx_skb_queue);
+
+       /* reset the clock to current kernel time */
+       fm10k_ts_reset(interface);
+}
+
+/**
+ * fm10k_get_ts_config - get current hardware timestamping configuration
+ * @netdev: network interface device structure
+ * @ifreq: ioctl data
+ *
+ * This function returns the current timestamping settings. Rather than
+ * attempt to deconstruct registers to fill in the values, simply keep a copy
+ * of the old settings around, and return a copy when requested.
+ */
+int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+       struct fm10k_intfc *interface = netdev_priv(netdev);
+       struct hwtstamp_config *config = &interface->ts_config;
+
+       return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+               -EFAULT : 0;
+}
+
+/**
+ * fm10k_set_ts_config - control hardware time stamping
+ * @netdev: network interface device structure
+ * @ifreq: ioctl data
+ *
+ * Outgoing time stamping can be enabled and disabled. Play nice and
+ * disable it when requested, although it shouldn't cause any overhead
+ * when no packet needs it. At most one packet in the queue may be
+ * marked for time stamping, otherwise it would be impossible to tell
+ * for sure to which packet the hardware time stamp belongs.
+ *
+ * Incoming time stamping has to be configured via the hardware
+ * filters. Not all combinations are supported, in particular event
+ * type has to be specified. Matching the kind of event packet is
+ * not supported, with the exception of "all V2 events regardless of
+ * level 2 or 4".
+ *
+ * Since hardware always timestamps Path delay packets when timestamping V2
+ * packets, regardless of the type specified in the register, only use V2
+ * Event mode. This more accurately tells the user what the hardware is going
+ * to do anyways.
+ */
+int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+       struct fm10k_intfc *interface = netdev_priv(netdev);
+       struct hwtstamp_config ts_config;
+
+       if (copy_from_user(&ts_config, ifr->ifr_data, sizeof(ts_config)))
+               return -EFAULT;
+
+       /* reserved for future extensions */
+       if (ts_config.flags)
+               return -EINVAL;
+
+       switch (ts_config.tx_type) {
+       case HWTSTAMP_TX_OFF:
+               break;
+       case HWTSTAMP_TX_ON:
+               /* we likely need some check here to see if this is supported */
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       switch (ts_config.rx_filter) {
+       case HWTSTAMP_FILTER_NONE:
+               interface->flags &= ~FM10K_FLAG_RX_TS_ENABLED;
+               break;
+       case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+       case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+       case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+       case HWTSTAMP_FILTER_ALL:
+               interface->flags |= FM10K_FLAG_RX_TS_ENABLED;
+               ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       /* save these settings for future reference */
+       interface->ts_config = ts_config;
+
+       return copy_to_user(ifr->ifr_data, &ts_config, sizeof(ts_config)) ?
+               -EFAULT : 0;
+}
+
+static int fm10k_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+       struct fm10k_intfc *interface;
+       struct fm10k_hw *hw;
+       int err;
+
+       interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+       hw = &interface->hw;
+
+       err = hw->mac.ops.adjust_systime(hw, ppb);
+
+       /* the only error we should see is if the value is out of range */
+       return (err == FM10K_ERR_PARAM) ? -ERANGE : err;
+}
+
+static int fm10k_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       struct fm10k_intfc *interface;
+       unsigned long flags;
+
+       interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+
+       write_lock_irqsave(&interface->systime_lock, flags);
+       interface->ptp_adjust += delta;
+       write_unlock_irqrestore(&interface->systime_lock, flags);
+
+       return 0;
+}
+
+static int fm10k_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+       struct fm10k_intfc *interface;
+       unsigned long flags;
+       u64 now;
+
+       interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+
+       read_lock_irqsave(&interface->systime_lock, flags);
+       now = fm10k_systime_read(interface) + interface->ptp_adjust;
+       read_unlock_irqrestore(&interface->systime_lock, flags);
+
+       *ts = ns_to_timespec(now);
+
+       return 0;
+}
+
+static int fm10k_ptp_settime(struct ptp_clock_info *ptp,
+                            const struct timespec *ts)
+{
+       struct fm10k_intfc *interface;
+       unsigned long flags;
+       u64 ns = timespec_to_ns(ts);
+
+       interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+
+       write_lock_irqsave(&interface->systime_lock, flags);
+       interface->ptp_adjust = fm10k_systime_read(interface) - ns;
+       write_unlock_irqrestore(&interface->systime_lock, flags);
+
+       return 0;
+}
+
+static int fm10k_ptp_enable(struct ptp_clock_info *ptp,
+                           struct ptp_clock_request *rq, int on)
+{
+       struct ptp_clock_time *t = &rq->perout.period;
+       struct fm10k_intfc *interface;
+       struct fm10k_hw *hw;
+       u64 period;
+       u32 step;
+
+       /* we can only support periodic output */
+       if (rq->type != PTP_CLK_REQ_PEROUT)
+               return -EINVAL;
+
+       /* verify the requested channel is there */
+       if (rq->perout.index >= ptp->n_per_out)
+               return -EINVAL;
+
+       /* we cannot enforce start time as there is no
+        * mechanism for that in the hardware, we can only control
+        * the period.
+        */
+
+       /* we cannot support periods greater than 4 seconds due to reg limit */
+       if (t->sec > 4 || t->sec < 0)
+               return -ERANGE;
+
+       interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+       hw = &interface->hw;
+
+       /* we simply cannot support the operation if we don't have BAR4 */
+       if (!hw->sw_addr)
+               return -ENOTSUPP;
+
+       /* convert to unsigned 64b ns, verify we can put it in a 32b register */
+       period = t->sec * 1000000000LL + t->nsec;
+
+       /* determine the minimum size for period */
+       step = 2 * (fm10k_read_reg(hw, FM10K_SYSTIME_CFG) &
+                   FM10K_SYSTIME_CFG_STEP_MASK);
+
+       /* verify the value is in range supported by hardware */
+       if ((period && (period < step)) || (period > U32_MAX))
+               return -ERANGE;
+
+       /* notify hardware of request to being sending pulses */
+       fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_PULSE(rq->perout.index),
+                          (u32)period);
+
+       return 0;
+}
+
+static struct ptp_pin_desc fm10k_ptp_pd[2] = {
+       {
+               .name = "IEEE1588_PULSE0",
+               .index = 0,
+               .func = PTP_PF_PEROUT,
+               .chan = 0
+       },
+       {
+               .name = "IEEE1588_PULSE1",
+               .index = 1,
+               .func = PTP_PF_PEROUT,
+               .chan = 1
+       }
+};
+
+static int fm10k_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+                           enum ptp_pin_function func, unsigned int chan)
+{
+       /* verify the requested pin is there */
+       if (pin >= ptp->n_pins || !ptp->pin_config)
+               return -EINVAL;
+
+       /* enforce locked channels, no changing them */
+       if (chan != ptp->pin_config[pin].chan)
+               return -EINVAL;
+
+       /* we want to keep the functions locked as well */
+       if (func != ptp->pin_config[pin].func)
+               return -EINVAL;
+
+       return 0;
+}
+
+void fm10k_ptp_register(struct fm10k_intfc *interface)
+{
+       struct ptp_clock_info *ptp_caps = &interface->ptp_caps;
+       struct device *dev = &interface->pdev->dev;
+       struct ptp_clock *ptp_clock;
+
+       snprintf(ptp_caps->name, sizeof(ptp_caps->name),
+                "%s", interface->netdev->name);
+       ptp_caps->owner         = THIS_MODULE;
+       /* This math is simply the inverse of the math in
+        * fm10k_adjust_systime_pf applied to an adjustment value
+        * of 2^30 - 1 which is the maximum value of the register:
+        *      max_ppb == ((2^30 - 1) * 5^9) / 2^31
+        */
+       ptp_caps->max_adj       = 976562;
+       ptp_caps->adjfreq       = fm10k_ptp_adjfreq;
+       ptp_caps->adjtime       = fm10k_ptp_adjtime;
+       ptp_caps->gettime       = fm10k_ptp_gettime;
+       ptp_caps->settime       = fm10k_ptp_settime;
+
+       /* provide pins if BAR4 is accessible */
+       if (interface->sw_addr) {
+               /* enable periodic outputs */
+               ptp_caps->n_per_out = 2;
+               ptp_caps->enable        = fm10k_ptp_enable;
+
+               /* enable clock pins */
+               ptp_caps->verify        = fm10k_ptp_verify;
+               ptp_caps->n_pins = 2;
+               ptp_caps->pin_config = fm10k_ptp_pd;
+       }
+
+       ptp_clock = ptp_clock_register(ptp_caps, dev);
+       if (IS_ERR(ptp_clock)) {
+               ptp_clock = NULL;
+               dev_err(dev, "ptp_clock_register failed\n");
+       } else {
+               dev_info(dev, "registered PHC device %s\n", ptp_caps->name);
+       }
+
+       interface->ptp_clock = ptp_clock;
+}
+
+void fm10k_ptp_unregister(struct fm10k_intfc *interface)
+{
+       struct ptp_clock *ptp_clock = interface->ptp_clock;
+       struct device *dev = &interface->pdev->dev;
+
+       if (!ptp_clock)
+               return;
+
+       interface->ptp_clock = NULL;
+
+       ptp_clock_unregister(ptp_clock);
+       dev_info(dev, "removed PHC %s\n", interface->ptp_caps.name);
+}