* [net-next 06/14] i40e: update driver version
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Alice Michael, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Alice Michael <alice.michael@intel.com>
The version numbers have not been kept up to date and this is
an effort to ammend that.
Signed-off-by: Alice Michael <alice.michael@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index b6f4ebb4557e..dbd6fffd9b85 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -26,8 +26,8 @@ static const char i40e_driver_string[] =
#define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 2
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 2
+#define DRV_VERSION_MINOR 7
+#define DRV_VERSION_BUILD 6
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
--
2.19.1
^ permalink raw reply related
* [net-next 11/14] virtchnl: Fix off by one error
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Alice Michael, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Alice Michael <alice.michael@intel.com>
When calculating the valid length for a VIRTCHNL_OP_ENABLE_CHANNELS
message, we accidentally allowed messages with one extra
virtchnl_channel_info structure on the end. This happened due
to an off by one error, because we forgot that valid_len already
accounted for one virtchnl_channel_info structure, so we need to
subtract one from the num_tc value.
Signed-off-by: Alice Michael <alice.michael@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
include/linux/avf/virtchnl.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h
index 3130dec40b93..7605b5919c3a 100644
--- a/include/linux/avf/virtchnl.h
+++ b/include/linux/avf/virtchnl.h
@@ -819,8 +819,8 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
if (msglen >= valid_len) {
struct virtchnl_tc_info *vti =
(struct virtchnl_tc_info *)msg;
- valid_len += vti->num_tc *
- sizeof(struct virtchnl_channel_info);
+ valid_len += (vti->num_tc - 1) *
+ sizeof(struct virtchnl_channel_info);
if (vti->num_tc == 0)
err_msg_format = true;
}
--
2.19.1
^ permalink raw reply related
* [net-next 14/14] i40e: prevent overlapping tx_timeout recover
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Alan Brady, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Alan Brady <alan.brady@intel.com>
If a TX hang occurs, we attempt to recover by incrementally resetting.
If we're starved for CPU time, it's possible the reset doesn't actually
complete (or even fire) before another tx_timeout fires causing us to
fly through the different resets without actually doing them.
This adds a bit to set and check if a timeout recovery is already
pending and, if so, bail out of tx_timeout. The bit will get cleared at
the end of i40e_rebuild when reset is complete.
Signed-off-by: Alan Brady <alan.brady@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 1 +
drivers/net/ethernet/intel/i40e/i40e_main.c | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index cda37d7ae5d6..8de9085bba9e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -122,6 +122,7 @@ enum i40e_state_t {
__I40E_MDD_EVENT_PENDING,
__I40E_VFLR_EVENT_PENDING,
__I40E_RESET_RECOVERY_PENDING,
+ __I40E_TIMEOUT_RECOVERY_PENDING,
__I40E_MISC_IRQ_REQUESTED,
__I40E_RESET_INTR_RECEIVED,
__I40E_REINIT_REQUESTED,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index d4461eec26bd..47f0fdadbac9 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -338,6 +338,10 @@ static void i40e_tx_timeout(struct net_device *netdev)
(pf->tx_timeout_last_recovery + netdev->watchdog_timeo)))
return; /* don't do any new action before the next timeout */
+ /* don't kick off another recovery if one is already pending */
+ if (test_and_set_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state))
+ return;
+
if (tx_ring) {
head = i40e_get_head(tx_ring);
/* Read interrupt register */
@@ -9631,6 +9635,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
clear_bit(__I40E_RESET_FAILED, pf->state);
clear_recovery:
clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state);
+ clear_bit(__I40E_TIMEOUT_RECOVERY_PENDING, pf->state);
}
/**
--
2.19.1
^ permalink raw reply related
* [net-next 12/14] i40e: Use correct shift for VLAN priority
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Richard Rodriguez, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Richard Rodriguez <richard.rodriguez@intel.com>
When using port VLAN, for VFs, and setting priority bits, the device
was sending out incorrect priority bits, and also setting the CFI
bit incorrectly.
To fix this, changed shift and mask bit definition for this function, to
use the correct ones.
Signed-off-by: Richard Rodriguez <richard.rodriguez@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
index bf67d62e2b5f..f9621026beef 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
@@ -13,9 +13,9 @@
#define I40E_DEFAULT_NUM_MDD_EVENTS_ALLOWED 3
#define I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED 10
-#define I40E_VLAN_PRIORITY_SHIFT 12
+#define I40E_VLAN_PRIORITY_SHIFT 13
#define I40E_VLAN_MASK 0xFFF
-#define I40E_PRIORITY_MASK 0x7000
+#define I40E_PRIORITY_MASK 0xE000
/* Various queue ctrls */
enum i40e_queue_ctrl {
--
2.19.1
^ permalink raw reply related
* [net-next 07/14] i40e: Allow disabling FW LLDP on X722 devices
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Patryk Małek, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Patryk Małek <patryk.malek@intel.com>
This patch allows disabling FW LLDP agent on X722 devices.
It also changes a source of information for this feature from
pf->hw_features to pf->hw.flags which are set in i40e_init_adminq.
Signed-off-by: Patryk Małek <patryk.malek@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 1 -
drivers/net/ethernet/intel/i40e/i40e_common.c | 3 +++
drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 13 +++++++------
drivers/net/ethernet/intel/i40e/i40e_main.c | 15 +++++++--------
4 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 5595a4614206..cda37d7ae5d6 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -495,7 +495,6 @@ struct i40e_pf {
#define I40E_HW_STOP_FW_LLDP BIT(16)
#define I40E_HW_PORT_ID_VALID BIT(17)
#define I40E_HW_RESTART_AUTONEG BIT(18)
-#define I40E_HW_STOPPABLE_FW_LLDP BIT(19)
u32 flags;
#define I40E_FLAG_RX_CSUM_ENABLED BIT(0)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 85f75b5978fc..97a9b1fb4763 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -3723,6 +3723,9 @@ i40e_aq_set_dcb_parameters(struct i40e_hw *hw, bool dcb_enable,
(struct i40e_aqc_set_dcb_parameters *)&desc.params.raw;
i40e_status status;
+ if (!(hw->flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE))
+ return I40E_ERR_DEVICE_NOT_SUPPORTED;
+
i40e_fill_default_direct_cmd_desc(&desc,
i40e_aqc_opc_set_dcb_parameters);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 9c1211ad2c6b..311edac272aa 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -4660,14 +4660,15 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
return -EOPNOTSUPP;
/* If the driver detected FW LLDP was disabled on init, this flag could
- * be set, however we do not support _changing_ the flag if NPAR is
- * enabled or FW API version < 1.7. There are situations where older
- * FW versions/NPAR enabled PFs could disable LLDP, however we _must_
- * not allow the user to enable/disable LLDP with this flag on
- * unsupported FW versions.
+ * be set, however we do not support _changing_ the flag:
+ * - on XL710 if NPAR is enabled or FW API version < 1.7
+ * - on X722 with FW API version < 1.6
+ * There are situations where older FW versions/NPAR enabled PFs could
+ * disable LLDP, however we _must_ not allow the user to enable/disable
+ * LLDP with this flag on unsupported FW versions.
*/
if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) {
- if (!(pf->hw_features & I40E_HW_STOPPABLE_FW_LLDP)) {
+ if (!(pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE)) {
dev_warn(&pf->pdev->dev,
"Device does not support changing FW LLDP\n");
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index dbd6fffd9b85..d4461eec26bd 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -11331,16 +11331,15 @@ static int i40e_sw_init(struct i40e_pf *pf)
/* IWARP needs one extra vector for CQP just like MISC.*/
pf->num_iwarp_msix = (int)num_online_cpus() + 1;
}
- /* Stopping the FW LLDP engine is only supported on the
- * XL710 with a FW ver >= 1.7. Also, stopping FW LLDP
- * engine is not supported if NPAR is functioning on this
- * part
+ /* Stopping FW LLDP engine is supported on XL710 and X722
+ * starting from FW versions determined in i40e_init_adminq.
+ * Stopping the FW LLDP engine is not supported on XL710
+ * if NPAR is functioning so unset this hw flag in this case.
*/
if (pf->hw.mac.type == I40E_MAC_XL710 &&
- !pf->hw.func_caps.npar_enable &&
- (pf->hw.aq.api_maj_ver > 1 ||
- (pf->hw.aq.api_maj_ver == 1 && pf->hw.aq.api_min_ver > 6)))
- pf->hw_features |= I40E_HW_STOPPABLE_FW_LLDP;
+ pf->hw.func_caps.npar_enable &&
+ (pf->hw.flags & I40E_HW_FLAG_FW_LLDP_STOPPABLE))
+ pf->hw.flags &= ~I40E_HW_FLAG_FW_LLDP_STOPPABLE;
#ifdef CONFIG_PCI_IOV
if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) {
--
2.19.1
^ permalink raw reply related
* [net-next 08/14] i40e: don't restart nway if autoneg not supported
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Mitch Williams, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Mitch Williams <mitch.a.williams@intel.com>
On link types that do not support autoneg, we cannot attempt to restart
nway negotiation. This results in a dead link that requires a power
cycle to remedy.
Fix this by saving off the autoneg state and checking this value before
we try to restart nway.
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 311edac272aa..1ed241a5c3f4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -1335,6 +1335,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
i40e_status status;
u8 aq_failures;
int err = 0;
+ u32 is_an;
/* Changing the port's flow control is not supported if this isn't the
* port's controlling PF
@@ -1347,15 +1348,14 @@ static int i40e_set_pauseparam(struct net_device *netdev,
if (vsi != pf->vsi[pf->lan_vsi])
return -EOPNOTSUPP;
- if (pause->autoneg != ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ?
- AUTONEG_ENABLE : AUTONEG_DISABLE)) {
+ is_an = hw_link_info->an_info & I40E_AQ_AN_COMPLETED;
+ if (pause->autoneg != is_an) {
netdev_info(netdev, "To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
return -EOPNOTSUPP;
}
/* If we have link and don't have autoneg */
- if (!test_bit(__I40E_DOWN, pf->state) &&
- !(hw_link_info->an_info & I40E_AQ_AN_COMPLETED)) {
+ if (!test_bit(__I40E_DOWN, pf->state) && !is_an) {
/* Send message that it might not necessarily work*/
netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n");
}
@@ -1406,7 +1406,7 @@ static int i40e_set_pauseparam(struct net_device *netdev,
err = -EAGAIN;
}
- if (!test_bit(__I40E_DOWN, pf->state)) {
+ if (!test_bit(__I40E_DOWN, pf->state) && is_an) {
/* Give it a little more time to try to come back */
msleep(75);
if (!test_bit(__I40E_DOWN, pf->state))
--
2.19.1
^ permalink raw reply related
* [net-next 05/14] i40e: Protect access to VF control methods
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Jan Sokolowski, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Jan Sokolowski <jan.sokolowski@intel.com>
A scenario has been found in which simultaneous
addition/removal and modification of VF's might cause
unstable behaviour, up to and including kernel panics.
Protect the methods that create/modify/destroy VF's
by locking them behind an atomically set bit in PF status
bitfield.
Signed-off-by: Jan Sokolowski <jan.sokolowski@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e.h | 1 +
.../ethernet/intel/i40e/i40e_virtchnl_pf.c | 64 +++++++++++++++++--
2 files changed, 60 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 876cac317e79..5595a4614206 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -146,6 +146,7 @@ enum i40e_state_t {
__I40E_CLIENT_SERVICE_REQUESTED,
__I40E_CLIENT_L2_CHANGE,
__I40E_CLIENT_RESET,
+ __I40E_VIRTCHNL_OP_PENDING,
/* This must be last as it determines the size of the BITMAP */
__I40E_STATE_SIZE__,
};
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index ac5698ed0b11..8e0a247e7e5a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -1675,13 +1675,20 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs)
int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct i40e_pf *pf = pci_get_drvdata(pdev);
+ int ret = 0;
+
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
if (num_vfs) {
if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
}
- return i40e_pci_sriov_enable(pdev, num_vfs);
+ ret = i40e_pci_sriov_enable(pdev, num_vfs);
+ goto sriov_configure_out;
}
if (!pci_vfs_assigned(pf->pdev)) {
@@ -1690,9 +1697,12 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
} else {
dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto sriov_configure_out;
}
- return 0;
+sriov_configure_out:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
+ return ret;
}
/***********************virtual channel routines******************/
@@ -3893,6 +3903,11 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
goto error_param;
}
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
if (is_multicast_ether_addr(mac)) {
dev_err(&pf->pdev->dev,
"Invalid Ethernet address %pM for VF %d\n", mac, vf_id);
@@ -3941,6 +3956,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
dev_info(&pf->pdev->dev, "Bring down and up the VF interface to make this change effective.\n");
error_param:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
@@ -3992,6 +4008,11 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
struct i40e_vf *vf;
int ret = 0;
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
/* validate the request */
ret = i40e_validate_vf(pf, vf_id);
if (ret)
@@ -4107,6 +4128,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id,
ret = 0;
error_pvid:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
@@ -4128,6 +4150,11 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
struct i40e_vf *vf;
int ret = 0;
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
/* validate the request */
ret = i40e_validate_vf(pf, vf_id);
if (ret)
@@ -4154,6 +4181,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
vf->tx_rate = max_tx_rate;
error:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
@@ -4174,6 +4202,11 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
struct i40e_vf *vf;
int ret = 0;
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
/* validate the request */
ret = i40e_validate_vf(pf, vf_id);
if (ret)
@@ -4209,6 +4242,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
ret = 0;
error_param:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
@@ -4230,6 +4264,11 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
int abs_vf_id;
int ret = 0;
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
/* validate the request */
if (vf_id >= pf->num_alloc_vfs) {
dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
@@ -4273,6 +4312,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
0, (u8 *)&pfe, sizeof(pfe), NULL);
error_out:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
@@ -4294,6 +4334,11 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
struct i40e_vf *vf;
int ret = 0;
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
/* validate the request */
if (vf_id >= pf->num_alloc_vfs) {
dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
@@ -4327,6 +4372,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
ret = -EIO;
}
out:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
@@ -4345,15 +4391,22 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
struct i40e_vf *vf;
int ret = 0;
+ if (test_and_set_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state)) {
+ dev_warn(&pf->pdev->dev, "Unable to configure VFs, other operation is pending.\n");
+ return -EAGAIN;
+ }
+
/* validate the request */
if (vf_id >= pf->num_alloc_vfs) {
dev_err(&pf->pdev->dev, "Invalid VF Identifier %d\n", vf_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (pf->flags & I40E_FLAG_MFP_ENABLED) {
dev_err(&pf->pdev->dev, "Trusted VF not supported in MFP mode.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
vf = &pf->vf[vf_id];
@@ -4376,5 +4429,6 @@ int i40e_ndo_set_vf_trust(struct net_device *netdev, int vf_id, bool setting)
}
out:
+ clear_bit(__I40E_VIRTCHNL_OP_PENDING, pf->state);
return ret;
}
--
2.19.1
^ permalink raw reply related
* [net-next 10/14] virtchnl: white space and reorder
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Alice Michael, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Alice Michael <alice.michael@intel.com>
White space change.
Move the check on the virtchnl_vsi_queue_config_info struct
to be close to the struct like all the other similar checks.
This keeps it clearer and easier to read.
Signed-off-by: Alice Michael <alice.michael@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
include/linux/avf/virtchnl.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h
index b2488055fd1d..3130dec40b93 100644
--- a/include/linux/avf/virtchnl.h
+++ b/include/linux/avf/virtchnl.h
@@ -171,7 +171,7 @@ struct virtchnl_msg {
VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg);
-/* Message descriptions and data structures.*/
+/* Message descriptions and data structures. */
/* VIRTCHNL_OP_VERSION
* VF posts its version number to the PF. PF responds with its version number
@@ -342,6 +342,8 @@ struct virtchnl_vsi_queue_config_info {
struct virtchnl_queue_pair_info qpair[1];
};
+VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
+
/* VIRTCHNL_OP_REQUEST_QUEUES
* VF sends this message to request the PF to allocate additional queues to
* this VF. Each VF gets a guaranteed number of queues on init but asking for
@@ -357,8 +359,6 @@ struct virtchnl_vf_res_request {
u16 num_queue_pairs;
};
-VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info);
-
/* VIRTCHNL_OP_CONFIG_IRQ_MAP
* VF uses this message to map vectors to queues.
* The rxq_map and txq_map fields are bitmaps used to indicate which queues
--
2.19.1
^ permalink raw reply related
* [net-next 13/14] i40e: suppress bogus error message
From: Jeff Kirsher @ 2018-11-14 23:10 UTC (permalink / raw)
To: davem; +Cc: Mitch Williams, netdev, nhorman, sassmann, Jeff Kirsher
In-Reply-To: <20181114231032.4013-1-jeffrey.t.kirsher@intel.com>
From: Mitch Williams <mitch.a.williams@intel.com>
The i40e driver complains about unprivileged VFs trying to configure
promiscuous mode each time a VF reset occurs. This isn't the fault of
the poor VF driver - the PF driver itself is making the request.
To fix this, skip the privilege check if the request is to disable all
promiscuous activity. This gets rid of the bogus message, but doesn't
affect privilege checks, since we really only care if the unprivileged
VF is trying to enable promiscuous mode.
Signed-off-by: Mitch Williams <mitch.a.williams@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
---
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index 8e0a247e7e5a..2ac23ebfbf31 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -1112,7 +1112,8 @@ static i40e_status i40e_config_vf_promiscuous_mode(struct i40e_vf *vf,
if (!i40e_vc_isvalid_vsi_id(vf, vsi_id) || !vsi)
return I40E_ERR_PARAM;
- if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) {
+ if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) &&
+ (allmulti || alluni)) {
dev_err(&pf->pdev->dev,
"Unprivileged VF %d is attempting to configure promiscuous mode\n",
vf->vf_id);
--
2.19.1
^ permalink raw reply related
* [PATCH V2 net-next 0/5] net: hns3: Add support of hardware GRO to HNS3 Driver
From: Salil Mehta @ 2018-11-15 9:29 UTC (permalink / raw)
To: davem
Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
linux-kernel, linux-rdma, linuxarm
This patch-set adds support of hardware assisted GRO feature to
HNS3 driver on Rev B(=0x21) platform. Current hardware only
supports TCP/IPv{4|6} flows.
Change Log:
V1->V2:
1. Remove redundant print reported by Leon Romanovsky.
Link: https://lkml.org/lkml/2018/11/13/715
Peng Li (5):
net: hns3: Enable HW GRO for Rev B(=0x21) HNS3 hardware
net: hns3: Add handling of GRO Pkts not fully RX'ed in NAPI poll
net: hns3: Add support for ethtool -K to enable/disable HW GRO
net: hns3: Add skb chain when num of RX buf exceeds MAX_SKB_FRAGS
net: hns3: Adds GRO params to SKB for the stack
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 7 +
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 289 ++++++++++++++----
.../net/ethernet/hisilicon/hns3/hns3_enet.h | 17 +-
.../hisilicon/hns3/hns3pf/hclge_cmd.h | 7 +
.../hisilicon/hns3/hns3pf/hclge_main.c | 39 +++
.../hisilicon/hns3/hns3vf/hclgevf_cmd.h | 8 +
.../hisilicon/hns3/hns3vf/hclgevf_main.c | 39 +++
7 files changed, 339 insertions(+), 67 deletions(-)
--
2.17.1
^ permalink raw reply
* [PATCH V2 net-next 1/5] net: hns3: Enable HW GRO for Rev B(=0x21) HNS3 hardware
From: Salil Mehta @ 2018-11-15 9:29 UTC (permalink / raw)
To: davem
Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
linux-kernel, linux-rdma, linuxarm
In-Reply-To: <20181115092925.11812-1-salil.mehta@huawei.com>
From: Peng Li <lipeng321@huawei.com>
HNS3 hardware Revision B(=0x21) supports Hardware GRO feature. This
patch enables this feature in the HNS3 PF/VF driver.
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 4 +++
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 4 ++-
.../hisilicon/hns3/hns3pf/hclge_cmd.h | 7 +++++
.../hisilicon/hns3/hns3pf/hclge_main.c | 30 ++++++++++++++++++
.../hisilicon/hns3/hns3vf/hclgevf_cmd.h | 8 +++++
.../hisilicon/hns3/hns3vf/hclgevf_main.c | 31 +++++++++++++++++++
6 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index f69d39f17bdd..21d934b7a2a3 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -52,6 +52,7 @@
#define HNAE3_UNIC_CLIENT_INITED_B 0x4
#define HNAE3_ROCE_CLIENT_INITED_B 0x5
#define HNAE3_DEV_SUPPORT_FD_B 0x6
+#define HNAE3_DEV_SUPPORT_GRO_B 0x7
#define HNAE3_DEV_SUPPORT_ROCE_DCB_BITS (BIT(HNAE3_DEV_SUPPORT_DCB_B) |\
BIT(HNAE3_DEV_SUPPORT_ROCE_B))
@@ -65,6 +66,9 @@
#define hnae3_dev_fd_supported(hdev) \
hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B)
+#define hnae3_dev_gro_supported(hdev) \
+ hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_GRO_B)
+
#define ring_ptr_move_fw(ring, p) \
((ring)->p = ((ring)->p + 1) % (ring)->desc_num)
#define ring_ptr_move_bw(ring, p) \
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 8d07ec668d5d..a510ddfd45a5 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -1714,8 +1714,10 @@ static void hns3_disable_sriov(struct pci_dev *pdev)
static void hns3_get_dev_capability(struct pci_dev *pdev,
struct hnae3_ae_dev *ae_dev)
{
- if (pdev->revision >= 0x21)
+ if (pdev->revision >= 0x21) {
hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B, 1);
+ hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_GRO_B, 1);
+ }
}
/* hns3_probe - Device initialization routine
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 872cd4bdd70d..aef044d08b11 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -152,6 +152,7 @@ enum hclge_opcode_type {
/* TSO command */
HCLGE_OPC_TSO_GENERIC_CONFIG = 0x0C01,
+ HCLGE_OPC_GRO_GENERIC_CONFIG = 0x0C10,
/* RSS commands */
HCLGE_OPC_RSS_GENERIC_CONFIG = 0x0D01,
@@ -758,6 +759,12 @@ struct hclge_cfg_tso_status_cmd {
u8 rsv[20];
};
+#define HCLGE_GRO_EN_B 0
+struct hclge_cfg_gro_status_cmd {
+ __le16 gro_en;
+ u8 rsv[22];
+};
+
#define HCLGE_TSO_MSS_MIN 256
#define HCLGE_TSO_MSS_MAX 9668
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 43bfc730a62d..3f8bd11d0824 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -921,6 +921,28 @@ static int hclge_config_tso(struct hclge_dev *hdev, int tso_mss_min,
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
+static int hclge_config_gro(struct hclge_dev *hdev, bool en)
+{
+ struct hclge_cfg_gro_status_cmd *req;
+ struct hclge_desc desc;
+ int ret;
+
+ if (!hnae3_dev_gro_supported(hdev))
+ return 0;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, false);
+ req = (struct hclge_cfg_gro_status_cmd *)desc.data;
+
+ req->gro_en = cpu_to_le16(en ? 1 : 0);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "GRO hardware config cmd failed, ret = %d\n", ret);
+
+ return ret;
+}
+
static int hclge_alloc_tqps(struct hclge_dev *hdev)
{
struct hclge_tqp *tqp;
@@ -7090,6 +7112,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
goto err_mdiobus_unreg;
}
+ ret = hclge_config_gro(hdev, true);
+ if (ret)
+ goto err_mdiobus_unreg;
+
ret = hclge_init_vlan_config(hdev);
if (ret) {
dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
@@ -7221,6 +7247,10 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
return ret;
}
+ ret = hclge_config_gro(hdev, true);
+ if (ret)
+ return ret;
+
ret = hclge_init_vlan_config(hdev);
if (ret) {
dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
index 090541de0c7d..47030b42341f 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h
@@ -87,6 +87,8 @@ enum hclgevf_opcode_type {
HCLGEVF_OPC_QUERY_TX_STATUS = 0x0B03,
HCLGEVF_OPC_QUERY_RX_STATUS = 0x0B13,
HCLGEVF_OPC_CFG_COM_TQP_QUEUE = 0x0B20,
+ /* GRO command */
+ HCLGEVF_OPC_GRO_GENERIC_CONFIG = 0x0C10,
/* RSS cmd */
HCLGEVF_OPC_RSS_GENERIC_CONFIG = 0x0D01,
HCLGEVF_OPC_RSS_INPUT_TUPLE = 0x0D02,
@@ -149,6 +151,12 @@ struct hclgevf_query_res_cmd {
__le16 rsv[7];
};
+#define HCLGEVF_GRO_EN_B 0
+struct hclgevf_cfg_gro_status_cmd {
+ __le16 gro_en;
+ u8 rsv[22];
+};
+
#define HCLGEVF_RSS_DEFAULT_OUTPORT_B 4
#define HCLGEVF_RSS_HASH_KEY_OFFSET_B 4
#define HCLGEVF_RSS_HASH_KEY_NUM 16
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
index 6b4d1477055f..2e889b829976 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
@@ -1655,6 +1655,29 @@ static int hclgevf_init_roce_base_info(struct hclgevf_dev *hdev)
return 0;
}
+static int hclgevf_config_gro(struct hclgevf_dev *hdev, bool en)
+{
+ struct hclgevf_cfg_gro_status_cmd *req;
+ struct hclgevf_desc desc;
+ int ret;
+
+ if (!hnae3_dev_gro_supported(hdev))
+ return 0;
+
+ hclgevf_cmd_setup_basic_desc(&desc, HCLGEVF_OPC_GRO_GENERIC_CONFIG,
+ false);
+ req = (struct hclgevf_cfg_gro_status_cmd *)desc.data;
+
+ req->gro_en = cpu_to_le16(en ? 1 : 0);
+
+ ret = hclgevf_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "VF GRO hardware config cmd failed, ret = %d.\n", ret);
+
+ return ret;
+}
+
static int hclgevf_rss_init_hw(struct hclgevf_dev *hdev)
{
struct hclgevf_rss_cfg *rss_cfg = &hdev->rss_cfg;
@@ -2122,6 +2145,10 @@ static int hclgevf_reset_hdev(struct hclgevf_dev *hdev)
return ret;
}
+ ret = hclgevf_config_gro(hdev, true);
+ if (ret)
+ return ret;
+
ret = hclgevf_init_vlan_config(hdev);
if (ret) {
dev_err(&hdev->pdev->dev,
@@ -2199,6 +2226,10 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
goto err_config;
}
+ ret = hclgevf_config_gro(hdev, true);
+ if (ret)
+ goto err_config;
+
/* Initialize RSS for this VF */
ret = hclgevf_rss_init_hw(hdev);
if (ret) {
--
2.17.1
^ permalink raw reply related
* [PATCH V2 net-next 4/5] net: hns3: Add skb chain when num of RX buf exceeds MAX_SKB_FRAGS
From: Salil Mehta @ 2018-11-15 9:29 UTC (permalink / raw)
To: davem
Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
linux-kernel, linux-rdma, linuxarm
In-Reply-To: <20181115092925.11812-1-salil.mehta@huawei.com>
From: Peng Li <lipeng321@huawei.com>
MAX_SKB_FRAGS in protocol stack is defined as:
MAX_SKB_FRAGS is 17 when PAGE_SIZE is 4K. If HW enable GRO, it may
merge small packets and the rx buffer may be more than
MAX_SKB_FRAGS. So driver will add skb chain when RX buffer num.
more than MAX_SKB_FRAGS.
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 37 ++++++++++++++++++-
.../net/ethernet/hisilicon/hns3/hns3_enet.h | 2 +
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 860067898471..7776089b6bc2 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -2361,6 +2361,9 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
static void hns3_rx_skb(struct hns3_enet_ring *ring, struct sk_buff *skb)
{
+ if (skb_has_frag_list(skb))
+ napi_gro_flush(&ring->tqp_vector->napi, false);
+
napi_gro_receive(&ring->tqp_vector->napi, skb);
}
@@ -2417,6 +2420,8 @@ static int hns3_alloc_skb(struct hns3_enet_ring *ring, int length,
prefetchw(skb->data);
ring->pending_buf = 1;
+ ring->frag_num = 0;
+ ring->tail_skb = NULL;
if (length <= HNS3_RX_HEAD_SIZE) {
memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
@@ -2435,7 +2440,7 @@ static int hns3_alloc_skb(struct hns3_enet_ring *ring, int length,
ring->pull_len = eth_get_headlen(va, HNS3_RX_HEAD_SIZE);
__skb_put(skb, ring->pull_len);
- hns3_nic_reuse_page(skb, 0, ring, ring->pull_len,
+ hns3_nic_reuse_page(skb, ring->frag_num++, ring, ring->pull_len,
desc_cb);
ring_ptr_move_fw(ring, next_to_clean);
@@ -2446,6 +2451,8 @@ static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc,
struct sk_buff **out_skb, bool pending)
{
struct sk_buff *skb = *out_skb;
+ struct sk_buff *head_skb = *out_skb;
+ struct sk_buff *new_skb;
struct hns3_desc_cb *desc_cb;
struct hns3_desc *pre_desc;
u32 bd_base_info;
@@ -2470,7 +2477,33 @@ static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc,
if (!hnae3_get_bit(bd_base_info, HNS3_RXD_VLD_B))
return -ENXIO;
- hns3_nic_reuse_page(skb, ring->pending_buf, ring, 0, desc_cb);
+ if (unlikely(ring->frag_num >= MAX_SKB_FRAGS)) {
+ new_skb = napi_alloc_skb(&ring->tqp_vector->napi,
+ HNS3_RX_HEAD_SIZE);
+ if (unlikely(!new_skb)) {
+ netdev_err(ring->tqp->handle->kinfo.netdev,
+ "alloc rx skb frag fail\n");
+ return -ENXIO;
+ }
+ ring->frag_num = 0;
+
+ if (ring->tail_skb) {
+ ring->tail_skb->next = new_skb;
+ ring->tail_skb = new_skb;
+ } else {
+ skb_shinfo(skb)->frag_list = new_skb;
+ ring->tail_skb = new_skb;
+ }
+ }
+
+ if (ring->tail_skb) {
+ head_skb->truesize += hnae3_buf_size(ring);
+ head_skb->data_len += le16_to_cpu(desc->rx.size);
+ head_skb->len += le16_to_cpu(desc->rx.size);
+ skb = ring->tail_skb;
+ }
+
+ hns3_nic_reuse_page(skb, ring->frag_num++, ring, 0, desc_cb);
ring_ptr_move_fw(ring, next_to_clean);
ring->pending_buf++;
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
index d8c0998127be..8e56b7e44978 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
@@ -402,6 +402,7 @@ struct hns3_enet_ring {
int next_to_clean;
int pull_len; /* head length for current packet */
+ u32 frag_num;
unsigned char *va; /* first buffer address for current packet */
u32 flag; /* ring attribute */
@@ -412,6 +413,7 @@ struct hns3_enet_ring {
int pending_buf;
struct sk_buff *skb;
+ struct sk_buff *tail_skb;
};
struct hns_queue;
--
2.17.1
^ permalink raw reply related
* [PATCH V2 net-next 5/5] net: hns3: Adds GRO params to SKB for the stack
From: Salil Mehta @ 2018-11-15 9:29 UTC (permalink / raw)
To: davem
Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
linux-kernel, linux-rdma, linuxarm
In-Reply-To: <20181115092925.11812-1-salil.mehta@huawei.com>
From: Peng Li <lipeng321@huawei.com>
When HW GRO enable, protocol stack will not do GRO again,
driver should add gro param to the skb for the protocol
stack..
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 43 +++++++++++++++++++
.../net/ethernet/hisilicon/hns3/hns3_enet.h | 9 ++--
2 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 7776089b6bc2..22220af92aa9 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -15,6 +15,7 @@
#include <linux/vermagic.h>
#include <net/gre.h>
#include <net/pkt_cls.h>
+#include <net/tcp.h>
#include <net/vxlan.h>
#include "hnae3.h"
@@ -2318,6 +2319,12 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
if (!(netdev->features & NETIF_F_RXCSUM))
return;
+ /* We MUST enable hardware checksum before enabling hardware GRO */
+ if (skb_shinfo(skb)->gso_size) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ return;
+ }
+
/* check if hardware has done checksum */
if (!hnae3_get_bit(bd_base_info, HNS3_RXD_L3L4P_B))
return;
@@ -2511,6 +2518,39 @@ static int hns3_add_frag(struct hns3_enet_ring *ring, struct hns3_desc *desc,
return 0;
}
+static void hns3_set_gro_param(struct sk_buff *skb, u32 l234info,
+ u32 bd_base_info)
+{
+ u16 gro_count;
+ u32 l3_type;
+
+ gro_count = hnae3_get_field(l234info, HNS3_RXD_GRO_COUNT_M,
+ HNS3_RXD_GRO_COUNT_S);
+ /* if there is no HW GRO, do not set gro params */
+ if (!gro_count)
+ return;
+
+ /* tcp_gro_complete() will copy NAPI_GRO_CB(skb)->count
+ * to skb_shinfo(skb)->gso_segs
+ */
+ NAPI_GRO_CB(skb)->count = gro_count;
+
+ l3_type = hnae3_get_field(l234info, HNS3_RXD_L3ID_M,
+ HNS3_RXD_L3ID_S);
+ if (l3_type == HNS3_L3_TYPE_IPV4)
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+ else if (l3_type == HNS3_L3_TYPE_IPV6)
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+ else
+ return;
+
+ skb_shinfo(skb)->gso_size = hnae3_get_field(bd_base_info,
+ HNS3_RXD_GRO_SIZE_M,
+ HNS3_RXD_GRO_SIZE_S);
+ if (skb_shinfo(skb)->gso_size)
+ tcp_gro_complete(skb);
+}
+
static void hns3_set_rx_skb_rss_type(struct hns3_enet_ring *ring,
struct sk_buff *skb)
{
@@ -2645,6 +2685,9 @@ static int hns3_handle_rx_bd(struct hns3_enet_ring *ring,
ring->tqp_vector->rx_group.total_bytes += skb->len;
+ /* This is needed in order to enable forwarding support */
+ hns3_set_gro_param(skb, l234info, bd_base_info);
+
hns3_rx_checksum(ring, skb, desc);
*out_skb = skb;
hns3_set_rx_skb_rss_type(ring, skb);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
index 8e56b7e44978..3365c9596983 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
@@ -109,6 +109,10 @@ enum hns3_nic_state {
#define HNS3_RXD_DOI_B 21
#define HNS3_RXD_OL3E_B 22
#define HNS3_RXD_OL4E_B 23
+#define HNS3_RXD_GRO_COUNT_S 24
+#define HNS3_RXD_GRO_COUNT_M (0x3f << HNS3_RXD_GRO_COUNT_S)
+#define HNS3_RXD_GRO_FIXID_B 30
+#define HNS3_RXD_GRO_ECN_B 31
#define HNS3_RXD_ODMAC_S 0
#define HNS3_RXD_ODMAC_M (0x3 << HNS3_RXD_ODMAC_S)
@@ -135,9 +139,8 @@ enum hns3_nic_state {
#define HNS3_RXD_TSIND_S 12
#define HNS3_RXD_TSIND_M (0x7 << HNS3_RXD_TSIND_S)
#define HNS3_RXD_LKBK_B 15
-#define HNS3_RXD_HDL_S 16
-#define HNS3_RXD_HDL_M (0x7ff << HNS3_RXD_HDL_S)
-#define HNS3_RXD_HSIND_B 31
+#define HNS3_RXD_GRO_SIZE_S 16
+#define HNS3_RXD_GRO_SIZE_M (0x3ff << HNS3_RXD_GRO_SIZE_S)
#define HNS3_TXD_L3T_S 0
#define HNS3_TXD_L3T_M (0x3 << HNS3_TXD_L3T_S)
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v3 0/7] include: add setbits32/clrbits32/clrsetbits32/setbits64/clrbits64/clrsetbits64
From: LABBE Corentin @ 2018-11-15 9:30 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Gilles.Muller, Julia.Lawall, agust, airlied, alexandre.torgue,
alistair, benh, carlo, davem, galak, joabreu, khilman,
matthias.bgg, maxime.ripard, michal.lkml, mpe, mporter,
narmstrong, nicolas.palix, oss, paulus, peppe.cavallaro, tj, vitb,
wens, netdev, linux-kernel, dri-devel, linux-ide, linux-sunxi,
linux-mediatek, linux-amlogic, linuxppc-dev
In-Reply-To: <20181024085700.GR30658@n2100.armlinux.org.uk>
On Wed, Oct 24, 2018 at 09:57:00AM +0100, Russell King - ARM Linux wrote:
> On Wed, Oct 24, 2018 at 07:35:46AM +0000, Corentin Labbe wrote:
> > This patchset adds a new set of functions which are open-coded in lot of
> > place.
> > Basicly the pattern is always the same, "read, modify a bit, write"
> > some driver and the powerpc arch already have thoses pattern them as functions. (like ahci_sunxi.c or dwmac-meson8b)
>
> The advantage of them being open-coded is that it's _obvious_ to the
> reviewer that there is a read-modify-write going on which, in a multi-
> threaded environment, may need some locking (so it should trigger a
> review of the locking around that code.)
>
> With it hidden inside a helper which has no locking itself, it becomes
> much easier to pass over in review, which means that races are much
> more likely to go unspotted - and that is bad news.
>
Hello
I understand your fear, but I think the benefit overhaul thoses.
Furthermore, drivers which I have converted does not need such locking.
If you want I can rename the header to linux/setbits-non-atomic.h for making obvious the lack of locking.
Regards
^ permalink raw reply
* Re: [PATCH v3 0/7] include: add setbits32/clrbits32/clrsetbits32/setbits64/clrbits64/clrsetbits64
From: Russell King - ARM Linux @ 2018-11-15 9:33 UTC (permalink / raw)
To: LABBE Corentin
Cc: Gilles.Muller, Julia.Lawall, agust, airlied, alexandre.torgue,
alistair, benh, carlo, davem, galak, joabreu, khilman,
matthias.bgg, maxime.ripard, michal.lkml, mpe, mporter,
narmstrong, nicolas.palix, oss, paulus, peppe.cavallaro, tj, vitb,
wens, netdev, linux-kernel, dri-devel, linux-ide, linux-sunxi,
linux-mediatek, linux-amlogic, linuxppc-dev
In-Reply-To: <20181115093034.GB23965@Red>
On Thu, Nov 15, 2018 at 10:30:34AM +0100, LABBE Corentin wrote:
> On Wed, Oct 24, 2018 at 09:57:00AM +0100, Russell King - ARM Linux wrote:
> > On Wed, Oct 24, 2018 at 07:35:46AM +0000, Corentin Labbe wrote:
> > > This patchset adds a new set of functions which are open-coded in lot of
> > > place.
> > > Basicly the pattern is always the same, "read, modify a bit, write"
> > > some driver and the powerpc arch already have thoses pattern them as functions. (like ahci_sunxi.c or dwmac-meson8b)
> >
> > The advantage of them being open-coded is that it's _obvious_ to the
> > reviewer that there is a read-modify-write going on which, in a multi-
> > threaded environment, may need some locking (so it should trigger a
> > review of the locking around that code.)
> >
> > With it hidden inside a helper which has no locking itself, it becomes
> > much easier to pass over in review, which means that races are much
> > more likely to go unspotted - and that is bad news.
> >
>
> Hello
>
> I understand your fear, but I think the benefit overhaul thoses.
> Furthermore, drivers which I have converted does not need such locking.
>
> If you want I can rename the header to linux/setbits-non-atomic.h for making obvious the lack of locking.
It'd probably be better in the function name - it then doesn't get
"lost" that it's non-atomic when it's included via other headers.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
According to speedtest.net: 11.9Mbps down 500kbps up
^ permalink raw reply
* [PATCH net-next 1/2] vhost_net: mitigate page reference counting during page frag refill
From: Jason Wang @ 2018-11-15 9:43 UTC (permalink / raw)
To: mst, jasowang, kvm, virtualization, netdev, linux-kernel
We do a get_page() which involves a atomic operation. This patch tries
to mitigate a per packet atomic operation by maintaining a reference
bias which is initially USHRT_MAX. Each time a page is got, instead of
calling get_page() we decrease the bias and when we find it's time to
use a new page we will decrease the bias at one time through
__page_cache_drain_cache().
Testpmd(virtio_user + vhost_net) + XDP_DROP on TAP shows about 1.6%
improvement.
Before: 4.63Mpps
After: 4.71Mpps
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
drivers/vhost/net.c | 54 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 51 insertions(+), 3 deletions(-)
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index ab11b2bee273..d919284f103b 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -141,6 +141,10 @@ struct vhost_net {
unsigned tx_zcopy_err;
/* Flush in progress. Protected by tx vq lock. */
bool tx_flush;
+ /* Private page frag */
+ struct page_frag page_frag;
+ /* Refcount bias of page frag */
+ int refcnt_bias;
};
static unsigned vhost_net_zcopy_mask __read_mostly;
@@ -637,14 +641,53 @@ static bool tx_can_batch(struct vhost_virtqueue *vq, size_t total_len)
!vhost_vq_avail_empty(vq->dev, vq);
}
+#define SKB_FRAG_PAGE_ORDER get_order(32768)
+
+static bool vhost_net_page_frag_refill(struct vhost_net *net, unsigned int sz,
+ struct page_frag *pfrag, gfp_t gfp)
+{
+ if (pfrag->page) {
+ if (pfrag->offset + sz <= pfrag->size)
+ return true;
+ __page_frag_cache_drain(pfrag->page, net->refcnt_bias);
+ }
+
+ pfrag->offset = 0;
+ net->refcnt_bias = 0;
+ if (SKB_FRAG_PAGE_ORDER) {
+ /* Avoid direct reclaim but allow kswapd to wake */
+ pfrag->page = alloc_pages((gfp & ~__GFP_DIRECT_RECLAIM) |
+ __GFP_COMP | __GFP_NOWARN |
+ __GFP_NORETRY,
+ SKB_FRAG_PAGE_ORDER);
+ if (likely(pfrag->page)) {
+ pfrag->size = PAGE_SIZE << SKB_FRAG_PAGE_ORDER;
+ goto done;
+ }
+ }
+ pfrag->page = alloc_page(gfp);
+ if (likely(pfrag->page)) {
+ pfrag->size = PAGE_SIZE;
+ goto done;
+ }
+ return false;
+
+done:
+ net->refcnt_bias = USHRT_MAX;
+ page_ref_add(pfrag->page, USHRT_MAX - 1);
+ return true;
+}
+
#define VHOST_NET_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD)
static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
struct iov_iter *from)
{
struct vhost_virtqueue *vq = &nvq->vq;
+ struct vhost_net *net = container_of(vq->dev, struct vhost_net,
+ dev);
struct socket *sock = vq->private_data;
- struct page_frag *alloc_frag = ¤t->task_frag;
+ struct page_frag *alloc_frag = &net->page_frag;
struct virtio_net_hdr *gso;
struct xdp_buff *xdp = &nvq->xdp[nvq->batched_xdp];
struct tun_xdp_hdr *hdr;
@@ -665,7 +708,8 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
buflen += SKB_DATA_ALIGN(len + pad);
alloc_frag->offset = ALIGN((u64)alloc_frag->offset, SMP_CACHE_BYTES);
- if (unlikely(!skb_page_frag_refill(buflen, alloc_frag, GFP_KERNEL)))
+ if (unlikely(!vhost_net_page_frag_refill(net, buflen,
+ alloc_frag, GFP_KERNEL)))
return -ENOMEM;
buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
@@ -703,7 +747,7 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
xdp->data_end = xdp->data + len;
hdr->buflen = buflen;
- get_page(alloc_frag->page);
+ --net->refcnt_bias;
alloc_frag->offset += buflen;
++nvq->batched_xdp;
@@ -1292,6 +1336,8 @@ static int vhost_net_open(struct inode *inode, struct file *f)
vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, EPOLLIN, dev);
f->private_data = n;
+ n->page_frag.page = NULL;
+ n->refcnt_bias = 0;
return 0;
}
@@ -1366,6 +1412,8 @@ static int vhost_net_release(struct inode *inode, struct file *f)
kfree(n->vqs[VHOST_NET_VQ_RX].rxq.queue);
kfree(n->vqs[VHOST_NET_VQ_TX].xdp);
kfree(n->dev.vqs);
+ if (n->page_frag.page)
+ __page_frag_cache_drain(n->page_frag.page, n->refcnt_bias);
kvfree(n);
return 0;
}
--
2.17.1
^ permalink raw reply related
* [PATCH net-next 2/2] tuntap: free XDP dropped packets in a batch
From: Jason Wang @ 2018-11-15 9:43 UTC (permalink / raw)
To: mst, jasowang, kvm, virtualization, netdev, linux-kernel
In-Reply-To: <20181115094310.17307-1-jasowang@redhat.com>
Thanks to the batched XDP buffs through msg_control. Instead of
calling put_page() for each page which involves a atomic operation,
let's batch them by record the last page that needs to be freed and
its refcnt count and free them in a batch.
Testpmd(virtio-user + vhost_net) + XDP_DROP shows 3.8% improvement.
Before: 4.71Mpps
After : 4.89Mpps
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
drivers/net/tun.c | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index a65779c6d72f..e90a7923a5f6 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -188,6 +188,11 @@ struct tun_file {
struct xdp_rxq_info xdp_rxq;
};
+struct tun_page {
+ struct page *page;
+ int count;
+};
+
struct tun_flow_entry {
struct hlist_node hash_link;
struct rcu_head rcu;
@@ -2377,9 +2382,16 @@ static void tun_sock_write_space(struct sock *sk)
kill_fasync(&tfile->fasync, SIGIO, POLL_OUT);
}
+static void tun_put_page(struct tun_page *tpage)
+{
+ if (tpage->page)
+ __page_frag_cache_drain(tpage->page, tpage->count);
+}
+
static int tun_xdp_one(struct tun_struct *tun,
struct tun_file *tfile,
- struct xdp_buff *xdp, int *flush)
+ struct xdp_buff *xdp, int *flush,
+ struct tun_page *tpage)
{
struct tun_xdp_hdr *hdr = xdp->data_hard_start;
struct virtio_net_hdr *gso = &hdr->gso;
@@ -2390,6 +2402,7 @@ static int tun_xdp_one(struct tun_struct *tun,
int buflen = hdr->buflen;
int err = 0;
bool skb_xdp = false;
+ struct page *page;
xdp_prog = rcu_dereference(tun->xdp_prog);
if (xdp_prog) {
@@ -2416,7 +2429,14 @@ static int tun_xdp_one(struct tun_struct *tun,
case XDP_PASS:
break;
default:
- put_page(virt_to_head_page(xdp->data));
+ page = virt_to_head_page(xdp->data);
+ if (tpage->page == page) {
+ ++tpage->count;
+ } else {
+ tun_put_page(tpage);
+ tpage->page = page;
+ tpage->count = 1;
+ }
return 0;
}
}
@@ -2480,6 +2500,7 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
return -EBADFD;
if (ctl && (ctl->type == TUN_MSG_PTR)) {
+ struct tun_page tpage = {0};
int n = ctl->num;
int flush = 0;
@@ -2488,7 +2509,7 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
for (i = 0; i < n; i++) {
xdp = &((struct xdp_buff *)ctl->ptr)[i];
- tun_xdp_one(tun, tfile, xdp, &flush);
+ tun_xdp_one(tun, tfile, xdp, &flush, &tpage);
}
if (flush)
@@ -2497,6 +2518,8 @@ static int tun_sendmsg(struct socket *sock, struct msghdr *m, size_t total_len)
rcu_read_unlock();
local_bh_enable();
+ tun_put_page(&tpage);
+
ret = total_len;
goto out;
}
--
2.17.1
^ permalink raw reply related
* RE: [Intel-wired-lan] [PATCH net] igb: fix uninitialized variables
From: Brown, Aaron F @ 2018-11-15 0:12 UTC (permalink / raw)
To: wangyunjian, netdev@vger.kernel.org,
intel-wired-lan@lists.osuosl.org
Cc: stone.zhou@huawei.com
In-Reply-To: <1541492832-10412-1-git-send-email-wangyunjian@huawei.com>
> From: Intel-wired-lan [mailto:intel-wired-lan-bounces@osuosl.org] On
> Behalf Of wangyunjian
> Sent: Tuesday, November 6, 2018 12:27 AM
> To: netdev@vger.kernel.org; intel-wired-lan@lists.osuosl.org
> Cc: stone.zhou@huawei.com; Yunjian Wang <wangyunjian@huawei.com>
> Subject: [Intel-wired-lan] [PATCH net] igb: fix uninitialized variables
>
> From: Yunjian Wang <wangyunjian@huawei.com>
>
> This patch fixes the variable 'phy_word' may be used uninitialized.
>
> Signed-off-by: Yunjian Wang <wangyunjian@huawei.com>
> ---
> drivers/net/ethernet/intel/igb/e1000_i210.c | 1 +
> 1 file changed, 1 insertion(+)
>
Tested-by: Aaron Brown <aaron.f.brown@intel.com>
^ permalink raw reply
* [PATCH net-next 0/8] net: eth: altera: tse: Add PTP and mSGDMA prefetcher
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
From: Dalon Westergreen <dalon.westergreen@intel.com>
This patch series cleans up the Altera TSE driver and adds support
for the newer msgdma prefetcher as well as ptp support when using
the msgdma prefetcher.
Dalon Westergreen (8):
net: eth: altera: tse_start_xmit ignores tx_buffer call response
net: eth: altera: set rx and tx ring size before init_dma call
net: eth: altera: tse: fix altera_dmaops declaration
net: eth: altera: tse: add optional function to start tx dma
net: eth: altera: tse: Move common functions to altera_utils
net: eth: altera: tse: add support for ptp and timestamping
net: eth: altera: tse: add msgdma prefetcher
net: eth: altera: tse: update devicetree bindings documentation
.../devicetree/bindings/net/altera_tse.txt | 98 +++-
drivers/net/ethernet/altera/Kconfig | 1 +
drivers/net/ethernet/altera/Makefile | 3 +-
.../altera/altera_msgdma_prefetcher.c | 433 ++++++++++++++++
.../altera/altera_msgdma_prefetcher.h | 30 ++
.../altera/altera_msgdmahw_prefetcher.h | 87 ++++
drivers/net/ethernet/altera/altera_ptp.c | 473 ++++++++++++++++++
drivers/net/ethernet/altera/altera_ptp.h | 77 +++
drivers/net/ethernet/altera/altera_sgdma.c | 14 +-
drivers/net/ethernet/altera/altera_tse.h | 100 ++--
.../net/ethernet/altera/altera_tse_ethtool.c | 29 ++
drivers/net/ethernet/altera/altera_tse_main.c | 244 ++++++++-
drivers/net/ethernet/altera/altera_utils.c | 30 ++
drivers/net/ethernet/altera/altera_utils.h | 46 ++
14 files changed, 1554 insertions(+), 111 deletions(-)
create mode 100644 drivers/net/ethernet/altera/altera_msgdma_prefetcher.c
create mode 100644 drivers/net/ethernet/altera/altera_msgdma_prefetcher.h
create mode 100644 drivers/net/ethernet/altera/altera_msgdmahw_prefetcher.h
create mode 100644 drivers/net/ethernet/altera/altera_ptp.c
create mode 100644 drivers/net/ethernet/altera/altera_ptp.h
--
2.19.1
^ permalink raw reply
* [PATCH net-next 1/8] net: eth: altera: tse_start_xmit ignores tx_buffer call response
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
In-Reply-To: <20181115005047.28464-1-dwesterg@gmail.com>
From: Dalon Westergreen <dalon.westergreen@intel.com>
The return from tx_buffer call in tse_start_xmit is
inapropriately ignored. tse_buffer calls should return
0 for success or NETDEV_TX_BUSY. tse_start_xmit should
return not report a successful transmit when the tse_buffer
call returns an error condition.
In addition to the above, the msgdma and sgdma do not return
the same value on success or failure. The sgdma_tx_buffer
returned 0 on failure and a positive number of transmitted
packets on success. Given that it only ever sends 1 packet,
this made no sense. The msgdma implementation msgdma_tx_buffer
returns 0 on success.
-> Don't ignore the return from tse_buffer calls
-> Fix sgdma tse_buffer call to return 0 on success
and NETDEV_TX_BUSY on failure.
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
---
drivers/net/ethernet/altera/altera_sgdma.c | 14 ++++++++------
drivers/net/ethernet/altera/altera_tse_main.c | 4 +++-
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/altera/altera_sgdma.c b/drivers/net/ethernet/altera/altera_sgdma.c
index 88ef67a998b4..eb47b9b820bb 100644
--- a/drivers/net/ethernet/altera/altera_sgdma.c
+++ b/drivers/net/ethernet/altera/altera_sgdma.c
@@ -15,6 +15,7 @@
*/
#include <linux/list.h>
+#include <linux/netdevice.h>
#include "altera_utils.h"
#include "altera_tse.h"
#include "altera_sgdmahw.h"
@@ -170,10 +171,11 @@ void sgdma_clear_txirq(struct altera_tse_private *priv)
SGDMA_CTRLREG_CLRINT);
}
-/* transmits buffer through SGDMA. Returns number of buffers
- * transmitted, 0 if not possible.
- *
- * tx_lock is held by the caller
+/* transmits buffer through SGDMA.
+ * original behavior returned the number of transmitted packets (always 1) &
+ * returned 0 on error. This differs from the msgdma. the calling function
+ * will now actually look at the code, so from now, 0 is good and return
+ * NETDEV_TX_BUSY when busy.
*/
int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
{
@@ -185,7 +187,7 @@ int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
/* wait 'til the tx sgdma is ready for the next transmit request */
if (sgdma_txbusy(priv))
- return 0;
+ return NETDEV_TX_BUSY;
sgdma_setup_descrip(cdesc, /* current descriptor */
ndesc, /* next descriptor */
@@ -202,7 +204,7 @@ int sgdma_tx_buffer(struct altera_tse_private *priv, struct tse_buffer *buffer)
/* enqueue the request to the pending transmit queue */
queue_tx(priv, buffer);
- return 1;
+ return 0;
}
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index baca8f704a45..dcb330129e23 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -606,7 +606,9 @@ static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
buffer->dma_addr = dma_addr;
buffer->len = nopaged_len;
- priv->dmaops->tx_buffer(priv, buffer);
+ ret = priv->dmaops->tx_buffer(priv, buffer);
+ if (ret)
+ goto out;
skb_tx_timestamp(skb);
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 2/8] net: eth: altera: set rx and tx ring size before init_dma call
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
In-Reply-To: <20181115005047.28464-1-dwesterg@gmail.com>
From: Dalon Westergreen <dalon.westergreen@intel.com>
It is more appropriate to set the rx and tx ring size before calling
the init function for the dma.
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
---
drivers/net/ethernet/altera/altera_tse_main.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index dcb330129e23..0c0e8f9bba9b 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -1166,6 +1166,10 @@ static int tse_open(struct net_device *dev)
int i;
unsigned long int flags;
+ /* set tx and rx ring size */
+ priv->rx_ring_size = dma_rx_num;
+ priv->tx_ring_size = dma_tx_num;
+
/* Reset and configure TSE MAC and probe associated PHY */
ret = priv->dmaops->init_dma(priv);
if (ret != 0) {
@@ -1208,8 +1212,6 @@ static int tse_open(struct net_device *dev)
priv->dmaops->reset_dma(priv);
/* Create and initialize the TX/RX descriptors chains. */
- priv->rx_ring_size = dma_rx_num;
- priv->tx_ring_size = dma_tx_num;
ret = alloc_init_skbufs(priv);
if (ret) {
netdev_err(dev, "DMA descriptors initialization failed\n");
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 3/8] net: eth: altera: tse: fix altera_dmaops declaration
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
In-Reply-To: <20181115005047.28464-1-dwesterg@gmail.com>
From: Dalon Westergreen <dalon.westergreen@intel.com>
The declaration of struct altera_dmaops does not have
identifier names. Add identifier names to confrom with
required coding styles.
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
---
drivers/net/ethernet/altera/altera_tse.h | 30 +++++++++++++-----------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index e2feee87180a..d5b97e02e6d6 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -396,20 +396,22 @@ struct altera_tse_private;
struct altera_dmaops {
int altera_dtype;
int dmamask;
- void (*reset_dma)(struct altera_tse_private *);
- void (*enable_txirq)(struct altera_tse_private *);
- void (*enable_rxirq)(struct altera_tse_private *);
- void (*disable_txirq)(struct altera_tse_private *);
- void (*disable_rxirq)(struct altera_tse_private *);
- void (*clear_txirq)(struct altera_tse_private *);
- void (*clear_rxirq)(struct altera_tse_private *);
- int (*tx_buffer)(struct altera_tse_private *, struct tse_buffer *);
- u32 (*tx_completions)(struct altera_tse_private *);
- void (*add_rx_desc)(struct altera_tse_private *, struct tse_buffer *);
- u32 (*get_rx_status)(struct altera_tse_private *);
- int (*init_dma)(struct altera_tse_private *);
- void (*uninit_dma)(struct altera_tse_private *);
- void (*start_rxdma)(struct altera_tse_private *);
+ void (*reset_dma)(struct altera_tse_private *priv);
+ void (*enable_txirq)(struct altera_tse_private *priv);
+ void (*enable_rxirq)(struct altera_tse_private *priv);
+ void (*disable_txirq)(struct altera_tse_private *priv);
+ void (*disable_rxirq)(struct altera_tse_private *priv);
+ void (*clear_txirq)(struct altera_tse_private *priv);
+ void (*clear_rxirq)(struct altera_tse_private *priv);
+ int (*tx_buffer)(struct altera_tse_private *priv,
+ struct tse_buffer *buffer);
+ u32 (*tx_completions)(struct altera_tse_private *priv);
+ void (*add_rx_desc)(struct altera_tse_private *priv,
+ struct tse_buffer *buffer);
+ u32 (*get_rx_status)(struct altera_tse_private *priv);
+ int (*init_dma)(struct altera_tse_private *priv);
+ void (*uninit_dma)(struct altera_tse_private *priv);
+ void (*start_rxdma)(struct altera_tse_private *priv);
};
/* This structure is private to each device.
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 4/8] net: eth: altera: tse: add optional function to start tx dma
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
In-Reply-To: <20181115005047.28464-1-dwesterg@gmail.com>
From: Dalon Westergreen <dalon.westergreen@intel.com>
Allow for optional start up of tx dma if the start_txdma
function is defined in altera_dmaops.
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
---
drivers/net/ethernet/altera/altera_tse.h | 1 +
drivers/net/ethernet/altera/altera_tse_main.c | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index d5b97e02e6d6..7f246040135d 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -412,6 +412,7 @@ struct altera_dmaops {
int (*init_dma)(struct altera_tse_private *priv);
void (*uninit_dma)(struct altera_tse_private *priv);
void (*start_rxdma)(struct altera_tse_private *priv);
+ void (*start_txdma)(struct altera_tse_private *priv);
};
/* This structure is private to each device.
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index 0c0e8f9bba9b..f6b6a14b1ce9 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -1256,6 +1256,9 @@ static int tse_open(struct net_device *dev)
priv->dmaops->start_rxdma(priv);
+ if (priv->dmaops->start_txdma)
+ priv->dmaops->start_txdma(priv);
+
/* Start MAC Rx/Tx */
spin_lock(&priv->mac_cfg_lock);
tse_set_mac(priv, true);
@@ -1658,6 +1661,7 @@ static const struct altera_dmaops altera_dtype_sgdma = {
.init_dma = sgdma_initialize,
.uninit_dma = sgdma_uninitialize,
.start_rxdma = sgdma_start_rxdma,
+ .start_txdma = NULL,
};
static const struct altera_dmaops altera_dtype_msgdma = {
@@ -1677,6 +1681,7 @@ static const struct altera_dmaops altera_dtype_msgdma = {
.init_dma = msgdma_initialize,
.uninit_dma = msgdma_uninitialize,
.start_rxdma = msgdma_start_rxdma,
+ .start_txdma = NULL,
};
static const struct of_device_id altera_tse_ids[] = {
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 5/8] net: eth: altera: tse: Move common functions to altera_utils
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
In-Reply-To: <20181115005047.28464-1-dwesterg@gmail.com>
From: Dalon Westergreen <dalon.westergreen@intel.com>
Move request_and_map and other shared functions to altera_utils. This
is the first step to moving common code out of tse specific code so
that it can be shared with future altera ethernet ip.
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
---
drivers/net/ethernet/altera/altera_tse.h | 45 ------------------
.../net/ethernet/altera/altera_tse_ethtool.c | 1 +
drivers/net/ethernet/altera/altera_tse_main.c | 32 +------------
drivers/net/ethernet/altera/altera_utils.c | 30 ++++++++++++
drivers/net/ethernet/altera/altera_utils.h | 46 +++++++++++++++++++
5 files changed, 78 insertions(+), 76 deletions(-)
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index 7f246040135d..f435fb0eca90 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -500,49 +500,4 @@ struct altera_tse_private {
*/
void altera_tse_set_ethtool_ops(struct net_device *);
-static inline
-u32 csrrd32(void __iomem *mac, size_t offs)
-{
- void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
- return readl(paddr);
-}
-
-static inline
-u16 csrrd16(void __iomem *mac, size_t offs)
-{
- void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
- return readw(paddr);
-}
-
-static inline
-u8 csrrd8(void __iomem *mac, size_t offs)
-{
- void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
- return readb(paddr);
-}
-
-static inline
-void csrwr32(u32 val, void __iomem *mac, size_t offs)
-{
- void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
-
- writel(val, paddr);
-}
-
-static inline
-void csrwr16(u16 val, void __iomem *mac, size_t offs)
-{
- void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
-
- writew(val, paddr);
-}
-
-static inline
-void csrwr8(u8 val, void __iomem *mac, size_t offs)
-{
- void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
-
- writeb(val, paddr);
-}
-
#endif /* __ALTERA_TSE_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c
index 7c367713c3e6..2998655ab316 100644
--- a/drivers/net/ethernet/altera/altera_tse_ethtool.c
+++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c
@@ -33,6 +33,7 @@
#include <linux/phy.h>
#include "altera_tse.h"
+#include "altera_utils.h"
#define TSE_STATS_LEN 31
#define TSE_NUM_REGS 128
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index f6b6a14b1ce9..b25d03506470 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -34,7 +34,6 @@
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mii.h>
@@ -44,7 +43,7 @@
#include <linux/of_net.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
-#include <linux/platform_device.h>
+#include <linux/ptp_classify.h>
#include <linux/skbuff.h>
#include <asm/cacheflush.h>
@@ -1332,35 +1331,6 @@ static struct net_device_ops altera_tse_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
};
-static int request_and_map(struct platform_device *pdev, const char *name,
- struct resource **res, void __iomem **ptr)
-{
- struct resource *region;
- struct device *device = &pdev->dev;
-
- *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
- if (*res == NULL) {
- dev_err(device, "resource %s not defined\n", name);
- return -ENODEV;
- }
-
- region = devm_request_mem_region(device, (*res)->start,
- resource_size(*res), dev_name(device));
- if (region == NULL) {
- dev_err(device, "unable to request %s\n", name);
- return -EBUSY;
- }
-
- *ptr = devm_ioremap_nocache(device, region->start,
- resource_size(region));
- if (*ptr == NULL) {
- dev_err(device, "ioremap_nocache of %s failed!", name);
- return -ENOMEM;
- }
-
- return 0;
-}
-
/* Probe Altera TSE MAC device
*/
static int altera_tse_probe(struct platform_device *pdev)
diff --git a/drivers/net/ethernet/altera/altera_utils.c b/drivers/net/ethernet/altera/altera_utils.c
index d7eeb1713ad2..bc33b7f0b0c5 100644
--- a/drivers/net/ethernet/altera/altera_utils.c
+++ b/drivers/net/ethernet/altera/altera_utils.c
@@ -42,3 +42,33 @@ int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask)
u32 value = csrrd32(ioaddr, offs);
return (value & bit_mask) ? 0 : 1;
}
+
+int request_and_map(struct platform_device *pdev, const char *name,
+ struct resource **res, void __iomem **ptr)
+{
+ struct resource *region;
+ struct device *device = &pdev->dev;
+
+ *res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!*res) {
+ dev_err(device, "resource %s not defined\n", name);
+ return -ENODEV;
+ }
+
+ region = devm_request_mem_region(device, (*res)->start,
+ resource_size(*res), dev_name(device));
+ if (!region) {
+ dev_err(device, "unable to request %s\n", name);
+ return -EBUSY;
+ }
+
+ *ptr = devm_ioremap_nocache(device, region->start,
+ resource_size(region));
+ if (!*ptr) {
+ dev_err(device, "ioremap_nocache of %s failed!", name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/net/ethernet/altera/altera_utils.h b/drivers/net/ethernet/altera/altera_utils.h
index baf100ccf587..bb7eff792bb7 100644
--- a/drivers/net/ethernet/altera/altera_utils.h
+++ b/drivers/net/ethernet/altera/altera_utils.h
@@ -14,7 +14,9 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#ifndef __ALTERA_UTILS_H__
#define __ALTERA_UTILS_H__
@@ -23,5 +25,49 @@ void tse_set_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask);
void tse_clear_bit(void __iomem *ioaddr, size_t offs, u32 bit_mask);
int tse_bit_is_set(void __iomem *ioaddr, size_t offs, u32 bit_mask);
int tse_bit_is_clear(void __iomem *ioaddr, size_t offs, u32 bit_mask);
+int request_and_map(struct platform_device *pdev, const char *name,
+ struct resource **res, void __iomem **ptr);
+
+static inline u32 csrrd32(void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ return readl(paddr);
+}
+
+static inline u16 csrrd16(void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ return readw(paddr);
+}
+
+static inline u8 csrrd8(void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ return readb(paddr);
+}
+
+static inline void csrwr32(u32 val, void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ writel(val, paddr);
+}
+
+static inline void csrwr16(u16 val, void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ writew(val, paddr);
+}
+
+static inline void csrwr8(u8 val, void __iomem *mac, size_t offs)
+{
+ void __iomem *paddr = (void __iomem *)((uintptr_t)mac + offs);
+
+ writeb(val, paddr);
+}
#endif /* __ALTERA_UTILS_H__*/
--
2.19.1
^ permalink raw reply related
* [PATCH net-next 6/8] net: eth: altera: tse: add support for ptp and timestamping
From: Dalon Westergreen @ 2018-11-15 0:50 UTC (permalink / raw)
To: netdev, dinguyen, thor.thayer; +Cc: Dalon Westergreen
In-Reply-To: <20181115005047.28464-1-dwesterg@gmail.com>
From: Dalon Westergreen <dalon.westergreen@intel.com>
Add support for the ptp clock used with the tse, and update
the driver to support timestamping when enabled. We also
enable debugfs entries for the ptp clock to allow some user
control and interaction with the ptp clock.
Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
---
drivers/net/ethernet/altera/Kconfig | 1 +
drivers/net/ethernet/altera/Makefile | 3 +-
drivers/net/ethernet/altera/altera_ptp.c | 473 ++++++++++++++++++
drivers/net/ethernet/altera/altera_ptp.h | 77 +++
drivers/net/ethernet/altera/altera_tse.h | 10 +
.../net/ethernet/altera/altera_tse_ethtool.c | 28 ++
drivers/net/ethernet/altera/altera_tse_main.c | 164 +++++-
7 files changed, 754 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/ethernet/altera/altera_ptp.c
create mode 100644 drivers/net/ethernet/altera/altera_ptp.h
diff --git a/drivers/net/ethernet/altera/Kconfig b/drivers/net/ethernet/altera/Kconfig
index fdddba51473e..36aee0fc0b51 100644
--- a/drivers/net/ethernet/altera/Kconfig
+++ b/drivers/net/ethernet/altera/Kconfig
@@ -2,6 +2,7 @@ config ALTERA_TSE
tristate "Altera Triple-Speed Ethernet MAC support"
depends on HAS_DMA
select PHYLIB
+ select PTP_1588_CLOCK
---help---
This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile
index d4a187e45369..ad80be42fa26 100644
--- a/drivers/net/ethernet/altera/Makefile
+++ b/drivers/net/ethernet/altera/Makefile
@@ -4,4 +4,5 @@
obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
-altera_msgdma.o altera_sgdma.o altera_utils.o
+ altera_msgdma.o altera_sgdma.o altera_utils.o \
+ altera_ptp.o
diff --git a/drivers/net/ethernet/altera/altera_ptp.c b/drivers/net/ethernet/altera/altera_ptp.c
new file mode 100644
index 000000000000..4467b3c90c59
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_ptp.c
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Altera PTP Hardware Clock (PHC) Linux driver
+ * Copyright (C) 2015-2016 Altera Corporation. All rights reserved.
+ * Copyright (C) 2017-2018 Intel Corporation. All rights reserved.
+ *
+ * Author(s):
+ * Dalon Westergreen <dalon.westergreen@intel.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/net_tstamp.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "altera_ptp.h"
+#include "altera_utils.h"
+
+#define NOMINAL_PPB 1000000000ULL
+#define TOD_PERIOD_MAX 0xfffff
+#define TOD_PERIOD_MIN 0
+#define TOD_DRIFT_ADJUST_FNS_MAX 0xffff
+#define TOD_DRIFT_ADJUST_RATE_MAX 0xffff
+#define TOD_ADJUST_COUNT_MAX 0xfffff
+#define TOD_ADJUST_MS_MAX (((((TOD_PERIOD_MAX) >> 16) + 1) * \
+ ((TOD_ADJUST_COUNT_MAX) + 1)) / \
+ 1000000UL)
+
+/* A fine ToD HW clock offset adjustment.
+ * To perform the fine offset adjustment the AdjustPeriod register is used
+ * to replace the Period register for AdjustCount clock cycles in hardware.
+ */
+static int fine_adjust_tod_clock(struct altera_ptp_private *priv,
+ u32 adjust_period, u32 adjust_count)
+{
+ int limit;
+
+ csrwr32(adjust_period, priv->tod_ctrl, tod_csroffs(adjust_period));
+ csrwr32(adjust_count, priv->tod_ctrl, tod_csroffs(adjust_count));
+
+ /* Wait for present offset adjustment update to complete */
+ limit = TOD_ADJUST_MS_MAX;
+ while (limit--) {
+ if (!csrrd32(priv->tod_ctrl, tod_csroffs(adjust_count)))
+ break;
+ mdelay(1);
+ }
+ if (limit < 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+/* A coarse ToD HW clock offset adjustment.
+ * The coarse time adjustment performs by adding or subtracting the delta value
+ * from the current ToD HW clock time.
+ */
+static int coarse_adjust_tod_clock(struct altera_ptp_private *priv, s64 delta)
+{
+ u64 seconds;
+ u32 seconds_msb;
+ u32 seconds_lsb;
+ u32 nanosec;
+ u64 now;
+
+ if (delta == 0)
+ goto out;
+
+ /* Get current time */
+ nanosec = csrrd32(priv->tod_ctrl, tod_csroffs(nanosec));
+ seconds_lsb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_lsb));
+ seconds_msb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_msb));
+
+ /* Calculate new time */
+ seconds = (((u64)(seconds_msb & 0x0000ffff)) << 32) | seconds_lsb;
+ now = seconds * NSEC_PER_SEC + nanosec + delta;
+
+ seconds = div_u64_rem(now, NSEC_PER_SEC, &nanosec);
+ seconds_msb = upper_32_bits(seconds) & 0x0000ffff;
+ seconds_lsb = lower_32_bits(seconds);
+
+ /* Set corrected time */
+ csrwr32(seconds_msb, priv->tod_ctrl, tod_csroffs(seconds_msb));
+ csrwr32(seconds_lsb, priv->tod_ctrl, tod_csroffs(seconds_lsb));
+ csrwr32(nanosec, priv->tod_ctrl, tod_csroffs(nanosec));
+
+out:
+ return 0;
+}
+
+static int adjust_fine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct altera_ptp_private *priv =
+ container_of(ptp, struct altera_ptp_private, ptp_clock_ops);
+ unsigned long flags;
+ int ret = 0;
+ u64 ppb;
+ u32 tod_period;
+ u32 tod_rem;
+ u32 tod_drift_adjust_fns;
+ u32 tod_drift_adjust_rate;
+ unsigned long rate;
+
+ priv->scaled_ppm = scaled_ppm;
+
+ /* only unlock if it is currently locked */
+ if (mutex_is_locked(&priv->ppm_mutex))
+ mutex_unlock(&priv->ppm_mutex);
+
+ if (!priv->ptp_correct_freq)
+ goto out;
+
+ rate = clk_get_rate(priv->tod_clk);
+
+ /* From scaled_ppm_to_ppb */
+ ppb = 1 + scaled_ppm;
+ ppb *= 125;
+ ppb >>= 13;
+
+ ppb += NOMINAL_PPB;
+
+ tod_period = div_u64_rem(ppb << 16, rate, &tod_rem);
+ if (tod_period > TOD_PERIOD_MAX) {
+ ret = -ERANGE;
+ goto out;
+ }
+
+ /* The drift of ToD adjusted periodically by adding a drift_adjust_fns
+ * correction value every drift_adjust_rate count of clock cycles.
+ */
+ tod_drift_adjust_fns = tod_rem / gcd(tod_rem, rate);
+ tod_drift_adjust_rate = rate / gcd(tod_rem, rate);
+
+ while ((tod_drift_adjust_fns > TOD_DRIFT_ADJUST_FNS_MAX) |
+ (tod_drift_adjust_rate > TOD_DRIFT_ADJUST_RATE_MAX)) {
+ tod_drift_adjust_fns = tod_drift_adjust_fns >> 1;
+ tod_drift_adjust_rate = tod_drift_adjust_rate >> 1;
+ }
+
+ if (tod_drift_adjust_fns == 0)
+ tod_drift_adjust_rate = 0;
+
+ spin_lock_irqsave(&priv->tod_lock, flags);
+ csrwr32(tod_period, priv->tod_ctrl, tod_csroffs(period));
+ csrwr32(0, priv->tod_ctrl, tod_csroffs(adjust_period));
+ csrwr32(0, priv->tod_ctrl, tod_csroffs(adjust_count));
+ csrwr32(tod_drift_adjust_fns, priv->tod_ctrl,
+ tod_csroffs(drift_adjust));
+ csrwr32(tod_drift_adjust_rate, priv->tod_ctrl,
+ tod_csroffs(drift_adjust_rate));
+ spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+out:
+ return ret;
+}
+
+static int adjust_time(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct altera_ptp_private *priv =
+ container_of(ptp, struct altera_ptp_private, ptp_clock_ops);
+ int ret = 0;
+ u64 abs_delta;
+ unsigned long flags;
+ u32 period;
+ u32 diff;
+ u64 count;
+ u32 rem;
+
+ if (!priv->ptp_correct_offs)
+ goto out;
+
+ if (delta < 0)
+ abs_delta = -delta;
+ else
+ abs_delta = delta;
+
+ spin_lock_irqsave(&priv->tod_lock, flags);
+
+ /* Get the maximum possible value of the Period register offset
+ * adjustment in nanoseconds scale. This depends on the current
+ * Period register settings and the maximum and minimum possible
+ * values of the Period register.
+ */
+ period = csrrd32(priv->tod_ctrl, tod_csroffs(period));
+
+ if (delta < 0)
+ diff = (period - TOD_PERIOD_MIN) >> 16;
+ else
+ diff = (TOD_PERIOD_MAX - period) >> 16;
+
+ /* Find out the least number of cycles needed for the Period register
+ * adjustment by delta nanoseconds.
+ */
+ while (diff) {
+ count = div_u64_rem(abs_delta, diff, &rem);
+ if (rem == 0)
+ break;
+ diff--;
+ }
+
+ if (diff && count && count < TOD_ADJUST_COUNT_MAX) {
+ /* Perform the fine time offset adjustment */
+ if (delta < 0)
+ period -= (diff << 16);
+ else
+ period += (diff << 16);
+
+ ret = fine_adjust_tod_clock(priv, period, count);
+
+ } else {
+ /* Perform the coarse time offset adjustment */
+ ret = coarse_adjust_tod_clock(priv, delta);
+ }
+
+ spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+out:
+ return ret;
+}
+
+static int get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+ struct altera_ptp_private *priv =
+ container_of(ptp, struct altera_ptp_private, ptp_clock_ops);
+ unsigned long flags;
+ u64 seconds;
+ u32 seconds_msb;
+ u32 seconds_lsb;
+ u32 nanosec;
+
+ spin_lock_irqsave(&priv->tod_lock, flags);
+ nanosec = csrrd32(priv->tod_ctrl, tod_csroffs(nanosec));
+ seconds_lsb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_lsb));
+ seconds_msb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_msb));
+ spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+ seconds = (((u64)(seconds_msb & 0x0000ffff)) << 32) | seconds_lsb;
+
+ ts->tv_nsec = nanosec;
+ ts->tv_sec = (__kernel_time_t)seconds;
+
+ return 0;
+}
+
+static int set_time(struct ptp_clock_info *ptp, const struct timespec64 *ts)
+{
+ struct altera_ptp_private *priv =
+ container_of(ptp, struct altera_ptp_private, ptp_clock_ops);
+ unsigned long flags;
+ u32 seconds_msb = upper_32_bits(ts->tv_sec) & 0x0000ffff;
+ u32 seconds_lsb = lower_32_bits(ts->tv_sec);
+ u32 nanosec = lower_32_bits(ts->tv_nsec);
+
+ spin_lock_irqsave(&priv->tod_lock, flags);
+ csrwr32(seconds_msb, priv->tod_ctrl, tod_csroffs(seconds_msb));
+ csrwr32(seconds_lsb, priv->tod_ctrl, tod_csroffs(seconds_lsb));
+ csrwr32(nanosec, priv->tod_ctrl, tod_csroffs(nanosec));
+ spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+ return 0;
+}
+
+static int enable_feature(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *request, int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct ptp_clock_info altera_ptp_clock_ops = {
+ .owner = THIS_MODULE,
+ .name = "altera_ptp",
+ .max_adj = 500000000,
+ .n_alarm = 0,
+ .n_ext_ts = 0,
+ .n_per_out = 0,
+ .pps = 0,
+ .adjfine = adjust_fine,
+ .adjtime = adjust_time,
+ .gettime64 = get_time,
+ .settime64 = set_time,
+ .enable = enable_feature,
+};
+
+/* ptp debugfs */
+static ssize_t ptp_tod_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct altera_ptp_private *priv = filp->private_data;
+ unsigned long flags;
+ u32 seconds_msb;
+ u32 seconds_lsb;
+ u32 nanosec;
+
+ if (*ppos != 0)
+ return 0;
+
+ if (count < (sizeof(u32) * 3))
+ return -ENOSPC;
+
+ spin_lock_irqsave(&priv->tod_lock, flags);
+ nanosec = csrrd32(priv->tod_ctrl, tod_csroffs(nanosec));
+ seconds_lsb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_lsb));
+ seconds_msb = csrrd32(priv->tod_ctrl, tod_csroffs(seconds_msb));
+ spin_unlock_irqrestore(&priv->tod_lock, flags);
+
+ if (copy_to_user(buffer, &seconds_msb, sizeof(u32)))
+ return -EFAULT;
+ buffer += sizeof(u32);
+
+ if (copy_to_user(buffer, &seconds_lsb, sizeof(u32)))
+ return -EFAULT;
+ buffer += sizeof(u32);
+
+ if (copy_to_user(buffer, &nanosec, sizeof(u32)))
+ return -EFAULT;
+
+ return (sizeof(u32) * 3);
+}
+
+static const struct file_operations ptp_dbg_tod_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = ptp_tod_read,
+ .write = NULL,
+};
+
+static ssize_t ptp_ppm_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct altera_ptp_private *priv = filp->private_data;
+
+ if (*ppos != 0)
+ return 0;
+
+ if (count < sizeof(long))
+ return -ENOSPC;
+
+ if (mutex_lock_interruptible(&priv->ppm_mutex))
+ return -EAGAIN;
+
+ if (copy_to_user(buffer, &priv->scaled_ppm, sizeof(long)))
+ return -EFAULT;
+
+ return sizeof(long);
+}
+
+static const struct file_operations ptp_dbg_ppm_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = ptp_ppm_read,
+ .write = NULL,
+};
+
+int altera_ptp_dbg_init(struct altera_ptp_private *priv,
+ struct dentry *dfs_dir_root)
+{
+ struct dentry *dfs_dir;
+
+ dfs_dir = debugfs_create_dir("ptp", dfs_dir_root);
+
+ /* Use u32 instead of bool */
+ if (!debugfs_create_u32("correct_offset", 0600, dfs_dir,
+ &priv->ptp_correct_offs))
+ return -ENOMEM;
+
+ /* Use u32 instead of bool */
+ if (!debugfs_create_u32("correct_frequency", 0600, dfs_dir,
+ &priv->ptp_correct_freq))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("tod", 0400, dfs_dir,
+ (void *)priv, &ptp_dbg_tod_ops))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("scaled_ppm", 0400, dfs_dir,
+ (void *)priv, &ptp_dbg_ppm_ops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Initialize PTP control block registers */
+int altera_init_ptp(struct altera_ptp_private *priv)
+{
+ int ret = 0;
+ struct timespec64 now;
+
+ ret = adjust_fine(&priv->ptp_clock_ops, 0l);
+ if (ret != 0)
+ goto out;
+
+ /* Initialize the hardware clock to the system time */
+ getnstimeofday64(&now);
+ set_time(&priv->ptp_clock_ops, &now);
+
+ priv->ptp_correct_offs = true;
+ priv->ptp_correct_freq = true;
+
+ spin_lock_init(&priv->tod_lock);
+
+ /* we want the mutex locked by default */
+ mutex_init(&priv->ppm_mutex);
+ mutex_lock(&priv->ppm_mutex);
+
+out:
+ return ret;
+}
+
+/* Register the PTP clock driver to kernel */
+int altera_ptp_register(struct altera_ptp_private *priv, struct device *device)
+{
+ int ret = 0;
+
+ priv->ptp_clock_ops = altera_ptp_clock_ops;
+
+ priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops, device);
+ if (IS_ERR(priv->ptp_clock)) {
+ priv->ptp_clock = NULL;
+ ret = -ENODEV;
+ }
+
+ if (priv->tod_clk)
+ ret = clk_prepare_enable(priv->tod_clk);
+
+ return ret;
+}
+
+/* Remove/unregister the ptp clock driver from the kernel */
+void altera_ptp_unregister(struct altera_ptp_private *priv)
+{
+ if (priv->ptp_clock) {
+ ptp_clock_unregister(priv->ptp_clock);
+ priv->ptp_clock = NULL;
+ }
+
+ if (priv->tod_clk)
+ clk_disable_unprepare(priv->tod_clk);
+}
+
+/* Common PTP probe function */
+int altera_ptp_probe(struct platform_device *pdev,
+ struct altera_ptp_private *priv)
+{
+ int ret = -ENODEV;
+ struct resource *ptp_res;
+
+ priv->dev = (struct net_device *)platform_get_drvdata(pdev);
+
+ /* Time-of-Day (ToD) Clock address space */
+ ret = request_and_map(pdev, "tod_ctrl", &ptp_res,
+ (void __iomem **)&priv->tod_ctrl);
+ if (ret)
+ goto err;
+
+ dev_info(&pdev->dev, "\tTOD Ctrl at 0x%08lx\n",
+ (unsigned long)ptp_res->start);
+
+ /* Time-of-Day (ToD) Clock period clock */
+ priv->tod_clk = devm_clk_get(&pdev->dev, "tod_clk");
+ if (IS_ERR(priv->tod_clk)) {
+ dev_err(&pdev->dev, "cannot obtain ToD period clock\n");
+ ret = -ENXIO;
+ goto err;
+ }
+err:
+ return ret;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/altera/altera_ptp.h b/drivers/net/ethernet/altera/altera_ptp.h
new file mode 100644
index 000000000000..66cd11902743
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_ptp.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Altera PTP Hardware Clock (PHC) Linux driver
+ * Copyright (C) 2015-2016 Altera Corporation. All rights reserved.
+ * Copyright (C) 2017-2018 Intel Corporation. All rights reserved.
+ *
+ * Author(s):
+ * Dalon Westergreen <dalon.westergreen@intel.com>
+ */
+
+#ifndef __ALTERA_PTP_H__
+#define __ALTERA_PTP_H__
+
+#include <linux/debugfs.h>
+#include <linux/netdevice.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+
+/* Altera Time-of-Day (ToD) clock register space. */
+struct altera_eth_tod {
+ u32 seconds_msb;
+ u32 seconds_lsb;
+ u32 nanosec;
+ u32 reserved1[0x1];
+ u32 period;
+ u32 adjust_period;
+ u32 adjust_count;
+ u32 drift_adjust;
+ u32 drift_adjust_rate;
+};
+
+#define tod_csroffs(a) (offsetof(struct altera_eth_tod, a))
+
+struct altera_ptp_private {
+ struct net_device *dev;
+
+ struct ptp_clock_info ptp_clock_ops;
+ struct ptp_clock *ptp_clock;
+
+ /* Time-of-Day (ToD) Clock address space */
+ struct altera_eth_tod __iomem *tod_ctrl;
+ struct clk *tod_clk;
+
+ /* Ability to turn on and turn off PTP offset correction and
+ * frequency correction
+ */
+ u32 ptp_correct_offs;
+ u32 ptp_correct_freq;
+
+ /* ToD clock registers protection */
+ spinlock_t tod_lock;
+
+ /* Debug for PTP interface with mutex for passing
+ * scaled_ppm from adjfine to debugfs
+ */
+ struct mutex ppm_mutex;
+ long scaled_ppm;
+};
+
+struct altera_timestamp {
+ u32 tstamp[3];
+};
+
+void altera_tx_hwtstamp(struct altera_ptp_private *priv, struct sk_buff *skb,
+ struct altera_timestamp *tstamp);
+void altera_rx_hwtstamp(struct altera_ptp_private *priv, struct sk_buff *skb,
+ struct altera_timestamp *tstamp);
+int altera_init_ptp(struct altera_ptp_private *priv);
+void altera_uninit_ptp(struct altera_ptp_private *priv);
+int altera_ptp_register(struct altera_ptp_private *priv, struct device *device);
+void altera_ptp_unregister(struct altera_ptp_private *priv);
+int altera_ptp_probe(struct platform_device *pdev,
+ struct altera_ptp_private *priv);
+int altera_ptp_dbg_init(struct altera_ptp_private *priv,
+ struct dentry *dfs_dir);
+
+#endif /* __ALTERA_PTP_H__ */
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index f435fb0eca90..508d5d015ca6 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -39,6 +39,8 @@
#include <linux/netdevice.h>
#include <linux/phy.h>
+#include "altera_ptp.h"
+
#define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR 10000
#define ALTERA_TSE_MAC_FIFO_WIDTH 4 /* TX/RX FIFO width in
* bytes
@@ -428,6 +430,12 @@ struct altera_tse_private {
/* TSE Revision */
u32 revision;
+ /* Shared PTP structure */
+ struct altera_ptp_private ptp_priv;
+ int hwts_tx_en;
+ int hwts_rx_en;
+ u32 ptp_enable;
+
/* mSGDMA Rx Dispatcher address space */
void __iomem *rx_dma_csr;
void __iomem *rx_dma_desc;
@@ -494,6 +502,8 @@ struct altera_tse_private {
u32 msg_enable;
struct altera_dmaops *dmaops;
+
+ struct dentry *dfs_dir;
};
/* Function prototypes
diff --git a/drivers/net/ethernet/altera/altera_tse_ethtool.c b/drivers/net/ethernet/altera/altera_tse_ethtool.c
index 2998655ab316..eeb6e5558c6a 100644
--- a/drivers/net/ethernet/altera/altera_tse_ethtool.c
+++ b/drivers/net/ethernet/altera/altera_tse_ethtool.c
@@ -30,6 +30,7 @@
#include <linux/ethtool.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
#include <linux/phy.h>
#include "altera_tse.h"
@@ -234,6 +235,32 @@ static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs,
buf[i] = csrrd32(priv->mac_dev, i * 4);
}
+static int tse_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+
+ if (priv->ptp_enable) {
+ if (priv->ptp_priv.ptp_clock)
+ info->phc_index =
+ ptp_clock_index(priv->ptp_priv.ptp_clock);
+
+ info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ info->tx_types = (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON);
+
+ info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_ALL);
+
+ return 0;
+ } else {
+ return ethtool_op_get_ts_info(dev, info);
+ }
+}
+
static const struct ethtool_ops tse_ethtool_ops = {
.get_drvinfo = tse_get_drvinfo,
.get_regs_len = tse_reglen,
@@ -246,6 +273,7 @@ static const struct ethtool_ops tse_ethtool_ops = {
.set_msglevel = tse_set_msglevel,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_ts_info = tse_get_ts_info,
};
void altera_tse_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index b25d03506470..3b0c940189be 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -29,14 +29,18 @@
*/
#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mii.h>
+#include <linux/net_tstamp.h>
#include <linux/netdevice.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
@@ -51,6 +55,7 @@
#include "altera_tse.h"
#include "altera_sgdma.h"
#include "altera_msgdma.h"
+#include "altera_ptp.h"
static atomic_t instance_count = ATOMIC_INIT(~0);
/* Module parameters */
@@ -609,7 +614,14 @@ static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (ret)
goto out;
- skb_tx_timestamp(skb);
+ /* Provide a hardware time stamp if requested.
+ */
+ if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ priv->hwts_tx_en))
+ /* declare that device is doing timestamping */
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ else
+ skb_tx_timestamp(skb);
priv->tx_prod++;
dev->stats.tx_bytes += skb->len;
@@ -1250,6 +1262,13 @@ static int tse_open(struct net_device *dev)
if (dev->phydev)
phy_start(dev->phydev);
+ ret = altera_init_ptp(&priv->ptp_priv);
+ if (ret)
+ netdev_warn(dev, "Failed PTP initialization\n");
+
+ priv->hwts_tx_en = 0;
+ priv->hwts_rx_en = 0;
+
napi_enable(&priv->napi);
netif_start_queue(dev);
@@ -1321,6 +1340,83 @@ static int tse_shutdown(struct net_device *dev)
return 0;
}
+/* ioctl to configure timestamping */
+static int tse_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ struct hwtstamp_config config;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->ptp_enable) {
+ netdev_alert(priv->dev, "Timestamping not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (cmd == SIOCSHWTSTAMP) {
+ if (copy_from_user(&config, ifr->ifr_data,
+ sizeof(struct hwtstamp_config)))
+ return -EFAULT;
+
+ if (config.flags)
+ return -EINVAL;
+
+ switch (config.tx_type) {
+ case HWTSTAMP_TX_OFF:
+ priv->hwts_tx_en = 0;
+ break;
+ case HWTSTAMP_TX_ON:
+ priv->hwts_tx_en = 1;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ priv->hwts_rx_en = 0;
+ config.rx_filter = HWTSTAMP_FILTER_NONE;
+ break;
+ default:
+ priv->hwts_rx_en = 1;
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+ break;
+ }
+
+ if (copy_to_user(ifr->ifr_data, &config,
+ sizeof(struct hwtstamp_config)))
+ return -EFAULT;
+ else
+ return 0;
+ }
+
+ if (cmd == SIOCGHWTSTAMP) {
+ config.flags = 0;
+
+ if (priv->hwts_tx_en)
+ config.tx_type = HWTSTAMP_TX_ON;
+ else
+ config.tx_type = HWTSTAMP_TX_OFF;
+
+ if (priv->hwts_rx_en)
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+ else
+ config.rx_filter = HWTSTAMP_FILTER_NONE;
+
+ if (copy_to_user(ifr->ifr_data, &config,
+ sizeof(struct hwtstamp_config)))
+ return -EFAULT;
+ else
+ return 0;
+ }
+
+ if (!dev->phydev)
+ return -EINVAL;
+
+ return phy_mii_ioctl(dev->phydev, ifr, cmd);
+}
+
static struct net_device_ops altera_tse_netdev_ops = {
.ndo_open = tse_open,
.ndo_stop = tse_shutdown,
@@ -1329,8 +1425,44 @@ static struct net_device_ops altera_tse_netdev_ops = {
.ndo_set_rx_mode = tse_set_rx_mode,
.ndo_change_mtu = tse_change_mtu,
.ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = tse_do_ioctl,
};
+/* Debugfs entries */
+int altera_tse_dbg_init(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+ char *buf;
+
+ buf = kasprintf(GFP_KERNEL, "%s_%s",
+ ALTERA_TSE_RESOURCE_NAME, dev->name);
+
+ priv->dfs_dir = debugfs_create_dir(buf, NULL);
+ if (!priv->dfs_dir) {
+ netdev_err(dev, "debugfs create directory failed\n");
+ goto err_dfs;
+ }
+
+ if (priv->ptp_enable)
+ if (altera_ptp_dbg_init(&priv->ptp_priv, priv->dfs_dir))
+ goto err_entry;
+
+ return 0;
+
+err_entry:
+ debugfs_remove_recursive(priv->dfs_dir);
+ priv->dfs_dir = NULL;
+err_dfs:
+ return -ENOMEM;
+}
+
+static void altera_tse_dbg_exit(struct net_device *dev)
+{
+ struct altera_tse_private *priv = netdev_priv(dev);
+
+ debugfs_remove_recursive(priv->dfs_dir);
+}
+
/* Probe Altera TSE MAC device
*/
static int altera_tse_probe(struct platform_device *pdev)
@@ -1580,6 +1712,31 @@ static int altera_tse_probe(struct platform_device *pdev)
netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret);
goto err_init_phy;
}
+
+ priv->ptp_enable = of_property_read_bool(pdev->dev.of_node,
+ "altr,has-ptp");
+ dev_info(&pdev->dev, "PTP Enable: %d\n", priv->ptp_enable);
+
+ if (priv->ptp_enable) {
+ /* MAP PTP */
+ ret = altera_ptp_probe(pdev, &priv->ptp_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot map PTP\n");
+ goto err_init_phy;
+ }
+ ret = altera_ptp_register(&priv->ptp_priv,
+ priv->device);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register PTP clock\n");
+ ret = -ENXIO;
+ goto err_init_phy;
+ }
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ if (altera_tse_dbg_init(ndev))
+ goto err_init_phy;
+#endif
return 0;
err_init_phy:
@@ -1606,7 +1763,12 @@ static int altera_tse_remove(struct platform_device *pdev)
of_phy_deregister_fixed_link(priv->device->of_node);
}
+#ifdef CONFIG_DEBUG_FS
+ altera_tse_dbg_exit(ndev);
+#endif
platform_set_drvdata(pdev, NULL);
+ if (priv->ptp_enable)
+ altera_ptp_unregister(&priv->ptp_priv);
altera_tse_mdio_destroy(ndev);
unregister_netdev(ndev);
free_netdev(ndev);
--
2.19.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox