Commit bec67592 authored by Min Li's avatar Min Li Committed by Jakub Kicinski
Browse files

ptp: ptp_clockmatrix: Add PTP_CLK_REQ_EXTTS support



Use TOD_READ_SECONDARY for extts to keep TOD_READ_PRIMARY
for gettime and settime exclusively. Before this change,
TOD_READ_PRIMARY was used for both extts and gettime/settime,
which would result in changing TOD read/write triggers between
operations. Using TOD_READ_SECONDARY would make extts
independent of gettime/settime operation

Signed-off-by: default avatarMin Li <min.li.xe@renesas.com>
Acked-by: default avatarRichard Cochran <richardcochran@gmail.com>
Link: https://lore.kernel.org/r/1652712427-14703-1-git-send-email-min.li.xe@renesas.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 5ff0348b
Loading
Loading
Loading
Loading
+180 −109
Original line number Diff line number Diff line
@@ -239,73 +239,97 @@ static int wait_for_boot_status_ready(struct idtcm *idtcm)
	return -EBUSY;
}

static int _idtcm_set_scsr_read_trig(struct idtcm_channel *channel,
				     enum scsr_read_trig_sel trig, u8 ref)
static int arm_tod_read_trig_sel_refclk(struct idtcm_channel *channel, u8 ref)
{
	struct idtcm *idtcm = channel->idtcm;
	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
	u8 val;
	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
	u8 val = 0;
	int err;

	if (trig == SCSR_TOD_READ_TRIG_SEL_REFCLK) {
		err = idtcm_read(idtcm, channel->tod_read_primary,
				 TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val));
		if (err)
			return err;

	val &= ~(WR_REF_INDEX_MASK << WR_REF_INDEX_SHIFT);
	val |= (ref << WR_REF_INDEX_SHIFT);

		err = idtcm_write(idtcm, channel->tod_read_primary,
				  TOD_READ_PRIMARY_SEL_CFG_0, &val, sizeof(val));
	err = idtcm_write(idtcm, channel->tod_read_secondary,
			  TOD_READ_SECONDARY_SEL_CFG_0, &val, sizeof(val));
	if (err)
		return err;
	}

	err = idtcm_read(idtcm, channel->tod_read_primary,
			 tod_read_cmd, &val, sizeof(val));
	if (err)
		return err;
	val = 0 | (SCSR_TOD_READ_TRIG_SEL_REFCLK << TOD_READ_TRIGGER_SHIFT);

	val &= ~(TOD_READ_TRIGGER_MASK << TOD_READ_TRIGGER_SHIFT);
	val |= (trig << TOD_READ_TRIGGER_SHIFT);
	val &= ~TOD_READ_TRIGGER_MODE; /* single shot */
	err = idtcm_write(idtcm, channel->tod_read_secondary, tod_read_cmd,
			  &val, sizeof(val));
	if (err)
		dev_err(idtcm->dev, "%s: err = %d", __func__, err);

	err = idtcm_write(idtcm, channel->tod_read_primary,
			  tod_read_cmd, &val, sizeof(val));
	return err;
}

static int idtcm_enable_extts(struct idtcm_channel *channel, u8 todn, u8 ref,
			      bool enable)
static bool is_single_shot(u8 mask)
{
	struct idtcm *idtcm = channel->idtcm;
	u8 old_mask = idtcm->extts_mask;
	u8 mask = 1 << todn;
	/* Treat single bit ToD masks as continuous trigger */
	return mask <= 8 && is_power_of_2(mask);
}

static int idtcm_extts_enable(struct idtcm_channel *channel,
			      struct ptp_clock_request *rq, int on)
{
	u8 index = rq->extts.index;
	struct idtcm *idtcm;
	u8 mask = 1 << index;
	int err = 0;
	u8 old_mask;
	int ref;

	if (todn >= MAX_TOD)
		return -EINVAL;
	idtcm = channel->idtcm;
	old_mask = idtcm->extts_mask;

	/* Reject requests with unsupported flags */
	if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
				PTP_RISING_EDGE |
				PTP_FALLING_EDGE |
				PTP_STRICT_FLAGS))
		return -EOPNOTSUPP;

	if (enable) {
		if (ref > 0xF) /* E_REF_CLK15 */
	/* Reject requests to enable time stamping on falling edge */
	if ((rq->extts.flags & PTP_ENABLE_FEATURE) &&
	    (rq->extts.flags & PTP_FALLING_EDGE))
		return -EOPNOTSUPP;

	if (index >= MAX_TOD)
		return -EINVAL;
		if (idtcm->extts_mask & mask)
			return 0;
		err = _idtcm_set_scsr_read_trig(&idtcm->channel[todn],
						SCSR_TOD_READ_TRIG_SEL_REFCLK,
						ref);

	if (on) {
		/* Support triggering more than one TOD_0/1/2/3 by same pin */
		/* Use the pin configured for the channel */
		ref = ptp_find_pin(channel->ptp_clock, PTP_PF_EXTTS, channel->tod);

		if (ref < 0) {
			dev_err(idtcm->dev, "%s: No valid pin found for TOD%d!\n",
				__func__, channel->tod);
			return -EBUSY;
		}

		err = arm_tod_read_trig_sel_refclk(&idtcm->channel[index], ref);

		if (err == 0) {
			idtcm->extts_mask |= mask;
			idtcm->event_channel[todn] = channel;
			idtcm->channel[todn].refn = ref;
		}
	} else
		idtcm->extts_mask &= ~mask;
			idtcm->event_channel[index] = channel;
			idtcm->channel[index].refn = ref;
			idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);

			if (old_mask)
				return 0;

	if (old_mask == 0 && idtcm->extts_mask)
			schedule_delayed_work(&idtcm->extts_work,
					      msecs_to_jiffies(EXTTS_PERIOD_MS));
		}
	} else {
		idtcm->extts_mask &= ~mask;
		idtcm->extts_single_shot = is_single_shot(idtcm->extts_mask);

		if (idtcm->extts_mask == 0)
			cancel_delayed_work(&idtcm->extts_work);
	}

	return err;
}
@@ -371,6 +395,31 @@ static void wait_for_chip_ready(struct idtcm *idtcm)
			 "Continuing while SYS APLL/DPLL is not locked");
}

static int _idtcm_gettime_triggered(struct idtcm_channel *channel,
				    struct timespec64 *ts)
{
	struct idtcm *idtcm = channel->idtcm;
	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_SECONDARY_CMD);
	u8 buf[TOD_BYTE_COUNT];
	u8 trigger;
	int err;

	err = idtcm_read(idtcm, channel->tod_read_secondary,
			 tod_read_cmd, &trigger, sizeof(trigger));
	if (err)
		return err;

	if (trigger & TOD_READ_TRIGGER_MASK)
		return -EBUSY;

	err = idtcm_read(idtcm, channel->tod_read_secondary,
			 TOD_READ_SECONDARY_BASE, buf, sizeof(buf));
	if (err)
		return err;

	return char_array_to_timespec(buf, sizeof(buf), ts);
}

static int _idtcm_gettime(struct idtcm_channel *channel,
			  struct timespec64 *ts, u8 timeout)
{
@@ -396,7 +445,7 @@ static int _idtcm_gettime(struct idtcm_channel *channel,
	} while (trigger & TOD_READ_TRIGGER_MASK);

	err = idtcm_read(idtcm, channel->tod_read_primary,
			 TOD_READ_PRIMARY, buf, sizeof(buf));
			 TOD_READ_PRIMARY_BASE, buf, sizeof(buf));
	if (err)
		return err;

@@ -415,67 +464,38 @@ static int idtcm_extts_check_channel(struct idtcm *idtcm, u8 todn)

	extts_channel = &idtcm->channel[todn];
	ptp_channel = idtcm->event_channel[todn];

	if (extts_channel == ptp_channel)
		dco_delay = ptp_channel->dco_delay;

	err = _idtcm_gettime(extts_channel, &ts, 1);
	if (err == 0) {
	err = _idtcm_gettime_triggered(extts_channel, &ts);
	if (err)
		return err;

	/* Triggered - save timestamp */
	event.type = PTP_CLOCK_EXTTS;
	event.index = todn;
	event.timestamp = timespec64_to_ns(&ts) - dco_delay;
	ptp_clock_event(ptp_channel->ptp_clock, &event);
	}
	return err;
}

static u8 idtcm_enable_extts_mask(struct idtcm_channel *channel,
				    u8 extts_mask, bool enable)
{
	struct idtcm *idtcm = channel->idtcm;
	int i, err;

	for (i = 0; i < MAX_TOD; i++) {
		u8 mask = 1 << i;
		u8 refn = idtcm->channel[i].refn;

		if (extts_mask & mask) {
			/* check extts before disabling it */
			if (enable == false) {
				err = idtcm_extts_check_channel(idtcm, i);
				/* trigger happened so we won't re-enable it */
				if (err == 0)
					extts_mask &= ~mask;
			}
			(void)idtcm_enable_extts(channel, i, refn, enable);
		}
	}

	return extts_mask;
	return err;
}

static int _idtcm_gettime_immediate(struct idtcm_channel *channel,
				    struct timespec64 *ts)
{
	struct idtcm *idtcm = channel->idtcm;
	u8 extts_mask = 0;
	int err;

	/* Disable extts */
	if (idtcm->extts_mask) {
		extts_mask = idtcm_enable_extts_mask(channel, idtcm->extts_mask,
						     false);
	}

	err = _idtcm_set_scsr_read_trig(channel,
					SCSR_TOD_READ_TRIG_SEL_IMMEDIATE, 0);
	if (err == 0)
		err = _idtcm_gettime(channel, ts, 10);

	/* Re-enable extts */
	if (extts_mask)
		idtcm_enable_extts_mask(channel, extts_mask, true);
	u16 tod_read_cmd = IDTCM_FW_REG(idtcm->fw_ver, V520, TOD_READ_PRIMARY_CMD);
	u8 val = (SCSR_TOD_READ_TRIG_SEL_IMMEDIATE << TOD_READ_TRIGGER_SHIFT);
	int err;

	err = idtcm_write(idtcm, channel->tod_read_primary,
			  tod_read_cmd, &val, sizeof(val));
	if (err)
		return err;

	return _idtcm_gettime(channel, ts, 10);
}

static int _sync_pll_output(struct idtcm *idtcm,
@@ -1702,6 +1722,9 @@ static int initialize_dco_operating_mode(struct idtcm_channel *channel)
/*
 * Maximum absolute value for write phase offset in picoseconds
 *
 * @channel:  channel
 * @delta_ns: delta in nanoseconds
 *
 * Destination signed register is 32-bit register in resolution of 50ps
 *
 * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
@@ -1958,8 +1981,7 @@ static int idtcm_enable(struct ptp_clock_info *ptp,
			err = idtcm_perout_enable(channel, &rq->perout, true);
		break;
	case PTP_CLK_REQ_EXTTS:
		err = idtcm_enable_extts(channel, rq->extts.index,
					 rq->extts.rsv[0], on);
		err = idtcm_extts_enable(channel, rq, on);
		break;
	default:
		break;
@@ -1982,13 +2004,6 @@ static int idtcm_enable_tod(struct idtcm_channel *channel)
	u8 cfg;
	int err;

	/* STEELAI-366 - Temporary workaround for ts2phc compatibility */
	if (0) {
		err = idtcm_output_mask_enable(channel, false);
		if (err)
			return err;
	}

	/*
	 * Start the TOD clock ticking.
	 */
@@ -2038,17 +2053,35 @@ static void idtcm_set_version_info(struct idtcm *idtcm)
		 product_id, hw_rev_id, config_select);
}

static int idtcm_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
			    enum ptp_pin_function func, unsigned int chan)
{
	switch (func) {
	case PTP_PF_NONE:
	case PTP_PF_EXTTS:
		break;
	case PTP_PF_PEROUT:
	case PTP_PF_PHYSYNC:
		return -1;
	}
	return 0;
}

static struct ptp_pin_desc pin_config[MAX_TOD][MAX_REF_CLK];

static const struct ptp_clock_info idtcm_caps = {
	.owner		= THIS_MODULE,
	.max_adj	= 244000,
	.n_per_out	= 12,
	.n_ext_ts	= MAX_TOD,
	.n_pins		= MAX_REF_CLK,
	.adjphase	= &idtcm_adjphase,
	.adjfine	= &idtcm_adjfine,
	.adjtime	= &idtcm_adjtime,
	.gettime64	= &idtcm_gettime,
	.settime64	= &idtcm_settime,
	.enable		= &idtcm_enable,
	.verify		= &idtcm_verify_pin,
	.do_aux_work	= &idtcm_work_handler,
};

@@ -2057,12 +2090,14 @@ static const struct ptp_clock_info idtcm_caps_deprecated = {
	.max_adj	= 244000,
	.n_per_out	= 12,
	.n_ext_ts	= MAX_TOD,
	.n_pins		= MAX_REF_CLK,
	.adjphase	= &idtcm_adjphase,
	.adjfine	= &idtcm_adjfine,
	.adjtime	= &idtcm_adjtime_deprecated,
	.gettime64	= &idtcm_gettime,
	.settime64	= &idtcm_settime_deprecated,
	.enable		= &idtcm_enable,
	.verify		= &idtcm_verify_pin,
	.do_aux_work	= &idtcm_work_handler,
};

@@ -2174,8 +2209,9 @@ static u32 idtcm_get_dco_delay(struct idtcm_channel *channel)
		n = 1;

	fodFreq = (u32)div_u64(m, n);

	if (fodFreq >= 500000000)
		return 18 * (u32)div_u64(NSEC_PER_SEC, fodFreq);
		return (u32)div_u64(18 * (u64)NSEC_PER_SEC, fodFreq);

	return 0;
}
@@ -2188,24 +2224,28 @@ static int configure_channel_tod(struct idtcm_channel *channel, u32 index)
	switch (index) {
	case 0:
		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_0);
		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_0);
		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_0);
		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_0);
		channel->sync_src = SYNC_SOURCE_DPLL0_TOD_PPS;
		break;
	case 1:
		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_1);
		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_1);
		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_1);
		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_1);
		channel->sync_src = SYNC_SOURCE_DPLL1_TOD_PPS;
		break;
	case 2:
		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_2);
		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_2);
		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_2);
		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_2);
		channel->sync_src = SYNC_SOURCE_DPLL2_TOD_PPS;
		break;
	case 3:
		channel->tod_read_primary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_PRIMARY_3);
		channel->tod_read_secondary = IDTCM_FW_REG(fw_ver, V520, TOD_READ_SECONDARY_3);
		channel->tod_write = IDTCM_FW_REG(fw_ver, V520, TOD_WRITE_3);
		channel->tod_n = IDTCM_FW_REG(fw_ver, V520, TOD_3);
		channel->sync_src = SYNC_SOURCE_DPLL3_TOD_PPS;
@@ -2221,6 +2261,7 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
{
	struct idtcm_channel *channel;
	int err;
	int i;

	if (!(index < MAX_TOD))
		return -EINVAL;
@@ -2248,6 +2289,17 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
	snprintf(channel->caps.name, sizeof(channel->caps.name),
		 "IDT CM TOD%u", index);

	channel->caps.pin_config = pin_config[index];

	for (i = 0; i < channel->caps.n_pins; ++i) {
		struct ptp_pin_desc *ppd = &channel->caps.pin_config[i];

		snprintf(ppd->name, sizeof(ppd->name), "input_ref%d", i);
		ppd->index = i;
		ppd->func = PTP_PF_NONE;
		ppd->chan = index;
	}

	err = initialize_dco_operating_mode(channel);
	if (err)
		return err;
@@ -2302,26 +2354,40 @@ static int idtcm_enable_extts_channel(struct idtcm *idtcm, u32 index)
static void idtcm_extts_check(struct work_struct *work)
{
	struct idtcm *idtcm = container_of(work, struct idtcm, extts_work.work);
	int err, i;
	struct idtcm_channel *channel;
	u8 mask;
	int err;
	int i;

	if (idtcm->extts_mask == 0)
		return;

	mutex_lock(idtcm->lock);

	for (i = 0; i < MAX_TOD; i++) {
		u8 mask = 1 << i;
		mask = 1 << i;

		if ((idtcm->extts_mask & mask) == 0)
			continue;

		if (idtcm->extts_mask & mask) {
		err = idtcm_extts_check_channel(idtcm, i);

		if (err == 0) {
			/* trigger clears itself, so clear the mask */
			if (err == 0)
			if (idtcm->extts_single_shot) {
				idtcm->extts_mask &= ~mask;
			} else {
				/* Re-arm */
				channel = &idtcm->channel[i];
				arm_tod_read_trig_sel_refclk(channel, channel->refn);
			}
		}
	}

	if (idtcm->extts_mask)
		schedule_delayed_work(&idtcm->extts_work,
				      msecs_to_jiffies(EXTTS_PERIOD_MS));

	mutex_unlock(idtcm->lock);
}

@@ -2342,6 +2408,11 @@ static void set_default_masks(struct idtcm *idtcm)
	idtcm->tod_mask = DEFAULT_TOD_MASK;
	idtcm->extts_mask = 0;

	idtcm->channel[0].tod = 0;
	idtcm->channel[1].tod = 1;
	idtcm->channel[2].tod = 2;
	idtcm->channel[3].tod = 3;

	idtcm->channel[0].pll = DEFAULT_TOD0_PTP_PLL;
	idtcm->channel[1].pll = DEFAULT_TOD1_PTP_PLL;
	idtcm->channel[2].pll = DEFAULT_TOD2_PTP_PLL;
@@ -2420,8 +2491,8 @@ static int idtcm_remove(struct platform_device *pdev)
{
	struct idtcm *idtcm = platform_get_drvdata(pdev);

	idtcm->extts_mask = 0;
	ptp_clock_unregister_all(idtcm);

	cancel_delayed_work_sync(&idtcm->extts_work);

	return 0;
+5 −0
Original line number Diff line number Diff line
@@ -10,11 +10,13 @@

#include <linux/ktime.h>
#include <linux/mfd/idt8a340_reg.h>
#include <linux/ptp_clock.h>
#include <linux/regmap.h>

#define FW_FILENAME	"idtcm.bin"
#define MAX_TOD		(4)
#define MAX_PLL		(8)
#define MAX_REF_CLK	(16)

#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)

@@ -90,6 +92,7 @@ struct idtcm_channel {
	u16			dpll_ctrl_n;
	u16			dpll_phase_pull_in;
	u16			tod_read_primary;
	u16			tod_read_secondary;
	u16			tod_write;
	u16			tod_n;
	u16			hw_dpll_n;
@@ -105,6 +108,7 @@ struct idtcm_channel {
	/* last input trigger for extts */
	u8			refn;
	u8			pll;
	u8			tod;
	u16			output_mask;
};

@@ -116,6 +120,7 @@ struct idtcm {
	enum fw_version		fw_ver;
	/* Polls for external time stamps */
	u8			extts_mask;
	bool			extts_single_shot;
	struct delayed_work	extts_work;
	/* Remember the ptp channel to report extts */
	struct idtcm_channel	*event_channel[MAX_TOD];
+11 −1
Original line number Diff line number Diff line
@@ -407,7 +407,7 @@
#define TOD_READ_PRIMARY_0                0xcc40
#define TOD_READ_PRIMARY_0_V520           0xcc50
/* 8-bit subns, 32-bit ns, 48-bit seconds */
#define TOD_READ_PRIMARY                  0x0000
#define TOD_READ_PRIMARY_BASE             0x0000
/* Counter increments after TOD write is completed */
#define TOD_READ_PRIMARY_COUNTER          0x000b
/* Read trigger configuration */
@@ -424,6 +424,16 @@

#define TOD_READ_SECONDARY_0              0xcc90
#define TOD_READ_SECONDARY_0_V520         0xcca0
/* 8-bit subns, 32-bit ns, 48-bit seconds */
#define TOD_READ_SECONDARY_BASE           0x0000
/* Counter increments after TOD write is completed */
#define TOD_READ_SECONDARY_COUNTER        0x000b
/* Read trigger configuration */
#define TOD_READ_SECONDARY_SEL_CFG_0      0x000c
/* Read trigger selection */
#define TOD_READ_SECONDARY_CMD            0x000e
#define TOD_READ_SECONDARY_CMD_V520       0x000f

#define TOD_READ_SECONDARY_1              0xcca0
#define TOD_READ_SECONDARY_1_V520         0xccb0
#define TOD_READ_SECONDARY_2              0xccb0