Commit 8d94a873 authored by David S. Miller's avatar David S. Miller

Merge branch 'PTP-support-for-the-SJA1105-DSA-driver'

Vladimir Oltean says:

====================
PTP support for the SJA1105 DSA driver

This patchset adds the following:

 - A timecounter/cyclecounter based PHC for the free-running
   timestamping clock of this switch.

 - A state machine implemented in the DSA tagger for SJA1105, which
   keeps track of metadata follow-up Ethernet frames (the switch's way
   of transmitting RX timestamps).

Clock manipulations on the actual hardware PTP clock will have to be
implemented anyway, for the TTEthernet block and the time-based ingress
policer.

v3 patchset can be found at:
https://lkml.org/lkml/2019/6/4/954

Changes from v3:

- Made it compile with the SJA1105 DSA driver and PTP driver as modules.

- Reworked/simplified/fixed some issues in 03/17
  (dsa_8021q_remove_header) and added an ASCII image that
  illustrates the transformation that is taking place.

- Removed a useless check for sja1105_is_link_local from 16/17 (RX
  timestamping) which also made previous 08/17 patch ("Move
  sja1105_is_link_local to include/linux") useless and therefore dropped.

v2 patchset can be found at:
https://lkml.org/lkml/2019/6/2/146

Changes from v2:

- Broke previous 09/10 patch (timestamping) into multiple smaller
  patches.

- Every patch in the series compiles.

v1 patchset can be found at:
https://lkml.org/lkml/2019/5/28/1093

Changes from v1:

- Removed the addition of the DSA .can_timestamp callback.

- Waiting for meta frames is done completely inside the tagger, and all
  frames emitted on RX are already partially timestamped.

- Added a global data structure for the tagger common to all ports.

- Made PTP work with ports in standalone mode, by limiting use of the
  DMAC-mangling "incl_srcpt" mode only when ports are bridged, aka when
  the DSA master is already promiscuous and can receive anything.
  Also changed meta frames to be sent at the 01-80-C2-00-00-0E DMAC.

- Made some progress w.r.t. observed negative path delay.  Apparently it
  only appears when the delay mechanism is the delay request-response
  (end-to-end) one. If peer delay is used (-P), the path delay is
  positive and appears reasonable for an 1000Base-T link (485 ns in
  steady state).

  SJA1105 as PTP slave (OC) with E2E path delay:

ptp4l[55.600]: master offset          8 s2 freq  +83677 path delay     -2390
ptp4l[56.600]: master offset         17 s2 freq  +83688 path delay     -2391
ptp4l[57.601]: master offset          6 s2 freq  +83682 path delay     -2391
ptp4l[58.601]: master offset         -1 s2 freq  +83677 path delay     -2391

  SJA1105 as PTP slave (OC) with P2P path delay:

ptp4l[48.343]: master offset          5 s2 freq  +83715 path delay       484
ptp4l[48.468]: master offset         -3 s2 freq  +83705 path delay       485
ptp4l[48.593]: master offset          0 s2 freq  +83708 path delay       485
ptp4l[48.718]: master offset          1 s2 freq  +83710 path delay       485
ptp4l[48.844]: master offset          1 s2 freq  +83710 path delay       485
ptp4l[48.969]: master offset         -5 s2 freq  +83702 path delay       485
ptp4l[49.094]: master offset          3 s2 freq  +83712 path delay       485
ptp4l[49.219]: master offset          4 s2 freq  +83714 path delay       485
ptp4l[49.344]: master offset         -5 s2 freq  +83702 path delay       485
ptp4l[49.469]: master offset          3 s2 freq  +83713 path delay       487
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a6cdeeb1 a602afd2
......@@ -16,3 +16,10 @@ tristate "NXP SJA1105 Ethernet switch family support"
- SJA1105Q (Gen. 2, No SGMII, TT-Ethernet)
- SJA1105R (Gen. 2, SGMII, No TT-Ethernet)
- SJA1105S (Gen. 2, SGMII, TT-Ethernet)
config NET_DSA_SJA1105_PTP
tristate "Support for the PTP clock on the NXP SJA1105 Ethernet switch"
depends on NET_DSA_SJA1105
help
This enables support for timestamping and PTP clock manipulations in
the SJA1105 DSA driver.
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_NET_DSA_SJA1105) += sja1105.o
obj-$(CONFIG_NET_DSA_SJA1105_PTP) += sja1105_ptp.o
sja1105-objs := \
sja1105_spi.o \
......
......@@ -5,6 +5,8 @@
#ifndef _SJA1105_H
#define _SJA1105_H
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#include <linux/dsa/sja1105.h>
#include <net/dsa.h>
#include <linux/mutex.h>
......@@ -27,6 +29,11 @@ struct sja1105_regs {
u64 rgu;
u64 config;
u64 rmii_pll1;
u64 ptp_control;
u64 ptpclk;
u64 ptpclkrate;
u64 ptptsclk;
u64 ptpegr_ts[SJA1105_NUM_PORTS];
u64 pad_mii_tx[SJA1105_NUM_PORTS];
u64 cgu_idiv[SJA1105_NUM_PORTS];
u64 rgmii_pad_mii_tx[SJA1105_NUM_PORTS];
......@@ -50,9 +57,19 @@ struct sja1105_info {
* switch core and device_id)
*/
u64 part_no;
/* E/T and P/Q/R/S have partial timestamps of different sizes.
* They must be reconstructed on both families anyway to get the full
* 64-bit values back.
*/
int ptp_ts_bits;
/* Also SPI commands are of different sizes to retrieve
* the egress timestamps.
*/
int ptpegr_ts_bytes;
const struct sja1105_dynamic_table_ops *dyn_ops;
const struct sja1105_table_ops *static_ops;
const struct sja1105_regs *regs;
int (*ptp_cmd)(const void *ctx, const void *data);
int (*reset_cmd)(const void *ctx, const void *data);
int (*setup_rgmii_delay)(const void *ctx, int port);
/* Prototypes from include/net/dsa.h */
......@@ -72,13 +89,25 @@ struct sja1105_private {
struct spi_device *spidev;
struct dsa_switch *ds;
struct sja1105_port ports[SJA1105_NUM_PORTS];
struct ptp_clock_info ptp_caps;
struct ptp_clock *clock;
/* The cycle counter translates the PTP timestamps (based on
* a free-running counter) into a software time domain.
*/
struct cyclecounter tstamp_cc;
struct timecounter tstamp_tc;
struct delayed_work refresh_work;
/* Serializes all operations on the cycle counter */
struct mutex ptp_lock;
/* Serializes transmission of management frames so that
* the switch doesn't confuse them with one another.
*/
struct mutex mgmt_lock;
struct sja1105_tagger_data tagger_data;
};
#include "sja1105_dynamic_config.h"
#include "sja1105_ptp.h"
struct sja1105_spi_message {
u64 access;
......
......@@ -378,6 +378,7 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
.addr = 0x38,
},
[BLK_IDX_L2_FORWARDING_PARAMS] = {0},
[BLK_IDX_AVB_PARAMS] = {0},
[BLK_IDX_GENERAL_PARAMS] = {
.entry_packing = sja1105et_general_params_entry_packing,
.cmd_packing = sja1105et_general_params_cmd_packing,
......@@ -441,6 +442,7 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
.addr = 0x38,
},
[BLK_IDX_L2_FORWARDING_PARAMS] = {0},
[BLK_IDX_AVB_PARAMS] = {0},
[BLK_IDX_GENERAL_PARAMS] = {
.entry_packing = sja1105et_general_params_entry_packing,
.cmd_packing = sja1105et_general_params_cmd_packing,
......
This diff is collapsed.
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
*/
#ifndef _SJA1105_PTP_H
#define _SJA1105_PTP_H
#if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP)
int sja1105_ptp_clock_register(struct sja1105_private *priv);
void sja1105_ptp_clock_unregister(struct sja1105_private *priv);
int sja1105_ptpegr_ts_poll(struct sja1105_private *priv, int port, u64 *ts);
int sja1105et_ptp_cmd(const void *ctx, const void *data);
int sja1105pqrs_ptp_cmd(const void *ctx, const void *data);
int sja1105_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *ts);
u64 sja1105_tstamp_reconstruct(struct sja1105_private *priv, u64 now,
u64 ts_partial);
int sja1105_ptp_reset(struct sja1105_private *priv);
#else
static inline int sja1105_ptp_clock_register(struct sja1105_private *priv)
{
return 0;
}
static inline void sja1105_ptp_clock_unregister(struct sja1105_private *priv)
{
return;
}
static inline int
sja1105_ptpegr_ts_poll(struct sja1105_private *priv, int port, u64 *ts)
{
return 0;
}
static inline u64 sja1105_tstamp_reconstruct(struct sja1105_private *priv,
u64 now, u64 ts_partial)
{
return 0;
}
static inline int sja1105_ptp_reset(struct sja1105_private *priv)
{
return 0;
}
#define sja1105et_ptp_cmd NULL
#define sja1105pqrs_ptp_cmd NULL
#define sja1105_get_ts_info NULL
#endif /* IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) */
#endif /* _SJA1105_PTP_H */
......@@ -100,6 +100,7 @@ int sja1105_spi_send_packed_buf(const struct sja1105_private *priv,
return 0;
}
EXPORT_SYMBOL_GPL(sja1105_spi_send_packed_buf);
/* If @rw is:
* - SPI_WRITE: creates and sends an SPI write message at absolute
......@@ -135,6 +136,7 @@ int sja1105_spi_send_int(const struct sja1105_private *priv,
return rc;
}
EXPORT_SYMBOL_GPL(sja1105_spi_send_int);
/* Should be used if a @packed_buf larger than SJA1105_SIZE_SPI_MSG_MAXLEN
* must be sent/received. Splitting the buffer into chunks and assembling
......@@ -478,7 +480,12 @@ int sja1105_static_config_upload(struct sja1105_private *priv)
dev_info(dev, "Succeeded after %d tried\n", RETRIES - retries);
}
rc = sja1105_ptp_reset(priv);
if (rc < 0)
dev_err(dev, "Failed to reset PTP clock: %d\n", rc);
dev_info(dev, "Reset switch and programmed static config\n");
out:
kfree(config_buf);
return rc;
......@@ -507,6 +514,11 @@ static struct sja1105_regs sja1105et_regs = {
.rgmii_tx_clk = {0x100016, 0x10001D, 0x100024, 0x10002B, 0x100032},
.rmii_ref_clk = {0x100015, 0x10001C, 0x100023, 0x10002A, 0x100031},
.rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
.ptpegr_ts = {0xC0, 0xC2, 0xC4, 0xC6, 0xC8},
.ptp_control = 0x17,
.ptpclk = 0x18, /* Spans 0x18 to 0x19 */
.ptpclkrate = 0x1A,
.ptptsclk = 0x1B, /* Spans 0x1B to 0x1C */
};
static struct sja1105_regs sja1105pqrs_regs = {
......@@ -533,6 +545,11 @@ static struct sja1105_regs sja1105pqrs_regs = {
.rmii_ref_clk = {0x100015, 0x10001B, 0x100021, 0x100027, 0x10002D},
.rmii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F},
.qlevel = {0x604, 0x614, 0x624, 0x634, 0x644},
.ptpegr_ts = {0xC0, 0xC4, 0xC8, 0xCC, 0xD0},
.ptp_control = 0x18,
.ptpclk = 0x19,
.ptpclkrate = 0x1B,
.ptptsclk = 0x1C,
};
struct sja1105_info sja1105e_info = {
......@@ -540,9 +557,12 @@ struct sja1105_info sja1105e_info = {
.part_no = SJA1105ET_PART_NO,
.static_ops = sja1105e_table_ops,
.dyn_ops = sja1105et_dyn_ops,
.ptp_ts_bits = 24,
.ptpegr_ts_bytes = 4,
.reset_cmd = sja1105et_reset_cmd,
.fdb_add_cmd = sja1105et_fdb_add,
.fdb_del_cmd = sja1105et_fdb_del,
.ptp_cmd = sja1105et_ptp_cmd,
.regs = &sja1105et_regs,
.name = "SJA1105E",
};
......@@ -551,9 +571,12 @@ struct sja1105_info sja1105t_info = {
.part_no = SJA1105ET_PART_NO,
.static_ops = sja1105t_table_ops,
.dyn_ops = sja1105et_dyn_ops,
.ptp_ts_bits = 24,
.ptpegr_ts_bytes = 4,
.reset_cmd = sja1105et_reset_cmd,
.fdb_add_cmd = sja1105et_fdb_add,
.fdb_del_cmd = sja1105et_fdb_del,
.ptp_cmd = sja1105et_ptp_cmd,
.regs = &sja1105et_regs,
.name = "SJA1105T",
};
......@@ -562,9 +585,12 @@ struct sja1105_info sja1105p_info = {
.part_no = SJA1105P_PART_NO,
.static_ops = sja1105p_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops,
.ptp_ts_bits = 32,
.ptpegr_ts_bytes = 8,
.reset_cmd = sja1105pqrs_reset_cmd,
.fdb_add_cmd = sja1105pqrs_fdb_add,
.fdb_del_cmd = sja1105pqrs_fdb_del,
.ptp_cmd = sja1105pqrs_ptp_cmd,
.regs = &sja1105pqrs_regs,
.name = "SJA1105P",
};
......@@ -573,9 +599,12 @@ struct sja1105_info sja1105q_info = {
.part_no = SJA1105Q_PART_NO,
.static_ops = sja1105q_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops,
.ptp_ts_bits = 32,
.ptpegr_ts_bytes = 8,
.reset_cmd = sja1105pqrs_reset_cmd,
.fdb_add_cmd = sja1105pqrs_fdb_add,
.fdb_del_cmd = sja1105pqrs_fdb_del,
.ptp_cmd = sja1105pqrs_ptp_cmd,
.regs = &sja1105pqrs_regs,
.name = "SJA1105Q",
};
......@@ -584,9 +613,12 @@ struct sja1105_info sja1105r_info = {
.part_no = SJA1105R_PART_NO,
.static_ops = sja1105r_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops,
.ptp_ts_bits = 32,
.ptpegr_ts_bytes = 8,
.reset_cmd = sja1105pqrs_reset_cmd,
.fdb_add_cmd = sja1105pqrs_fdb_add,
.fdb_del_cmd = sja1105pqrs_fdb_del,
.ptp_cmd = sja1105pqrs_ptp_cmd,
.regs = &sja1105pqrs_regs,
.name = "SJA1105R",
};
......@@ -596,8 +628,11 @@ struct sja1105_info sja1105s_info = {
.static_ops = sja1105s_table_ops,
.dyn_ops = sja1105pqrs_dyn_ops,
.regs = &sja1105pqrs_regs,
.ptp_ts_bits = 32,
.ptpegr_ts_bytes = 8,
.reset_cmd = sja1105pqrs_reset_cmd,
.fdb_add_cmd = sja1105pqrs_fdb_add,
.fdb_del_cmd = sja1105pqrs_fdb_del,
.ptp_cmd = sja1105pqrs_ptp_cmd,
.name = "SJA1105S",
};
......@@ -35,6 +35,7 @@ void sja1105_pack(void *buf, const u64 *val, int start, int end, size_t len)
}
dump_stack();
}
EXPORT_SYMBOL_GPL(sja1105_pack);
void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
{
......@@ -52,6 +53,7 @@ void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
start, end);
dump_stack();
}
EXPORT_SYMBOL_GPL(sja1105_unpack);
void sja1105_packing(void *buf, u64 *val, int start, int end,
size_t len, enum packing_op op)
......@@ -74,6 +76,7 @@ void sja1105_packing(void *buf, u64 *val, int start, int end,
}
dump_stack();
}
EXPORT_SYMBOL_GPL(sja1105_packing);
/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
u32 sja1105_crc32(const void *buf, size_t len)
......@@ -91,6 +94,28 @@ u32 sja1105_crc32(const void *buf, size_t len)
return ~crc;
}
static size_t sja1105et_avb_params_entry_packing(void *buf, void *entry_ptr,
enum packing_op op)
{
const size_t size = SJA1105ET_SIZE_AVB_PARAMS_ENTRY;
struct sja1105_avb_params_entry *entry = entry_ptr;
sja1105_packing(buf, &entry->destmeta, 95, 48, size, op);
sja1105_packing(buf, &entry->srcmeta, 47, 0, size, op);
return size;
}
static size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr,
enum packing_op op)
{
const size_t size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY;
struct sja1105_avb_params_entry *entry = entry_ptr;
sja1105_packing(buf, &entry->destmeta, 125, 78, size, op);
sja1105_packing(buf, &entry->srcmeta, 77, 30, size, op);
return size;
}
static size_t sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,
enum packing_op op)
{
......@@ -423,6 +448,7 @@ static u64 blk_id_map[BLK_IDX_MAX] = {
[BLK_IDX_MAC_CONFIG] = BLKID_MAC_CONFIG,
[BLK_IDX_L2_LOOKUP_PARAMS] = BLKID_L2_LOOKUP_PARAMS,
[BLK_IDX_L2_FORWARDING_PARAMS] = BLKID_L2_FORWARDING_PARAMS,
[BLK_IDX_AVB_PARAMS] = BLKID_AVB_PARAMS,
[BLK_IDX_GENERAL_PARAMS] = BLKID_GENERAL_PARAMS,
[BLK_IDX_XMII_PARAMS] = BLKID_XMII_PARAMS,
};
......@@ -624,6 +650,12 @@ struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = {
.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
},
[BLK_IDX_AVB_PARAMS] = {
.packing = sja1105et_avb_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_avb_params_entry),
.packed_entry_size = SJA1105ET_SIZE_AVB_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
},
[BLK_IDX_GENERAL_PARAMS] = {
.packing = sja1105et_general_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
......@@ -682,6 +714,12 @@ struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = {
.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
},
[BLK_IDX_AVB_PARAMS] = {
.packing = sja1105et_avb_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_avb_params_entry),
.packed_entry_size = SJA1105ET_SIZE_AVB_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
},
[BLK_IDX_GENERAL_PARAMS] = {
.packing = sja1105et_general_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
......@@ -740,6 +778,12 @@ struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = {
.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
},
[BLK_IDX_AVB_PARAMS] = {
.packing = sja1105pqrs_avb_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_avb_params_entry),
.packed_entry_size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
},
[BLK_IDX_GENERAL_PARAMS] = {
.packing = sja1105pqrs_general_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
......@@ -798,6 +842,12 @@ struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = {
.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
},
[BLK_IDX_AVB_PARAMS] = {
.packing = sja1105pqrs_avb_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_avb_params_entry),
.packed_entry_size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
},
[BLK_IDX_GENERAL_PARAMS] = {
.packing = sja1105pqrs_general_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
......@@ -856,6 +906,12 @@ struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = {
.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
},
[BLK_IDX_AVB_PARAMS] = {
.packing = sja1105pqrs_avb_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_avb_params_entry),
.packed_entry_size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
},
[BLK_IDX_GENERAL_PARAMS] = {
.packing = sja1105pqrs_general_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
......@@ -914,6 +970,12 @@ struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX] = {
.packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT,
},
[BLK_IDX_AVB_PARAMS] = {
.packing = sja1105pqrs_avb_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_avb_params_entry),
.packed_entry_size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY,
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
},
[BLK_IDX_GENERAL_PARAMS] = {
.packing = sja1105pqrs_general_params_entry_packing,
.unpacked_entry_size = sizeof(struct sja1105_general_params_entry),
......
......@@ -20,10 +20,12 @@
#define SJA1105ET_SIZE_MAC_CONFIG_ENTRY 28
#define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_ENTRY 4
#define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY 40
#define SJA1105ET_SIZE_AVB_PARAMS_ENTRY 12
#define SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY 20
#define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY 32
#define SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY 16
#define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY 44
#define SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY 16
/* UM10944.pdf Page 11, Table 2. Configuration Blocks */
enum {
......@@ -34,6 +36,7 @@ enum {
BLKID_MAC_CONFIG = 0x09,
BLKID_L2_LOOKUP_PARAMS = 0x0D,
BLKID_L2_FORWARDING_PARAMS = 0x0E,
BLKID_AVB_PARAMS = 0x10,
BLKID_GENERAL_PARAMS = 0x11,
BLKID_XMII_PARAMS = 0x4E,
};
......@@ -46,6 +49,7 @@ enum sja1105_blk_idx {
BLK_IDX_MAC_CONFIG,
BLK_IDX_L2_LOOKUP_PARAMS,
BLK_IDX_L2_FORWARDING_PARAMS,
BLK_IDX_AVB_PARAMS,
BLK_IDX_GENERAL_PARAMS,
BLK_IDX_XMII_PARAMS,
BLK_IDX_MAX,
......@@ -64,6 +68,7 @@ enum sja1105_blk_idx {
#define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT 1
#define SJA1105_MAX_GENERAL_PARAMS_COUNT 1
#define SJA1105_MAX_XMII_PARAMS_COUNT 1
#define SJA1105_MAX_AVB_PARAMS_COUNT 1
#define SJA1105_MAX_FRAME_MEMORY 929
......@@ -179,6 +184,11 @@ struct sja1105_l2_policing_entry {
u64 partition;
};
struct sja1105_avb_params_entry {
u64 destmeta;
u64 srcmeta;
};
struct sja1105_mac_config_entry {
u64 top[8];
u64 base[8];
......
......@@ -20,9 +20,6 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
u16 tpid, u16 tci);
struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *pt, u16 *tpid, u16 *tci);
u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port);
u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port);
......@@ -31,6 +28,8 @@ int dsa_8021q_rx_switch_id(u16 vid);
int dsa_8021q_rx_source_port(u16 vid);
struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb);
#else
int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
......@@ -45,12 +44,6 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
return NULL;
}
struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *pt, u16 *tpid, u16 *tci)
{
return NULL;
}
u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port)
{
return 0;
......@@ -71,6 +64,11 @@ int dsa_8021q_rx_source_port(u16 vid)
return 0;
}
struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb)
{
return NULL;
}
#endif /* IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q) */
#endif /* _NET_DSA_8021Q_H */
......@@ -12,6 +12,7 @@
#include <net/dsa.h>
#define ETH_P_SJA1105 ETH_P_DSA_8021Q
#define ETH_P_SJA1105_META 0x0008
/* IEEE 802.3 Annex 57A: Slow Protocols PDUs (01:80:C2:xx:xx:xx) */
#define SJA1105_LINKLOCAL_FILTER_A 0x0180C2000000ull
......@@ -20,8 +21,41 @@
#define SJA1105_LINKLOCAL_FILTER_B 0x011B19000000ull
#define SJA1105_LINKLOCAL_FILTER_B_MASK 0xFFFFFF000000ull
/* Source and Destination MAC of follow-up meta frames.
* Whereas the choice of SMAC only affects the unique identification of the
* switch as sender of meta frames, the DMAC must be an address that is present
* in the DSA master port's multicast MAC filter.
* 01-80-C2-00-00-0E is a good choice for this, as all profiles of IEEE 1588
* over L2 use this address for some purpose already.
*/
#define SJA1105_META_SMAC 0x222222222222ull
#define SJA1105_META_DMAC 0x0180C200000Eull
/* Global tagger data: each struct sja1105_port has a reference to
* the structure defined in struct sja1105_private.
*/
struct sja1105_tagger_data {
struct sk_buff_head skb_rxtstamp_queue;
struct work_struct rxtstamp_work;
struct sk_buff *stampable_skb;
/* Protects concurrent access to the meta state machine
* from taggers running on multiple ports on SMP systems
*/
spinlock_t meta_lock;
bool hwts_rx_en;
};
struct sja1105_skb_cb {
u32 meta_tstamp;
};
#define SJA1105_SKB_CB(skb) \
((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb))
struct sja1105_port {
struct sja1105_tagger_data *data;
struct dsa_port *dp;
bool hwts_tx_en;
int mgmt_slot;
};
......
......@@ -357,6 +357,7 @@ struct dsa_switch_ops {
int port);
int (*setup)(struct dsa_switch *ds);
void (*teardown)(struct dsa_switch *ds);
u32 (*get_phy_flags)(struct dsa_switch *ds, int port);
/*
......
......@@ -408,6 +408,9 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
dsa_switch_unregister_notifier(ds);
if (ds->ops->teardown)
ds->ops->teardown(ds);
if (ds->devlink) {
devlink_unregister(ds->devlink);
devlink_free(ds->devlink);
......
......@@ -423,6 +423,8 @@ static void dsa_skb_tx_timestamp(struct dsa_slave_priv *p,
if (!clone)
return;
DSA_SKB_CB(skb)->clone = clone;
if (ds->ops->port_txtstamp(ds, p->dp->index, clone, type))
return;
......@@ -460,6 +462,7 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
u64_stats_update_end(&s->syncp);
DSA_SKB_CB(skb)->deferred_xmit = false;
DSA_SKB_CB(skb)->clone = NULL;
/* Identify PTP protocol packets, clone them, and pass them to the
* switch driver
......
......@@ -235,31 +235,48 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(dsa_8021q_xmit);
struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *pt, u16 *tpid, u16 *tci)
{
struct vlan_ethhdr *tag;
if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
return NULL;
tag = vlan_eth_hdr(skb);
*tpid = ntohs(tag->h_vlan_proto);
*tci = ntohs(tag->h_vlan_TCI);
/* skb->data points in the middle of the VLAN tag,
* after tpid and before tci. This is because so far,
* ETH_HLEN (DMAC, SMAC, EtherType) bytes were pulled.
/* In the DSA packet_type handler, skb->data points in the middle of the VLAN
* tag, after tpid and before tci. This is because so far, ETH_HLEN
* (DMAC, SMAC, EtherType) bytes were pulled.
* There are 2 bytes of VLAN tag left in skb->data, and upper
* layers expect the 'real' EtherType to be consumed as well.
* Coincidentally, a VLAN header is also of the same size as
* the number of bytes that need to be pulled.
*
* skb_mac_header skb->data
* | |
* v v
* | | | | | | | | | | | | | | | | | | |
* +-----------------------+-----------------------+-------+-------+-------+
* | Destination MAC | Source MAC | TPID | TCI | EType |
* +-----------------------+-----------------------+-------+-------+-------+
* ^ | |
* |<--VLAN_HLEN-->to <---VLAN_HLEN--->
* from |
* >>>>>>> v
* >>>>>>> | | | | | | | | | | | | | | |
* >>>>>>> +-----------------------+-----------------------+-------+
* >>>>>>> | Destination MAC | Source MAC | EType |
* +-----------------------+-----------------------+-------+
* ^ ^
* (now part of | |
* skb->head) skb_mac_header skb->data
*/
skb_pull_rcsum(skb, VLAN_HLEN);
struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb)
{
u8 *from = skb_mac_header(skb);
u8 *dest = from + VLAN_HLEN;
memmove(dest, from, ETH_HLEN - VLAN_HLEN);
skb_pull(skb, VLAN_HLEN);
skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
skb_pull_rcsum(skb, ETH_HLEN);
return skb;
}
EXPORT_SYMBOL_GPL(dsa_8021q_rcv);
EXPORT_SYMBOL_GPL(dsa_8021q_remove_header);
static const struct dsa_device_ops dsa_8021q_netdev_ops = {
.name = "8021q",
......
......@@ -13,6 +13,8 @@ static inline bool sja1105_is_link_local(const struct sk_buff *skb)
const struct ethhdr *hdr = eth_hdr(skb);
u64 dmac = ether_addr_to_u64(hdr->h_dest);
if (ntohs(hdr->h_proto) == ETH_P_SJA1105_META)
return false;
if ((dmac & SJA1105_LINKLOCAL_FILTER_A_MASK) ==
SJA1105_LINKLOCAL_FILTER_A)
return true;
......@@ -22,15 +24,61 @@ static inline bool sja1105_is_link_local(const struct sk_buff *skb)
return false;
}
struct sja1105_meta {
u64 tstamp;
u64 dmac_byte_4;
u64 dmac_byte_3;
u64 source_port;
u64 switch_id;
};
static void sja1105_meta_unpack(const struct sk_buff *skb,
struct sja1105_meta *meta)
{
u8 *buf = skb_mac_header(skb) + ETH_HLEN;
/* UM10944.pdf section 4.2.17 AVB Parameters:
* Structure of the meta-data follow-up frame.
* It is in network byte order, so there are no quirks
* while unpacking the meta frame.
*
* Also SJA1105 E/T only populates bits 23:0 of the timestamp
* whereas P/Q/R/S does 32 bits. Since the structure is the
* same and the E/T puts zeroes in the high-order byte, use
* a unified unpacking command for both device series.
*/
packing(buf, &meta->tstamp, 31, 0, 4, UNPACK, 0);
packing(buf + 4, &meta->dmac_byte_4, 7, 0, 1, UNPACK, 0);
packing(buf + 5, &meta->dmac_byte_3, 7, 0, 1, UNPACK, 0);
packing(buf + 6, &meta->source_port, 7, 0, 1, UNPACK, 0);
packing(buf + 7, &meta->switch_id, 7, 0, 1, UNPACK, 0);
}
static inline bool sja1105_is_meta_frame(const struct sk_buff *skb)
{
const struct ethhdr *hdr = eth_hdr(skb);
u64 smac = ether_addr_to_u64(hdr->h_source);
u64 dmac = ether_addr_to_u64(hdr->h_dest);
if (smac != SJA1105_META_SMAC)
return false;
if (dmac != SJA1105_META_DMAC)
return false;
if (ntohs(hdr->h_proto) != ETH_P_SJA1105_META)
return false;
return true;
}
/* This is the first time the tagger sees the frame on RX.
* Figure out if we can decode it, and if we can, annotate skb->cb with how we
* plan to do that, so we don't need to check again in the rcv function.
* Figure out if we can decode it.
*/
static bool sja1105_filter(const struct sk_buff *skb, struct net_device *dev)
{
if (!dsa_port_is_vlan_filtering(dev->dsa_ptr))
return true;
if (sja1105_is_link_local(skb))
return true;
if (!dsa_port_is_vlan_filtering(dev->dsa_ptr))
if (sja1105_is_meta_frame(skb))
return true;
return false;
}
......@@ -62,25 +110,152 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
((pcp << VLAN_PRIO_SHIFT) | tx_vid));
}
static void sja1105_transfer_meta(struct sk_buff *skb,
const struct sja1105_meta *meta)
{
struct ethhdr *hdr = eth_hdr(skb);
hdr->h_dest[3] = meta->dmac_byte_3;
hdr->h_dest[4] = meta->dmac_byte_4;
SJA1105_SKB_CB(skb)->meta_tstamp = meta->tstamp;
}
/* This is a simple state machine which follows the hardware mechanism of
* generating RX timestamps:
*
* After each timestampable skb (all traffic for which send_meta1 and
* send_meta0 is true, aka all MAC-filtered link-local traffic) a meta frame
* containing a partial timestamp is immediately generated by the switch and
* sent as a follow-up to the link-local frame on the CPU port.
*
* The meta frames have no unique identifier (such as sequence number) by which
* one may pair them to the correct timestampable frame.
* Instead, the switch has internal logic that ensures no frames are sent on
* the CPU port between a link-local timestampable frame and its corresponding
* meta follow-up. It also ensures strict ordering between ports (lower ports
* have higher priority towards the CPU port). For this reason, a per-port
* data structure is not needed/desirable.
*
* This function pairs the link-local frame with its partial timestamp from the
* meta follow-up frame. The full timestamp will be reconstructed later in a
* work queue.
*/
static struct sk_buff
*sja1105_rcv_meta_state_machine(struct sk_buff *skb,
struct sja1105_meta *meta,
bool is_link_local,
bool is_meta)
{
struct sja1105_port *sp;
struct dsa_port *dp;
dp = dsa_slave_to_port(skb->dev);
sp = dp->priv;
/* Step 1: A timestampable frame was received.
* Buffer it until we get its meta frame.
*/
if (is_link_local && sp->data->hwts_rx_en) {
spin_lock(&sp->data->meta_lock);
/* Was this a link-local frame instead of the meta
* that we were expecting?
*/
if (sp->data->stampable_skb) {
dev_err_ratelimited(dp->ds->dev,
"Expected meta frame, is %12llx "
"in the DSA master multicast filter?\n",
SJA1105_META_DMAC);
}
/* Hold a reference to avoid dsa_switch_rcv
* from freeing the skb.
*/
sp->data->stampable_skb = skb_get(skb);
spin_unlock(&sp->data->meta_lock);
/* Tell DSA we got nothing */
return NULL;
/* Step 2: The meta frame arrived.
* Time to take the stampable skb out of the closet, annotate it
* with the partial timestamp, and pretend that we received it
* just now (basically masquerade the buffered frame as the meta
* frame, which serves no further purpose).
*/
} else if (is_meta) {
struct sk_buff *stampable_skb;
spin_lock(&sp->data->meta_lock);
stampable_skb = sp->data->stampable_skb;
sp->data->stampable_skb = NULL;
/* Was this a meta frame instead of the link-local
* that we were expecting?
*/
if (!stampable_skb) {
dev_err_ratelimited(dp->ds->dev,
"Unexpected meta frame\n");
spin_unlock(&sp->data->meta_lock);
return NULL;
}
if (stampable_skb->dev != skb->dev) {
dev_err_ratelimited(dp->ds->dev,
"Meta frame on wrong port\n");
spin_unlock(&sp->data->meta_lock);
return NULL;
}
/* Free the meta frame and give DSA the buffered stampable_skb
* for further processing up the network stack.
*/
kfree_skb(skb);
skb = skb_copy(stampable_skb, GFP_ATOMIC);
if (!skb) {
dev_err_ratelimited(dp->ds->dev,
"Failed to copy stampable skb\n");
return NULL;
}
sja1105_transfer_meta(skb, meta);
/* The cached copy will be freed now */
skb_unref(stampable_skb);
spin_unlock(&sp->data->meta_lock);
}
return skb;
}
static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
struct net_device *netdev,
struct packet_type *pt)
{
struct ethhdr *hdr = eth_hdr(skb);
u64 source_port, switch_id;
struct sk_buff *nskb;
struct sja1105_meta meta = {0};
int source_port, switch_id;
struct vlan_ethhdr *hdr;
u16 tpid, vid, tci;
bool is_link_local;
bool is_tagged;
bool is_meta;
nskb = dsa_8021q_rcv(skb, netdev, pt, &tpid, &tci);
is_tagged = (nskb && tpid == ETH_P_SJA1105);
skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
vid = tci & VLAN_VID_MASK;
hdr = vlan_eth_hdr(skb);
tpid = ntohs(hdr->h_vlan_proto);
is_tagged = (tpid == ETH_P_SJA1105);
is_link_local = sja1105_is_link_local(skb);
is_meta = sja1105_is_meta_frame(skb);
skb->offload_fwd_mark = 1;
if (sja1105_is_link_local(skb)) {
if (is_tagged) {
/* Normal traffic path. */
tci = ntohs(hdr->h_vlan_TCI);
vid = tci & VLAN_VID_MASK;
source_port = dsa_8021q_rx_source_port(vid);
switch_id = dsa_8021q_rx_switch_id(vid);
skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
} else if (is_link_local) {
/* Management traffic path. Switch embeds the switch ID and
* port ID into bytes of the destination MAC, courtesy of
* the incl_srcpt options.
......@@ -90,10 +265,12 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
/* Clear the DMAC bytes that were mangled by the switch */
hdr->h_dest[3] = 0;
hdr->h_dest[4] = 0;
} else if (is_meta) {
sja1105_meta_unpack(skb, &meta);
source_port = meta.source_port;
switch_id = meta.switch_id;
} else {
/* Normal traffic path. */
source_port = dsa_8021q_rx_source_port(vid);
switch_id = dsa_8021q_rx_switch_id(vid);
return NULL;
}
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
......@@ -106,10 +283,10 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
* it there, see dsa_switch_rcv: skb_push(skb, ETH_HLEN).
*/
if (is_tagged)
memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - VLAN_HLEN,
ETH_HLEN - VLAN_HLEN);
skb = dsa_8021q_remove_header(skb);
return skb;
return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local,
is_meta);
}
static struct dsa_device_ops sja1105_netdev_ops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment