2022-11-28 10:50:53

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 00/12] net: dsa: microchip: add PTP support for KSZ9563/KSZ8563 and LAN937x

KSZ9563/KSZ8563 and LAN937x switch are capable for supporting IEEE 1588 PTP
protocol. LAN937x has the same PTP register set similar to KSZ9563, hence the
implementation has been made common for the KSZ switches. KSZ9563 does not
support two step timestamping but LAN937x supports both. Tested the 1step &
2step p2p timestamping in LAN937x and p2p1step timestamping in KSZ9563.

This patch series is based on the Christian Eggers PTP support for KSZ9563.
Applied the Christian patch and updated as per the latest refactoring of KSZ
series code. The features added on top are PTP packet Interrupt
implementation based on nested handler, LAN937x two step timestamping and
programmable per_out pins.

Link: https://www.spinics.net/lists/netdev/msg705531.html

RFC v2 -> Patch v1
- Changed the patch author based on past patch submission
- Changed the commit message prefix as net: dsa: microchip: ptp
Individual patch changes are listed in correspondig commits.

RFC v1 -> v2
- Added the p2p1step timestamping and conditional execution of 2 step for
LAN937x only.
- Added the periodic output support

Arun Ramadoss (4):
net: dsa: microchip: ptp: add 4 bytes in tail tag when ptp enabled
net: dsa: microchip: ptp: enable interrupt for timestamping
net: dsa: microchip: ptp: add 2 step timestamping for LAN937x
net: dsa: microchip: ptp: add support for perout programmable pins

Christian Eggers (8):
net: dsa: microchip: ptp: add the posix clock support
net: dsa: microchip: ptp: Initial hardware time stamping support
net: dsa: microchip: ptp: Manipulating absolute time using ptp hw
clock
net: ptp: add helper for one-step P2P clocks
net: dsa: microchip: ptp: add packet reception timestamping
net: dsa: microchip: ptp: add packet transmission timestamping
net: dsa: microchip: ptp: move pdelay_rsp correction field to tail tag
net: dsa: microchip: ptp: add periodic output signal

MAINTAINERS | 1 +
drivers/net/dsa/microchip/Kconfig | 11 +
drivers/net/dsa/microchip/Makefile | 5 +
drivers/net/dsa/microchip/ksz_common.c | 45 +-
drivers/net/dsa/microchip/ksz_common.h | 46 +
drivers/net/dsa/microchip/ksz_ptp.c | 1132 +++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 101 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 147 +++
include/linux/dsa/ksz_common.h | 55 ++
include/linux/ptp_classify.h | 73 ++
net/dsa/tag_ksz.c | 275 +++++-
11 files changed, 1873 insertions(+), 18 deletions(-)
create mode 100644 drivers/net/dsa/microchip/ksz_ptp.c
create mode 100644 drivers/net/dsa/microchip/ksz_ptp.h
create mode 100644 drivers/net/dsa/microchip/ksz_ptp_reg.h
create mode 100644 include/linux/dsa/ksz_common.h


base-commit: a6e3d86ece0b42a571a11055ace5c3148cb7ce76
--
2.36.1


2022-11-28 10:50:57

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 04/12] net: dsa: microchip: ptp: Manipulating absolute time using ptp hw clock

From: Christian Eggers <[email protected]>

This patch is used for reconstructing the absolute time from the 32bit
hardware time stamping value. The do_aux ioctl is used for reading the
ptp hardware clock and store it to global variable.
The timestamped value in tail tag during rx and register during tx are
32 bit value (2 bit seconds and 30 bit nanoseconds). The time taken to
read entire ptp clock will be time consuming. In order to speed up, the
software clock is maintained. This clock time will be added to 32 bit
timestamp to get the absolute time stamp.

Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
---

RFC v1
- This patch is based on Christian Eggers Initial hardware timestamping
support
---
drivers/net/dsa/microchip/ksz_ptp.c | 58 ++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 3 ++
2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 184aa57a8489..415522ef4ce9 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -22,11 +22,20 @@

static int ksz_ptp_enable_mode(struct ksz_device *dev, bool enable)
{
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
u16 data = 0;
+ int ret;

- if (enable)
+ if (enable) {
data = PTP_ENABLE;

+ ret = ptp_schedule_worker(ptp_data->clock, 0);
+ if (ret)
+ return ret;
+ } else {
+ ptp_cancel_worker_sync(ptp_data->clock);
+ }
+
return ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_ENABLE, data);
}

@@ -200,6 +209,12 @@ static int ksz_ptp_settime(struct ptp_clock_info *ptp,
goto error_return;

ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
+ if (ret)
+ goto error_return;
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = *ts;
+ spin_unlock_bh(&ptp_data->clock_lock);

error_return:
mutex_unlock(&ptp_data->lock);
@@ -254,6 +269,7 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct timespec64 delta64 = ns_to_timespec64(delta);
s32 sec, nsec;
u16 data16;
int ret;
@@ -286,15 +302,51 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
data16 |= PTP_STEP_DIR;

ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16);
+ if (ret)
+ goto error_return;
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64);
+ spin_unlock_bh(&ptp_data->clock_lock);

error_return:
mutex_unlock(&ptp_data->lock);
return ret;
}

+/* Function is pointer to the do_aux_work in the ptp_clock capability */
+static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct timespec64 ts;
+
+ mutex_lock(&ptp_data->lock);
+ _ksz_ptp_gettime(dev, &ts);
+ mutex_unlock(&ptp_data->lock);
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time = ts;
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+ return HZ; /* reschedule in 1 second */
+}
+
static int ksz_ptp_start_clock(struct ksz_device *dev)
{
- return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
+ int ret;
+
+ ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
+ if (ret)
+ return ret;
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_data->clock_time.tv_sec = 0;
+ ptp_data->clock_time.tv_nsec = 0;
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+ return 0;
}

static const struct ptp_clock_info ksz_ptp_caps = {
@@ -305,6 +357,7 @@ static const struct ptp_clock_info ksz_ptp_caps = {
.settime64 = ksz_ptp_settime,
.adjfine = ksz_ptp_adjfine,
.adjtime = ksz_ptp_adjtime,
+ .do_aux_work = ksz_ptp_do_aux_work,
};

int ksz_ptp_clock_register(struct dsa_switch *ds)
@@ -315,6 +368,7 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)

ptp_data = &dev->ptp_data;
mutex_init(&ptp_data->lock);
+ spin_lock_init(&ptp_data->clock_lock);

ptp_data->caps = ksz_ptp_caps;

diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 17f455c3b2c5..81fa2e8b9cf4 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -15,6 +15,9 @@ struct ksz_ptp_data {
struct ptp_clock *clock;
/* Serializes all operations on the PTP hardware clock */
struct mutex lock;
+ /* lock for accessing the clock_time */
+ spinlock_t clock_lock;
+ struct timespec64 clock_time;
};

int ksz_ptp_clock_register(struct dsa_switch *ds);
--
2.36.1

2022-11-28 10:51:05

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 02/12] net: dsa: microchip: ptp: Initial hardware time stamping support

From: Christian Eggers <[email protected]>

This patch adds the routine for get_ts_info, hwstamp_get, set. This enables
the PTP support towards userspace applications such as linuxptp.
Tx timestamping can be enabled per port and Rx timestamping enabled
globally.

Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>

---
RFC v2 -> Patch v1
- moved tagger set and get function to separate patch
- Removed unnecessary comments
---
drivers/net/dsa/microchip/ksz_common.c | 2 +
drivers/net/dsa/microchip/ksz_common.h | 4 ++
drivers/net/dsa/microchip/ksz_ptp.c | 77 +++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 14 +++++
4 files changed, 95 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 2d09cd141db6..7b85b258270c 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2873,6 +2873,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.port_change_mtu = ksz_change_mtu,
.port_max_mtu = ksz_max_mtu,
.get_ts_info = ksz_get_ts_info,
+ .port_hwtstamp_get = ksz_hwtstamp_get,
+ .port_hwtstamp_set = ksz_hwtstamp_set,
};

struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 5a6bfd42c6f9..cd20f39a565f 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -103,6 +103,10 @@ struct ksz_port {
struct ksz_device *ksz_dev;
struct ksz_irq pirq;
u8 num;
+#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
+ u8 hwts_tx_en;
+ bool hwts_rx_en;
+#endif
};

struct ksz_device {
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index c737635ca266..a41418c6adf6 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -36,15 +36,88 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts)
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;

- ts->tx_types = BIT(HWTSTAMP_TX_OFF);
+ ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ONESTEP_P2P);

- ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
+ ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);

ts->phc_index = ptp_clock_index(ptp_data->clock);

return 0;
}

+int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
+{
+ struct ksz_device *dev = ds->priv;
+ struct hwtstamp_config config;
+
+ config.flags = 0;
+
+ config.tx_type = dev->ports[port].hwts_tx_en;
+
+ if (dev->ports[port].hwts_rx_en)
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+ else
+ config.rx_filter = HWTSTAMP_FILTER_NONE;
+
+ return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
+ -EFAULT : 0;
+}
+
+static int ksz_set_hwtstamp_config(struct ksz_device *dev, int port,
+ struct hwtstamp_config *config)
+{
+ struct ksz_port *prt = &dev->ports[port];
+
+ if (config->flags)
+ return -EINVAL;
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ONESTEP_P2P:
+ prt->hwts_tx_en = config->tx_type;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ prt->hwts_rx_en = false;
+ break;
+ default:
+ prt->hwts_rx_en = true;
+ break;
+ }
+
+ return 0;
+}
+
+int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+ struct hwtstamp_config config;
+ int ret;
+
+ ptp_data = &dev->ptp_data;
+
+ mutex_lock(&ptp_data->lock);
+
+ ret = copy_from_user(&config, ifr->ifr_data, sizeof(config));
+ if (ret)
+ goto error_return;
+
+ ret = ksz_set_hwtstamp_config(dev, port, &config);
+ if (ret)
+ goto error_return;
+
+ ret = copy_to_user(ifr->ifr_data, &config, sizeof(config));
+
+error_return:
+ mutex_unlock(&ptp_data->lock);
+ return ret;
+}
+
static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
{
u32 nanoseconds;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index ea9fa46caa01..17f455c3b2c5 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -23,6 +23,8 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds);

int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *ts);
+int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
+int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);

#else

@@ -40,6 +42,18 @@ static inline void ksz_ptp_clock_unregister(struct dsa_switch *ds) { }

#define ksz_get_ts_info NULL

+static inline int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
+ struct ifreq *ifr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct ifreq *ifr)
+{
+ return -EOPNOTSUPP;
+}
+
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */

#endif
--
2.36.1

2022-11-28 10:56:24

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

From: Christian Eggers <[email protected]>

This patch implement routines (adjfine, adjtime, gettime and settime)
for manipulating the chip's PTP clock. It registers the ptp caps
to posix clock register.

Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>

---
RFC v2 -> Patch v1
- Repharsed the Kconfig help text
- Removed IS_ERR_OR_NULL check in ptp_clock_unregister
- Add the check for ptp_data->clock in ksz_ptp_ts_info
- Renamed MAX_DRIFT_CORR to KSZ_MAX_DRIFT_CORR
- Removed the comments
- Variables declaration in reverse christmas tree
- Added the ptp_clock_optional
---
drivers/net/dsa/microchip/Kconfig | 11 +
drivers/net/dsa/microchip/Makefile | 5 +
drivers/net/dsa/microchip/ksz_common.c | 14 +-
drivers/net/dsa/microchip/ksz_common.h | 16 ++
drivers/net/dsa/microchip/ksz_ptp.c | 265 ++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 45 ++++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 57 +++++
7 files changed, 412 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/dsa/microchip/ksz_ptp.c
create mode 100644 drivers/net/dsa/microchip/ksz_ptp.h
create mode 100644 drivers/net/dsa/microchip/ksz_ptp_reg.h

diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig
index 913f83ef013c..0546c573668a 100644
--- a/drivers/net/dsa/microchip/Kconfig
+++ b/drivers/net/dsa/microchip/Kconfig
@@ -11,6 +11,7 @@ menuconfig NET_DSA_MICROCHIP_KSZ_COMMON
config NET_DSA_MICROCHIP_KSZ9477_I2C
tristate "KSZ series I2C connected switch driver"
depends on NET_DSA_MICROCHIP_KSZ_COMMON && I2C
+ depends on PTP_1588_CLOCK_OPTIONAL
select REGMAP_I2C
help
Select to enable support for registering switches configured through I2C.
@@ -18,10 +19,20 @@ config NET_DSA_MICROCHIP_KSZ9477_I2C
config NET_DSA_MICROCHIP_KSZ_SPI
tristate "KSZ series SPI connected switch driver"
depends on NET_DSA_MICROCHIP_KSZ_COMMON && SPI
+ depends on PTP_1588_CLOCK_OPTIONAL
select REGMAP_SPI
help
Select to enable support for registering switches configured through SPI.

+config NET_DSA_MICROCHIP_KSZ_PTP
+ bool "Support for the PTP clock on the KSZ9563/LAN937x Ethernet Switch"
+ depends on NET_DSA_MICROCHIP_KSZ_COMMON && PTP_1588_CLOCK
+ help
+ Select to enable support for timestamping & PTP clock manipulation in
+ KSZ8563/KSZ9563/LAN937x series of switches. KSZ9563/KSZ8563 supports
+ only one step timestamping. LAN937x switch supports both one step and
+ two step timestamping.
+
config NET_DSA_MICROCHIP_KSZ8863_SMI
tristate "KSZ series SMI connected switch driver"
depends on NET_DSA_MICROCHIP_KSZ_COMMON
diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile
index 28873559efc2..48360cc9fc68 100644
--- a/drivers/net/dsa/microchip/Makefile
+++ b/drivers/net/dsa/microchip/Makefile
@@ -4,6 +4,11 @@ ksz_switch-objs := ksz_common.o
ksz_switch-objs += ksz9477.o
ksz_switch-objs += ksz8795.o
ksz_switch-objs += lan937x_main.o
+
+ifdef CONFIG_NET_DSA_MICROCHIP_KSZ_PTP
+ksz_switch-objs += ksz_ptp.o
+endif
+
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o
obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 8c8db315317d..2d09cd141db6 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -24,6 +24,7 @@
#include <net/switchdev.h>

#include "ksz_common.h"
+#include "ksz_ptp.h"
#include "ksz8.h"
#include "ksz9477.h"
#include "lan937x.h"
@@ -2016,10 +2017,16 @@ static int ksz_setup(struct dsa_switch *ds)
}
}

+ ret = ksz_ptp_clock_register(ds);
+ if (ret) {
+ dev_err(dev->dev, "Failed to register PTP clock: %d\n", ret);
+ goto out_pirq;
+ }
+
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
- goto out_pirq;
+ goto out_ptp_clock_unregister;
}

/* start switch */
@@ -2028,6 +2035,8 @@ static int ksz_setup(struct dsa_switch *ds)

return 0;

+out_ptp_clock_unregister:
+ ksz_ptp_clock_unregister(ds);
out_pirq:
if (dev->irq > 0)
dsa_switch_for_each_user_port(dp, dev->ds)
@@ -2044,6 +2053,8 @@ static void ksz_teardown(struct dsa_switch *ds)
struct ksz_device *dev = ds->priv;
struct dsa_port *dp;

+ ksz_ptp_clock_unregister(ds);
+
if (dev->irq > 0) {
dsa_switch_for_each_user_port(dp, dev->ds)
ksz_irq_free(&dev->ports[dp->index].pirq);
@@ -2861,6 +2872,7 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz_change_mtu,
.port_max_mtu = ksz_max_mtu,
+ .get_ts_info = ksz_get_ts_info,
};

struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index c6726cbd5465..5a6bfd42c6f9 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -15,6 +15,8 @@
#include <net/dsa.h>
#include <linux/irq.h>

+#include "ksz_ptp.h"
+
#define KSZ_MAX_NUM_PORTS 8

struct ksz_device;
@@ -141,6 +143,7 @@ struct ksz_device {
u16 port_mask;
struct mutex lock_irq; /* IRQ Access */
struct ksz_irq girq;
+ struct ksz_ptp_data ptp_data;
};

/* List of supported models */
@@ -444,6 +447,19 @@ static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
return ret;
}

+static inline int ksz_rmw16(struct ksz_device *dev, u32 reg, u16 mask,
+ u16 value)
+{
+ int ret;
+
+ ret = regmap_update_bits(dev->regmap[1], reg, mask, value);
+ if (ret)
+ dev_err(dev->dev, "can't rmw 16bit reg: 0x%x %pe\n", reg,
+ ERR_PTR(ret));
+
+ return ret;
+}
+
static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value)
{
u32 val[2];
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
new file mode 100644
index 000000000000..c737635ca266
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Microchip KSZ PTP Implementation
+ * Copyright (C) 2022 Microchip Technology Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "ksz_common.h"
+#include "ksz_ptp.h"
+#include "ksz_ptp_reg.h"
+
+#define ptp_caps_to_data(d) container_of((d), struct ksz_ptp_data, caps)
+#define ptp_data_to_ksz_dev(d) container_of((d), struct ksz_device, ptp_data)
+
+#define KSZ_MAX_DRIFT_CORR 6250000
+
+#define KSZ_PTP_INC_NS 40 /* HW clock is incremented every 40 ns (by 40) */
+#define KSZ_PTP_SUBNS_BITS 32
+
+/* The function is return back the capability of timestamping feature when
+ * requested through ethtool -T <interface> utility
+ */
+int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ if (!ptp_data->clock)
+ return -ENODEV;
+
+ ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ ts->tx_types = BIT(HWTSTAMP_TX_OFF);
+
+ ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
+
+ ts->phc_index = ptp_clock_index(ptp_data->clock);
+
+ return 0;
+}
+
+static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
+{
+ u32 nanoseconds;
+ u32 seconds;
+ u8 phase;
+ int ret;
+
+ /* Copy current PTP clock into shadow registers and read */
+ ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_READ_TIME, PTP_READ_TIME);
+ if (ret)
+ return ret;
+
+ ret = ksz_read8(dev, REG_PTP_RTC_SUB_NANOSEC__2, &phase);
+ if (ret)
+ return ret;
+
+ ret = ksz_read32(dev, REG_PTP_RTC_NANOSEC, &nanoseconds);
+ if (ret)
+ return ret;
+
+ ret = ksz_read32(dev, REG_PTP_RTC_SEC, &seconds);
+ if (ret)
+ return ret;
+
+ ts->tv_sec = seconds;
+ ts->tv_nsec = nanoseconds + phase * 8;
+
+ return 0;
+}
+
+static int ksz_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+ ret = _ksz_ptp_gettime(dev, ts);
+ mutex_unlock(&ptp_data->lock);
+
+ return ret;
+}
+
+static int ksz_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ /* Write to shadow registers and Load PTP clock */
+ ret = ksz_write16(dev, REG_PTP_RTC_SUB_NANOSEC__2, PTP_RTC_0NS);
+ if (ret)
+ goto error_return;
+
+ ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec);
+ if (ret)
+ goto error_return;
+
+ ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec);
+ if (ret)
+ goto error_return;
+
+ ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
+
+error_return:
+ mutex_unlock(&ptp_data->lock);
+
+ return ret;
+}
+
+static int ksz_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ if (scaled_ppm) {
+ s64 ppb, adj;
+ u32 data32;
+
+ /* see scaled_ppm_to_ppb() in ptp_clock.c for details */
+ ppb = 1 + scaled_ppm;
+ ppb *= 125;
+ ppb *= KSZ_PTP_INC_NS;
+ ppb <<= KSZ_PTP_SUBNS_BITS - 13;
+ adj = div_s64(ppb, NSEC_PER_SEC);
+
+ data32 = abs(adj);
+ data32 &= PTP_SUBNANOSEC_M;
+ if (adj >= 0)
+ data32 |= PTP_RATE_DIR;
+
+ ret = ksz_write32(dev, REG_PTP_SUBNANOSEC_RATE, data32);
+ if (ret)
+ goto error_return;
+
+ ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE,
+ PTP_CLK_ADJ_ENABLE);
+ if (ret)
+ goto error_return;
+ } else {
+ ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE, 0);
+ if (ret)
+ goto error_return;
+ }
+
+error_return:
+ mutex_unlock(&ptp_data->lock);
+ return ret;
+}
+
+static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ s32 sec, nsec;
+ u16 data16;
+ int ret;
+
+ mutex_lock(&ptp_data->lock);
+
+ /* do not use ns_to_timespec64(),
+ * both sec and nsec are subtracted by hw
+ */
+ sec = div_s64_rem(delta, NSEC_PER_SEC, &nsec);
+
+ ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, abs(nsec));
+ if (ret)
+ goto error_return;
+
+ ret = ksz_write32(dev, REG_PTP_RTC_SEC, abs(sec));
+ if (ret)
+ goto error_return;
+
+ ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16);
+ if (ret)
+ goto error_return;
+
+ data16 |= PTP_STEP_ADJ;
+
+ /*PTP_STEP_DIR -- 0: subtract, 1: add */
+ if (delta < 0)
+ data16 &= ~PTP_STEP_DIR;
+ else
+ data16 |= PTP_STEP_DIR;
+
+ ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16);
+
+error_return:
+ mutex_unlock(&ptp_data->lock);
+ return ret;
+}
+
+static int ksz_ptp_start_clock(struct ksz_device *dev)
+{
+ return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
+}
+
+static const struct ptp_clock_info ksz_ptp_caps = {
+ .owner = THIS_MODULE,
+ .name = "Microchip Clock",
+ .max_adj = KSZ_MAX_DRIFT_CORR,
+ .gettime64 = ksz_ptp_gettime,
+ .settime64 = ksz_ptp_settime,
+ .adjfine = ksz_ptp_adjfine,
+ .adjtime = ksz_ptp_adjtime,
+};
+
+int ksz_ptp_clock_register(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+ int ret;
+
+ ptp_data = &dev->ptp_data;
+ mutex_init(&ptp_data->lock);
+
+ ptp_data->caps = ksz_ptp_caps;
+
+ ret = ksz_ptp_start_clock(dev);
+ if (ret)
+ return ret;
+
+ ptp_data->clock = ptp_clock_register(&ptp_data->caps, dev->dev);
+ if (IS_ERR_OR_NULL(ptp_data->clock))
+ return PTR_ERR(ptp_data->clock);
+
+ ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_802_1AS, PTP_802_1AS);
+ if (ret)
+ goto error_unregister_clock;
+
+ return 0;
+
+error_unregister_clock:
+ ptp_clock_unregister(ptp_data->clock);
+ return ret;
+}
+
+void ksz_ptp_clock_unregister(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ if (ptp_data->clock)
+ ptp_clock_unregister(ptp_data->clock);
+}
+
+MODULE_AUTHOR("Christian Eggers <[email protected]>");
+MODULE_AUTHOR("Arun Ramadoss <[email protected]>");
+MODULE_DESCRIPTION("PTP support for KSZ switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
new file mode 100644
index 000000000000..ea9fa46caa01
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip KSZ PTP Implementation
+ * Copyright (C) 2022 Microchip Technology Inc.
+ */
+
+#ifndef _NET_DSA_DRIVERS_KSZ_PTP_H
+#define _NET_DSA_DRIVERS_KSZ_PTP_H
+
+#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
+
+#include <linux/ptp_clock_kernel.h>
+
+struct ksz_ptp_data {
+ struct ptp_clock_info caps;
+ struct ptp_clock *clock;
+ /* Serializes all operations on the PTP hardware clock */
+ struct mutex lock;
+};
+
+int ksz_ptp_clock_register(struct dsa_switch *ds);
+
+void ksz_ptp_clock_unregister(struct dsa_switch *ds);
+
+int ksz_get_ts_info(struct dsa_switch *ds, int port,
+ struct ethtool_ts_info *ts);
+
+#else
+
+struct ksz_ptp_data {
+ /* Serializes all operations on the PTP hardware clock */
+ struct mutex lock;
+};
+
+static inline int ksz_ptp_clock_register(struct dsa_switch *ds)
+{
+ return 0;
+}
+
+static inline void ksz_ptp_clock_unregister(struct dsa_switch *ds) { }
+
+#define ksz_get_ts_info NULL
+
+#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
+
+#endif
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
new file mode 100644
index 000000000000..e578a0006ecf
--- /dev/null
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Microchip KSZ PTP register definitions
+ * Copyright (C) 2022 Microchip Technology Inc.
+ */
+
+#ifndef __KSZ_PTP_REGS_H
+#define __KSZ_PTP_REGS_H
+
+/* 5 - PTP Clock */
+#define REG_PTP_CLK_CTRL 0x0500
+
+#define PTP_STEP_ADJ BIT(6)
+#define PTP_STEP_DIR BIT(5)
+#define PTP_READ_TIME BIT(4)
+#define PTP_LOAD_TIME BIT(3)
+#define PTP_CLK_ADJ_ENABLE BIT(2)
+#define PTP_CLK_ENABLE BIT(1)
+#define PTP_CLK_RESET BIT(0)
+
+#define REG_PTP_RTC_SUB_NANOSEC__2 0x0502
+
+#define PTP_RTC_SUB_NANOSEC_M 0x0007
+#define PTP_RTC_0NS 0x00
+
+#define REG_PTP_RTC_NANOSEC 0x0504
+#define REG_PTP_RTC_NANOSEC_H 0x0504
+#define REG_PTP_RTC_NANOSEC_L 0x0506
+
+#define REG_PTP_RTC_SEC 0x0508
+#define REG_PTP_RTC_SEC_H 0x0508
+#define REG_PTP_RTC_SEC_L 0x050A
+
+#define REG_PTP_SUBNANOSEC_RATE 0x050C
+#define REG_PTP_SUBNANOSEC_RATE_H 0x050C
+#define PTP_SUBNANOSEC_M 0x3FFFFFFF
+
+#define PTP_RATE_DIR BIT(31)
+#define PTP_TMP_RATE_ENABLE BIT(30)
+
+#define REG_PTP_SUBNANOSEC_RATE_L 0x050E
+
+#define REG_PTP_RATE_DURATION 0x0510
+#define REG_PTP_RATE_DURATION_H 0x0510
+#define REG_PTP_RATE_DURATION_L 0x0512
+
+#define REG_PTP_MSG_CONF1 0x0514
+
+#define PTP_802_1AS BIT(7)
+#define PTP_ENABLE BIT(6)
+#define PTP_ETH_ENABLE BIT(5)
+#define PTP_IPV4_UDP_ENABLE BIT(4)
+#define PTP_IPV6_UDP_ENABLE BIT(3)
+#define PTP_TC_P2P BIT(2)
+#define PTP_MASTER BIT(1)
+#define PTP_1STEP BIT(0)
+
+#endif
--
2.36.1

2022-11-28 10:57:04

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 07/12] net: dsa: microchip: ptp: add packet reception timestamping

From: Christian Eggers <[email protected]>

This patch adds the routines for timestamping received ptp packets.
Whenever the ptp packet is received, the 4 byte hardware time stamped
value is append to its packet. This 4 byte value is extracted from the
tail tag and reconstructed to absolute time and assigned to skb
hwtstamp.

Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>

---
RFC v2 -> Patch v1
- Fixed compilation issue
---
drivers/net/dsa/microchip/ksz_common.c | 13 +++++
drivers/net/dsa/microchip/ksz_ptp.c | 28 +++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 6 +++
include/linux/dsa/ksz_common.h | 15 ++++++
net/dsa/tag_ksz.c | 68 +++++++++++++++++++++++---
5 files changed, 124 insertions(+), 6 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 9bfd7dd5cd31..306bdc1469d2 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -5,6 +5,7 @@
* Copyright (C) 2017-2019 Microchip Technology Inc.
*/

+#include <linux/dsa/ksz_common.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
@@ -2453,6 +2454,17 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
return proto;
}

+static int ksz_connect_tag_protocol(struct dsa_switch *ds,
+ enum dsa_tag_protocol proto)
+{
+ struct ksz_tagger_data *tagger_data;
+
+ tagger_data = ksz_tagger_data(ds);
+ tagger_data->meta_tstamp_handler = ksz_tstamp_reconstruct;
+
+ return 0;
+}
+
static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port,
bool flag, struct netlink_ext_ack *extack)
{
@@ -2849,6 +2861,7 @@ static int ksz_switch_detect(struct ksz_device *dev)

static const struct dsa_switch_ops ksz_switch_ops = {
.get_tag_protocol = ksz_get_tag_protocol,
+ .connect_tag_protocol = ksz_connect_tag_protocol,
.get_phy_flags = ksz_get_phy_flags,
.setup = ksz_setup,
.teardown = ksz_teardown,
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 515a40c9f107..d878e922c275 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -353,6 +353,34 @@ static int ksz_ptp_start_clock(struct ksz_device *dev)
return 0;
}

+ktime_t ksz_tstamp_reconstruct(struct dsa_switch *ds, ktime_t tstamp)
+{
+ struct ksz_device *dev = ds->priv;
+ struct timespec64 ptp_clock_time;
+ struct ksz_ptp_data *ptp_data;
+ struct timespec64 diff;
+ struct timespec64 ts;
+
+ ptp_data = &dev->ptp_data;
+ ts = ktime_to_timespec64(tstamp);
+
+ spin_lock_bh(&ptp_data->clock_lock);
+ ptp_clock_time = ptp_data->clock_time;
+ spin_unlock_bh(&ptp_data->clock_lock);
+
+ /* calculate full time from partial time stamp */
+ ts.tv_sec = (ptp_clock_time.tv_sec & ~3) | ts.tv_sec;
+
+ /* find nearest possible point in time */
+ diff = timespec64_sub(ts, ptp_clock_time);
+ if (diff.tv_sec > 2)
+ ts.tv_sec -= 4;
+ else if (diff.tv_sec < -2)
+ ts.tv_sec += 4;
+
+ return timespec64_to_ktime(ts);
+}
+
static const struct ptp_clock_info ksz_ptp_caps = {
.owner = THIS_MODULE,
.name = "Microchip Clock",
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 5cec8f2e8420..13c06c24b488 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -30,6 +30,7 @@ int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p);
+ktime_t ksz_tstamp_reconstruct(struct dsa_switch *ds, ktime_t tstamp);

#else

@@ -66,6 +67,11 @@ static inline int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)

static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}

+static inline ktime_t ksz_tstamp_reconstruct(struct dsa_switch *ds, ktime_t tstamp)
+{
+ return 0;
+}
+
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */

#endif
diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h
index 62996860b887..0be0cd903727 100644
--- a/include/linux/dsa/ksz_common.h
+++ b/include/linux/dsa/ksz_common.h
@@ -9,9 +9,24 @@

#include <net/dsa.h>

+/* All time stamps from the KSZ consist of 2 bits for seconds and 30 bits for
+ * nanoseconds. This is NOT the same as 32 bits for nanoseconds.
+ */
+#define KSZ_TSTAMP_SEC_MASK GENMASK(31, 30)
+#define KSZ_TSTAMP_NSEC_MASK GENMASK(29, 0)
+
+static inline ktime_t ksz_decode_tstamp(u32 tstamp)
+{
+ u64 ns = FIELD_GET(KSZ_TSTAMP_SEC_MASK, tstamp) * NSEC_PER_SEC +
+ FIELD_GET(KSZ_TSTAMP_NSEC_MASK, tstamp);
+
+ return ns_to_ktime(ns);
+}
+
struct ksz_tagger_data {
bool (*hwtstamp_get_state)(struct dsa_switch *ds);
void (*hwtstamp_set_state)(struct dsa_switch *ds, bool on);
+ ktime_t (*meta_tstamp_handler)(struct dsa_switch *ds, ktime_t tstamp);
};

static inline struct ksz_tagger_data *
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 828af38f0598..41ce5ff5ed41 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -7,6 +7,7 @@
#include <linux/dsa/ksz_common.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
+#include <linux/ptp_classify.h>
#include <net/dsa.h>

#include "tag.h"
@@ -157,10 +158,11 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
* tag0 : Prioritization (not used now)
* tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
*
- * For Egress (KSZ9477 -> Host), 1 byte is added before FCS.
+ * For Egress (KSZ9477 -> Host), 1/5 bytes is added before FCS.
* ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
+ * ts : time stamp (Present only if bit 7 of tag0 is set)
* tag0 : zero-based value represents port
* (eg, 0x00=port1, 0x02=port3, 0x06=port7)
*/
@@ -172,6 +174,57 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
#define KSZ9477_TAIL_TAG_OVERRIDE BIT(9)
#define KSZ9477_TAIL_TAG_LOOKUP BIT(10)

+static void ksz_rcv_timestamp(struct sk_buff *skb, u8 *tag,
+ struct net_device *dev, unsigned int port)
+{
+ struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
+ struct dsa_switch *ds = dev->dsa_ptr->ds;
+ u8 *tstamp_raw = tag - KSZ_PTP_TAG_LEN;
+ struct ksz_tagger_data *tagger_data;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_type;
+ u8 ptp_msg_type;
+ ktime_t tstamp;
+ s64 correction;
+
+ tagger_data = ksz_tagger_data(ds);
+ if (!tagger_data->meta_tstamp_handler)
+ return;
+
+ /* convert time stamp and write to skb */
+ tstamp = ksz_decode_tstamp(get_unaligned_be32(tstamp_raw));
+ memset(hwtstamps, 0, sizeof(*hwtstamps));
+ hwtstamps->hwtstamp = tagger_data->meta_tstamp_handler(ds, tstamp);
+
+ if (skb_headroom(skb) < ETH_HLEN)
+ return;
+
+ __skb_push(skb, ETH_HLEN);
+ ptp_type = ptp_classify_raw(skb);
+ __skb_pull(skb, ETH_HLEN);
+
+ if (ptp_type == PTP_CLASS_NONE)
+ return;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_type);
+ if (!ptp_hdr)
+ return;
+
+ ptp_msg_type = ptp_get_msgtype(ptp_hdr, ptp_type);
+ if (ptp_msg_type != PTP_MSGTYPE_PDELAY_REQ)
+ return;
+
+ /* Only subtract the partial time stamp from the correction field. When
+ * the hardware adds the egress time stamp to the correction field of
+ * the PDelay_Resp message on tx, also only the partial time stamp will
+ * be added.
+ */
+ correction = (s64)get_unaligned_be64(&ptp_hdr->correction);
+ correction -= ktime_to_ns(tstamp) << 16;
+
+ ptp_header_update_correction(skb, ptp_type, ptp_hdr, correction);
+}
+
/* Time stamp tag is only inserted if PTP is enabled in hardware. */
static void ksz_xmit_timestamp(struct dsa_port *dp, struct sk_buff *skb)
{
@@ -220,8 +273,10 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev)
unsigned int len = KSZ_EGRESS_TAG_LEN;

/* Extra 4-bytes PTP timestamp */
- if (tag[0] & KSZ9477_PTP_TAG_INDICATION)
- len += KSZ9477_PTP_TAG_LEN;
+ if (tag[0] & KSZ9477_PTP_TAG_INDICATION) {
+ ksz_rcv_timestamp(skb, tag, dev, port);
+ len += KSZ_PTP_TAG_LEN;
+ }

return ksz_common_rcv(skb, dev, port, len);
}
@@ -287,10 +342,11 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME);
* tag0 : represents tag override, lookup and valid
* tag1 : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x80=port8)
*
- * For rcv, 1 byte is added before FCS.
+ * For rcv, 1/5 bytes is added before FCS.
* ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|FCS(4bytes)
* ---------------------------------------------------------------------------
+ * ts : time stamp (Present only if bit 7 of tag0 is set)
* tag0 : zero-based value represents port
* (eg, 0x00=port1, 0x02=port3, 0x07=port8)
*/
--
2.36.1

2022-11-28 11:19:25

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 05/12] net: dsa: microchip: ptp: enable interrupt for timestamping

PTP Interrupt mask and status register differ from the global and port
interrupt mechanism by two methods. One is that for global/port
interrupt enabling we have to clear the bit but for ptp interrupt we
have to set the bit. And other is bit12:0 is reserver in ptp interrupt
registers. This forced to not use the generic implementation of
global/port interrupt method routine. This patch implement the ptp
interrupt mechanism to read the timestamp register for sync, pdelay_req
and pdelay_resp.

Signed-off-by: Arun Ramadoss <[email protected]>

---
RFC v2 -> Patch v1
- Moved the acking of interrupts before calling the handle_nested_irq to
avoid race condition between deferred xmit and Irq threads
---
drivers/net/dsa/microchip/ksz_common.c | 15 +-
drivers/net/dsa/microchip/ksz_common.h | 11 ++
drivers/net/dsa/microchip/ksz_ptp.c | 200 ++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 9 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 19 +++
5 files changed, 252 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 7b85b258270c..9bfd7dd5cd31 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2014,13 +2014,17 @@ static int ksz_setup(struct dsa_switch *ds)
ret = ksz_pirq_setup(dev, dp->index);
if (ret)
goto out_girq;
+
+ ret = ksz_ptp_irq_setup(ds, dp->index);
+ if (ret)
+ goto out_pirq;
}
}

ret = ksz_ptp_clock_register(ds);
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n", ret);
- goto out_pirq;
+ goto out_ptpirq;
}

ret = ksz_mdio_register(dev);
@@ -2037,6 +2041,10 @@ static int ksz_setup(struct dsa_switch *ds)

out_ptp_clock_unregister:
ksz_ptp_clock_unregister(ds);
+out_ptpirq:
+ if (dev->irq > 0)
+ dsa_switch_for_each_user_port(dp, dev->ds)
+ ksz_ptp_irq_free(ds, dp->index);
out_pirq:
if (dev->irq > 0)
dsa_switch_for_each_user_port(dp, dev->ds)
@@ -2056,8 +2064,11 @@ static void ksz_teardown(struct dsa_switch *ds)
ksz_ptp_clock_unregister(ds);

if (dev->irq > 0) {
- dsa_switch_for_each_user_port(dp, dev->ds)
+ dsa_switch_for_each_user_port(dp, dev->ds) {
+ ksz_ptp_irq_free(ds, dp->index);
+
ksz_irq_free(&dev->ports[dp->index].pirq);
+ }

ksz_irq_free(&dev->girq);
}
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 4c5b35a7883c..ffb9495e1bec 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -20,6 +20,7 @@
#define KSZ_MAX_NUM_PORTS 8

struct ksz_device;
+struct ksz_port;

struct vlan_table {
u32 table[3];
@@ -83,6 +84,13 @@ struct ksz_irq {
struct ksz_device *dev;
};

+struct ksz_ptp_irq {
+ struct ksz_port *port;
+ u16 ts_reg;
+ char name[16];
+ int irq_num;
+};
+
struct ksz_port {
bool remove_tag; /* Remove Tag flag set, for ksz8795 only */
bool learning;
@@ -105,6 +113,8 @@ struct ksz_port {
u8 num;
#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
u8 hwts_tx_en;
+ struct ksz_irq ptpirq;
+ struct ksz_ptp_irq ptpmsg_irq[3];
#endif
};

@@ -606,6 +616,7 @@ static inline int is_lan937x(struct ksz_device *dev)
#define REG_PORT_INT_MASK 0x001F

#define PORT_SRC_PHY_INT 1
+#define PORT_SRC_PTP_INT 2

/* Regmap tables generation */
#define KSZ_SPI_OP_RD 3
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 415522ef4ce9..515a40c9f107 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -4,6 +4,8 @@
*/

#include <linux/dsa/ksz_common.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
@@ -20,6 +22,8 @@
#define KSZ_PTP_INC_NS 40 /* HW clock is incremented every 40 ns (by 40) */
#define KSZ_PTP_SUBNS_BITS 32

+#define KSZ_PTP_INT_START 13
+
static int ksz_ptp_enable_mode(struct ksz_device *dev, bool enable)
{
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
@@ -402,6 +406,202 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds)
ptp_clock_unregister(ptp_data->clock);
}

+static irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id)
+{
+ return IRQ_NONE;
+}
+
+static irqreturn_t ksz_ptp_irq_thread_fn(int irq, void *dev_id)
+{
+ struct ksz_irq *ptpirq = dev_id;
+ unsigned int nhandled = 0;
+ struct ksz_device *dev;
+ unsigned int sub_irq;
+ u16 data;
+ int ret;
+ u8 n;
+
+ dev = ptpirq->dev;
+
+ ret = ksz_read16(dev, ptpirq->reg_status, &data);
+ if (ret)
+ goto out;
+
+ /* Clear the interrupts W1C */
+ ret = ksz_write16(dev, ptpirq->reg_status, data);
+ if (ret)
+ return IRQ_NONE;
+
+ for (n = 0; n < ptpirq->nirqs; ++n) {
+ if (data & BIT(n + KSZ_PTP_INT_START)) {
+ sub_irq = irq_find_mapping(ptpirq->domain, n);
+ handle_nested_irq(sub_irq);
+ ++nhandled;
+ }
+ }
+
+out:
+ return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
+}
+
+static void ksz_ptp_irq_mask(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ kirq->masked &= ~BIT(d->hwirq + KSZ_PTP_INT_START);
+}
+
+static void ksz_ptp_irq_unmask(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ kirq->masked |= BIT(d->hwirq + KSZ_PTP_INT_START);
+}
+
+static void ksz_ptp_irq_bus_lock(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ mutex_lock(&kirq->dev->lock_irq);
+}
+
+static void ksz_ptp_irq_bus_sync_unlock(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+ struct ksz_device *dev = kirq->dev;
+ int ret;
+
+ ret = ksz_write16(dev, kirq->reg_mask, kirq->masked);
+ if (ret)
+ dev_err(dev->dev, "failed to change IRQ mask\n");
+
+ mutex_unlock(&dev->lock_irq);
+}
+
+static const struct irq_chip ksz_ptp_irq_chip = {
+ .name = "ksz-irq",
+ .irq_mask = ksz_ptp_irq_mask,
+ .irq_unmask = ksz_ptp_irq_unmask,
+ .irq_bus_lock = ksz_ptp_irq_bus_lock,
+ .irq_bus_sync_unlock = ksz_ptp_irq_bus_sync_unlock,
+};
+
+static int ksz_ptp_irq_domain_map(struct irq_domain *d,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &ksz_ptp_irq_chip, handle_level_irq);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops ksz_ptp_irq_domain_ops = {
+ .map = ksz_ptp_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int ksz_ptp_msg_irq_setup(struct ksz_port *port)
+{
+ u16 ts_reg[] = {REG_PTP_PORT_PDRESP_TS, REG_PTP_PORT_XDELAY_TS,
+ REG_PTP_PORT_SYNC_TS};
+ struct ksz_device *dev = port->ksz_dev;
+ struct ksz_irq *ptpirq = &port->ptpirq;
+ struct ksz_ptp_irq *ptpmsg_irq;
+ int ret;
+ u8 n;
+
+ for (n = 0; n < ptpirq->nirqs; n++) {
+ ptpmsg_irq = &port->ptpmsg_irq[n];
+
+ ptpmsg_irq->port = port;
+ ptpmsg_irq->ts_reg = dev->dev_ops->get_port_addr(port->num,
+ ts_reg[n]);
+ ptpmsg_irq->irq_num = irq_create_mapping(ptpirq->domain, n);
+ if (ptpmsg_irq->irq_num < 0) {
+ ret = ptpmsg_irq->irq_num;
+ goto out;
+ }
+
+ snprintf(ptpmsg_irq->name, sizeof(ptpmsg_irq->name),
+ "PTP-MSG-%d", n);
+
+ ret = request_threaded_irq(ptpmsg_irq->irq_num, NULL,
+ ksz_ptp_msg_thread_fn,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ ptpmsg_irq->name, ptpmsg_irq);
+ if (ret)
+ goto out;
+ }
+
+ return 0;
+
+out:
+ while (n--)
+ irq_dispose_mapping(port->ptpmsg_irq[n].irq_num);
+
+ return ret;
+}
+
+int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
+{
+ struct ksz_device *dev = ds->priv;
+ const struct ksz_dev_ops *ops = dev->dev_ops;
+ struct ksz_port *port = &dev->ports[p];
+ struct ksz_irq *ptpirq = &port->ptpirq;
+ int ret;
+
+ ptpirq->dev = dev;
+ ptpirq->masked = 0;
+ ptpirq->nirqs = 3;
+ ptpirq->reg_mask = ops->get_port_addr(p, REG_PTP_PORT_TX_INT_ENABLE__2);
+ ptpirq->reg_status = ops->get_port_addr(p,
+ REG_PTP_PORT_TX_INT_STATUS__2);
+ snprintf(ptpirq->name, sizeof(ptpirq->name), "ptp_irq-%d", p);
+
+ ptpirq->irq_num = irq_find_mapping(port->pirq.domain, PORT_SRC_PTP_INT);
+ if (ptpirq->irq_num < 0)
+ return ptpirq->irq_num;
+
+ ptpirq->domain = irq_domain_add_simple(dev->dev->of_node, ptpirq->nirqs,
+ 0, &ksz_ptp_irq_domain_ops,
+ ptpirq);
+ if (!ptpirq->domain)
+ return -ENOMEM;
+
+ ret = request_threaded_irq(ptpirq->irq_num, NULL, ksz_ptp_irq_thread_fn,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ ptpirq->name, ptpirq);
+ if (ret)
+ goto out;
+
+ ret = ksz_ptp_msg_irq_setup(port);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ irq_dispose_mapping(ptpirq->irq_num);
+
+ return ret;
+}
+
+void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *port = &dev->ports[p];
+ struct ksz_irq *ptpirq = &port->ptpirq;
+ u8 n;
+
+ free_irq(ptpirq->irq_num, ptpirq);
+
+ for (n = 0; n < ptpirq->nirqs; n++)
+ irq_dispose_mapping(port->ptpmsg_irq[n].irq_num);
+
+ irq_domain_remove(ptpirq->domain);
+}
+
MODULE_AUTHOR("Christian Eggers <[email protected]>");
MODULE_AUTHOR("Arun Ramadoss <[email protected]>");
MODULE_DESCRIPTION("PTP support for KSZ switch");
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 81fa2e8b9cf4..5cec8f2e8420 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -28,6 +28,8 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
+int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
+void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p);

#else

@@ -57,6 +59,13 @@ static inline int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
return -EOPNOTSUPP;
}

+static inline int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
+{
+ return 0;
+}
+
+static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
+
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */

#endif
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index e578a0006ecf..0c5a1193e1a1 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -54,4 +54,23 @@
#define PTP_MASTER BIT(1)
#define PTP_1STEP BIT(0)

+/* Port PTP Register */
+#define REG_PTP_PORT_RX_DELAY__2 0x0C00
+#define REG_PTP_PORT_TX_DELAY__2 0x0C02
+#define REG_PTP_PORT_ASYM_DELAY__2 0x0C04
+
+#define REG_PTP_PORT_XDELAY_TS 0x0C08
+#define REG_PTP_PORT_SYNC_TS 0x0C0C
+#define REG_PTP_PORT_PDRESP_TS 0x0C10
+
+#define REG_PTP_PORT_TX_INT_STATUS__2 0x0C14
+#define REG_PTP_PORT_TX_INT_ENABLE__2 0x0C16
+
+#define PTP_PORT_SYNC_INT BIT(15)
+#define PTP_PORT_XDELAY_REQ_INT BIT(14)
+#define PTP_PORT_PDELAY_RESP_INT BIT(13)
+#define KSZ_SYNC_MSG 2
+#define KSZ_XDREQ_MSG 1
+#define KSZ_PDRES_MSG 0
+
#endif
--
2.36.1

2022-11-28 11:19:47

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 09/12] net: dsa: microchip: ptp: move pdelay_rsp correction field to tail tag

From: Christian Eggers <[email protected]>

For PDelay_Resp messages we will likely have a negative value in the
correction field. The switch hardware cannot correctly update such
values (produces an off by one error in the UDP checksum), so it must be
moved to the time stamp field in the tail tag. Format of the correction
field is 48 bit ns + 16 bit fractional ns. After updating the
correction field, clone is no longer required hence it is freed.

Signed-off-by: Christian Eggers <[email protected]>
---
RFC v3 -> Patch v1
- Patch is separated from transmission logic patch
---
drivers/net/dsa/microchip/ksz_ptp.c | 3 +++
include/linux/dsa/ksz_common.h | 2 ++
net/dsa/tag_ksz.c | 42 ++++++++++++++++++++++++++++-
3 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 5eb5aca92556..f0b7fcca045b 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -182,6 +182,7 @@ void ksz_port_txtstamp(struct dsa_switch *ds, int port,

switch (ptp_msg_type) {
case PTP_MSGTYPE_PDELAY_REQ:
+ case PTP_MSGTYPE_PDELAY_RESP:
break;

default:
@@ -194,6 +195,8 @@ void ksz_port_txtstamp(struct dsa_switch *ds, int port,

/* caching the value to be used in later */
KSZ_SKB_CB(skb)->clone = clone;
+ KSZ_SKB_CB(clone)->ptp_type = type;
+ KSZ_SKB_CB(clone)->ptp_msg_type = ptp_msg_type;
}

static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
diff --git a/include/linux/dsa/ksz_common.h b/include/linux/dsa/ksz_common.h
index 5f0e0c614e94..370ce9b56902 100644
--- a/include/linux/dsa/ksz_common.h
+++ b/include/linux/dsa/ksz_common.h
@@ -38,6 +38,8 @@ struct ksz_tagger_data {

struct ksz_skb_cb {
struct sk_buff *clone;
+ unsigned int ptp_type;
+ u8 ptp_msg_type;
};

#define KSZ_SKB_CB(skb) \
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 6ed94dc0f18e..2a08e48f41f8 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -241,14 +241,54 @@ static void ksz_rcv_timestamp(struct sk_buff *skb, u8 *tag,
/* Time stamp tag is only inserted if PTP is enabled in hardware. */
static void ksz_xmit_timestamp(struct dsa_port *dp, struct sk_buff *skb)
{
+ struct sk_buff *clone = KSZ_SKB_CB(skb)->clone;
struct ksz_tagger_private *priv;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_type;
+ u32 tstamp_raw = 0;
+ u8 ptp_msg_type;
+ s64 correction;

priv = ksz_tagger_private(dp->ds);

if (!test_bit(KSZ_HWTS_EN, &priv->state))
return;

- put_unaligned_be32(0, skb_put(skb, KSZ_PTP_TAG_LEN));
+ if (!clone)
+ goto output_tag;
+
+ ptp_type = KSZ_SKB_CB(clone)->ptp_type;
+ if (ptp_type == PTP_CLASS_NONE)
+ goto output_tag;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_type);
+ if (!ptp_hdr)
+ goto output_tag;
+
+ ptp_msg_type = KSZ_SKB_CB(clone)->ptp_msg_type;
+ if (ptp_msg_type != PTP_MSGTYPE_PDELAY_RESP)
+ goto output_tag;
+
+ correction = (s64)get_unaligned_be64(&ptp_hdr->correction);
+
+ if (correction < 0) {
+ struct timespec64 ts;
+
+ ts = ns_to_timespec64(-correction >> 16);
+ tstamp_raw = ((ts.tv_sec & 3) << 30) | ts.tv_nsec;
+
+ /* Set correction field to 0 and update UDP checksum. */
+ ptp_header_update_correction(skb, ptp_type, ptp_hdr, 0);
+ }
+
+ /* For PDelay_Resp messages, the clone is not required in
+ * skb_complete_tx_timestamp() and should be freed here.
+ */
+ kfree_skb(clone);
+ KSZ_SKB_CB(skb)->clone = NULL;
+
+output_tag:
+ put_unaligned_be32(tstamp_raw, skb_put(skb, KSZ_PTP_TAG_LEN));
}

/* Defer transmit if waiting for egress time stamp is required. */
--
2.36.1

2022-11-28 11:22:13

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 11/12] net: dsa: microchip: ptp: add periodic output signal

From: Christian Eggers <[email protected]>

LAN937x and KSZ PTP supported switches has Three Trigger output unit.
This TOU can used to generate the periodic signal for PTP. TOU has the
cycle width register of 32 bit in size and period width register of 24
bit, each value is of 8ns so the pulse width can be maximum 125ms.

Tested using ./testptp -d /dev/ptp0 -p 1000000000 -w 100000000 for
generating the 10ms pulse width

Signed-off-by: Christian Eggers <[email protected]>
Co-developed-by: Arun Ramadoss <[email protected]>
Signed-off-by: Arun Ramadoss <[email protected]>
---
drivers/net/dsa/microchip/ksz_common.h | 13 +
drivers/net/dsa/microchip/ksz_ptp.c | 317 ++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 8 +
drivers/net/dsa/microchip/ksz_ptp_reg.h | 71 ++++++
4 files changed, 409 insertions(+)

diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index dbde70a8990a..a5f5ba489186 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -476,6 +476,19 @@ static inline int ksz_rmw16(struct ksz_device *dev, u32 reg, u16 mask,
return ret;
}

+static inline int ksz_rmw32(struct ksz_device *dev, u32 reg, u32 mask,
+ u32 value)
+{
+ int ret;
+
+ ret = regmap_update_bits(dev->regmap[2], reg, mask, value);
+ if (ret)
+ dev_err(dev->dev, "can't rmw 32bit reg: 0x%x %pe\n", reg,
+ ERR_PTR(ret));
+
+ return ret;
+}
+
static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value)
{
u32 val[2];
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 79ed31fd1398..15b863c85cb1 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -24,6 +24,269 @@

#define KSZ_PTP_INT_START 13

+static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts);
+
+static int ksz_ptp_tou_gpio(struct ksz_device *dev)
+{
+ int ret;
+
+ ret = ksz_rmw32(dev, REG_SW_GLOBAL_LED_OVR__4, LED_OVR_1, LED_OVR_1);
+ if (ret)
+ return ret;
+
+ return ksz_rmw32(dev, REG_SW_GLOBAL_LED_SRC__4,
+ LED_SRC_PTP_GPIO_1, LED_SRC_PTP_GPIO_1);
+}
+
+static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
+{
+ u32 data;
+ int ret;
+
+ /* Reset trigger unit (clears TRIGGER_EN, but not GPIOSTATx) */
+ ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, TRIG_RESET, TRIG_RESET);
+
+ data = FIELD_PREP(TRIG_DONE_M, BIT(unit));
+ ret = ksz_write32(dev, REG_PTP_TRIG_STATUS__4, data);
+ if (ret)
+ return ret;
+
+ data = FIELD_PREP(TRIG_INT_M, BIT(unit));
+ ret = ksz_write32(dev, REG_PTP_INT_STATUS__4, data);
+ if (ret)
+ return ret;
+
+ /* Clear reset and set GPIO direction */
+ return ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, (TRIG_RESET | TRIG_ENABLE),
+ 0);
+}
+
+static int ksz_ptp_tou_pulse_verify(u64 pulse_ns)
+{
+ u32 data;
+
+ if (pulse_ns & 0x3)
+ return -EINVAL;
+
+ data = (pulse_ns / 8);
+ if (!FIELD_FIT(TRIG_PULSE_WIDTH_M, data))
+ return -ERANGE;
+
+ return 0;
+}
+
+static int ksz_ptp_tou_target_time_set(struct ksz_device *dev,
+ struct timespec64 const *ts)
+{
+ int ret;
+
+ /* Hardware has only 32 bit */
+ if ((ts->tv_sec & 0xffffffff) != ts->tv_sec)
+ return -EINVAL;
+
+ ret = ksz_write32(dev, REG_TRIG_TARGET_NANOSEC, ts->tv_nsec);
+ if (ret)
+ return ret;
+
+ ret = ksz_write32(dev, REG_TRIG_TARGET_SEC, ts->tv_sec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit)
+{
+ u32 data;
+ int ret;
+
+ ret = ksz_rmw32(dev, REG_PTP_CTRL_STAT__4, TRIG_ENABLE | GPIO_OUT,
+ TRIG_ENABLE | GPIO_OUT);
+ if (ret)
+ return ret;
+
+ /* Check error flag:
+ * - the ACTIVE flag is NOT cleared an error!
+ */
+ ret = ksz_read32(dev, REG_PTP_TRIG_STATUS__4, &data);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(TRIG_ERROR_M, data) & (1 << unit)) {
+ dev_err(dev->dev, "%s: Trigger unit%d error!\n", __func__,
+ unit);
+ ret = -EIO;
+ /* Unit will be reset on next access */
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ksz_ptp_configure_perout(struct ksz_device *dev,
+ u32 cycle_width_ns, u32 pulse_width_ns,
+ struct timespec64 const *target_time,
+ u8 index)
+{
+ u32 data;
+ int ret;
+
+ data = FIELD_PREP(TRIG_NOTIFY, 1) |
+ FIELD_PREP(TRIG_GPO_M, index) |
+ FIELD_PREP(TRIG_PATTERN_M, TRIG_POS_PERIOD);
+ ret = ksz_write32(dev, REG_TRIG_CTRL__4, data);
+ if (ret)
+ return ret;
+
+ ret = ksz_write32(dev, REG_TRIG_CYCLE_WIDTH, cycle_width_ns);
+ if (ret)
+ return ret;
+
+ /* Set cycle count 0 - Infinite */
+ ret = ksz_rmw32(dev, REG_TRIG_CYCLE_CNT, TRIG_CYCLE_CNT_M, 0);
+ if (ret)
+ return ret;
+
+ data = (pulse_width_ns / 8);
+ ret = ksz_write32(dev, REG_TRIG_PULSE_WIDTH__4, data);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_tou_target_time_set(dev, target_time);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#define KSZ_PEROUT_VALID_FLAGS ( \
+ PTP_PEROUT_DUTY_CYCLE \
+ )
+
+static int ksz_ptp_enable_perout(struct ksz_device *dev,
+ struct ptp_perout_request const *request,
+ int on)
+{
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
+ u64 cycle_width_ns;
+ u64 pulse_width_ns;
+ int pin = 0;
+ u32 data32;
+ int ret;
+
+ if (request->flags & ~KSZ_PEROUT_VALID_FLAGS)
+ return -EINVAL;
+
+ if (ptp_data->tou_mode != KSZ_PTP_TOU_PEROUT &&
+ ptp_data->tou_mode != KSZ_PTP_TOU_IDLE)
+ return -EBUSY;
+
+ data32 = FIELD_PREP(PTP_GPIO_INDEX, pin) |
+ FIELD_PREP(PTP_TOU_INDEX, request->index);
+ ret = ksz_rmw32(dev, REG_PTP_UNIT_INDEX__4,
+ PTP_GPIO_INDEX | PTP_TOU_INDEX, data32);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_tou_reset(dev, request->index);
+ if (ret)
+ return ret;
+
+ if (!on) {
+ ptp_data->tou_mode = KSZ_PTP_TOU_IDLE;
+ return 0;
+ }
+
+ ptp_data->perout_target_time_first.tv_sec = request->start.sec;
+ ptp_data->perout_target_time_first.tv_nsec = request->start.nsec;
+
+ ptp_data->perout_period.tv_sec = request->period.sec;
+ ptp_data->perout_period.tv_nsec = request->period.nsec;
+
+ cycle_width_ns = timespec64_to_ns(&ptp_data->perout_period);
+ if ((cycle_width_ns & TRIG_CYCLE_WIDTH_M) != cycle_width_ns)
+ return -EINVAL;
+
+ if (request->flags & PTP_PEROUT_DUTY_CYCLE) {
+ pulse_width_ns = request->on.sec * NSEC_PER_SEC +
+ request->on.nsec;
+ } else {
+ /* Use a duty cycle of 50%. Maximum pulse width supported by the
+ * hardware is a little bit more than 125 ms.
+ */
+ pulse_width_ns = min_t(u64,
+ (request->period.sec * NSEC_PER_SEC
+ + request->period.nsec) / 2
+ / 8 * 8,
+ 125000000LL);
+ }
+
+ ret = ksz_ptp_tou_pulse_verify(pulse_width_ns);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_configure_perout(dev, cycle_width_ns, pulse_width_ns,
+ &ptp_data->perout_target_time_first,
+ pin);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_tou_gpio(dev);
+ if (ret)
+ return ret;
+
+ ret = ksz_ptp_tou_start(dev, request->index);
+ if (ret)
+ return ret;
+
+ ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT;
+
+ return 0;
+}
+
+static int ksz_ptp_restart_perout(struct ksz_device *dev)
+{
+ struct ksz_ptp_data *ptp_data = &dev->ptp_data;
+ s64 now_ns, first_ns, period_ns, next_ns;
+ struct ptp_perout_request request;
+ struct timespec64 next;
+ struct timespec64 now;
+ unsigned int count;
+ int ret;
+
+ ret = _ksz_ptp_gettime(dev, &now);
+ if (ret)
+ return ret;
+
+ now_ns = timespec64_to_ns(&now);
+ first_ns = timespec64_to_ns(&ptp_data->perout_target_time_first);
+
+ /* Calculate next perout event based on start time and period */
+ period_ns = timespec64_to_ns(&ptp_data->perout_period);
+
+ if (first_ns < now_ns) {
+ count = div_u64(now_ns - first_ns, period_ns);
+ next_ns = first_ns + count * period_ns;
+ } else {
+ next_ns = first_ns;
+ }
+
+ /* Ensure 100 ms guard time prior next event */
+ while (next_ns < now_ns + 100000000)
+ next_ns += period_ns;
+
+ /* Restart periodic output signal */
+ next = ns_to_timespec64(next_ns);
+ request.start.sec = next.tv_sec;
+ request.start.nsec = next.tv_nsec;
+ request.period.sec = ptp_data->perout_period.tv_sec;
+ request.period.nsec = ptp_data->perout_period.tv_nsec;
+ request.index = 0;
+ request.flags = 0;
+
+ return ksz_ptp_enable_perout(dev, &request, 1);
+}
+
static int ksz_ptp_enable_mode(struct ksz_device *dev, bool enable)
{
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
@@ -291,6 +554,20 @@ static int ksz_ptp_settime(struct ptp_clock_info *ptp,
if (ret)
goto error_return;

+ switch (ptp_data->tou_mode) {
+ case KSZ_PTP_TOU_IDLE:
+ break;
+
+ case KSZ_PTP_TOU_PEROUT:
+ dev_info(dev->dev, "Restarting periodic output signal\n");
+
+ ret = ksz_ptp_restart_perout(dev);
+ if (ret)
+ goto error_return;
+
+ break;
+ }
+
spin_lock_bh(&ptp_data->clock_lock);
ptp_data->clock_time = *ts;
spin_unlock_bh(&ptp_data->clock_lock);
@@ -384,6 +661,20 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
if (ret)
goto error_return;

+ switch (ptp_data->tou_mode) {
+ case KSZ_PTP_TOU_IDLE:
+ break;
+
+ case KSZ_PTP_TOU_PEROUT:
+ dev_info(dev->dev, "Restarting periodic output signal\n");
+
+ ret = ksz_ptp_restart_perout(dev);
+ if (ret)
+ goto error_return;
+
+ break;
+ }
+
spin_lock_bh(&ptp_data->clock_lock);
ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64);
spin_unlock_bh(&ptp_data->clock_lock);
@@ -393,6 +684,30 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
return ret;
}

+static int ksz_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *req, int on)
+{
+ struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
+ struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
+ struct ptp_perout_request *request = &req->perout;
+ int ret;
+
+ switch (req->type) {
+ case PTP_CLK_REQ_PEROUT:
+ if (request->index > ptp->n_per_out)
+ return -EINVAL;
+
+ mutex_lock(&ptp_data->lock);
+ ret = ksz_ptp_enable_perout(dev, request, on);
+ mutex_unlock(&ptp_data->lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
/* Function is pointer to the do_aux_work in the ptp_clock capability */
static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
{
@@ -508,6 +823,8 @@ static const struct ptp_clock_info ksz_ptp_caps = {
.adjfine = ksz_ptp_adjfine,
.adjtime = ksz_ptp_adjtime,
.do_aux_work = ksz_ptp_do_aux_work,
+ .enable = ksz_ptp_enable,
+ .n_per_out = 3,
};

int ksz_ptp_clock_register(struct dsa_switch *ds)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index ff169d119169..94ffd8bc0603 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -10,6 +10,11 @@

#include <linux/ptp_clock_kernel.h>

+enum ksz_ptp_tou_mode {
+ KSZ_PTP_TOU_IDLE,
+ KSZ_PTP_TOU_PEROUT,
+};
+
struct ksz_ptp_data {
struct ptp_clock_info caps;
struct ptp_clock *clock;
@@ -18,6 +23,9 @@ struct ksz_ptp_data {
/* lock for accessing the clock_time */
spinlock_t clock_lock;
struct timespec64 clock_time;
+ enum ksz_ptp_tou_mode tou_mode;
+ struct timespec64 perout_target_time_first; /* start of first perout pulse */
+ struct timespec64 perout_period;
};

int ksz_ptp_clock_register(struct dsa_switch *ds);
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index 0c5a1193e1a1..f714deb55681 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -6,6 +6,14 @@
#ifndef __KSZ_PTP_REGS_H
#define __KSZ_PTP_REGS_H

+#define REG_SW_GLOBAL_LED_OVR__4 0x0120
+#define LED_OVR_2 BIT(1)
+#define LED_OVR_1 BIT(0)
+
+#define REG_SW_GLOBAL_LED_SRC__4 0x0128
+#define LED_SRC_PTP_GPIO_1 BIT(3)
+#define LED_SRC_PTP_GPIO_2 BIT(2)
+
/* 5 - PTP Clock */
#define REG_PTP_CLK_CTRL 0x0500

@@ -54,6 +62,69 @@
#define PTP_MASTER BIT(1)
#define PTP_1STEP BIT(0)

+#define REG_PTP_UNIT_INDEX__4 0x0520
+
+#define PTP_GPIO_INDEX GENMASK(19, 16)
+#define PTP_TSI_INDEX BIT(8)
+#define PTP_TOU_INDEX GENMASK(1, 0)
+
+#define REG_PTP_TRIG_STATUS__4 0x0524
+
+#define TRIG_ERROR_M GENMASK(18, 16)
+#define TRIG_DONE_M GENMASK(2, 0)
+
+#define REG_PTP_INT_STATUS__4 0x0528
+
+#define TRIG_INT_M GENMASK(18, 16)
+#define TS_INT_M GENMASK(1, 0)
+
+#define REG_PTP_CTRL_STAT__4 0x052C
+
+#define GPIO_IN BIT(7)
+#define GPIO_OUT BIT(6)
+#define TS_INT_ENABLE BIT(5)
+#define TRIG_ACTIVE BIT(4)
+#define TRIG_ENABLE BIT(3)
+#define TRIG_RESET BIT(2)
+#define TS_ENABLE BIT(1)
+#define TS_RESET BIT(0)
+
+#define REG_TRIG_TARGET_NANOSEC 0x0530
+#define REG_TRIG_TARGET_SEC 0x0534
+
+#define REG_TRIG_CTRL__4 0x0538
+
+#define TRIG_CASCADE_ENABLE BIT(31)
+#define TRIG_CASCADE_TAIL BIT(30)
+#define TRIG_CASCADE_UPS_M GENMASK(29, 26)
+#define TRIG_NOW BIT(25)
+#define TRIG_NOTIFY BIT(24)
+#define TRIG_EDGE BIT(23)
+#define TRIG_PATTERN_M GENMASK(22, 20)
+#define TRIG_NEG_EDGE 0
+#define TRIG_POS_EDGE 1
+#define TRIG_NEG_PULSE 2
+#define TRIG_POS_PULSE 3
+#define TRIG_NEG_PERIOD 4
+#define TRIG_POS_PERIOD 5
+#define TRIG_REG_OUTPUT 6
+#define TRIG_GPO_M GENMASK(19, 16)
+#define TRIG_CASCADE_ITERATE_CNT_M GENMASK(15, 0)
+
+#define REG_TRIG_CYCLE_WIDTH 0x053C
+#define TRIG_CYCLE_WIDTH_M GENMASK(31, 0)
+
+#define REG_TRIG_CYCLE_CNT 0x0540
+
+#define TRIG_CYCLE_CNT_M GENMASK(31, 16)
+#define TRIG_BIT_PATTERN_M GENMASK(15, 0)
+
+#define REG_TRIG_ITERATE_TIME 0x0544
+
+#define REG_TRIG_PULSE_WIDTH__4 0x0548
+
+#define TRIG_PULSE_WIDTH_M GENMASK(23, 0)
+
/* Port PTP Register */
#define REG_PTP_PORT_RX_DELAY__2 0x0C00
#define REG_PTP_PORT_TX_DELAY__2 0x0C02
--
2.36.1

2022-11-28 11:30:27

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 06/12] net: ptp: add helper for one-step P2P clocks

From: Christian Eggers <[email protected]>

For P2P delay measurement, the ingress time stamp of the PDelay_Req is
required for the correction field of the PDelay_Resp. The application
echoes back the correction field of the PDelay_Req when sending the
PDelay_Resp.

Some hardware (like the ZHAW InES PTP time stamping IP core) subtracts
the ingress timestamp autonomously from the correction field, so that
the hardware only needs to add the egress timestamp on tx. Other
hardware (like the Microchip KSZ9563) reports the ingress time stamp via
an interrupt and requires that the software provides this time stamp via
tail-tag on tx.

In order to avoid introducing a further application interface for this,
the driver can simply emulate the behavior of the InES device and
subtract the ingress time stamp in software from the correction field.

On egress, the correction field can either be kept as it is (and the
time stamp field in the tail-tag is set to zero) or move the value from
the correction field back to the tail-tag.

Changing the correction field requires updating the UDP checksum (if UDP
is used as transport).

Signed-off-by: Christian Eggers <[email protected]>
---
include/linux/ptp_classify.h | 73 ++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)

diff --git a/include/linux/ptp_classify.h b/include/linux/ptp_classify.h
index 2b6ea36ad162..e32efe3c4d66 100644
--- a/include/linux/ptp_classify.h
+++ b/include/linux/ptp_classify.h
@@ -10,8 +10,12 @@
#ifndef _PTP_CLASSIFY_H_
#define _PTP_CLASSIFY_H_

+#include <asm/unaligned.h>
#include <linux/ip.h>
+#include <linux/ktime.h>
#include <linux/skbuff.h>
+#include <linux/udp.h>
+#include <net/checksum.h>

#define PTP_CLASS_NONE 0x00 /* not a PTP event message */
#define PTP_CLASS_V1 0x01 /* protocol version 1 */
@@ -129,6 +133,67 @@ static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
return msgtype;
}

+/**
+ * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field)
+ * @old: old field value
+ * @new: new field value
+ * @oldsum: previous checksum
+ *
+ * This function can be used to calculate a new checksum when only a single
+ * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h.
+ *
+ * Return: Updated checksum
+ */
+static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum)
+{
+ __be64 diff[2] = { ~old, new };
+
+ return csum_partial(diff, sizeof(diff), oldsum);
+}
+
+/**
+ * ptp_header_update_correction - Update PTP header's correction field
+ * @skb: packet buffer
+ * @type: type of the packet (see ptp_classify_raw())
+ * @hdr: ptp header
+ * @correction: new correction value
+ *
+ * This updates the correction field of a PTP header and updates the UDP
+ * checksum (if UDP is used as transport). It is needed for hardware capable of
+ * one-step P2P that does not already modify the correction field of Pdelay_Req
+ * event messages on ingress.
+ */
+static inline
+void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
+ struct ptp_header *hdr, s64 correction)
+{
+ __be64 correction_old;
+ struct udphdr *uhdr;
+
+ /* previous correction value is required for checksum update. */
+ memcpy(&correction_old, &hdr->correction, sizeof(correction_old));
+
+ /* write new correction value */
+ put_unaligned_be64((u64)correction, &hdr->correction);
+
+ switch (type & PTP_CLASS_PMASK) {
+ case PTP_CLASS_IPV4:
+ case PTP_CLASS_IPV6:
+ /* locate udp header */
+ uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr));
+ break;
+ default:
+ return;
+ }
+
+ /* update checksum */
+ uhdr->check = csum_fold(ptp_check_diff8(correction_old,
+ hdr->correction,
+ ~csum_unfold(uhdr->check)));
+ if (!uhdr->check)
+ uhdr->check = CSUM_MANGLED_0;
+}
+
/**
* ptp_msg_is_sync - Evaluates whether the given skb is a PTP Sync message
* @skb: packet buffer
@@ -166,5 +231,13 @@ static inline bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type)
{
return false;
}
+
+static inline
+void ptp_onestep_p2p_move_t2_to_correction(struct sk_buff *skb,
+ unsigned int type,
+ struct ptp_header *hdr,
+ ktime_t t2)
+{
+}
#endif
#endif /* _PTP_CLASSIFY_H_ */
--
2.36.1

2022-11-28 11:59:54

by Arun Ramadoss

[permalink] [raw]
Subject: [Patch net-next v1 12/12] net: dsa: microchip: ptp: add support for perout programmable pins

There are two programmable pins available for Trigger output unit to
generate periodic pulses. This patch add verify_pin for the available 2
pins and configure it with respect to GPIO index for the TOU unit.

Signed-off-by: Arun Ramadoss <[email protected]>
---
Patch v1
- patch is new
---
drivers/net/dsa/microchip/ksz_ptp.c | 41 +++++++++++++++++++++++++++--
drivers/net/dsa/microchip/ksz_ptp.h | 3 +++
2 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 15b863c85cb1..445d220f1a1b 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -30,12 +30,14 @@ static int ksz_ptp_tou_gpio(struct ksz_device *dev)
{
int ret;

- ret = ksz_rmw32(dev, REG_SW_GLOBAL_LED_OVR__4, LED_OVR_1, LED_OVR_1);
+ ret = ksz_rmw32(dev, REG_SW_GLOBAL_LED_OVR__4, LED_OVR_1 | LED_OVR_2,
+ LED_OVR_1 | LED_OVR_2);
if (ret)
return ret;

return ksz_rmw32(dev, REG_SW_GLOBAL_LED_SRC__4,
- LED_SRC_PTP_GPIO_1, LED_SRC_PTP_GPIO_1);
+ LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2,
+ LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2);
}

static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
@@ -181,6 +183,10 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
ptp_data->tou_mode != KSZ_PTP_TOU_IDLE)
return -EBUSY;

+ pin = ptp_find_pin(ptp_data->clock, PTP_PF_PEROUT, request->index);
+ if (pin < 0)
+ return -EINVAL;
+
data32 = FIELD_PREP(PTP_GPIO_INDEX, pin) |
FIELD_PREP(PTP_TOU_INDEX, request->index);
ret = ksz_rmw32(dev, REG_PTP_UNIT_INDEX__4,
@@ -708,6 +714,23 @@ static int ksz_ptp_enable(struct ptp_clock_info *ptp,
return ret;
}

+static int ksz_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ int ret = 0;
+
+ switch (func) {
+ case PTP_PF_NONE:
+ case PTP_PF_PEROUT:
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
/* Function is pointer to the do_aux_work in the ptp_clock capability */
static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
{
@@ -824,6 +847,8 @@ static const struct ptp_clock_info ksz_ptp_caps = {
.adjtime = ksz_ptp_adjtime,
.do_aux_work = ksz_ptp_do_aux_work,
.enable = ksz_ptp_enable,
+ .verify = ksz_ptp_verify_pin,
+ .n_pins = KSZ_PTP_N_GPIO,
.n_per_out = 3,
};

@@ -832,6 +857,7 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
struct ksz_device *dev = ds->priv;
struct ksz_ptp_data *ptp_data;
int ret;
+ u8 i;

ptp_data = &dev->ptp_data;
mutex_init(&ptp_data->lock);
@@ -843,6 +869,17 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
if (ret)
return ret;

+ for (i = 0; i < KSZ_PTP_N_GPIO; i++) {
+ struct ptp_pin_desc *ptp_pin = &ptp_data->pin_config[i];
+
+ snprintf(ptp_pin->name,
+ sizeof(ptp_pin->name), "ksz_ptp_pin_%02d", i);
+ ptp_pin->index = i;
+ ptp_pin->func = PTP_PF_NONE;
+ }
+
+ ptp_data->caps.pin_config = ptp_data->pin_config;
+
ptp_data->clock = ptp_clock_register(&ptp_data->caps, dev->dev);
if (IS_ERR_OR_NULL(ptp_data->clock))
return PTR_ERR(ptp_data->clock);
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 94ffd8bc0603..390364a177ea 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -10,6 +10,8 @@

#include <linux/ptp_clock_kernel.h>

+#define KSZ_PTP_N_GPIO 2
+
enum ksz_ptp_tou_mode {
KSZ_PTP_TOU_IDLE,
KSZ_PTP_TOU_PEROUT,
@@ -18,6 +20,7 @@ enum ksz_ptp_tou_mode {
struct ksz_ptp_data {
struct ptp_clock_info caps;
struct ptp_clock *clock;
+ struct ptp_pin_desc pin_config[KSZ_PTP_N_GPIO];
/* Serializes all operations on the PTP hardware clock */
struct mutex lock;
/* lock for accessing the clock_time */
--
2.36.1

2022-11-28 15:15:51

by Pavan Chebbi

[permalink] [raw]
Subject: Re: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

On Mon, Nov 28, 2022 at 4:03 PM Arun Ramadoss
<[email protected]> wrote:

> diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
> new file mode 100644
> index 000000000000..e578a0006ecf
> --- /dev/null
> +++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
> @@ -0,0 +1,57 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Microchip KSZ PTP register definitions
> + * Copyright (C) 2022 Microchip Technology Inc.
> + */
> +
> +#ifndef __KSZ_PTP_REGS_H
> +#define __KSZ_PTP_REGS_H
> +
> +/* 5 - PTP Clock */
> +#define REG_PTP_CLK_CTRL 0x0500
> +
> +#define PTP_STEP_ADJ BIT(6)
> +#define PTP_STEP_DIR BIT(5)
> +#define PTP_READ_TIME BIT(4)
> +#define PTP_LOAD_TIME BIT(3)

PTP_WRITE_TIME sounds more intuitive than PTP_LOAD_TIME?
Also I see that all the #defines are introduced in this patch, some of
which are used later. It is a good idea to introduce the #defines in
the same patches where they are being used for the first time.
I will be looking at the entire series but am responding to this now.

> +#define PTP_CLK_ADJ_ENABLE BIT(2)
> +#define PTP_CLK_ENABLE BIT(1)
> +#define PTP_CLK_RESET BIT(0)
> +
> +#define REG_PTP_RTC_SUB_NANOSEC__2 0x0502
> +
> +#define PTP_RTC_SUB_NANOSEC_M 0x0007
> +#define PTP_RTC_0NS 0x00
> +
> +#define REG_PTP_RTC_NANOSEC 0x0504
> +#define REG_PTP_RTC_NANOSEC_H 0x0504
> +#define REG_PTP_RTC_NANOSEC_L 0x0506
> +
> +#define REG_PTP_RTC_SEC 0x0508
> +#define REG_PTP_RTC_SEC_H 0x0508
> +#define REG_PTP_RTC_SEC_L 0x050A
> +
> +#define REG_PTP_SUBNANOSEC_RATE 0x050C
> +#define REG_PTP_SUBNANOSEC_RATE_H 0x050C
> +#define PTP_SUBNANOSEC_M 0x3FFFFFFF
> +
> +#define PTP_RATE_DIR BIT(31)
> +#define PTP_TMP_RATE_ENABLE BIT(30)
> +
> +#define REG_PTP_SUBNANOSEC_RATE_L 0x050E
> +
> +#define REG_PTP_RATE_DURATION 0x0510
> +#define REG_PTP_RATE_DURATION_H 0x0510
> +#define REG_PTP_RATE_DURATION_L 0x0512
> +
> +#define REG_PTP_MSG_CONF1 0x0514
> +
> +#define PTP_802_1AS BIT(7)
> +#define PTP_ENABLE BIT(6)
> +#define PTP_ETH_ENABLE BIT(5)
> +#define PTP_IPV4_UDP_ENABLE BIT(4)
> +#define PTP_IPV6_UDP_ENABLE BIT(3)
> +#define PTP_TC_P2P BIT(2)
> +#define PTP_MASTER BIT(1)
> +#define PTP_1STEP BIT(0)
> +
> +#endif
> --
> 2.36.1
>


Attachments:
smime.p7s (4.11 kB)
S/MIME Cryptographic Signature

2022-11-28 15:24:14

by Christian Eggers

[permalink] [raw]
Subject: Re: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

On Monday, 28 November 2022, 15:49:33 CET, Pavan Chebbi wrote:
> On Mon, Nov 28, 2022 at 4:03 PM Arun Ramadoss
> <[email protected]> wrote:
>
> > diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
> > new file mode 100644
> > index 000000000000..e578a0006ecf
> > --- /dev/null
> > +++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
> > @@ -0,0 +1,57 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Microchip KSZ PTP register definitions
> > + * Copyright (C) 2022 Microchip Technology Inc.
> > + */
> > +
> > +#ifndef __KSZ_PTP_REGS_H
> > +#define __KSZ_PTP_REGS_H
> > +
> > +/* 5 - PTP Clock */
> > +#define REG_PTP_CLK_CTRL 0x0500
> > +
> > +#define PTP_STEP_ADJ BIT(6)
> > +#define PTP_STEP_DIR BIT(5)
> > +#define PTP_READ_TIME BIT(4)
> > +#define PTP_LOAD_TIME BIT(3)
>
> PTP_WRITE_TIME sounds more intuitive than PTP_LOAD_TIME?
PTP_LOAD_TIME has been derived from the data sheet:

-------------8<--------------
PTP Clock Load
--------------
Setting this bit will cause the PTP clock to be loaded with the time value in
registers 0x0502 to 0x050B.
------------->8--------------

I would also prefer PTP_WRITE_TIME. But is it ok to deviate from data sheet?

> Also I see that all the #defines are introduced in this patch, some of
> which are used later. It is a good idea to introduce the #defines in
> the same patches where they are being used for the first time.
> I will be looking at the entire series but am responding to this now.
>
> > +#define PTP_CLK_ADJ_ENABLE BIT(2)
> > +#define PTP_CLK_ENABLE BIT(1)
> > +#define PTP_CLK_RESET BIT(0)
> > +
> > +#define REG_PTP_RTC_SUB_NANOSEC__2 0x0502
> > +
> > +#define PTP_RTC_SUB_NANOSEC_M 0x0007
> > +#define PTP_RTC_0NS 0x00
> > +
> > +#define REG_PTP_RTC_NANOSEC 0x0504
> > +#define REG_PTP_RTC_NANOSEC_H 0x0504
> > +#define REG_PTP_RTC_NANOSEC_L 0x0506
> > +
> > +#define REG_PTP_RTC_SEC 0x0508
> > +#define REG_PTP_RTC_SEC_H 0x0508
> > +#define REG_PTP_RTC_SEC_L 0x050A
> > +
> > +#define REG_PTP_SUBNANOSEC_RATE 0x050C
> > +#define REG_PTP_SUBNANOSEC_RATE_H 0x050C
> > +#define PTP_SUBNANOSEC_M 0x3FFFFFFF
> > +
> > +#define PTP_RATE_DIR BIT(31)
> > +#define PTP_TMP_RATE_ENABLE BIT(30)
> > +
> > +#define REG_PTP_SUBNANOSEC_RATE_L 0x050E
> > +
> > +#define REG_PTP_RATE_DURATION 0x0510
> > +#define REG_PTP_RATE_DURATION_H 0x0510
> > +#define REG_PTP_RATE_DURATION_L 0x0512
> > +
> > +#define REG_PTP_MSG_CONF1 0x0514
> > +
> > +#define PTP_802_1AS BIT(7)
> > +#define PTP_ENABLE BIT(6)
> > +#define PTP_ETH_ENABLE BIT(5)
> > +#define PTP_IPV4_UDP_ENABLE BIT(4)
> > +#define PTP_IPV6_UDP_ENABLE BIT(3)
> > +#define PTP_TC_P2P BIT(2)
> > +#define PTP_MASTER BIT(1)
> > +#define PTP_1STEP BIT(0)
> > +
> > +#endif
> > --
> > 2.36.1
> >
>




2022-11-29 01:22:35

by kernel test robot

[permalink] [raw]
Subject: Re: [Patch net-next v1 07/12] net: dsa: microchip: ptp: add packet reception timestamping

Hi Arun,

I love your patch! Yet something to improve:

[auto build test ERROR on a6e3d86ece0b42a571a11055ace5c3148cb7ce76]

url: https://github.com/intel-lab-lkp/linux/commits/Arun-Ramadoss/net-dsa-microchip-add-PTP-support-for-KSZ9563-KSZ8563-and-LAN937x/20221128-183731
base: a6e3d86ece0b42a571a11055ace5c3148cb7ce76
patch link: https://lore.kernel.org/r/20221128103227.23171-8-arun.ramadoss%40microchip.com
patch subject: [Patch net-next v1 07/12] net: dsa: microchip: ptp: add packet reception timestamping
config: arm-randconfig-r046-20221128
compiler: arm-linux-gnueabi-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/104ec46b17d7bf278a05fca30b1cd513c4f7adac
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Arun-Ramadoss/net-dsa-microchip-add-PTP-support-for-KSZ9563-KSZ8563-and-LAN937x/20221128-183731
git checkout 104ec46b17d7bf278a05fca30b1cd513c4f7adac
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash net/dsa/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

net/dsa/tag_ksz.c: In function 'ksz_rcv_timestamp':
>> net/dsa/tag_ksz.c:225:9: error: implicit declaration of function 'ptp_header_update_correction' [-Werror=implicit-function-declaration]
225 | ptp_header_update_correction(skb, ptp_type, ptp_hdr, correction);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors


vim +/ptp_header_update_correction +225 net/dsa/tag_ksz.c

176
177 static void ksz_rcv_timestamp(struct sk_buff *skb, u8 *tag,
178 struct net_device *dev, unsigned int port)
179 {
180 struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
181 struct dsa_switch *ds = dev->dsa_ptr->ds;
182 u8 *tstamp_raw = tag - KSZ_PTP_TAG_LEN;
183 struct ksz_tagger_data *tagger_data;
184 struct ptp_header *ptp_hdr;
185 unsigned int ptp_type;
186 u8 ptp_msg_type;
187 ktime_t tstamp;
188 s64 correction;
189
190 tagger_data = ksz_tagger_data(ds);
191 if (!tagger_data->meta_tstamp_handler)
192 return;
193
194 /* convert time stamp and write to skb */
195 tstamp = ksz_decode_tstamp(get_unaligned_be32(tstamp_raw));
196 memset(hwtstamps, 0, sizeof(*hwtstamps));
197 hwtstamps->hwtstamp = tagger_data->meta_tstamp_handler(ds, tstamp);
198
199 if (skb_headroom(skb) < ETH_HLEN)
200 return;
201
202 __skb_push(skb, ETH_HLEN);
203 ptp_type = ptp_classify_raw(skb);
204 __skb_pull(skb, ETH_HLEN);
205
206 if (ptp_type == PTP_CLASS_NONE)
207 return;
208
209 ptp_hdr = ptp_parse_header(skb, ptp_type);
210 if (!ptp_hdr)
211 return;
212
213 ptp_msg_type = ptp_get_msgtype(ptp_hdr, ptp_type);
214 if (ptp_msg_type != PTP_MSGTYPE_PDELAY_REQ)
215 return;
216
217 /* Only subtract the partial time stamp from the correction field. When
218 * the hardware adds the egress time stamp to the correction field of
219 * the PDelay_Resp message on tx, also only the partial time stamp will
220 * be added.
221 */
222 correction = (s64)get_unaligned_be64(&ptp_hdr->correction);
223 correction -= ktime_to_ns(tstamp) << 16;
224
> 225 ptp_header_update_correction(skb, ptp_type, ptp_hdr, correction);
226 }
227

--
0-DAY CI Kernel Test Service
https://01.org/lkp


Attachments:
(No filename) (3.84 kB)
config (176.80 kB)
Download all attachments

2022-11-29 09:13:58

by Pavan Chebbi

[permalink] [raw]
Subject: Re: [Patch net-next v1 04/12] net: dsa: microchip: ptp: Manipulating absolute time using ptp hw clock

On Mon, Nov 28, 2022 at 4:04 PM Arun Ramadoss
<[email protected]> wrote:
> +/* Function is pointer to the do_aux_work in the ptp_clock capability */
> +static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> +{
> + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> + struct timespec64 ts;
> +
> + mutex_lock(&ptp_data->lock);
> + _ksz_ptp_gettime(dev, &ts);
> + mutex_unlock(&ptp_data->lock);
> +
> + spin_lock_bh(&ptp_data->clock_lock);
> + ptp_data->clock_time = ts;
> + spin_unlock_bh(&ptp_data->clock_lock);

If I understand this correctly, the software clock is updated with
full 64b every 1s. However only 32b timestamp registers are read while
processing packets and higher bits from this clock are used.
How do you ensure these higher order bits are in sync with the higher
order bits in the HW? IOW, what if lower 32b have wrapped around and
you are required to stamp a packet but you still don't have aux worker
updated.

> +
> + return HZ; /* reschedule in 1 second */
> +}
> +
> static int ksz_ptp_start_clock(struct ksz_device *dev)
> {
> - return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
> + struct ksz_ptp_data *ptp_data = &dev->ptp_data;
> + int ret;
> +
> + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
> + if (ret)
> + return ret;
> +
> + spin_lock_bh(&ptp_data->clock_lock);
> + ptp_data->clock_time.tv_sec = 0;
> + ptp_data->clock_time.tv_nsec = 0;
> + spin_unlock_bh(&ptp_data->clock_lock);
> +
> + return 0;
> }
>
> static const struct ptp_clock_info ksz_ptp_caps = {
> @@ -305,6 +357,7 @@ static const struct ptp_clock_info ksz_ptp_caps = {
> .settime64 = ksz_ptp_settime,
> .adjfine = ksz_ptp_adjfine,
> .adjtime = ksz_ptp_adjtime,
> + .do_aux_work = ksz_ptp_do_aux_work,
> };
>
> int ksz_ptp_clock_register(struct dsa_switch *ds)
> @@ -315,6 +368,7 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
>
> ptp_data = &dev->ptp_data;
> mutex_init(&ptp_data->lock);
> + spin_lock_init(&ptp_data->clock_lock);
>
> ptp_data->caps = ksz_ptp_caps;
>
> diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
> index 17f455c3b2c5..81fa2e8b9cf4 100644
> --- a/drivers/net/dsa/microchip/ksz_ptp.h
> +++ b/drivers/net/dsa/microchip/ksz_ptp.h
> @@ -15,6 +15,9 @@ struct ksz_ptp_data {
> struct ptp_clock *clock;
> /* Serializes all operations on the PTP hardware clock */
> struct mutex lock;
> + /* lock for accessing the clock_time */
> + spinlock_t clock_lock;
> + struct timespec64 clock_time;
> };
>
> int ksz_ptp_clock_register(struct dsa_switch *ds);
> --
> 2.36.1
>


Attachments:
smime.p7s (4.11 kB)
S/MIME Cryptographic Signature

2022-11-29 09:18:21

by Pavan Chebbi

[permalink] [raw]
Subject: Re: [Patch net-next v1 11/12] net: dsa: microchip: ptp: add periodic output signal

On Mon, Nov 28, 2022 at 4:05 PM Arun Ramadoss
<[email protected]> wrote:

> +static int ksz_ptp_enable(struct ptp_clock_info *ptp,
> + struct ptp_clock_request *req, int on)
> +{
> + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> + struct ptp_perout_request *request = &req->perout;
> + int ret;
> +
> + switch (req->type) {
> + case PTP_CLK_REQ_PEROUT:
> + if (request->index > ptp->n_per_out)
> + return -EINVAL;

Should be -EOPNOTSUPP ? I see some other places where -EOPNOTSUPP is
more appropriate.

> +
> + mutex_lock(&ptp_data->lock);
> + ret = ksz_ptp_enable_perout(dev, request, on);
> + mutex_unlock(&ptp_data->lock);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> /* Function is pointer to the do_aux_work in the ptp_clock capability */
> static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> {
> @@ -508,6 +823,8 @@ static const struct ptp_clock_info ksz_ptp_caps = {
> .adjfine = ksz_ptp_adjfine,
> .adjtime = ksz_ptp_adjtime,
> .do_aux_work = ksz_ptp_do_aux_work,
> + .enable = ksz_ptp_enable,
> + .n_per_out = 3,
> };
>


Attachments:
smime.p7s (4.11 kB)
S/MIME Cryptographic Signature

2022-11-29 09:19:12

by Pavan Chebbi

[permalink] [raw]
Subject: Re: [Patch net-next v1 02/12] net: dsa: microchip: ptp: Initial hardware time stamping support

On Mon, Nov 28, 2022 at 4:03 PM Arun Ramadoss
<[email protected]> wrote:
>
> From: Christian Eggers <[email protected]>
>
> This patch adds the routine for get_ts_info, hwstamp_get, set. This enables
> the PTP support towards userspace applications such as linuxptp.
> Tx timestamping can be enabled per port and Rx timestamping enabled
> globally.
>
> Signed-off-by: Christian Eggers <[email protected]>
> Co-developed-by: Arun Ramadoss <[email protected]>
> Signed-off-by: Arun Ramadoss <[email protected]>
>
> ---
> RFC v2 -> Patch v1
> - moved tagger set and get function to separate patch
> - Removed unnecessary comments
> ---
> drivers/net/dsa/microchip/ksz_common.c | 2 +
> drivers/net/dsa/microchip/ksz_common.h | 4 ++
> drivers/net/dsa/microchip/ksz_ptp.c | 77 +++++++++++++++++++++++++-
> drivers/net/dsa/microchip/ksz_ptp.h | 14 +++++
> 4 files changed, 95 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
> index 2d09cd141db6..7b85b258270c 100644
> --- a/drivers/net/dsa/microchip/ksz_common.c
> +++ b/drivers/net/dsa/microchip/ksz_common.c
> @@ -2873,6 +2873,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
> .port_change_mtu = ksz_change_mtu,
> .port_max_mtu = ksz_max_mtu,
> .get_ts_info = ksz_get_ts_info,
> + .port_hwtstamp_get = ksz_hwtstamp_get,
> + .port_hwtstamp_set = ksz_hwtstamp_set,
> };
>
> struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
> diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
> index 5a6bfd42c6f9..cd20f39a565f 100644
> --- a/drivers/net/dsa/microchip/ksz_common.h
> +++ b/drivers/net/dsa/microchip/ksz_common.h
> @@ -103,6 +103,10 @@ struct ksz_port {
> struct ksz_device *ksz_dev;
> struct ksz_irq pirq;
> u8 num;
> +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
> + u8 hwts_tx_en;
> + bool hwts_rx_en;

I see that the hwts_rx_en gets removed in the later patch. Instead you
could add rx filters support only later when you have the final code
in place.

> +#endif
> };
>
> struct ksz_device {
> diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
> index c737635ca266..a41418c6adf6 100644
> --- a/drivers/net/dsa/microchip/ksz_ptp.c
> +++ b/drivers/net/dsa/microchip/ksz_ptp.c
> @@ -36,15 +36,88 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts)
> SOF_TIMESTAMPING_RX_HARDWARE |
> SOF_TIMESTAMPING_RAW_HARDWARE;
>
> - ts->tx_types = BIT(HWTSTAMP_TX_OFF);
> + ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ONESTEP_P2P);
>
> - ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
> + ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
>
> ts->phc_index = ptp_clock_index(ptp_data->clock);
>
> return 0;
> }
>
> +int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
> +{
> + struct ksz_device *dev = ds->priv;
> + struct hwtstamp_config config;
> +
> + config.flags = 0;
> +
> + config.tx_type = dev->ports[port].hwts_tx_en;
> +
> + if (dev->ports[port].hwts_rx_en)
> + config.rx_filter = HWTSTAMP_FILTER_ALL;
> + else
> + config.rx_filter = HWTSTAMP_FILTER_NONE;
> +
> + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
> + -EFAULT : 0;
> +}
> +
> +static int ksz_set_hwtstamp_config(struct ksz_device *dev, int port,
> + struct hwtstamp_config *config)
> +{
> + struct ksz_port *prt = &dev->ports[port];
> +
> + if (config->flags)
> + return -EINVAL;
> +
> + switch (config->tx_type) {
> + case HWTSTAMP_TX_OFF:
> + case HWTSTAMP_TX_ONESTEP_P2P:
> + prt->hwts_tx_en = config->tx_type;
> + break;
> + default:
> + return -ERANGE;
> + }
> +
> + switch (config->rx_filter) {
> + case HWTSTAMP_FILTER_NONE:
> + prt->hwts_rx_en = false;
> + break;
> + default:
> + prt->hwts_rx_en = true;
> + break;
> + }
> +
> + return 0;
> +}
> +
> +int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
> +{
> + struct ksz_device *dev = ds->priv;
> + struct ksz_ptp_data *ptp_data;
> + struct hwtstamp_config config;
> + int ret;
> +
> + ptp_data = &dev->ptp_data;
> +
> + mutex_lock(&ptp_data->lock);
> +
> + ret = copy_from_user(&config, ifr->ifr_data, sizeof(config));
> + if (ret)
> + goto error_return;
> +
> + ret = ksz_set_hwtstamp_config(dev, port, &config);
> + if (ret)
> + goto error_return;
> +
> + ret = copy_to_user(ifr->ifr_data, &config, sizeof(config));
> +
> +error_return:
> + mutex_unlock(&ptp_data->lock);
> + return ret;
> +}
> +
> static int _ksz_ptp_gettime(struct ksz_device *dev, struct timespec64 *ts)
> {
> u32 nanoseconds;
> diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
> index ea9fa46caa01..17f455c3b2c5 100644
> --- a/drivers/net/dsa/microchip/ksz_ptp.h
> +++ b/drivers/net/dsa/microchip/ksz_ptp.h
> @@ -23,6 +23,8 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds);
>
> int ksz_get_ts_info(struct dsa_switch *ds, int port,
> struct ethtool_ts_info *ts);
> +int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);
> +int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);
>
> #else
>
> @@ -40,6 +42,18 @@ static inline void ksz_ptp_clock_unregister(struct dsa_switch *ds) { }
>
> #define ksz_get_ts_info NULL
>
> +static inline int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
> + struct ifreq *ifr)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
> + struct ifreq *ifr)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> #endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
>
> #endif
> --
> 2.36.1
>


Attachments:
smime.p7s (4.11 kB)
S/MIME Cryptographic Signature

2022-11-29 10:10:36

by Pavan Chebbi

[permalink] [raw]
Subject: Re: [Patch net-next v1 11/12] net: dsa: microchip: ptp: add periodic output signal

On Tue, Nov 29, 2022 at 2:23 PM Pavan Chebbi <[email protected]> wrote:
>
> On Mon, Nov 28, 2022 at 4:05 PM Arun Ramadoss
> <[email protected]> wrote:
>
> > +static int ksz_ptp_enable(struct ptp_clock_info *ptp,
> > + struct ptp_clock_request *req, int on)
> > +{
> > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > + struct ptp_perout_request *request = &req->perout;
> > + int ret;
> > +
> > + switch (req->type) {
> > + case PTP_CLK_REQ_PEROUT:
> > + if (request->index > ptp->n_per_out)
> > + return -EINVAL;
>
> Should be -EOPNOTSUPP ? I see some other places where -EOPNOTSUPP is
> more appropriate.
>
> > +
> > + mutex_lock(&ptp_data->lock);
> > + ret = ksz_ptp_enable_perout(dev, request, on);
> > + mutex_unlock(&ptp_data->lock);
> > + break;
> > + default:
> > + return -EINVAL;

OK I really meant here.

> > + }
> > +
> > + return ret;
> > +}
> > +
> > /* Function is pointer to the do_aux_work in the ptp_clock capability */
> > static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> > {
> > @@ -508,6 +823,8 @@ static const struct ptp_clock_info ksz_ptp_caps = {
> > .adjfine = ksz_ptp_adjfine,
> > .adjtime = ksz_ptp_adjtime,
> > .do_aux_work = ksz_ptp_do_aux_work,
> > + .enable = ksz_ptp_enable,
> > + .n_per_out = 3,
> > };
> >


Attachments:
smime.p7s (4.11 kB)
S/MIME Cryptographic Signature

2022-11-30 05:10:04

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 11/12] net: dsa: microchip: ptp: add periodic output signal

Hi Pavan,

On Tue, 2022-11-29 at 14:23 +0530, Pavan Chebbi wrote:
> On Mon, Nov 28, 2022 at 4:05 PM Arun Ramadoss
> <[email protected]> wrote:
>
> > +static int ksz_ptp_enable(struct ptp_clock_info *ptp,
> > + struct ptp_clock_request *req, int on)
> > +{
> > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > + struct ptp_perout_request *request = &req->perout;
> > + int ret;
> > +
> > + switch (req->type) {
> > + case PTP_CLK_REQ_PEROUT:
> > + if (request->index > ptp->n_per_out)
> > + return -EINVAL;
>
> Should be -EOPNOTSUPP ? I see some other places where -EOPNOTSUPP is
> more appropriate.

I got a offline comment like This check is probably redundant (already
checked in period_store() and ptp_ioctl()). I am looking into whether
this check is required or already handled in upper layers.
If the check is required, then I feel -EINVAL/-ERANGE should be
reasonable. Because we are supporting periodic output only thing is
index is out of bound. If we return -EOPNOTSUPP, it indicates we are
not supporting periodic output.

>
> > +
> > + mutex_lock(&ptp_data->lock);
> > + ret = ksz_ptp_enable_perout(dev, request, on);
> > + mutex_unlock(&ptp_data->lock);
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > /* Function is pointer to the do_aux_work in the ptp_clock
> > capability */
> > static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> > {
> > @@ -508,6 +823,8 @@ static const struct ptp_clock_info ksz_ptp_caps
> > = {
> > .adjfine = ksz_ptp_adjfine,
> > .adjtime = ksz_ptp_adjtime,
> > .do_aux_work = ksz_ptp_do_aux_work,
> > + .enable = ksz_ptp_enable,
> > + .n_per_out = 3,
> > };
> >

2022-11-30 05:14:36

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 02/12] net: dsa: microchip: ptp: Initial hardware time stamping support

Hi Pavan,
Thanks for the comment.

On Tue, 2022-11-29 at 14:19 +0530, Pavan Chebbi wrote:
> On Mon, Nov 28, 2022 at 4:03 PM Arun Ramadoss
> <[email protected]> wrote:
> >
> > From: Christian Eggers <[email protected]>
> >
> > This patch adds the routine for get_ts_info, hwstamp_get, set. This
> > enables
> > the PTP support towards userspace applications such as linuxptp.
> > Tx timestamping can be enabled per port and Rx timestamping enabled
> > globally.
> >
> > Signed-off-by: Christian Eggers <[email protected]>
> > Co-developed-by: Arun Ramadoss <[email protected]>
> > Signed-off-by: Arun Ramadoss <[email protected]>
> >
> > ---
> > RFC v2 -> Patch v1
> > - moved tagger set and get function to separate patch
> > - Removed unnecessary comments
> > ---
> > drivers/net/dsa/microchip/ksz_common.c | 2 +
> > drivers/net/dsa/microchip/ksz_common.h | 4 ++
> > drivers/net/dsa/microchip/ksz_ptp.c | 77
> > +++++++++++++++++++++++++-
> > drivers/net/dsa/microchip/ksz_ptp.h | 14 +++++
> > 4 files changed, 95 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/net/dsa/microchip/ksz_common.c
> > b/drivers/net/dsa/microchip/ksz_common.c
> > index 2d09cd141db6..7b85b258270c 100644
> > --- a/drivers/net/dsa/microchip/ksz_common.c
> > +++ b/drivers/net/dsa/microchip/ksz_common.c
> > @@ -2873,6 +2873,8 @@ static const struct dsa_switch_ops
> > ksz_switch_ops = {
> > .port_change_mtu = ksz_change_mtu,
> > .port_max_mtu = ksz_max_mtu,
> > .get_ts_info = ksz_get_ts_info,
> > + .port_hwtstamp_get = ksz_hwtstamp_get,
> > + .port_hwtstamp_set = ksz_hwtstamp_set,
> > };
> >
> > struct ksz_device *ksz_switch_alloc(struct device *base, void
> > *priv)
> > diff --git a/drivers/net/dsa/microchip/ksz_common.h
> > b/drivers/net/dsa/microchip/ksz_common.h
> > index 5a6bfd42c6f9..cd20f39a565f 100644
> > --- a/drivers/net/dsa/microchip/ksz_common.h
> > +++ b/drivers/net/dsa/microchip/ksz_common.h
> > @@ -103,6 +103,10 @@ struct ksz_port {
> > struct ksz_device *ksz_dev;
> > struct ksz_irq pirq;
> > u8 num;
> > +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
> > + u8 hwts_tx_en;
> > + bool hwts_rx_en;
>
> I see that the hwts_rx_en gets removed in the later patch. Instead
> you
> could add rx filters support only later when you have the final code
> in place.

In RFC v2, this patch 2 and next patch 3 are in single patch. There are
two simple reasons, I had splitted them two. One is to have logical
commit and ease of code review. Patch 2 will add the hwtstamp_set and
get dsa hooks and patch 3 to mechanism to coordinate with the tag_ksz.c
to add additional 4 bytes in tail tag when PTP is enabled based on
tagger_data.
Second reason to split is to have patch authorship. I had used the
Christian Eggers patch and extending support for LAN937x. In the
initial series, it has dsa_port->priv variable to store the
ptp_shared_data. Now priv variable is removed from dsa_port so I used
tagger_data based on sja1105 implementation. As it is different from
intial patch series, moved to separate patch.

>
> > +#endif
> > };
> >
> >
> >
> >
> >
> > #endif
> > --
> > 2.36.1
> >

2022-11-30 05:15:05

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 11/12] net: dsa: microchip: ptp: add periodic output signal

On Tue, 2022-11-29 at 15:27 +0530, Pavan Chebbi wrote:
> On Tue, Nov 29, 2022 at 2:23 PM Pavan Chebbi <
> [email protected]> wrote:
> >
> > On Mon, Nov 28, 2022 at 4:05 PM Arun Ramadoss
> > <[email protected]> wrote:
> >
> > > +static int ksz_ptp_enable(struct ptp_clock_info *ptp,
> > > + struct ptp_clock_request *req, int on)
> > > +{
> > > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > > + struct ptp_perout_request *request = &req->perout;
> > > + int ret;
> > > +
> > > + switch (req->type) {
> > > + case PTP_CLK_REQ_PEROUT:
> > > + if (request->index > ptp->n_per_out)
> > > + return -EINVAL;
> >
> > Should be -EOPNOTSUPP ? I see some other places where -EOPNOTSUPP
> > is
> > more appropriate.
> >
> > > +
> > > + mutex_lock(&ptp_data->lock);
> > > + ret = ksz_ptp_enable_perout(dev, request, on);
> > > + mutex_unlock(&ptp_data->lock);
> > > + break;
> > > + default:
> > > + return -EINVAL;
>
> OK I really meant here.

Ok. I will update -EINVAL to -EOPNOTSUPP.

>
> > > + }
> > > +
> > > + return ret;
> > > +}
> > > +
> > > /* Function is pointer to the do_aux_work in the ptp_clock
> > > capability */
> > > static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> > > {
> > > @@ -508,6 +823,8 @@ static const struct ptp_clock_info
> > > ksz_ptp_caps = {
> > > .adjfine = ksz_ptp_adjfine,
> > > .adjtime = ksz_ptp_adjtime,
> > > .do_aux_work = ksz_ptp_do_aux_work,
> > > + .enable = ksz_ptp_enable,
> > > + .n_per_out = 3,
> > > };
> > >

2022-11-30 05:33:34

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

Hi Pavan,

On Mon, 2022-11-28 at 20:19 +0530, Pavan Chebbi wrote:
> On Mon, Nov 28, 2022 at 4:03 PM Arun Ramadoss
> <[email protected]> wrote:
>
> > diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h
> > b/drivers/net/dsa/microchip/ksz_ptp_reg.h
> > new file mode 100644
> > index 000000000000..e578a0006ecf
> > --- /dev/null
> > +++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
> > @@ -0,0 +1,57 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/* Microchip KSZ PTP register definitions
> > + * Copyright (C) 2022 Microchip Technology Inc.
> > + */
> > +
> > +#ifndef __KSZ_PTP_REGS_H
> > +#define __KSZ_PTP_REGS_H
> > +
> > +/* 5 - PTP Clock */
> > +#define REG_PTP_CLK_CTRL 0x0500
> > +
> > +#define PTP_STEP_ADJ BIT(6)
> > +#define PTP_STEP_DIR BIT(5)
> > +#define PTP_READ_TIME BIT(4)
> > +#define PTP_LOAD_TIME BIT(3)
>
> PTP_WRITE_TIME sounds more intuitive than PTP_LOAD_TIME?

To have similar naming convention between code and Datasheet it is
named as Load time.

> Also I see that all the #defines are introduced in this patch, some
> of
> which are used later. It is a good idea to introduce the #defines in
> the same patches where they are being used for the first time.
> I will be looking at the entire series but am responding to this now.

The patches are splitted into multiple smaller patches for review. I
missed to move header file defines to appropriate patches. Sure I will
move it.

>
> > +#define PTP_CLK_ADJ_ENABLE BIT(2)
> > +#define PTP_CLK_ENABLE BIT(1)
> > +#define PTP_CLK_RESET BIT(0)
> > +
> > +#define REG_PTP_RTC_SUB_NANOSEC__2 0x0502
> > +
> > +#define PTP_RTC_SUB_NANOSEC_M 0x0007
> > +#define PTP_RTC_0NS 0x00
> > +
> > +#define REG_PTP_RTC_NANOSEC 0x0504
> > +#define REG_PTP_RTC_NANOSEC_H 0x0504
> > +#define REG_PTP_RTC_NANOSEC_L 0x0506
> > +
> > +#define REG_PTP_RTC_SEC 0x0508
> > +#define REG_PTP_RTC_SEC_H 0x0508
> > +#define REG_PTP_RTC_SEC_L 0x050A
> > +
> >
> >
> > +#endif
> > --
> > 2.36.1
> >

2022-11-30 05:38:38

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 04/12] net: dsa: microchip: ptp: Manipulating absolute time using ptp hw clock

Hi Pavan,
Thanks for the review comment.

On Tue, 2022-11-29 at 14:13 +0530, Pavan Chebbi wrote:
> On Mon, Nov 28, 2022 at 4:04 PM Arun Ramadoss
> <[email protected]> wrote:
> > +/* Function is pointer to the do_aux_work in the ptp_clock
> > capability */
> > +static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> > +{
> > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > + struct timespec64 ts;
> > +
> > + mutex_lock(&ptp_data->lock);
> > + _ksz_ptp_gettime(dev, &ts);
> > + mutex_unlock(&ptp_data->lock);
> > +
> > + spin_lock_bh(&ptp_data->clock_lock);
> > + ptp_data->clock_time = ts;
> > + spin_unlock_bh(&ptp_data->clock_lock);
>
> If I understand this correctly, the software clock is updated with
> full 64b every 1s. However only 32b timestamp registers are read
> while
> processing packets and higher bits from this clock are used.
> How do you ensure these higher order bits are in sync with the higher
> order bits in the HW? IOW, what if lower 32b have wrapped around and
> you are required to stamp a packet but you still don't have aux
> worker
> updated.

The Ptp Hardware Clock (PHC) seconds register is 32 bit wide. To have
register overflow it takes 4,294,967,296 seconds which is approximately
around 136 Years. So, it is bigger value and assume that we don't need
to care of PHC second register overflow.
For the packet timestamping, value is read from 32 bit register. This
register is splited into 2 bits seconds + 30 bits nanoseconds register.
In the ksz_tstamp_reconstruct function, lower 2 bits in the ptp_data-
>clock_time is cleared and 2 bits from the timestamp register are
added.

spin_lock_bh(&ptp_data->clock_lock);
ptp_clock_time = ptp_data->clock_time;
spin_unlock_bh(&ptp_data->clock_lock);

/* calculate full time from partial time stamp */
ts.tv_sec = (ptp_clock_time.tv_sec & ~3) | ts.tv_sec;

>
> > +
> > + return HZ; /* reschedule in 1 second */
> > +}
> > +
> > static int ksz_ptp_start_clock(struct ksz_device *dev)
> > {
> > - return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE,
> > PTP_CLK_ENABLE);
> > + struct ksz_ptp_data *ptp_data = &dev->ptp_data;
> > + int ret;
> > +
> > + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE,
> > PTP_CLK_ENABLE);
> > + if (ret)
> > + return ret;
> > +
> > + spin_lock_bh(&ptp_data->clock_lock);
> > + ptp_data->clock_time.tv_sec = 0;
> > + ptp_data->clock_time.tv_nsec = 0;
> > + spin_unlock_bh(&ptp_data->clock_lock);
> > +
> > + return 0;
> > }
> >
> >
> > --
> > 2.36.1
> >

2022-11-30 06:33:38

by Pavan Chebbi

[permalink] [raw]
Subject: Re: [Patch net-next v1 04/12] net: dsa: microchip: ptp: Manipulating absolute time using ptp hw clock

On Wed, Nov 30, 2022 at 9:52 AM <[email protected]> wrote:
>
> Hi Pavan,
> Thanks for the review comment.
>
> On Tue, 2022-11-29 at 14:13 +0530, Pavan Chebbi wrote:
> > On Mon, Nov 28, 2022 at 4:04 PM Arun Ramadoss
> > <[email protected]> wrote:
> > > +/* Function is pointer to the do_aux_work in the ptp_clock
> > > capability */
> > > +static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> > > +{
> > > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > > + struct timespec64 ts;
> > > +
> > > + mutex_lock(&ptp_data->lock);
> > > + _ksz_ptp_gettime(dev, &ts);
> > > + mutex_unlock(&ptp_data->lock);
> > > +
> > > + spin_lock_bh(&ptp_data->clock_lock);
> > > + ptp_data->clock_time = ts;
> > > + spin_unlock_bh(&ptp_data->clock_lock);
> >
> > If I understand this correctly, the software clock is updated with
> > full 64b every 1s. However only 32b timestamp registers are read
> > while
> > processing packets and higher bits from this clock are used.
> > How do you ensure these higher order bits are in sync with the higher
> > order bits in the HW? IOW, what if lower 32b have wrapped around and
> > you are required to stamp a packet but you still don't have aux
> > worker
> > updated.
>
> The Ptp Hardware Clock (PHC) seconds register is 32 bit wide. To have
> register overflow it takes 4,294,967,296 seconds which is approximately
> around 136 Years. So, it is bigger value and assume that we don't need
> to care of PHC second register overflow.
> For the packet timestamping, value is read from 32 bit register. This
> register is splited into 2 bits seconds + 30 bits nanoseconds register.
> In the ksz_tstamp_reconstruct function, lower 2 bits in the ptp_data-
> >clock_time is cleared and 2 bits from the timestamp register are
> added.
>
> spin_lock_bh(&ptp_data->clock_lock);
> ptp_clock_time = ptp_data->clock_time;
> spin_unlock_bh(&ptp_data->clock_lock);
>
> /* calculate full time from partial time stamp */
> ts.tv_sec = (ptp_clock_time.tv_sec & ~3) | ts.tv_sec;
>
OK thanks. Looks like nano sec and seconds are not being stitched, if
that had happened the rollover would be very frequent. But stitching
the seconds with higher bits still has this problem.
But it should be OK since the window is so large.

> >
> > > +
> > > + return HZ; /* reschedule in 1 second */
> > > +}
> > > +
> > > static int ksz_ptp_start_clock(struct ksz_device *dev)
> > > {
> > > - return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE,
> > > PTP_CLK_ENABLE);
> > > + struct ksz_ptp_data *ptp_data = &dev->ptp_data;
> > > + int ret;
> > > +
> > > + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE,
> > > PTP_CLK_ENABLE);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + spin_lock_bh(&ptp_data->clock_lock);
> > > + ptp_data->clock_time.tv_sec = 0;
> > > + ptp_data->clock_time.tv_nsec = 0;
> > > + spin_unlock_bh(&ptp_data->clock_lock);
> > > +
> > > + return 0;
> > > }
> > >
> > >
> > > --
> > > 2.36.1
> > >


Attachments:
smime.p7s (4.11 kB)
S/MIME Cryptographic Signature

2022-11-30 23:10:45

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

On Mon, Nov 28, 2022 at 03:56:30PM +0100, Christian Eggers wrote:
> > > +#define PTP_LOAD_TIME BIT(3)
> >
> > PTP_WRITE_TIME sounds more intuitive than PTP_LOAD_TIME?
>
> PTP_LOAD_TIME has been derived from the data sheet:
>
> -------------8<--------------
> PTP Clock Load
> --------------
> Setting this bit will cause the PTP clock to be loaded with the time value in
> registers 0x0502 to 0x050B.
> ------------->8--------------
>
> I would also prefer PTP_WRITE_TIME. But is it ok to deviate from data sheet?

It depends. When the datasheet has succint and uniquely identifiable
names for registers, there's no reason to not use them. Exceptions are
obnoxious things like "BASIC_MODE_CONTROL_REGISTER" or "MDIO_CONTROL_1"
which get abbreviated in kernel code to "BMCR" and "MDIO_CTRL1".

When the register names in the datasheet are literally prose ("PTP Clock Load",
with spaces and all), some divergence from the datasheet will have to
exist no matter what you do. So I guess you can just go for what makes
the most sense and is in line with existing kernel conventions. People
who cross-reference kernel definitions with the datasheet will still
have a hell of a life, but hey, you can tell them it's not your fault
you can't name a C variable "PTP Clock Load".

OTOH, I don't find "PTP_LOAD_TIME" unintuitive, but I won't oppose a
rename if there's agreement from people who care more than me.

2022-12-01 01:01:25

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

On Mon, Nov 28, 2022 at 04:02:16PM +0530, Arun Ramadoss wrote:
> From: Christian Eggers <[email protected]>
>
> This patch implement routines (adjfine, adjtime, gettime and settime)
> for manipulating the chip's PTP clock. It registers the ptp caps
> to posix clock register.
>
> Signed-off-by: Christian Eggers <[email protected]>
> Co-developed-by: Arun Ramadoss <[email protected]>
> Signed-off-by: Arun Ramadoss <[email protected]>
>
> ---
> RFC v2 -> Patch v1
> - Repharsed the Kconfig help text
> - Removed IS_ERR_OR_NULL check in ptp_clock_unregister
> - Add the check for ptp_data->clock in ksz_ptp_ts_info
> - Renamed MAX_DRIFT_CORR to KSZ_MAX_DRIFT_CORR
> - Removed the comments
> - Variables declaration in reverse christmas tree
> - Added the ptp_clock_optional
> ---
> diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
> index c6726cbd5465..5a6bfd42c6f9 100644
> --- a/drivers/net/dsa/microchip/ksz_common.h
> +++ b/drivers/net/dsa/microchip/ksz_common.h
> @@ -444,6 +447,19 @@ static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value)
> return ret;
> }
>
> +static inline int ksz_rmw16(struct ksz_device *dev, u32 reg, u16 mask,
> + u16 value)
> +{
> + int ret;
> +
> + ret = regmap_update_bits(dev->regmap[1], reg, mask, value);
> + if (ret)
> + dev_err(dev->dev, "can't rmw 16bit reg: 0x%x %pe\n", reg,
> + ERR_PTR(ret));

Is the colon misplaced? What do you want to say, "can't rmw 16bit reg: 0x0 -EIO",
or "can't rmw 16bit reg 0x0: -EIO"?

Reminds me of a joke:
"The inventor of the Oxford comma has died. Tributes have been led by
J.K. Rowling, his wife and the Queen of England".

> +
> + return ret;
> +}
> +
> static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value)
> {
> u32 val[2];
> +static int ksz_ptp_settime(struct ptp_clock_info *ptp,
> + const struct timespec64 *ts)
> +{
> + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> + int ret;
> +
> + mutex_lock(&ptp_data->lock);
> +
> + /* Write to shadow registers and Load PTP clock */
> + ret = ksz_write16(dev, REG_PTP_RTC_SUB_NANOSEC__2, PTP_RTC_0NS);
> + if (ret)
> + goto error_return;
> +
> + ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec);
> + if (ret)
> + goto error_return;
> +
> + ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec);
> + if (ret)
> + goto error_return;
> +
> + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
> +
> +error_return:

I would avoid naming labels with "error_", if the success code path is
also going to run through the code they point to. "goto unlock" sounds
about right.

> + mutex_unlock(&ptp_data->lock);
> +
> + return ret;
> +}
> +
> +static const struct ptp_clock_info ksz_ptp_caps = {
> + .owner = THIS_MODULE,
> + .name = "Microchip Clock",
> + .max_adj = KSZ_MAX_DRIFT_CORR,
> + .gettime64 = ksz_ptp_gettime,
> + .settime64 = ksz_ptp_settime,
> + .adjfine = ksz_ptp_adjfine,
> + .adjtime = ksz_ptp_adjtime,
> +};

Is it a conscious decision to have this structure declared here in the
.rodata section (I think that's where this goes?), when it will only be
used as a blueprint for the implicit memcpy (struct assignment) in
ksz_ptp_clock_register()?

Just saying that it would be possible to initialize the fields in
ptp_data->caps even without resorting to declaring one extra structure,
which consumes space. I'll leave you alone if you ACK that you know your
assignment below is a struct copy and not a pointer assignment.

> +
> +int ksz_ptp_clock_register(struct dsa_switch *ds)
> +{
> + struct ksz_device *dev = ds->priv;
> + struct ksz_ptp_data *ptp_data;
> + int ret;
> +
> + ptp_data = &dev->ptp_data;
> + mutex_init(&ptp_data->lock);
> +
> + ptp_data->caps = ksz_ptp_caps;
> +
> + ret = ksz_ptp_start_clock(dev);
> + if (ret)
> + return ret;
> +
> + ptp_data->clock = ptp_clock_register(&ptp_data->caps, dev->dev);
> + if (IS_ERR_OR_NULL(ptp_data->clock))
> + return PTR_ERR(ptp_data->clock);
> +
> + ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_802_1AS, PTP_802_1AS);
> + if (ret)
> + goto error_unregister_clock;

Registering a structure with a subsystem generally means that it becomes
immediately accessible to user space, and its (POSIX clock) ops are callable.

You haven't explained what PTP_802_1AS does, concretely, even though
I asked for a comment in the previous patch set. Is it okay for the PTP
clock to be registered while the PTP_802_1AS bit hasn't been yet written?
The first few operations might take place with it still unset.

I know what 802.1AS is, I just don't know what the register field does.

> +
> + return 0;
> +
> +error_unregister_clock:
> + ptp_clock_unregister(ptp_data->clock);
> + return ret;
> +}

2022-12-01 01:52:15

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [Patch net-next v1 04/12] net: dsa: microchip: ptp: Manipulating absolute time using ptp hw clock

On Mon, Nov 28, 2022 at 04:02:19PM +0530, Arun Ramadoss wrote:
> diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
> index 184aa57a8489..415522ef4ce9 100644
> --- a/drivers/net/dsa/microchip/ksz_ptp.c
> +++ b/drivers/net/dsa/microchip/ksz_ptp.c
> @@ -200,6 +209,12 @@ static int ksz_ptp_settime(struct ptp_clock_info *ptp,
> goto error_return;
>
> ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
> + if (ret)
> + goto error_return;
> +
> + spin_lock_bh(&ptp_data->clock_lock);

Why disable bottom halves? Where is the bottom half that this races with?

> + ptp_data->clock_time = *ts;
> + spin_unlock_bh(&ptp_data->clock_lock);
>
> error_return:
> mutex_unlock(&ptp_data->lock);
> @@ -254,6 +269,7 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
> {
> struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> + struct timespec64 delta64 = ns_to_timespec64(delta);
> s32 sec, nsec;
> u16 data16;
> int ret;
> @@ -286,15 +302,51 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
> data16 |= PTP_STEP_DIR;
>
> ret = ksz_write16(dev, REG_PTP_CLK_CTRL, data16);
> + if (ret)
> + goto error_return;
> +
> + spin_lock_bh(&ptp_data->clock_lock);
> + ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64);
> + spin_unlock_bh(&ptp_data->clock_lock);
>
> error_return:
> mutex_unlock(&ptp_data->lock);
> return ret;
> }
>
> +/* Function is pointer to the do_aux_work in the ptp_clock capability */
> +static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> +{
> + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> + struct timespec64 ts;
> +
> + mutex_lock(&ptp_data->lock);
> + _ksz_ptp_gettime(dev, &ts);
> + mutex_unlock(&ptp_data->lock);

Why don't you call ksz_ptp_gettime(ptp, &ts) directly?

> +
> + spin_lock_bh(&ptp_data->clock_lock);
> + ptp_data->clock_time = ts;
> + spin_unlock_bh(&ptp_data->clock_lock);
> +
> + return HZ; /* reschedule in 1 second */
> +}
> +
> static int ksz_ptp_start_clock(struct ksz_device *dev)
> {
> - return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
> + struct ksz_ptp_data *ptp_data = &dev->ptp_data;
> + int ret;
> +
> + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE, PTP_CLK_ENABLE);
> + if (ret)
> + return ret;
> +
> + spin_lock_bh(&ptp_data->clock_lock);
> + ptp_data->clock_time.tv_sec = 0;
> + ptp_data->clock_time.tv_nsec = 0;
> + spin_unlock_bh(&ptp_data->clock_lock);

Does ksz_ptp_start_clock() race with anything? The PTP clock has not
even been registered by the time this has been called. This is literally
an example of the "spin_lock_init(); spin_lock();" antipattern.

> +
> + return 0;
> }

2022-12-01 01:53:05

by Vladimir Oltean

[permalink] [raw]
Subject: Re: [Patch net-next v1 02/12] net: dsa: microchip: ptp: Initial hardware time stamping support

On Mon, Nov 28, 2022 at 04:02:17PM +0530, Arun Ramadoss wrote:
> diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
> index 5a6bfd42c6f9..cd20f39a565f 100644
> --- a/drivers/net/dsa/microchip/ksz_common.h
> +++ b/drivers/net/dsa/microchip/ksz_common.h
> @@ -103,6 +103,10 @@ struct ksz_port {
> struct ksz_device *ksz_dev;
> struct ksz_irq pirq;
> u8 num;
> +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
> + u8 hwts_tx_en;

Variable named "en" (enable) which takes the values 0 or 2? Not good.
Also, why is the type not enum hwtstamp_tx_types, but u8? Can't you name
this "enum hwtstamp_tx_types tx_type"?

> + bool hwts_rx_en;
> +#endif
> };
>
> struct ksz_device {
> diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
> index c737635ca266..a41418c6adf6 100644
> --- a/drivers/net/dsa/microchip/ksz_ptp.c
> +++ b/drivers/net/dsa/microchip/ksz_ptp.c
> @@ -36,15 +36,88 @@ int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts)
> SOF_TIMESTAMPING_RX_HARDWARE |
> SOF_TIMESTAMPING_RAW_HARDWARE;
>
> - ts->tx_types = BIT(HWTSTAMP_TX_OFF);
> + ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ONESTEP_P2P);
>
> - ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
> + ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
>
> ts->phc_index = ptp_clock_index(ptp_data->clock);
>
> return 0;
> }
>
> +int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
> +{
> + struct ksz_device *dev = ds->priv;
> + struct hwtstamp_config config;
> +
> + config.flags = 0;
> +
> + config.tx_type = dev->ports[port].hwts_tx_en;
> +
> + if (dev->ports[port].hwts_rx_en)
> + config.rx_filter = HWTSTAMP_FILTER_ALL;
> + else
> + config.rx_filter = HWTSTAMP_FILTER_NONE;
> +
> + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
> + -EFAULT : 0;
> +}
> +
> +static int ksz_set_hwtstamp_config(struct ksz_device *dev, int port,
> + struct hwtstamp_config *config)
> +{
> + struct ksz_port *prt = &dev->ports[port];
> +
> + if (config->flags)
> + return -EINVAL;
> +
> + switch (config->tx_type) {
> + case HWTSTAMP_TX_OFF:
> + case HWTSTAMP_TX_ONESTEP_P2P:
> + prt->hwts_tx_en = config->tx_type;
> + break;
> + default:
> + return -ERANGE;
> + }
> +
> + switch (config->rx_filter) {
> + case HWTSTAMP_FILTER_NONE:
> + prt->hwts_rx_en = false;
> + break;
> + default:
> + prt->hwts_rx_en = true;
> + break;
> + }
> +
> + return 0;
> +}
> +
> +int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
> +{
> + struct ksz_device *dev = ds->priv;
> + struct ksz_ptp_data *ptp_data;
> + struct hwtstamp_config config;
> + int ret;
> +
> + ptp_data = &dev->ptp_data;
> +
> + mutex_lock(&ptp_data->lock);

I'm not sure that this mutex serves any purpose at all?

One could have argued that concurrent calls to ksz_hwtstamp_get()
shouldn't be able to see incoherent values of prt->hwts_tx_en and of
prt->hwts_rx_en.

But ksz_hwtstamp_get() doesn't acquire this mutex, so that is not true,
this isn't why the mutex is acquired here. I don't know why it is.

> +
> + ret = copy_from_user(&config, ifr->ifr_data, sizeof(config));
> + if (ret)
> + goto error_return;
> +
> + ret = ksz_set_hwtstamp_config(dev, port, &config);
> + if (ret)
> + goto error_return;
> +
> + ret = copy_to_user(ifr->ifr_data, &config, sizeof(config));
> +
> +error_return:
> + mutex_unlock(&ptp_data->lock);
> + return ret;
> +}

2022-12-01 10:32:25

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 01/12] net: dsa: microchip: ptp: add the posix clock support

Hi Vladimir,

On Thu, 2022-12-01 at 02:17 +0200, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
>
> On Mon, Nov 28, 2022 at 04:02:16PM +0530, Arun Ramadoss wrote:
> > From: Christian Eggers <[email protected]>
> >
> > This patch implement routines (adjfine, adjtime, gettime and
> > settime)
> > for manipulating the chip's PTP clock. It registers the ptp caps
> > to posix clock register.
> >
> > Signed-off-by: Christian Eggers <[email protected]>
> > Co-developed-by: Arun Ramadoss <[email protected]>
> > Signed-off-by: Arun Ramadoss <[email protected]>
> >
> > ---
> > RFC v2 -> Patch v1
> > - Repharsed the Kconfig help text
> > - Removed IS_ERR_OR_NULL check in ptp_clock_unregister
> > - Add the check for ptp_data->clock in ksz_ptp_ts_info
> > - Renamed MAX_DRIFT_CORR to KSZ_MAX_DRIFT_CORR
> > - Removed the comments
> > - Variables declaration in reverse christmas tree
> > - Added the ptp_clock_optional
> > ---
> > diff --git a/drivers/net/dsa/microchip/ksz_common.h
> > b/drivers/net/dsa/microchip/ksz_common.h
> > index c6726cbd5465..5a6bfd42c6f9 100644
> > --- a/drivers/net/dsa/microchip/ksz_common.h
> > +++ b/drivers/net/dsa/microchip/ksz_common.h
> > @@ -444,6 +447,19 @@ static inline int ksz_write32(struct
> > ksz_device *dev, u32 reg, u32 value)
> > return ret;
> > }
> >
> > +static inline int ksz_rmw16(struct ksz_device *dev, u32 reg, u16
> > mask,
> > + u16 value)
> > +{
> > + int ret;
> > +
> > + ret = regmap_update_bits(dev->regmap[1], reg, mask, value);
> > + if (ret)
> > + dev_err(dev->dev, "can't rmw 16bit reg: 0x%x %pe\n",
> > reg,
> > + ERR_PTR(ret));
>
> Is the colon misplaced? What do you want to say, "can't rmw 16bit
> reg: 0x0 -EIO",
> or "can't rmw 16bit reg 0x0: -EIO"?
>
> Reminds me of a joke:
> "The inventor of the Oxford comma has died. Tributes have been led by
> J.K. Rowling, his wife and the Queen of England".

Its a copy paste problem. I reused the exisiting inline functions based
on patch *net: dsa: microchip: add support for regmap_access_tables*.
I will move the semicolon after 0x%x:

>
> > +
> > + return ret;
> > +}
> > +
> > static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64
> > value)
> > {
> > u32 val[2];
> > +static int ksz_ptp_settime(struct ptp_clock_info *ptp,
> > + const struct timespec64 *ts)
> > +{
> > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > + int ret;
> > +
> > + mutex_lock(&ptp_data->lock);
> > +
> > + /* Write to shadow registers and Load PTP clock */
> > + ret = ksz_write16(dev, REG_PTP_RTC_SUB_NANOSEC__2,
> > PTP_RTC_0NS);
> > + if (ret)
> > + goto error_return;
> > +
> > + ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec);
> > + if (ret)
> > + goto error_return;
> > +
> + ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec);
> > + if (ret)
> > + goto error_return;
> > +
> > + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME,
> > PTP_LOAD_TIME);
> > +
> > +error_return:
>
> I would avoid naming labels with "error_", if the success code path
> is
> also going to run through the code they point to. "goto unlock"
> sounds
> about right.

Ok. I will rename the goto block.

>
> > + mutex_unlock(&ptp_data->lock);
> > +
> > + return ret;
> > +}
> > +
> > +static const struct ptp_clock_info ksz_ptp_caps = {
> > + .owner = THIS_MODULE,
> > + .name = "Microchip Clock",
> > + .max_adj = KSZ_MAX_DRIFT_CORR,
> > + .gettime64 = ksz_ptp_gettime,
> > + .settime64 = ksz_ptp_settime,
> > + .adjfine = ksz_ptp_adjfine,
> > + .adjtime = ksz_ptp_adjtime,
> > +};
>
> Is it a conscious decision to have this structure declared here in
> the
> .rodata section (I think that's where this goes?), when it will only
> be
> used as a blueprint for the implicit memcpy (struct assignment) in
> ksz_ptp_clock_register()?

To reduce number of line in the ksz_ptp_clock_register(), I moved the
structure intialization outside of function. Referred other dsa
implementation found this type in
drivers/net/dsa/ocelot/felix_vsc9959.c, just reused it.
I didn't thought of .rodata section and memcpy overhead.

I will move this initialization within ksz_ptp_clock_register.

>
> Just saying that it would be possible to initialize the fields in
> ptp_data->caps even without resorting to declaring one extra
> structure,
> which consumes space. I'll leave you alone if you ACK that you know
> your
> assignment below is a struct copy and not a pointer assignment.
>
> > +
> > +int ksz_ptp_clock_register(struct dsa_switch *ds)
> > +{
> > + struct ksz_device *dev = ds->priv;
> > + struct ksz_ptp_data *ptp_data;
> > + int ret;
> > +
> > + ptp_data = &dev->ptp_data;
> > + mutex_init(&ptp_data->lock);
> > +
> > + ptp_data->caps = ksz_ptp_caps;
> > +
> > + ret = ksz_ptp_start_clock(dev);
> > + if (ret)
> > + return ret;
> > +
> > + ptp_data->clock = ptp_clock_register(&ptp_data->caps, dev-
> > >dev);
> > + if (IS_ERR_OR_NULL(ptp_data->clock))
> > + return PTR_ERR(ptp_data->clock);
> > +
> > + ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_802_1AS,
> > PTP_802_1AS);
> > + if (ret)
> > + goto error_unregister_clock;
>
> Registering a structure with a subsystem generally means that it
> becomes
> immediately accessible to user space, and its (POSIX clock) ops are
> callable.
>
> You haven't explained what PTP_802_1AS does, concretely, even though
> I asked for a comment in the previous patch set.

I overlooked the comment in the previous patch set. Christian also gave
offline comment that, this bit is reserved in KSZ9563 datasheet.
This bit should be set whenever we operate in 802.1AS(gPTP).
When this bit, then all the PTP packets will be forwared to host port
and none to other ports.
After changing my patch to include 1 step timestamping, I think it
should be set only for LAN937x 2 step mode.

> Is it okay for the PTP
> clock to be registered while the PTP_802_1AS bit hasn't been yet
> written?
> The first few operations might take place with it still unset.
>
> I know what 802.1AS is, I just don't know what the register field
> does.



>
> > +
> > + return 0;
> > +
> > +error_unregister_clock:
> > + ptp_clock_unregister(ptp_data->clock);
> > + return ret;
> > +}

2022-12-01 10:57:42

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 02/12] net: dsa: microchip: ptp: Initial hardware time stamping support

Hi Vladimir,

On Thu, 2022-12-01 at 02:39 +0200, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
>
> On Mon, Nov 28, 2022 at 04:02:17PM +0530, Arun Ramadoss wrote:
> > diff --git a/drivers/net/dsa/microchip/ksz_common.h
> > b/drivers/net/dsa/microchip/ksz_common.h
> > index 5a6bfd42c6f9..cd20f39a565f 100644
> > --- a/drivers/net/dsa/microchip/ksz_common.h
> > +++ b/drivers/net/dsa/microchip/ksz_common.h
> > @@ -103,6 +103,10 @@ struct ksz_port {
> > struct ksz_device *ksz_dev;
> > struct ksz_irq pirq;
> > u8 num;
> > +#if IS_ENABLED(CONFIG_NET_DSA_MICROCHIP_KSZ_PTP)
> > + u8 hwts_tx_en;
>
> Variable named "en" (enable) which takes the values 0 or 2? Not good.
> Also, why is the type not enum hwtstamp_tx_types, but u8? Can't you
> name
> this "enum hwtstamp_tx_types tx_type"?
>
> > + bool hwts_rx_en;
> > +#endif
> > };

I will rename variable.

> >
> > struct ksz_device {
> > diff --git a/drivers/net/dsa/microchip/ksz_ptp.c
> > b/drivers/net/dsa/microchip/ksz_ptp.c
> > index c737635ca266..a41418c6adf6 100644
> > --- a/drivers/net/dsa/microchip/ksz_ptp.c
> > +++ b/drivers/net/dsa/microchip/ksz_ptp.c
> > @@ -36,15 +36,88 @@ int ksz_get_ts_info(struct dsa_switch *ds, int
> > port, struct ethtool_ts_info *ts)
> > SOF_TIMESTAMPING_RX_HARDWARE |
> > SOF_TIMESTAMPING_RAW_HARDWARE;
> >
> > - ts->tx_types = BIT(HWTSTAMP_TX_OFF);
> > + ts->tx_types = BIT(HWTSTAMP_TX_OFF) |
> > BIT(HWTSTAMP_TX_ONESTEP_P2P);
> >
> > - ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE);
> > + ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
> > BIT(HWTSTAMP_FILTER_ALL);
> >
> > ts->phc_index = ptp_clock_index(ptp_data->clock);
> >
> > return 0;
> > }
> >
> > +int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq
> > *ifr)
> > +{
> > + struct ksz_device *dev = ds->priv;
> > + struct hwtstamp_config config;
> > +
> > + config.flags = 0;
> > +
> > + config.tx_type = dev->ports[port].hwts_tx_en;
> > +
> > + if (dev->ports[port].hwts_rx_en)
> > + config.rx_filter = HWTSTAMP_FILTER_ALL;
> > + else
> > + config.rx_filter = HWTSTAMP_FILTER_NONE;
> > +
> > + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
> > + -EFAULT : 0;
> > +}
> > +
> > +static int ksz_set_hwtstamp_config(struct ksz_device *dev, int
> > port,
> > + struct hwtstamp_config *config)
> > +{
> > + struct ksz_port *prt = &dev->ports[port];
> > +
> > + if (config->flags)
> > + return -EINVAL;
> > +
> > + switch (config->tx_type) {
> > + case HWTSTAMP_TX_OFF:
> > + case HWTSTAMP_TX_ONESTEP_P2P:
> > + prt->hwts_tx_en = config->tx_type;
> > + break;
> > + default:
> > + return -ERANGE;
> > + }
> > +
> > + switch (config->rx_filter) {
> > + case HWTSTAMP_FILTER_NONE:
> > + prt->hwts_rx_en = false;
> > + break;
> > + default:
> > + prt->hwts_rx_en = true;
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq
> > *ifr)
> > +{
> > + struct ksz_device *dev = ds->priv;
> > + struct ksz_ptp_data *ptp_data;
> > + struct hwtstamp_config config;
> > + int ret;
> > +
> > + ptp_data = &dev->ptp_data;
> > +
> > + mutex_lock(&ptp_data->lock);
>
> I'm not sure that this mutex serves any purpose at all?
>
> One could have argued that concurrent calls to ksz_hwtstamp_get()
> shouldn't be able to see incoherent values of prt->hwts_tx_en and of
> prt->hwts_rx_en.
>
> But ksz_hwtstamp_get() doesn't acquire this mutex, so that is not
> true,
> this isn't why the mutex is acquired here. I don't know why it is.

Mutex is not needed. I will remove it.

>
> > +
> > + ret = copy_from_user(&config, ifr->ifr_data, sizeof(config));
> > + if (ret)
> > + goto error_return;
> > +
> > + ret = ksz_set_hwtstamp_config(dev, port, &config);
> > + if (ret)
> > + goto error_return;
> > +
> > + ret = copy_to_user(ifr->ifr_data, &config, sizeof(config));
> > +
> > +error_return:
> > + mutex_unlock(&ptp_data->lock);
> > + return ret;
> > +}

2022-12-02 10:35:46

by Arun Ramadoss

[permalink] [raw]
Subject: Re: [Patch net-next v1 04/12] net: dsa: microchip: ptp: Manipulating absolute time using ptp hw clock

Hi Vladimir,
Thanks for the review comment.

On Thu, 2022-12-01 at 03:04 +0200, Vladimir Oltean wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you
> know the content is safe
>
> On Mon, Nov 28, 2022 at 04:02:19PM +0530, Arun Ramadoss wrote:
> > diff --git a/drivers/net/dsa/microchip/ksz_ptp.c
> > b/drivers/net/dsa/microchip/ksz_ptp.c
> > index 184aa57a8489..415522ef4ce9 100644
> > --- a/drivers/net/dsa/microchip/ksz_ptp.c
> > +++ b/drivers/net/dsa/microchip/ksz_ptp.c
> > @@ -200,6 +209,12 @@ static int ksz_ptp_settime(struct
> > ptp_clock_info *ptp,
> > goto error_return;
> >
> > ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME,
> > PTP_LOAD_TIME);
> > + if (ret)
> > + goto error_return;
> > +
> > + spin_lock_bh(&ptp_data->clock_lock);
>
> Why disable bottom halves? Where is the bottom half that this races
> with?

The interrupts are added in the following patches in the series. During
the deferred packet timestamping, partial timestamps are reconstructed
to absolute time using the ptp_data->clock_time in the bottom halves.
So we need this spin_lock_bh.

>
> > + ptp_data->clock_time = *ts;
> > + spin_unlock_bh(&ptp_data->clock_lock);
> >
> > error_return:
> > mutex_unlock(&ptp_data->lock);
> > }
> >
> > +/* Function is pointer to the do_aux_work in the ptp_clock
> > capability */
> > +static long ksz_ptp_do_aux_work(struct ptp_clock_info *ptp)
> > +{
> > + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
> > + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
> > + struct timespec64 ts;
> > +
> > + mutex_lock(&ptp_data->lock);
> > + _ksz_ptp_gettime(dev, &ts);
> > + mutex_unlock(&ptp_data->lock);
>
> Why don't you call ksz_ptp_gettime(ptp, &ts) directly?

I will use ksz_ptp_gettime directly.

>
> > +
> > + spin_lock_bh(&ptp_data->clock_lock);
> > + ptp_data->clock_time = ts;
> > + spin_unlock_bh(&ptp_data->clock_lock);
> > +
> > + return HZ; /* reschedule in 1 second */
> > +}
> > +
> > static int ksz_ptp_start_clock(struct ksz_device *dev)
> > {
> > - return ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE,
> > PTP_CLK_ENABLE);
> > + struct ksz_ptp_data *ptp_data = &dev->ptp_data;
> > + int ret;
> > +
> > + ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ENABLE,
> > PTP_CLK_ENABLE);
> > + if (ret)
> > + return ret;
> > +
> > + spin_lock_bh(&ptp_data->clock_lock);
> > + ptp_data->clock_time.tv_sec = 0;
> > + ptp_data->clock_time.tv_nsec = 0;
> > + spin_unlock_bh(&ptp_data->clock_lock);
>
> Does ksz_ptp_start_clock() race with anything? The PTP clock has not
> even been registered by the time this has been called. This is
> literally
> an example of the "spin_lock_init(); spin_lock();" antipattern.

Yes, this function is called before PTP clock registeration. I will
remove the spin_lock for here.

>
> > +
> > + return 0;
> > }