Merge branch 'add-netc-timer-ptp-driver-and-add-ptp-support-for-i-mx95'

Wei Fang says:

====================
Add NETC Timer PTP driver and add PTP support for i.MX95

This series adds NETC Timer PTP clock driver, which supports precise
periodic pulse, time capture on external pulse and PTP synchronization.
It also adds PTP support to the enetc v4 driver for i.MX95 and optimizes
the PTP-related code in the enetc driver.
====================

Link: https://patch.msgid.link/20250829050615.1247468-1-wei.fang@nxp.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2025-09-02 13:13:53 +02:00
commit 48195dd18f
16 changed files with 1416 additions and 107 deletions

View File

@ -108,6 +108,11 @@ properties:
$ref: "#/properties/phy-handle"
deprecated: true
ptp-timer:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Specifies a reference to a node representing an IEEE 1588 PTP device.
rx-fifo-depth:
$ref: /schemas/types.yaml#/definitions/uint32
description:

View File

@ -81,10 +81,6 @@ properties:
An array of two references: the first is the FMan RX port and the second
is the TX port used by this MAC.
ptp-timer:
$ref: /schemas/types.yaml#/definitions/phandle
description: A reference to the IEEE1588 timer
phys:
description: A reference to the SerDes lane(s)
maxItems: 1

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/ptp/nxp,ptp-netc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP NETC V4 Timer PTP clock
description:
NETC V4 Timer provides current time with nanosecond resolution, precise
periodic pulse, pulse on timeout (alarm), and time capture on external
pulse support. And it supports time synchronization as required for
IEEE 1588 and IEEE 802.1AS-2020.
maintainers:
- Wei Fang <wei.fang@nxp.com>
- Clark Wang <xiaoning.wang@nxp.com>
properties:
compatible:
enum:
- pci1131,ee02
reg:
maxItems: 1
clocks:
maxItems: 1
description:
The reference clock of NETC Timer, can be selected between 3 different
clock sources using an integrated hardware mux TMR_CTRL[CK_SEL].
The "ccm" means the reference clock comes from CCM of SoC.
The "ext" means the reference clock comes from external IO pins.
If not present, indicates that the system clock of NETC IP is selected
as the reference clock.
clock-names:
enum:
- ccm
- ext
required:
- compatible
- reg
allOf:
- $ref: /schemas/pci/pci-device.yaml
unevaluatedProperties: false
examples:
- |
pcie {
#address-cells = <3>;
#size-cells = <2>;
ptp-timer@18,0 {
compatible = "pci1131,ee02";
reg = <0x00c000 0 0 0 0>;
clocks = <&scmi_clk 18>;
clock-names = "ccm";
};
};

View File

@ -18292,6 +18292,15 @@ F: Documentation/devicetree/bindings/clock/*imx*
F: drivers/clk/imx/
F: include/dt-bindings/clock/*imx*
NXP NETC TIMER PTP CLOCK DRIVER
M: Wei Fang <wei.fang@nxp.com>
M: Clark Wang <xiaoning.wang@nxp.com>
L: imx@lists.linux.dev
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
F: drivers/ptp/ptp_netc.c
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained

View File

@ -28,6 +28,7 @@ config NXP_NTMP
config FSL_ENETC
tristate "ENETC PF driver"
depends on PTP_1588_CLOCK_OPTIONAL
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_IERB
@ -45,6 +46,7 @@ config FSL_ENETC
config NXP_ENETC4
tristate "ENETC4 PF driver"
depends on PTP_1588_CLOCK_OPTIONAL
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_MDIO
@ -62,6 +64,7 @@ config NXP_ENETC4
config FSL_ENETC_VF
tristate "ENETC VF driver"
depends on PTP_1588_CLOCK_OPTIONAL
depends on PCI_MSI
select FSL_ENETC_CORE
select FSL_ENETC_MDIO

View File

@ -221,22 +221,111 @@ static void enetc_unwind_tx_frame(struct enetc_bdr *tx_ring, int count, int i)
}
}
static void enetc_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
{
u32 val = ENETC_PM0_SINGLE_STEP_EN;
val |= ENETC_SET_SINGLE_STEP_OFFSET(offset);
if (udp)
val |= ENETC_PM0_SINGLE_STEP_CH;
/* The "Correction" field of a packet is updated based on the
* current time and the timestamp provided
*/
enetc_port_mac_wr(si, ENETC_PM0_SINGLE_STEP, val);
}
static void enetc4_set_one_step_ts(struct enetc_si *si, bool udp, int offset)
{
u32 val = PM_SINGLE_STEP_EN;
val |= PM_SINGLE_STEP_OFFSET_SET(offset);
if (udp)
val |= PM_SINGLE_STEP_CH;
enetc_port_mac_wr(si, ENETC4_PM_SINGLE_STEP(0), val);
}
static u32 enetc_update_ptp_sync_msg(struct enetc_ndev_priv *priv,
struct sk_buff *skb, bool csum_offload)
{
struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
u16 tstamp_off = enetc_cb->origin_tstamp_off;
u16 corr_off = enetc_cb->correction_off;
struct enetc_si *si = priv->si;
struct enetc_hw *hw = &si->hw;
__be32 new_sec_l, new_nsec;
__be16 new_sec_h;
u32 lo, hi, nsec;
u8 *data;
u64 sec;
lo = enetc_rd_hot(hw, ENETC_SICTR0);
hi = enetc_rd_hot(hw, ENETC_SICTR1);
sec = (u64)hi << 32 | lo;
nsec = do_div(sec, 1000000000);
/* Update originTimestamp field of Sync packet
* - 48 bits seconds field
* - 32 bits nanseconds field
*
* In addition, if csum_offload is false, the UDP checksum needs
* to be updated by software after updating originTimestamp field,
* otherwise the hardware will calculate the wrong checksum when
* updating the correction field and update it to the packet.
*/
data = skb_mac_header(skb);
new_sec_h = htons((sec >> 32) & 0xffff);
new_sec_l = htonl(sec & 0xffffffff);
new_nsec = htonl(nsec);
if (enetc_cb->udp && !csum_offload) {
struct udphdr *uh = udp_hdr(skb);
__be32 old_sec_l, old_nsec;
__be16 old_sec_h;
old_sec_h = *(__be16 *)(data + tstamp_off);
inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
new_sec_h, false);
old_sec_l = *(__be32 *)(data + tstamp_off + 2);
inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
new_sec_l, false);
old_nsec = *(__be32 *)(data + tstamp_off + 6);
inet_proto_csum_replace4(&uh->check, skb, old_nsec,
new_nsec, false);
}
*(__be16 *)(data + tstamp_off) = new_sec_h;
*(__be32 *)(data + tstamp_off + 2) = new_sec_l;
*(__be32 *)(data + tstamp_off + 6) = new_nsec;
/* Configure single-step register */
if (is_enetc_rev1(si))
enetc_set_one_step_ts(si, enetc_cb->udp, corr_off);
else
enetc4_set_one_step_ts(si, enetc_cb->udp, corr_off);
return lo & ENETC_TXBD_TSTAMP;
}
static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
{
bool do_vlan, do_onestep_tstamp = false, do_twostep_tstamp = false;
struct enetc_ndev_priv *priv = netdev_priv(tx_ring->ndev);
struct enetc_hw *hw = &priv->si->hw;
struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
struct enetc_tx_swbd *tx_swbd;
int len = skb_headlen(skb);
union enetc_tx_bd temp_bd;
u8 msgtype, twostep, udp;
bool csum_offload = false;
union enetc_tx_bd *txbd;
u16 offset1, offset2;
int i, count = 0;
skb_frag_t *frag;
unsigned int f;
dma_addr_t dma;
u8 flags = 0;
u32 tstamp;
enetc_clear_tx_bd(&temp_bd);
if (skb->ip_summed == CHECKSUM_PARTIAL) {
@ -256,11 +345,19 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
temp_bd.l4_aux = FIELD_PREP(ENETC_TX_BD_L4T,
ENETC_TXBD_L4T_UDP);
flags |= ENETC_TXBD_FLAGS_CSUM_LSO | ENETC_TXBD_FLAGS_L4CS;
csum_offload = true;
} else if (skb_checksum_help(skb)) {
return 0;
}
}
if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
do_onestep_tstamp = true;
tstamp = enetc_update_ptp_sync_msg(priv, skb, csum_offload);
} else if (enetc_cb->flag & ENETC_F_TX_TSTAMP) {
do_twostep_tstamp = true;
}
i = tx_ring->next_to_use;
txbd = ENETC_TXBD(*tx_ring, i);
prefetchw(txbd);
@ -280,17 +377,6 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
count++;
do_vlan = skb_vlan_tag_present(skb);
if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep, &offset1,
&offset2) ||
msgtype != PTP_MSGTYPE_SYNC || twostep)
WARN_ONCE(1, "Bad packet for one-step timestamping\n");
else
do_onestep_tstamp = true;
} else if (skb->cb[0] & ENETC_F_TX_TSTAMP) {
do_twostep_tstamp = true;
}
tx_swbd->do_twostep_tstamp = do_twostep_tstamp;
tx_swbd->qbv_en = !!(priv->active_offloads & ENETC_F_QBV);
tx_swbd->check_wb = tx_swbd->do_twostep_tstamp || tx_swbd->qbv_en;
@ -333,65 +419,9 @@ static int enetc_map_tx_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb)
}
if (do_onestep_tstamp) {
__be32 new_sec_l, new_nsec;
u32 lo, hi, nsec, val;
__be16 new_sec_h;
u8 *data;
u64 sec;
lo = enetc_rd_hot(hw, ENETC_SICTR0);
hi = enetc_rd_hot(hw, ENETC_SICTR1);
sec = (u64)hi << 32 | lo;
nsec = do_div(sec, 1000000000);
/* Configure extension BD */
temp_bd.ext.tstamp = cpu_to_le32(lo & 0x3fffffff);
temp_bd.ext.tstamp = cpu_to_le32(tstamp);
e_flags |= ENETC_TXBD_E_FLAGS_ONE_STEP_PTP;
/* Update originTimestamp field of Sync packet
* - 48 bits seconds field
* - 32 bits nanseconds field
*
* In addition, the UDP checksum needs to be updated
* by software after updating originTimestamp field,
* otherwise the hardware will calculate the wrong
* checksum when updating the correction field and
* update it to the packet.
*/
data = skb_mac_header(skb);
new_sec_h = htons((sec >> 32) & 0xffff);
new_sec_l = htonl(sec & 0xffffffff);
new_nsec = htonl(nsec);
if (udp) {
struct udphdr *uh = udp_hdr(skb);
__be32 old_sec_l, old_nsec;
__be16 old_sec_h;
old_sec_h = *(__be16 *)(data + offset2);
inet_proto_csum_replace2(&uh->check, skb, old_sec_h,
new_sec_h, false);
old_sec_l = *(__be32 *)(data + offset2 + 2);
inet_proto_csum_replace4(&uh->check, skb, old_sec_l,
new_sec_l, false);
old_nsec = *(__be32 *)(data + offset2 + 6);
inet_proto_csum_replace4(&uh->check, skb, old_nsec,
new_nsec, false);
}
*(__be16 *)(data + offset2) = new_sec_h;
*(__be32 *)(data + offset2 + 2) = new_sec_l;
*(__be32 *)(data + offset2 + 6) = new_nsec;
/* Configure single-step register */
val = ENETC_PM0_SINGLE_STEP_EN;
val |= ENETC_SET_SINGLE_STEP_OFFSET(offset1);
if (udp)
val |= ENETC_PM0_SINGLE_STEP_CH;
enetc_port_mac_wr(priv->si, ENETC_PM0_SINGLE_STEP,
val);
} else if (do_twostep_tstamp) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
e_flags |= ENETC_TXBD_E_FLAGS_TWO_STEP_PTP;
@ -938,12 +968,13 @@ static int enetc_map_tx_tso_buffs(struct enetc_bdr *tx_ring, struct sk_buff *skb
static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_bdr *tx_ring;
int count;
/* Queue one-step Sync packet if already locked */
if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
if (test_and_set_bit_lock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS,
&priv->flags)) {
skb_queue_tail(&priv->tx_skbs, skb);
@ -1005,24 +1036,29 @@ static netdev_tx_t enetc_start_xmit(struct sk_buff *skb,
netdev_tx_t enetc_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
struct enetc_ndev_priv *priv = netdev_priv(ndev);
u8 udp, msgtype, twostep;
u16 offset1, offset2;
/* Mark tx timestamp type on skb->cb[0] if requires */
/* Mark tx timestamp type on enetc_cb->flag if requires */
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
(priv->active_offloads & ENETC_F_TX_TSTAMP_MASK)) {
skb->cb[0] = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
} else {
skb->cb[0] = 0;
}
(priv->active_offloads & ENETC_F_TX_TSTAMP_MASK))
enetc_cb->flag = priv->active_offloads & ENETC_F_TX_TSTAMP_MASK;
else
enetc_cb->flag = 0;
/* Fall back to two-step timestamp if not one-step Sync packet */
if (skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
if (enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP) {
if (enetc_ptp_parse(skb, &udp, &msgtype, &twostep,
&offset1, &offset2) ||
msgtype != PTP_MSGTYPE_SYNC || twostep != 0)
skb->cb[0] = ENETC_F_TX_TSTAMP;
msgtype != PTP_MSGTYPE_SYNC || twostep != 0) {
enetc_cb->flag = ENETC_F_TX_TSTAMP;
} else {
enetc_cb->udp = !!udp;
enetc_cb->correction_off = offset1;
enetc_cb->origin_tstamp_off = offset2;
}
}
return enetc_start_xmit(skb, ndev);
@ -1214,7 +1250,9 @@ static bool enetc_clean_tx_ring(struct enetc_bdr *tx_ring, int napi_budget)
if (xdp_frame) {
xdp_return_frame(xdp_frame);
} else if (skb) {
if (unlikely(skb->cb[0] & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
struct enetc_skb_cb *enetc_cb = ENETC_SKB_CB(skb);
if (unlikely(enetc_cb->flag & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)) {
/* Start work to release lock for next one-step
* timestamping packet. And send one skb in
* tx_skbs queue if has.
@ -1397,8 +1435,7 @@ static void enetc_get_offloads(struct enetc_bdr *rx_ring,
__vlan_hwaccel_put_tag(skb, tpid, le16_to_cpu(rxbd->r.vlan_opt));
}
if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) &&
(priv->active_offloads & ENETC_F_RX_TSTAMP))
if (priv->active_offloads & ENETC_F_RX_TSTAMP)
enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb);
}
@ -3301,7 +3338,7 @@ int enetc_hwtstamp_set(struct net_device *ndev,
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int err, new_offloads = priv->active_offloads;
if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
if (!enetc_ptp_clock_is_enabled(priv->si))
return -EOPNOTSUPP;
switch (config->tx_type) {
@ -3351,7 +3388,7 @@ int enetc_hwtstamp_get(struct net_device *ndev,
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK))
if (!enetc_ptp_clock_is_enabled(priv->si))
return -EOPNOTSUPP;
if (priv->active_offloads & ENETC_F_TX_ONESTEP_SYNC_TSTAMP)

View File

@ -54,6 +54,15 @@ struct enetc_tx_swbd {
u8 qbv_en:1;
};
struct enetc_skb_cb {
u8 flag;
bool udp;
u16 correction_off;
u16 origin_tstamp_off;
};
#define ENETC_SKB_CB(skb) ((struct enetc_skb_cb *)((skb)->cb))
struct enetc_lso_t {
bool ipv6;
bool tcp;
@ -217,7 +226,7 @@ static inline union enetc_rx_bd *enetc_rxbd(struct enetc_bdr *rx_ring, int i)
{
int hw_idx = i;
if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) && rx_ring->ext_en)
if (rx_ring->ext_en)
hw_idx = 2 * i;
return &(((union enetc_rx_bd *)rx_ring->bd_base)[hw_idx]);
@ -231,7 +240,7 @@ static inline void enetc_rxbd_next(struct enetc_bdr *rx_ring,
new_rxbd++;
if (IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK) && rx_ring->ext_en)
if (rx_ring->ext_en)
new_rxbd++;
if (unlikely(++new_index == rx_ring->bd_count)) {
@ -589,6 +598,14 @@ static inline void enetc_cbd_free_data_mem(struct enetc_si *si, int size,
void enetc_reset_ptcmsdur(struct enetc_hw *hw);
void enetc_set_ptcmsdur(struct enetc_hw *hw, u32 *queue_max_sdu);
static inline bool enetc_ptp_clock_is_enabled(struct enetc_si *si)
{
if (is_enetc_rev1(si))
return IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK);
return IS_ENABLED(CONFIG_PTP_NETC_V4_TIMER);
}
#ifdef CONFIG_FSL_ENETC_QOS
int enetc_qos_query_caps(struct net_device *ndev, void *type_data);
int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);

View File

@ -171,6 +171,12 @@
/* Port MAC 0/1 Pause Quanta Threshold Register */
#define ENETC4_PM_PAUSE_THRESH(mac) (0x5064 + (mac) * 0x400)
#define ENETC4_PM_SINGLE_STEP(mac) (0x50c0 + (mac) * 0x400)
#define PM_SINGLE_STEP_CH BIT(6)
#define PM_SINGLE_STEP_OFFSET GENMASK(15, 7)
#define PM_SINGLE_STEP_OFFSET_SET(o) FIELD_PREP(PM_SINGLE_STEP_OFFSET, o)
#define PM_SINGLE_STEP_EN BIT(31)
/* Port MAC 0 Interface Mode Control Register */
#define ENETC4_PM_IF_MODE(mac) (0x5300 + (mac) * 0x400)
#define PM_IF_MODE_IFMODE GENMASK(2, 0)

View File

@ -569,6 +569,9 @@ static const struct net_device_ops enetc4_ndev_ops = {
.ndo_set_features = enetc4_pf_set_features,
.ndo_vlan_rx_add_vid = enetc_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = enetc_vlan_rx_del_vid,
.ndo_eth_ioctl = enetc_ioctl,
.ndo_hwtstamp_get = enetc_hwtstamp_get,
.ndo_hwtstamp_set = enetc_hwtstamp_set,
};
static struct phylink_pcs *

View File

@ -4,6 +4,9 @@
#include <linux/ethtool_netlink.h>
#include <linux/net_tstamp.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/ptp_clock_kernel.h>
#include "enetc.h"
static const u32 enetc_si_regs[] = {
@ -877,23 +880,54 @@ static int enetc_set_coalesce(struct net_device *ndev,
return 0;
}
static int enetc_get_ts_info(struct net_device *ndev,
struct kernel_ethtool_ts_info *info)
static int enetc4_get_phc_index_by_pdev(struct enetc_si *si)
{
struct pci_bus *bus = si->pdev->bus;
struct pci_dev *timer_pdev;
unsigned int devfn;
int phc_index;
switch (si->revision) {
case ENETC_REV_4_1:
devfn = PCI_DEVFN(24, 0);
break;
default:
return -1;
}
timer_pdev = pci_get_slot(bus, devfn);
if (!timer_pdev)
return -1;
phc_index = ptp_clock_index_by_dev(&timer_pdev->dev);
pci_dev_put(timer_pdev);
return phc_index;
}
static int enetc4_get_phc_index(struct enetc_si *si)
{
struct device_node *np = si->pdev->dev.of_node;
struct device_node *timer_np;
int phc_index;
if (!np)
return enetc4_get_phc_index_by_pdev(si);
timer_np = of_parse_phandle(np, "ptp-timer", 0);
if (!timer_np)
return enetc4_get_phc_index_by_pdev(si);
phc_index = ptp_clock_index_by_of_node(timer_np);
of_node_put(timer_np);
return phc_index;
}
static void enetc_get_ts_generic_info(struct net_device *ndev,
struct kernel_ethtool_ts_info *info)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
int *phc_idx;
phc_idx = symbol_get(enetc_phc_index);
if (phc_idx) {
info->phc_index = *phc_idx;
symbol_put(enetc_phc_index);
}
if (!IS_ENABLED(CONFIG_FSL_ENETC_PTP_CLOCK)) {
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
return 0;
}
info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
@ -908,6 +942,36 @@ static int enetc_get_ts_info(struct net_device *ndev,
info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_ALL);
}
static int enetc_get_ts_info(struct net_device *ndev,
struct kernel_ethtool_ts_info *info)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
struct enetc_si *si = priv->si;
int *phc_idx;
if (!enetc_ptp_clock_is_enabled(si))
goto timestamp_tx_sw;
if (is_enetc_rev1(si)) {
phc_idx = symbol_get(enetc_phc_index);
if (phc_idx) {
info->phc_index = *phc_idx;
symbol_put(enetc_phc_index);
}
} else {
info->phc_index = enetc4_get_phc_index(si);
if (info->phc_index < 0)
goto timestamp_tx_sw;
}
enetc_get_ts_generic_info(ndev, info);
return 0;
timestamp_tx_sw:
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE;
return 0;
}
@ -1296,6 +1360,7 @@ const struct ethtool_ops enetc4_pf_ethtool_ops = {
.get_rxfh = enetc_get_rxfh,
.set_rxfh = enetc_set_rxfh,
.get_rxfh_fields = enetc_get_rxfh_fields,
.get_ts_info = enetc_get_ts_info,
};
void enetc_set_ethtool_ops(struct net_device *ndev)

View File

@ -614,6 +614,7 @@ enum enetc_txbd_flags {
#define ENETC_TXBD_STATS_WIN BIT(7)
#define ENETC_TXBD_TXSTART_MASK GENMASK(24, 0)
#define ENETC_TXBD_FLAGS_OFFSET 24
#define ENETC_TXBD_TSTAMP GENMASK(29, 0)
static inline __le32 enetc_txbd_set_tx_start(u64 tx_start, u8 flags)
{

View File

@ -252,4 +252,15 @@ config PTP_S390
driver provides the raw clock value without the delta to
userspace. That way userspace programs like chrony could steer
the kernel clock.
config PTP_NETC_V4_TIMER
tristate "NXP NETC V4 Timer PTP Driver"
depends on PTP_1588_CLOCK
depends on PCI_MSI
help
This driver adds support for using the NXP NETC V4 Timer as a PTP
clock, the clock is used by ENETC V4 or NETC V4 Switch for PTP time
synchronization. It also supports periodic output signal (e.g. PPS)
and external trigger timestamping.
endmenu

View File

@ -23,3 +23,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o
obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
obj-$(CONFIG_PTP_DFL_TOD) += ptp_dfl_tod.o
obj-$(CONFIG_PTP_S390) += ptp_s390.o
obj-$(CONFIG_PTP_NETC_V4_TIMER) += ptp_netc.o

View File

@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/posix-clock.h>
#include <linux/pps_kernel.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
@ -488,6 +489,58 @@ int ptp_clock_index(struct ptp_clock *ptp)
}
EXPORT_SYMBOL(ptp_clock_index);
static int ptp_clock_of_node_match(struct device *dev, const void *data)
{
const struct device_node *parent_np = data;
return (dev->parent && dev_of_node(dev->parent) == parent_np);
}
int ptp_clock_index_by_of_node(struct device_node *np)
{
struct ptp_clock *ptp;
struct device *dev;
int phc_index;
dev = class_find_device(&ptp_class, NULL, np,
ptp_clock_of_node_match);
if (!dev)
return -1;
ptp = dev_get_drvdata(dev);
phc_index = ptp_clock_index(ptp);
put_device(dev);
return phc_index;
}
EXPORT_SYMBOL_GPL(ptp_clock_index_by_of_node);
static int ptp_clock_dev_match(struct device *dev, const void *data)
{
const struct device *parent = data;
return dev->parent == parent;
}
int ptp_clock_index_by_dev(struct device *parent)
{
struct ptp_clock *ptp;
struct device *dev;
int phc_index;
dev = class_find_device(&ptp_class, NULL, parent,
ptp_clock_dev_match);
if (!dev)
return -1;
ptp = dev_get_drvdata(dev);
phc_index = ptp_clock_index(ptp);
put_device(dev);
return phc_index;
}
EXPORT_SYMBOL_GPL(ptp_clock_index_by_dev);
int ptp_find_pin(struct ptp_clock *ptp,
enum ptp_pin_function func, unsigned int chan)
{

1017
drivers/ptp/ptp_netc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -360,6 +360,24 @@ extern void ptp_clock_event(struct ptp_clock *ptp,
extern int ptp_clock_index(struct ptp_clock *ptp);
/**
* ptp_clock_index_by_of_node() - obtain the device index of
* a PTP clock based on the PTP device of_node
*
* @np: The device of_node pointer of the PTP device.
* Return: The PHC index on success or -1 on failure.
*/
int ptp_clock_index_by_of_node(struct device_node *np);
/**
* ptp_clock_index_by_dev() - obtain the device index of
* a PTP clock based on the PTP device.
*
* @parent: The parent device (PTP device) pointer of the PTP clock.
* Return: The PHC index on success or -1 on failure.
*/
int ptp_clock_index_by_dev(struct device *parent);
/**
* ptp_find_pin() - obtain the pin index of a given auxiliary function
*
@ -425,6 +443,10 @@ static inline void ptp_clock_event(struct ptp_clock *ptp,
{ }
static inline int ptp_clock_index(struct ptp_clock *ptp)
{ return -1; }
static inline int ptp_clock_index_by_of_node(struct device_node *np)
{ return -1; }
static inline int ptp_clock_index_by_dev(struct device *parent)
{ return -1; }
static inline int ptp_find_pin(struct ptp_clock *ptp,
enum ptp_pin_function func, unsigned int chan)
{ return -1; }