mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
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:
commit
48195dd18f
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
63
Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
Normal file
63
Documentation/devicetree/bindings/ptp/nxp,ptp-netc.yaml
Normal 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";
|
||||
};
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
1017
drivers/ptp/ptp_netc.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -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; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user