summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/intel/i40e/i40e_ptp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/intel/i40e/i40e_ptp.c')
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ptp.c135
1 files changed, 102 insertions, 33 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 101f439acda6..bb7fe98b3a6c 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -25,7 +25,6 @@
******************************************************************************/
#include "i40e.h"
-#include <linux/export.h>
#include <linux/ptp_classify.h>
/* The XL710 timesync is very much like Intel's 82599 design when it comes to
@@ -216,7 +215,7 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp,
}
/**
- * i40e_ptp_enable - Enable/disable ancillary features of the PHC subsystem
+ * i40e_ptp_feature_enable - Enable/disable ancillary features of the PHC subsystem
* @ptp: The PTP clock structure
* @rq: The requested feature to change
* @on: Enable/disable flag
@@ -224,8 +223,8 @@ static int i40e_ptp_settime(struct ptp_clock_info *ptp,
* The XL710 does not support any of the ancillary features of the PHC
* subsystem, so this function may just return.
**/
-static int i40e_ptp_enable(struct ptp_clock_info *ptp,
- struct ptp_clock_request *rq, int on)
+static int i40e_ptp_feature_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
{
return -EOPNOTSUPP;
}
@@ -315,6 +314,7 @@ void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf)
skb_tstamp_tx(pf->ptp_tx_skb, &shhwtstamps);
dev_kfree_skb_any(pf->ptp_tx_skb);
pf->ptp_tx_skb = NULL;
+ clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, &pf->state);
}
/**
@@ -423,28 +423,23 @@ int i40e_ptp_get_ts_config(struct i40e_pf *pf, struct ifreq *ifr)
}
/**
- * i40e_ptp_set_ts_config - ioctl interface to control the HW timestamping
+ * i40e_ptp_set_timestamp_mode - setup hardware for requested timestamp mode
* @pf: Board private structure
- * @ifreq: ioctl data
+ * @config: hwtstamp settings requested or saved
*
- * Respond to the user filter requests and make the appropriate hardware
- * changes here. The XL710 cannot support splitting of the Tx/Rx timestamping
- * logic, so keep track in software of whether to indicate these timestamps
- * or not.
+ * Control hardware registers to enter the specific mode requested by the
+ * user. Also used during reset path to ensure that timestamp settings are
+ * maintained.
*
- * It is permissible to "upgrade" the user request to a broader filter, as long
- * as the user receives the timestamps they care about and the user is notified
- * the filter has been broadened.
+ * Note: modifies config in place, and may update the requested mode to be
+ * more broad if the specific filter is not directly supported.
**/
-int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr)
+static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf,
+ struct hwtstamp_config *config)
{
struct i40e_hw *hw = &pf->hw;
- struct hwtstamp_config *config = &pf->tstamp_config;
u32 pf_id, tsyntype, regval;
- if (copy_from_user(config, ifr->ifr_data, sizeof(*config)))
- return -EFAULT;
-
/* Reserved for future extensions. */
if (config->flags)
return -EINVAL;
@@ -452,8 +447,12 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr)
/* Confirm that 1588 is supported on this PF. */
pf_id = (rd32(hw, I40E_PRTTSYN_CTL0) & I40E_PRTTSYN_CTL0_PF_ID_MASK) >>
I40E_PRTTSYN_CTL0_PF_ID_SHIFT;
- if (hw->pf_id != pf_id)
- return -EINVAL;
+ if (hw->pf_id != pf_id) {
+ dev_err(&pf->pdev->dev,
+ "PF %d attempted to control timestamp mode on port %d, which is owned by PF %d\n",
+ hw->pf_id, hw->port, pf_id);
+ return -EPERM;
+ }
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
@@ -535,23 +534,59 @@ int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr)
wr32(hw, I40E_PRTTSYN_CTL1, regval);
}
- return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+ return 0;
+}
+
+/**
+ * i40e_ptp_set_ts_config - ioctl interface to control the HW timestamping
+ * @pf: Board private structure
+ * @ifreq: ioctl data
+ *
+ * Respond to the user filter requests and make the appropriate hardware
+ * changes here. The XL710 cannot support splitting of the Tx/Rx timestamping
+ * logic, so keep track in software of whether to indicate these timestamps
+ * or not.
+ *
+ * It is permissible to "upgrade" the user request to a broader filter, as long
+ * as the user receives the timestamps they care about and the user is notified
+ * the filter has been broadened.
+ **/
+int i40e_ptp_set_ts_config(struct i40e_pf *pf, struct ifreq *ifr)
+{
+ struct hwtstamp_config config;
+ int err;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ err = i40e_ptp_set_timestamp_mode(pf, &config);
+ if (err)
+ return err;
+
+ /* save these settings for future reference */
+ pf->tstamp_config = config;
+
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
-EFAULT : 0;
}
/**
- * i40e_ptp_init - Initialize the 1588 support and register the PHC
+ * i40e_ptp_create_clock - Create PTP clock device for userspace
* @pf: Board private structure
*
- * This function registers the device clock as a PHC. If it is successful, it
- * starts the clock in the hardware.
+ * This function creates a new PTP clock device. It only creates one if we
+ * don't already have one, so it is safe to call. Will return error if it
+ * can't create one, but success if we already have a device. Should be used
+ * by i40e_ptp_init to create clock initially, and prevent global resets from
+ * creating new clock devices.
**/
-void i40e_ptp_init(struct i40e_pf *pf)
+static long i40e_ptp_create_clock(struct i40e_pf *pf)
{
- struct i40e_hw *hw = &pf->hw;
- struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev;
+ /* no need to create a clock device if we already have one */
+ if (!IS_ERR_OR_NULL(pf->ptp_clock))
+ return 0;
- strncpy(pf->ptp_caps.name, "i40e", sizeof(pf->ptp_caps.name));
+ strncpy(pf->ptp_caps.name, i40e_driver_name, sizeof(pf->ptp_caps.name));
pf->ptp_caps.owner = THIS_MODULE;
pf->ptp_caps.max_adj = 999999999;
pf->ptp_caps.n_ext_ts = 0;
@@ -560,11 +595,46 @@ void i40e_ptp_init(struct i40e_pf *pf)
pf->ptp_caps.adjtime = i40e_ptp_adjtime;
pf->ptp_caps.gettime = i40e_ptp_gettime;
pf->ptp_caps.settime = i40e_ptp_settime;
- pf->ptp_caps.enable = i40e_ptp_enable;
+ pf->ptp_caps.enable = i40e_ptp_feature_enable;
/* Attempt to register the clock before enabling the hardware. */
pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev);
if (IS_ERR(pf->ptp_clock)) {
+ return PTR_ERR(pf->ptp_clock);
+ }
+
+ /* clear the hwtstamp settings here during clock create, instead of
+ * during regular init, so that we can maintain settings across a
+ * reset or suspend.
+ */
+ pf->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+ pf->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
+
+ return 0;
+}
+
+/**
+ * i40e_ptp_init - Initialize the 1588 support after device probe or reset
+ * @pf: Board private structure
+ *
+ * This function sets device up for 1588 support. The first time it is run, it
+ * will create a PHC clock device. It does not create a clock device if one
+ * already exists. It also reconfigures the device after a reset.
+ **/
+void i40e_ptp_init(struct i40e_pf *pf)
+{
+ struct net_device *netdev = pf->vsi[pf->lan_vsi]->netdev;
+ struct i40e_hw *hw = &pf->hw;
+ long err;
+
+ /* we have to initialize the lock first, since we can't control
+ * when the user will enter the PHC device entry points
+ */
+ spin_lock_init(&pf->tmreg_lock);
+
+ /* ensure we have a clock device */
+ err = i40e_ptp_create_clock(pf);
+ if (err) {
pf->ptp_clock = NULL;
dev_err(&pf->pdev->dev, "%s: ptp_clock_register failed\n",
__func__);
@@ -572,8 +642,6 @@ void i40e_ptp_init(struct i40e_pf *pf)
struct timespec ts;
u32 regval;
- spin_lock_init(&pf->tmreg_lock);
-
dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__,
netdev->name);
pf->flags |= I40E_FLAG_PTP;
@@ -589,8 +657,8 @@ void i40e_ptp_init(struct i40e_pf *pf)
/* Set the increment value per clock tick. */
i40e_ptp_set_increment(pf);
- /* reset the tstamp_config */
- memset(&pf->tstamp_config, 0, sizeof(pf->tstamp_config));
+ /* reset timestamping mode */
+ i40e_ptp_set_timestamp_mode(pf, &pf->tstamp_config);
/* Set the clock value. */
ts = ktime_to_timespec(ktime_get_real());
@@ -614,6 +682,7 @@ void i40e_ptp_stop(struct i40e_pf *pf)
if (pf->ptp_tx_skb) {
dev_kfree_skb_any(pf->ptp_tx_skb);
pf->ptp_tx_skb = NULL;
+ clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, &pf->state);
}
if (pf->ptp_clock) {