net: dsa: microchip: ptp: add the posix clock support
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 <ceggers@arri.de> Co-developed-by: Arun Ramadoss <arun.ramadoss@microchip.com> Signed-off-by: Arun Ramadoss <arun.ramadoss@microchip.com> Reviewed-by: Vladimir Oltean <olteanv@gmail.com> # mostly api Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
72863e08c3
commit
eac1ea2026
@ -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
|
||||
|
@ -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
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <net/switchdev.h>
|
||||
|
||||
#include "ksz_common.h"
|
||||
#include "ksz_ptp.h"
|
||||
#include "ksz8.h"
|
||||
#include "ksz9477.h"
|
||||
#include "lan937x.h"
|
||||
@ -2102,10 +2103,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 */
|
||||
@ -2114,6 +2121,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)
|
||||
@ -2130,6 +2139,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);
|
||||
|
@ -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;
|
||||
@ -140,6 +142,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 */
|
||||
@ -443,6 +446,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];
|
||||
|
236
drivers/net/dsa/microchip/ksz_ptp.c
Normal file
236
drivers/net/dsa/microchip/ksz_ptp.c
Normal file
@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Microchip KSZ PTP Implementation
|
||||
*
|
||||
* Copyright (C) 2020 ARRI Lighting
|
||||
* 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)
|
||||
|
||||
/* Sub-nanoseconds-adj,max * sub-nanoseconds / 40ns * 1ns
|
||||
* = (2^30-1) * (2 ^ 32) / 40 ns * 1 ns = 6249999
|
||||
*/
|
||||
#define KSZ_MAX_DRIFT_CORR 6249999
|
||||
|
||||
#define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */
|
||||
#define KSZ_PTP_SUBNS_BITS 32
|
||||
|
||||
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 unlock;
|
||||
|
||||
ret = ksz_write32(dev, REG_PTP_RTC_NANOSEC, ts->tv_nsec);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_write32(dev, REG_PTP_RTC_SEC, ts->tv_sec);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_LOAD_TIME, PTP_LOAD_TIME);
|
||||
|
||||
unlock:
|
||||
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);
|
||||
u64 base, adj;
|
||||
bool negative;
|
||||
u32 data32;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ptp_data->lock);
|
||||
|
||||
if (scaled_ppm) {
|
||||
base = KSZ_PTP_INC_NS << KSZ_PTP_SUBNS_BITS;
|
||||
negative = diff_by_scaled_ppm(base, scaled_ppm, &adj);
|
||||
|
||||
data32 = (u32)adj;
|
||||
data32 &= PTP_SUBNANOSEC_M;
|
||||
if (!negative)
|
||||
data32 |= PTP_RATE_DIR;
|
||||
|
||||
ret = ksz_write32(dev, REG_PTP_SUBNANOSEC_RATE, data32);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE,
|
||||
PTP_CLK_ADJ_ENABLE);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
} else {
|
||||
ret = ksz_rmw16(dev, REG_PTP_CLK_CTRL, PTP_CLK_ADJ_ENABLE, 0);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
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 unlock;
|
||||
|
||||
ret = ksz_write32(dev, REG_PTP_RTC_SEC, abs(sec));
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_read16(dev, REG_PTP_CLK_CTRL, &data16);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
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);
|
||||
|
||||
unlock:
|
||||
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);
|
||||
}
|
||||
|
||||
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.owner = THIS_MODULE;
|
||||
snprintf(ptp_data->caps.name, 16, "Microchip Clock");
|
||||
ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR;
|
||||
ptp_data->caps.gettime64 = ksz_ptp_gettime;
|
||||
ptp_data->caps.settime64 = ksz_ptp_settime;
|
||||
ptp_data->caps.adjfine = ksz_ptp_adjfine;
|
||||
ptp_data->caps.adjtime = ksz_ptp_adjtime;
|
||||
|
||||
ret = ksz_ptp_start_clock(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Currently only P2P mode is supported. When 802_1AS bit is set, it
|
||||
* forwards all PTP packets to host port and none to other ports.
|
||||
*/
|
||||
ret = ksz_rmw16(dev, REG_PTP_MSG_CONF1, PTP_TC_P2P | PTP_802_1AS,
|
||||
PTP_TC_P2P | PTP_802_1AS);
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 <ceggers@arri.de>");
|
||||
MODULE_AUTHOR("Arun Ramadoss <arun.ramadoss@microchip.com>");
|
||||
MODULE_DESCRIPTION("PTP support for KSZ switch");
|
||||
MODULE_LICENSE("GPL");
|
42
drivers/net/dsa/microchip/ksz_ptp.h
Normal file
42
drivers/net/dsa/microchip/ksz_ptp.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Microchip KSZ PTP Implementation
|
||||
*
|
||||
* Copyright (C) 2020 ARRI Lighting
|
||||
* 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);
|
||||
|
||||
#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) { }
|
||||
|
||||
#endif /* End of CONFIG_NET_DSA_MICROCHIP_KSZ_PTP */
|
||||
|
||||
#endif
|
52
drivers/net/dsa/microchip/ksz_ptp_reg.h
Normal file
52
drivers/net/dsa/microchip/ksz_ptp_reg.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* 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_SEC 0x0508
|
||||
|
||||
#define REG_PTP_SUBNANOSEC_RATE 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
|
Loading…
Reference in New Issue
Block a user