Commit 2d8967e8 authored by Jiawen Wu's avatar Jiawen Wu Committed by Jakub Kicinski
Browse files

net: ngbe: Add support for 1PPS and TOD



Implement support for generating a 1pps output signal on SDP0.
And support custom firmware to output TOD.

Signed-off-by: default avatarJiawen Wu <jiawenwu@trustnetic.com>
Link: https://patch.msgid.link/20250218023432.146536-5-jiawenwu@trustnetic.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 704145a8
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -393,6 +393,25 @@ int wx_host_interface_command(struct wx *wx, u32 *buffer,
}
EXPORT_SYMBOL(wx_host_interface_command);

int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles)
{
	struct wx_hic_set_pps pps_cmd;

	pps_cmd.hdr.cmd = FW_PPS_SET_CMD;
	pps_cmd.hdr.buf_len = FW_PPS_SET_LEN;
	pps_cmd.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
	pps_cmd.lan_id = wx->bus.func;
	pps_cmd.enable = (u8)enable;
	pps_cmd.nsec = nsec;
	pps_cmd.cycles = cycles;
	pps_cmd.hdr.checksum = FW_DEFAULT_CHECKSUM;

	return wx_host_interface_command(wx, (u32 *)&pps_cmd,
					 sizeof(pps_cmd),
					 WX_HI_COMMAND_TIMEOUT,
					 false);
}

/**
 *  wx_read_ee_hostif_data - Read EEPROM word using a host interface cmd
 *  assuming that the semaphore is already obtained.
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ void wx_control_hw(struct wx *wx, bool drv);
int wx_mng_present(struct wx *wx);
int wx_host_interface_command(struct wx *wx, u32 *buffer,
			      u32 length, u32 timeout, bool return_data);
int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles);
int wx_read_ee_hostif(struct wx *wx, u16 offset, u16 *data);
int wx_read_ee_hostif_buffer(struct wx *wx,
			     u16 offset, u16 words, u16 *data);
+178 −3
Original line number Diff line number Diff line
@@ -88,6 +88,9 @@ static int wx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
	timecounter_adjtime(&wx->hw_tc, delta);
	write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);

	if (wx->ptp_setup_sdp)
		wx->ptp_setup_sdp(wx);

	return 0;
}

@@ -118,6 +121,9 @@ static int wx_ptp_settime64(struct ptp_clock_info *ptp,
	timecounter_init(&wx->hw_tc, &wx->hw_cc, ns);
	write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);

	if (wx->ptp_setup_sdp)
		wx->ptp_setup_sdp(wx);

	return 0;
}

@@ -324,6 +330,160 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
	return ts_done ? 1 : HZ;
}

static u64 wx_ptp_trigger_calc(struct wx *wx)
{
	struct cyclecounter *cc = &wx->hw_cc;
	unsigned long flags;
	u64 ns = 0;
	u32 rem;

	/* Read the current clock time, and save the cycle counter value */
	write_seqlock_irqsave(&wx->hw_tc_lock, flags);
	ns = timecounter_read(&wx->hw_tc);
	wx->pps_edge_start = wx->hw_tc.cycle_last;
	write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
	wx->pps_edge_end = wx->pps_edge_start;

	/* Figure out how far past the next second we are */
	div_u64_rem(ns, WX_NS_PER_SEC, &rem);

	/* Figure out how many nanoseconds to add to round the clock edge up
	 * to the next full second
	 */
	rem = (WX_NS_PER_SEC - rem);

	/* Adjust the clock edge to align with the next full second. */
	wx->pps_edge_start += div_u64(((u64)rem << cc->shift), cc->mult);
	wx->pps_edge_end += div_u64(((u64)(rem + wx->pps_width) <<
				     cc->shift), cc->mult);

	return (ns + rem);
}

static int wx_ptp_setup_sdp(struct wx *wx)
{
	struct cyclecounter *cc = &wx->hw_cc;
	u32 tsauxc;
	u64 nsec;

	if (wx->pps_width >= WX_NS_PER_SEC) {
		wx_err(wx, "PTP pps width cannot be longer than 1s!\n");
		return -EINVAL;
	}

	/* disable the pin first */
	wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
	WX_WRITE_FLUSH(wx);

	if (!test_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags)) {
		if (wx->pps_enabled) {
			wx->pps_enabled = false;
			wx_set_pps(wx, false, 0, 0);
		}
		return 0;
	}

	wx->pps_enabled = true;
	nsec = wx_ptp_trigger_calc(wx);
	wx_set_pps(wx, wx->pps_enabled, nsec, wx->pps_edge_start);

	tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
		WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
	wr32ptp(wx, WX_TSC_1588_TRGT_L(0), (u32)wx->pps_edge_start);
	wr32ptp(wx, WX_TSC_1588_TRGT_H(0), (u32)(wx->pps_edge_start >> 32));
	wr32ptp(wx, WX_TSC_1588_TRGT_L(1), (u32)wx->pps_edge_end);
	wr32ptp(wx, WX_TSC_1588_TRGT_H(1), (u32)(wx->pps_edge_end >> 32));
	wr32ptp(wx, WX_TSC_1588_SDP(0),
		WX_TSC_1588_SDP_FUN_SEL_TT0 | WX_TSC_1588_SDP_OUT_LEVEL_H);
	wr32ptp(wx, WX_TSC_1588_SDP(1), WX_TSC_1588_SDP_FUN_SEL_TS0);
	wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
	wr32ptp(wx, WX_TSC_1588_INT_EN, WX_TSC_1588_INT_EN_TT1);
	WX_WRITE_FLUSH(wx);

	/* Adjust the clock edge to align with the next full second. */
	wx->sec_to_cc = div_u64(((u64)WX_NS_PER_SEC << cc->shift), cc->mult);

	return 0;
}

static int wx_ptp_feature_enable(struct ptp_clock_info *ptp,
				 struct ptp_clock_request *rq, int on)
{
	struct wx *wx = container_of(ptp, struct wx, ptp_caps);

	/**
	 * When PPS is enabled, unmask the interrupt for the ClockOut
	 * feature, so that the interrupt handler can send the PPS
	 * event when the clock SDP triggers. Clear mask when PPS is
	 * disabled
	 */
	if (rq->type != PTP_CLK_REQ_PEROUT || !wx->ptp_setup_sdp)
		return -EOPNOTSUPP;

	/* Reject requests with unsupported flags */
	if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
				 PTP_PEROUT_PHASE))
		return -EOPNOTSUPP;

	if (rq->perout.phase.sec || rq->perout.phase.nsec) {
		wx_err(wx, "Absolute start time not supported.\n");
		return -EINVAL;
	}

	if (rq->perout.period.sec != 1 || rq->perout.period.nsec) {
		wx_err(wx, "Only 1pps is supported.\n");
		return -EINVAL;
	}

	if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
		struct timespec64 ts_on;

		ts_on.tv_sec = rq->perout.on.sec;
		ts_on.tv_nsec = rq->perout.on.nsec;
		wx->pps_width = timespec64_to_ns(&ts_on);
	} else {
		wx->pps_width = 120000000;
	}

	if (on)
		set_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
	else
		clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);

	return wx->ptp_setup_sdp(wx);
}

void wx_ptp_check_pps_event(struct wx *wx)
{
	u32 tsauxc, int_status;

	/* this check is necessary in case the interrupt was enabled via some
	 * alternative means (ex. debug_fs). Better to check here than
	 * everywhere that calls this function.
	 */
	if (!wx->ptp_clock)
		return;

	int_status = rd32ptp(wx, WX_TSC_1588_INT_ST);
	if (int_status & WX_TSC_1588_INT_ST_TT1) {
		/* disable the pin first */
		wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
		WX_WRITE_FLUSH(wx);

		wx_ptp_trigger_calc(wx);

		tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
			 WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
		wr32ptp(wx, WX_TSC_1588_TRGT_L(0), (u32)wx->pps_edge_start);
		wr32ptp(wx, WX_TSC_1588_TRGT_H(0), (u32)(wx->pps_edge_start >> 32));
		wr32ptp(wx, WX_TSC_1588_TRGT_L(1), (u32)wx->pps_edge_end);
		wr32ptp(wx, WX_TSC_1588_TRGT_H(1), (u32)(wx->pps_edge_end >> 32));
		wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
		WX_WRITE_FLUSH(wx);
	}
}
EXPORT_SYMBOL(wx_ptp_check_pps_event);

static long wx_ptp_create_clock(struct wx *wx)
{
	struct net_device *netdev = wx->netdev;
@@ -338,17 +498,22 @@ static long wx_ptp_create_clock(struct wx *wx)
	wx->ptp_caps.owner = THIS_MODULE;
	wx->ptp_caps.n_alarm = 0;
	wx->ptp_caps.n_ext_ts = 0;
	wx->ptp_caps.n_per_out = 0;
	wx->ptp_caps.pps = 0;
	wx->ptp_caps.adjfine = wx_ptp_adjfine;
	wx->ptp_caps.adjtime = wx_ptp_adjtime;
	wx->ptp_caps.gettimex64 = wx_ptp_gettimex64;
	wx->ptp_caps.settime64 = wx_ptp_settime64;
	wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work;
	if (wx->mac.type == wx_mac_em)
	if (wx->mac.type == wx_mac_em) {
		wx->ptp_caps.max_adj = 500000000;
	else
		wx->ptp_caps.n_per_out = 1;
		wx->ptp_setup_sdp = wx_ptp_setup_sdp;
		wx->ptp_caps.enable = wx_ptp_feature_enable;
	} else {
		wx->ptp_caps.max_adj = 250000000;
		wx->ptp_caps.n_per_out = 0;
		wx->ptp_setup_sdp = NULL;
	}

	wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev);
	if (IS_ERR(wx->ptp_clock)) {
@@ -580,6 +745,12 @@ void wx_ptp_reset(struct wx *wx)

	wx->last_overflow_check = jiffies;
	ptp_schedule_worker(wx->ptp_clock, HZ);

	/* Now that the shift has been calculated and the systime
	 * registers reset, (re-)enable the Clock out feature
	 */
	if (wx->ptp_setup_sdp)
		wx->ptp_setup_sdp(wx);
}
EXPORT_SYMBOL(wx_ptp_reset);

@@ -620,6 +791,10 @@ void wx_ptp_suspend(struct wx *wx)
	if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
		return;

	clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
	if (wx->ptp_setup_sdp)
		wx->ptp_setup_sdp(wx);

	wx_ptp_clear_tx_timestamp(wx);
}
EXPORT_SYMBOL(wx_ptp_suspend);
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#ifndef _WX_PTP_H_
#define _WX_PTP_H_

void wx_ptp_check_pps_event(struct wx *wx);
void wx_ptp_reset_cyclecounter(struct wx *wx);
void wx_ptp_reset(struct wx *wx);
void wx_ptp_init(struct wx *wx);
+35 −0
Original line number Diff line number Diff line
@@ -281,6 +281,23 @@
#define WX_TSC_1588_SYSTIML          0x11F0C
#define WX_TSC_1588_SYSTIMH          0x11F10
#define WX_TSC_1588_INC              0x11F14
#define WX_TSC_1588_INT_ST           0x11F20
#define WX_TSC_1588_INT_ST_TT1       BIT(5)
#define WX_TSC_1588_INT_EN           0x11F24
#define WX_TSC_1588_INT_EN_TT1       BIT(5)
#define WX_TSC_1588_AUX_CTL          0x11F28
#define WX_TSC_1588_AUX_CTL_EN_TS0   BIT(8)
#define WX_TSC_1588_AUX_CTL_EN_TT1   BIT(2)
#define WX_TSC_1588_AUX_CTL_PLSG     BIT(1)
#define WX_TSC_1588_AUX_CTL_EN_TT0   BIT(0)
#define WX_TSC_1588_TRGT_L(i)        (0x11F2C + ((i) * 8)) /* [0,1] */
#define WX_TSC_1588_TRGT_H(i)        (0x11F30 + ((i) * 8)) /* [0,1] */
#define WX_TSC_1588_SDP(i)           (0x11F5C + ((i) * 4)) /* [0,3] */
#define WX_TSC_1588_SDP_OUT_LEVEL_H  FIELD_PREP(BIT(4), 0)
#define WX_TSC_1588_SDP_OUT_LEVEL_L  FIELD_PREP(BIT(4), 1)
#define WX_TSC_1588_SDP_FUN_SEL_MASK GENMASK(2, 0)
#define WX_TSC_1588_SDP_FUN_SEL_TT0  FIELD_PREP(WX_TSC_1588_SDP_FUN_SEL_MASK, 1)
#define WX_TSC_1588_SDP_FUN_SEL_TS0  FIELD_PREP(WX_TSC_1588_SDP_FUN_SEL_MASK, 5)

/************************************** MNG ********************************/
#define WX_MNG_SWFW_SYNC             0x1E008
@@ -410,6 +427,8 @@ enum WX_MSCA_CMD_value {
#define FW_CEM_CMD_RESERVED          0X0
#define FW_CEM_MAX_RETRIES           3
#define FW_CEM_RESP_STATUS_SUCCESS   0x1
#define FW_PPS_SET_CMD               0xF6
#define FW_PPS_SET_LEN               0x14

#define WX_SW_REGION_PTR             0x1C

@@ -730,6 +749,15 @@ struct wx_hic_reset {
	u16 reset_type;
};

struct wx_hic_set_pps {
	struct wx_hic_hdr hdr;
	u8 lan_id;
	u8 enable;
	u16 pad2;
	u64 nsec;
	u64 cycles;
};

/* Bus parameters */
struct wx_bus_info {
	u8 func;
@@ -1068,6 +1096,7 @@ enum wx_pf_flags {
	WX_FLAG_FDIR_PERFECT,
	WX_FLAG_RX_HWTSTAMP_ENABLED,
	WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
	WX_FLAG_PTP_PPS_ENABLED,
	WX_PF_FLAGS_NBITS               /* must be last */
};

@@ -1168,7 +1197,13 @@ struct wx {
	void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
	void (*configure_fdir)(struct wx *wx);
	void (*do_reset)(struct net_device *netdev);
	int (*ptp_setup_sdp)(struct wx *wx);

	bool pps_enabled;
	u64 pps_width;
	u64 pps_edge_start;
	u64 pps_edge_end;
	u64 sec_to_cc;
	u32 base_incval;
	u32 tx_hwtstamp_pkts;
	u32 tx_hwtstamp_timeouts;
Loading