Subject: Re: [PATCH net-next v5 2/2] ptp: add FemtoClock3 Wireless as ptp hardware clock

From: Lee Jones <[email-masked]>
Date: Thu, 21 Dec 2023 16:16:04 +0000

On Wed, 20 Dec 2023, Min Li wrote:

> From: Min Li <[email-masked]>
> 
> The RENESAS FemtoClock3 Wireless is a high-performance jitter attenuator,
> frequency translator, and clock synthesizer. The device is comprised of 3
> digital PLLs (DPLL) to track CLKIN inputs and three independent low phase
> noise fractional output dividers (FOD) that output low phase noise clocks.
> 
> FemtoClock3 supports one Time Synchronization (Time Sync) channel to enable
> an external processor to control the phase and frequency of the Time Sync
> channel and to take phase measurements using the TDC. Intended applications
> are synchronization using the precision time protocol (PTP) and
> synchronization with 0.5 Hz and 1 Hz signals from GNSS.
> 
> Signed-off-by: Min Li <[email-masked]>
> ---
>  drivers/ptp/Kconfig                |   12 +
>  drivers/ptp/Makefile               |    1 +
>  drivers/ptp/ptp_fc3.c              | 1036 ++++++++++++++++++++++++++++
>  drivers/ptp/ptp_fc3.h              |   45 ++
>  include/linux/mfd/idtRC38xxx_reg.h |  273 ++++++++

Why MFD?  Where else is this being used?

>  5 files changed, 1367 insertions(+)
>  create mode 100644 drivers/ptp/ptp_fc3.c
>  create mode 100644 drivers/ptp/ptp_fc3.h
>  create mode 100644 include/linux/mfd/idtRC38xxx_reg.h
> 
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 5dd5f188e..604541dcb 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -155,6 +155,18 @@ config PTP_1588_CLOCK_IDTCM
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called ptp_clockmatrix.
>  
> +config PTP_1588_CLOCK_FC3W
> +	tristate "RENESAS FemtoClock3 Wireless as PTP clock"
> +	depends on PTP_1588_CLOCK && I2C
> +	default n
> +	help
> +	  This driver adds support for using Renesas FemtoClock3 Wireless
> +	  as a PTP clock. This clock is only useful if your time stamping
> +	  MAC is connected to the RENESAS chip.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called ptp_fc3.
> +
>  config PTP_1588_CLOCK_MOCK
>  	tristate "Mock-up PTP clock"
>  	depends on PTP_1588_CLOCK
> diff --git a/drivers/ptp/ptp_fc3.h b/drivers/ptp/ptp_fc3.h
> new file mode 100644
> index 000000000..897101579
> --- /dev/null
> +++ b/drivers/ptp/ptp_fc3.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * PTP hardware clock driver for the FemtoClock3 family of timing and
> + * synchronization devices.
> + *
> + * Copyright (C) 2023 Integrated Device Technology, Inc., a Renesas Company.
> + */
> +#ifndef PTP_IDTFC3_H
> +#define PTP_IDTFC3_H
> +
> +#include <linux/ktime.h>
> +#include <linux/ptp_clock.h>
> +#include <linux/regmap.h>
> +
> +#define FW_FILENAME	"idtfc3.bin"
> +
> +#define MAX_FFO_PPB	(244000)
> +#define TDC_GET_PERIOD	(10)
> +
> +struct idtfc3 {
> +	struct ptp_clock_info	caps;
> +	struct ptp_clock	*ptp_clock;
> +	struct device		*dev;
> +	/* Mutex to protect operations from being interrupted */
> +	struct mutex		*lock;
> +	struct device		*mfd;

parent

Also, you can derive this from 'dev' above.  You don't need both.

> +	struct regmap		*regmap;
> +	struct idtfc3_hw_param	hw_param;
> +	u32			sub_sync_count;
> +	u32			ns_per_sync;
> +	int			tdc_offset_sign;
> +	u64			tdc_apll_freq;
> +	u32			time_ref_freq;
> +	u16			fod_n;
> +	u8			lpf_mode;
> +	/* Time counter */
> +	u32			last_counter;
> +	s64			ns;
> +	u32			ns_per_counter;
> +	u32			tc_update_period;
> +	u32			tc_write_timeout;
> +	s64			tod_write_overhead;
> +};
> +
> +#endif /* PTP_IDTFC3_H */
> diff --git a/include/linux/mfd/idtRC38xxx_reg.h b/include/linux/mfd/idtRC38xxx_reg.h
> new file mode 100644
> index 000000000..edddaae61
> --- /dev/null
> +++ b/include/linux/mfd/idtRC38xxx_reg.h
> @@ -0,0 +1,273 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Register Map - Based on PolarBear_CSRs.RevA.xlsx (2023-04-21)
> + *
> + * Copyright (C) 2023 Integrated Device Technology, Inc., a Renesas Company.
> + */
> +#ifndef HAVE_IDTRC38XXX_REG
> +#define HAVE_IDTRC38XXX_REG

MFD not HAVE.

> +
> +/* GLOBAL */

Everything in here is global?

> +#define SOFT_RESET_CTRL		(0x15) /* Specific to FC3W */
> +#define MISC_CTRL		(0x14) /* Specific to FC3A */
> +#define APLL_REINIT		BIT(1)
> +#define APLL_REINIT_VFC3A	BIT(2)
> +
> +#define DEVICE_ID		(0x2)
> +#define DEVICE_ID_MASK		(0x1000) /* Bit 12 is 1 if FC3W and 0 if FC3A */
> +#define DEVICE_ID_SHIFT		(12)
> +
> +/* FOD */
> +#define FOD_0		(0x300)
> +#define FOD_0_VFC3A	(0x400)
> +#define FOD_1		(0x340)
> +#define FOD_1_VFC3A	(0x440)
> +#define FOD_2		(0x380)
> +#define FOD_2_VFC3A	(0x480)
> +
> +/* TDCAPLL */
> +#define TDC_CTRL		(0x44a) /* Specific to FC3W */
> +#define TDC_ENABLE_CTRL		(0x169) /* Specific to FC3A */
> +#define TDC_DAC_CAL_CTRL	(0x16a) /* Specific to FC3A */
> +#define TDC_EN			BIT(0)
> +#define TDC_DAC_RECAL_REQ	BIT(1)
> +#define TDC_DAC_RECAL_REQ_VFC3A	BIT(0)
> +
> +#define TDC_FB_DIV_INT_CNFG		(0x442)
> +#define TDC_FB_DIV_INT_CNFG_VFC3A	(0x162)
> +#define TDC_FB_DIV_INT_MASK		GENMASK(7, 0)
> +#define TDC_REF_DIV_CNFG		(0x443)
> +#define TDC_REF_DIV_CNFG_VFC3A		(0x163)
> +#define TDC_REF_DIV_CONFIG_MASK		GENMASK(2, 0)
> +
> +/* TIME SYNC CHANNEL */
> +#define TIME_CLOCK_SRC		(0xa01) /* Specific to FC3W */
> +#define TIME_CLOCK_COUNT	(0xa00) /* Specific to FC3W */
> +#define TIME_CLOCK_COUNT_MASK	GENMASK(5, 0)
> +
> +#define SUB_SYNC_GEN_CNFG	(0xa04)
> +
> +#define TOD_COUNTER_READ_REQ		(0xa5f)
> +#define TOD_COUNTER_READ_REQ_VFC3A	(0x6df)
> +#define TOD_SYNC_LOAD_VAL_CTRL		(0xa10)
> +#define TOD_SYNC_LOAD_VAL_CTRL_VFC3A	(0x690)
> +#define SYNC_COUNTER_MASK		GENMASK(51, 0)
> +#define SUB_SYNC_COUNTER_MASK		GENMASK(30, 0)
> +#define TOD_SYNC_LOAD_REQ_CTRL		(0xa21)
> +#define TOD_SYNC_LOAD_REQ_CTRL_VFC3A	(0x6a1)
> +#define SYNC_LOAD_ENABLE		BIT(1)
> +#define SUB_SYNC_LOAD_ENABLE		BIT(0)
> +#define SYNC_LOAD_REQ			BIT(0)
> +
> +#define LPF_MODE_CNFG		(0xa80)
> +#define LPF_MODE_CNFG_VFC3A	(0x700)
> +enum lpf_mode {
> +	LPF_DISABLED = 0,
> +	LPF_WP       = 1,
> +	LPF_HOLDOVER = 2,
> +	LPF_WF       = 3,
> +	LPF_INVALID  = 4
> +};
> +#define LPF_CTRL	(0xa98)
> +#define LPF_CTRL_VFC3A	(0x718)
> +#define LPF_EN		BIT(0)
> +
> +#define LPF_BW_CNFG	(0xa81)
> +#define LPF_BW_SHIFT	GENMASK(7, 3)
> +#define LPF_BW_MULT		GENMASK(2, 0)
> +#define LPF_BW_SHIFT_DEFAULT	(0xb)
> +#define LPF_BW_MULT_DEFAULT		(0x0)
> +#define LPF_BW_SHIFT_1PPS		(0x5)
> +
> +#define LPF_WR_PHASE_CTRL	(0xaa8)
> +#define LPF_WR_PHASE_CTRL_VFC3A	(0x728)
> +#define LPF_WR_FREQ_CTRL	(0xab0)
> +#define LPF_WR_FREQ_CTRL_VFC3A	(0x730)
> +
> +#define TIME_CLOCK_TDC_FANOUT_CNFG	(0xB00)
> +#define TIME_SYNC_TO_TDC_EN	BIT(0)
> +#define SIG1_MUX_SEL_MASK	GENMASK(7, 4)
> +#define SIG2_MUX_SEL_MASK	GENMASK(11, 8)
> +enum tdc_mux_sel {
> +	REF0 = 0,
> +	REF1 = 1,
> +	REF2 = 2,
> +	REF3 = 3,
> +	REF_CLK5 = 4,
> +	REF_CLK6 = 5,
> +	DPLL_FB_TO_TDC = 6,
> +	DPLL_FB_DIVIDED_TO_TDC = 7,
> +	TIME_CLK_DIVIDED = 8,
> +	TIME_SYNC = 9,
> +};
> +
> +#define TIME_CLOCK_MEAS_CNFG	(0xB04)
> +#define TDC_MEAS_MODE	BIT(0)
> +enum tdc_meas_mode {
> +	CONTINUOUS = 0,
> +	ONE_SHOT = 1,
> +	MEAS_MODE_INVALID = 2,
> +};
> +
> +#define TIME_CLOCK_MEAS_DIV_CNFG	(0xB08)
> +#define TIME_REF_DIV_MASK	GENMASK(29, 24)
> +
> +#define TIME_CLOCK_MEAS_CTRL	(0xB10)
> +#define TDC_MEAS_EN	BIT(0)
> +#define TDC_MEAS_START	BIT(1)
> +
> +#define TDC_FIFO_READ_REQ	(0xB2F)
> +#define TDC_FIFO_READ		(0xB30)
> +#define COARSE_MEAS_MASK	GENMASK(39, 13)
> +#define FINE_MEAS_MASK		GENMASK(12, 0)
> +
> +#define TDC_FIFO_CTRL		(0xB12)
> +#define FIFO_CLEAR		BIT(0)
> +#define TDC_FIFO_STS		(0xB38)
> +#define FIFO_FULL		BIT(1)
> +#define FIFO_EMPTY		BIT(0)
> +#define TDC_FIFO_EVENT		(0xB39)
> +#define FIFO_OVERRUN		BIT(1)
> +
> +/* DPLL */
> +#define MAX_REFERENCE_INDEX	(3)
> +#define MAX_NUM_REF_PRIORITY	(4)
> +
> +#define MAX_DPLL_INDEX	(2)
> +
> +#define DPLL_STS		(0x580)
> +#define DPLL_STS_VFC3A		(0x571)
> +#define DPLL_STATE_STS_MASK	(0x70)
> +#define DPLL_STATE_STS_SHIFT	(4)
> +#define DPLL_REF_SEL_STS_MASK	(0x6)
> +#define DPLL_REF_SEL_STS_SHIFT	(1)
> +
> +#define DPLL_REF_PRIORITY_CNFG			(0x502)
> +#define DPLL_REFX_PRIORITY_DISABLE_MASK		(0xf)
> +#define DPLL_REF0_PRIORITY_ENABLE_AND_SET_MASK	(0x31)
> +#define DPLL_REF1_PRIORITY_ENABLE_AND_SET_MASK	(0xc2)
> +#define DPLL_REF2_PRIORITY_ENABLE_AND_SET_MASK	(0x304)
> +#define DPLL_REF3_PRIORITY_ENABLE_AND_SET_MASK	(0xc08)
> +#define DPLL_REF0_PRIORITY_SHIFT		(4)
> +#define DPLL_REF1_PRIORITY_SHIFT		(6)
> +#define DPLL_REF2_PRIORITY_SHIFT		(8)
> +#define DPLL_REF3_PRIORITY_SHIFT		(10)
> +
> +enum dpll_state {
> +	DPLL_STATE_MIN             = 0,
> +	DPLL_STATE_FREERUN         = DPLL_STATE_MIN,
> +	DPLL_STATE_LOCKED          = 1,
> +	DPLL_STATE_HOLDOVER        = 2,
> +	DPLL_STATE_WRITE_FREQUENCY = 3,
> +	DPLL_STATE_ACQUIRE         = 4,
> +	DPLL_STATE_HITLESS_SWITCH  = 5,
> +	DPLL_STATE_MAX             = DPLL_STATE_HITLESS_SWITCH
> +};
> +
> +/* REFMON */
> +#define LOSMON_STS_0		(0x81e)
> +#define LOSMON_STS_0_VFC3A	(0x18e)
> +#define LOSMON_STS_1		(0x82e)
> +#define LOSMON_STS_1_VFC3A	(0x19e)
> +#define LOSMON_STS_2		(0x83e)
> +#define LOSMON_STS_2_VFC3A	(0x1ae)
> +#define LOSMON_STS_3		(0x84e)
> +#define LOSMON_STS_3_VFC3A	(0x1be)
> +#define LOS_STS_MASK		(0x1)
> +
> +#define FREQMON_STS_0		(0x874)
> +#define FREQMON_STS_0_VFC3A	(0x1d4)
> +#define FREQMON_STS_1		(0x894)
> +#define FREQMON_STS_1_VFC3A	(0x1f4)
> +#define FREQMON_STS_2		(0x8b4)
> +#define FREQMON_STS_2_VFC3A	(0x214)
> +#define FREQMON_STS_3		(0x8d4)
> +#define FREQMON_STS_3_VFC3A	(0x234)
> +#define FREQ_FAIL_STS_SHIFT	(31)
> +
> +/* Firmware interface */
> +#define TIME_CLK_FREQ_ADDR	(0xffa0)
> +#define XTAL_FREQ_ADDR		(0xffa1)
> +
> +/*
> + * Return register address and field mask based on passed in firmware version
> + */
> +#define IDTFC3_FW_REG(FW, VER, REG)	(((FW) < (VER)) ? (REG) : (REG##_##VER))
> +#define IDTFC3_FW_FIELD(FW, VER, FIELD)	(((FW) < (VER)) ? (FIELD) : (FIELD##_##VER))
> +enum fw_version {
> +	V_DEFAULT = 0,
> +	VFC3W     = 1,
> +	VFC3A     = 2
> +};
> +
> +/* XTAL_FREQ_ADDR/TIME_CLK_FREQ_ADDR */
> +enum {
> +	FREQ_MIN     = 0,
> +	FREQ_25M     = 1,
> +	FREQ_49_152M = 2,
> +	FREQ_50M     = 3,
> +	FREQ_100M    = 4,
> +	FREQ_125M    = 5,
> +	FREQ_250M    = 6,
> +	FREQ_MAX
> +};
> +
> +struct idtfc3_hw_param {
> +	u32 xtal_freq;
> +	u32 time_clk_freq;
> +};
> +
> +struct idtfc3_fwrc {
> +	u8 hiaddr;
> +	u8 loaddr;
> +	u8 value;
> +	u8 reserved;
> +} __packed;

How much of the last 300 lines are actually being used?

> +static inline void idtfc3_default_hw_param(struct idtfc3_hw_param *hw_param)
> +{
> +	hw_param->xtal_freq = 49152000;
> +	hw_param->time_clk_freq = 25000000;
> +}

This is an odd use of an inline function.

Why not define them and use them else where?

> +static inline int idtfc3_set_hw_param(struct idtfc3_hw_param *hw_param,
> +				      u16 addr, u8 val)
> +{
> +	if (addr == XTAL_FREQ_ADDR)
> +		switch (val) {
> +		case FREQ_49_152M:
> +			hw_param->xtal_freq = 49152000;
> +			break;
> +		case FREQ_50M:
> +			hw_param->xtal_freq = 50000000;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	else if (addr == TIME_CLK_FREQ_ADDR)
> +		switch (val) {
> +		case FREQ_25M:
> +			hw_param->time_clk_freq = 25000000;
> +			break;
> +		case FREQ_50M:
> +			hw_param->time_clk_freq = 50000000;
> +			break;
> +		case FREQ_100M:
> +			hw_param->time_clk_freq = 100000000;
> +			break;
> +		case FREQ_125M:
> +			hw_param->time_clk_freq = 125000000;
> +			break;
> +		case FREQ_250M:
> +			hw_param->time_clk_freq = 250000000;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	else
> +		return -EFAULT;
> +
> +	return 0;
> +}

As above.  Why is this being shoved in here?

> +#endif

-- 
Lee Jones [李琼斯]