162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci#include <linux/spi/spi.h>
562306a36Sopenharmony_ci#include "sja1105.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/* The adjfine API clamps ppb between [-32,768,000, 32,768,000], and
862306a36Sopenharmony_ci * therefore scaled_ppm between [-2,147,483,648, 2,147,483,647].
962306a36Sopenharmony_ci * Set the maximum supported ppb to a round value smaller than the maximum.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Percentually speaking, this is a +/- 0.032x adjustment of the
1262306a36Sopenharmony_ci * free-running counter (0.968x to 1.032x).
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci#define SJA1105_MAX_ADJ_PPB		32000000
1562306a36Sopenharmony_ci#define SJA1105_SIZE_PTP_CMD		4
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* PTPSYNCTS has no interrupt or update mechanism, because the intended
1862306a36Sopenharmony_ci * hardware use case is for the timestamp to be collected synchronously,
1962306a36Sopenharmony_ci * immediately after the CAS_MASTER SJA1105 switch has performed a CASSYNC
2062306a36Sopenharmony_ci * one-shot toggle (no return to level) on the PTP_CLK pin. When used as a
2162306a36Sopenharmony_ci * generic extts source, the PTPSYNCTS register needs polling and a comparison
2262306a36Sopenharmony_ci * with the old value. The polling interval is configured as the Nyquist rate
2362306a36Sopenharmony_ci * of a signal with 50% duty cycle and 1Hz frequency, which is sadly all that
2462306a36Sopenharmony_ci * this hardware can do (but may be enough for some setups). Anything of higher
2562306a36Sopenharmony_ci * frequency than 1 Hz will be lost, since there is no timestamp FIFO.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#define SJA1105_EXTTS_INTERVAL		(HZ / 6)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*            This range is actually +/- SJA1105_MAX_ADJ_PPB
3062306a36Sopenharmony_ci *            divided by 1000 (ppb -> ppm) and with a 16-bit
3162306a36Sopenharmony_ci *            "fractional" part (actually fixed point).
3262306a36Sopenharmony_ci *                                    |
3362306a36Sopenharmony_ci *                                    v
3462306a36Sopenharmony_ci * Convert scaled_ppm from the +/- ((10^6) << 16) range
3562306a36Sopenharmony_ci * into the +/- (1 << 31) range.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * This forgoes a "ppb" numeric representation (up to NSEC_PER_SEC)
3862306a36Sopenharmony_ci * and defines the scaling factor between scaled_ppm and the actual
3962306a36Sopenharmony_ci * frequency adjustments of the PHC.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci *   ptpclkrate = scaled_ppm * 2^31 / (10^6 * 2^16)
4262306a36Sopenharmony_ci *   simplifies to
4362306a36Sopenharmony_ci *   ptpclkrate = scaled_ppm * 2^9 / 5^6
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ci#define SJA1105_CC_MULT_NUM		(1 << 9)
4662306a36Sopenharmony_ci#define SJA1105_CC_MULT_DEM		15625
4762306a36Sopenharmony_ci#define SJA1105_CC_MULT			0x80000000
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cienum sja1105_ptp_clk_mode {
5062306a36Sopenharmony_ci	PTP_ADD_MODE = 1,
5162306a36Sopenharmony_ci	PTP_SET_MODE = 0,
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define extts_to_data(t) \
5562306a36Sopenharmony_ci		container_of((t), struct sja1105_ptp_data, extts_timer)
5662306a36Sopenharmony_ci#define ptp_caps_to_data(d) \
5762306a36Sopenharmony_ci		container_of((d), struct sja1105_ptp_data, caps)
5862306a36Sopenharmony_ci#define ptp_data_to_sja1105(d) \
5962306a36Sopenharmony_ci		container_of((d), struct sja1105_private, ptp_data)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciint sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
6462306a36Sopenharmony_ci	struct hwtstamp_config config;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
6762306a36Sopenharmony_ci		return -EFAULT;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	switch (config.tx_type) {
7062306a36Sopenharmony_ci	case HWTSTAMP_TX_OFF:
7162306a36Sopenharmony_ci		priv->hwts_tx_en &= ~BIT(port);
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci	case HWTSTAMP_TX_ON:
7462306a36Sopenharmony_ci		priv->hwts_tx_en |= BIT(port);
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	default:
7762306a36Sopenharmony_ci		return -ERANGE;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	switch (config.rx_filter) {
8162306a36Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
8262306a36Sopenharmony_ci		priv->hwts_rx_en &= ~BIT(port);
8362306a36Sopenharmony_ci		break;
8462306a36Sopenharmony_ci	default:
8562306a36Sopenharmony_ci		priv->hwts_rx_en |= BIT(port);
8662306a36Sopenharmony_ci		break;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
9062306a36Sopenharmony_ci		return -EFAULT;
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciint sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
9762306a36Sopenharmony_ci	struct hwtstamp_config config;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	config.flags = 0;
10062306a36Sopenharmony_ci	if (priv->hwts_tx_en & BIT(port))
10162306a36Sopenharmony_ci		config.tx_type = HWTSTAMP_TX_ON;
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		config.tx_type = HWTSTAMP_TX_OFF;
10462306a36Sopenharmony_ci	if (priv->hwts_rx_en & BIT(port))
10562306a36Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
10662306a36Sopenharmony_ci	else
10762306a36Sopenharmony_ci		config.rx_filter = HWTSTAMP_FILTER_NONE;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
11062306a36Sopenharmony_ci		-EFAULT : 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciint sja1105_get_ts_info(struct dsa_switch *ds, int port,
11462306a36Sopenharmony_ci			struct ethtool_ts_info *info)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
11762306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Called during cleanup */
12062306a36Sopenharmony_ci	if (!ptp_data->clock)
12162306a36Sopenharmony_ci		return -ENODEV;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
12462306a36Sopenharmony_ci				SOF_TIMESTAMPING_RX_HARDWARE |
12562306a36Sopenharmony_ci				SOF_TIMESTAMPING_RAW_HARDWARE;
12662306a36Sopenharmony_ci	info->tx_types = (1 << HWTSTAMP_TX_OFF) |
12762306a36Sopenharmony_ci			 (1 << HWTSTAMP_TX_ON);
12862306a36Sopenharmony_ci	info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
12962306a36Sopenharmony_ci			   (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT);
13062306a36Sopenharmony_ci	info->phc_index = ptp_clock_index(ptp_data->clock);
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_civoid sja1105et_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,
13562306a36Sopenharmony_ci			       enum packing_op op)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	const int size = SJA1105_SIZE_PTP_CMD;
13862306a36Sopenharmony_ci	/* No need to keep this as part of the structure */
13962306a36Sopenharmony_ci	u64 valid = 1;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	sja1105_packing(buf, &valid,           31, 31, size, op);
14262306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);
14362306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op);
14462306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op);
14562306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->stopptpcp,  27, 27, size, op);
14662306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->resptp,      2,  2, size, op);
14762306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->corrclk4ts,  1,  1, size, op);
14862306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->ptpclkadd,   0,  0, size, op);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_civoid sja1105pqrs_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,
15262306a36Sopenharmony_ci				 enum packing_op op)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	const int size = SJA1105_SIZE_PTP_CMD;
15562306a36Sopenharmony_ci	/* No need to keep this as part of the structure */
15662306a36Sopenharmony_ci	u64 valid = 1;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	sja1105_packing(buf, &valid,           31, 31, size, op);
15962306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);
16062306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op);
16162306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op);
16262306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->stopptpcp,  27, 27, size, op);
16362306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->resptp,      3,  3, size, op);
16462306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->corrclk4ts,  2,  2, size, op);
16562306a36Sopenharmony_ci	sja1105_packing(buf, &cmd->ptpclkadd,   0,  0, size, op);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint sja1105_ptp_commit(struct dsa_switch *ds, struct sja1105_ptp_cmd *cmd,
16962306a36Sopenharmony_ci		       sja1105_spi_rw_mode_t rw)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	const struct sja1105_private *priv = ds->priv;
17262306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
17362306a36Sopenharmony_ci	u8 buf[SJA1105_SIZE_PTP_CMD] = {0};
17462306a36Sopenharmony_ci	int rc;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (rw == SPI_WRITE)
17762306a36Sopenharmony_ci		priv->info->ptp_cmd_packing(buf, cmd, PACK);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	rc = sja1105_xfer_buf(priv, rw, regs->ptp_control, buf,
18062306a36Sopenharmony_ci			      SJA1105_SIZE_PTP_CMD);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (rw == SPI_READ)
18362306a36Sopenharmony_ci		priv->info->ptp_cmd_packing(buf, cmd, UNPACK);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return rc;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* The switch returns partial timestamps (24 bits for SJA1105 E/T, which wrap
18962306a36Sopenharmony_ci * around in 0.135 seconds, and 32 bits for P/Q/R/S, wrapping around in 34.35
19062306a36Sopenharmony_ci * seconds).
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * This receives the RX or TX MAC timestamps, provided by hardware as
19362306a36Sopenharmony_ci * the lower bits of the cycle counter, sampled at the time the timestamp was
19462306a36Sopenharmony_ci * collected.
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * To reconstruct into a full 64-bit-wide timestamp, the cycle counter is
19762306a36Sopenharmony_ci * read and the high-order bits are filled in.
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci * Must be called within one wraparound period of the partial timestamp since
20062306a36Sopenharmony_ci * it was generated by the MAC.
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic u64 sja1105_tstamp_reconstruct(struct dsa_switch *ds, u64 now,
20362306a36Sopenharmony_ci				      u64 ts_partial)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
20662306a36Sopenharmony_ci	u64 partial_tstamp_mask = CYCLECOUNTER_MASK(priv->info->ptp_ts_bits);
20762306a36Sopenharmony_ci	u64 ts_reconstructed;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	ts_reconstructed = (now & ~partial_tstamp_mask) | ts_partial;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Check lower bits of current cycle counter against the timestamp.
21262306a36Sopenharmony_ci	 * If the current cycle counter is lower than the partial timestamp,
21362306a36Sopenharmony_ci	 * then wraparound surely occurred and must be accounted for.
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci	if ((now & partial_tstamp_mask) <= ts_partial)
21662306a36Sopenharmony_ci		ts_reconstructed -= (partial_tstamp_mask + 1);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return ts_reconstructed;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/* Reads the SPI interface for an egress timestamp generated by the switch
22262306a36Sopenharmony_ci * for frames sent using management routes.
22362306a36Sopenharmony_ci *
22462306a36Sopenharmony_ci * SJA1105 E/T layout of the 4-byte SPI payload:
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * 31    23    15    7     0
22762306a36Sopenharmony_ci * |     |     |     |     |
22862306a36Sopenharmony_ci * +-----+-----+-----+     ^
22962306a36Sopenharmony_ci *          ^              |
23062306a36Sopenharmony_ci *          |              |
23162306a36Sopenharmony_ci *  24-bit timestamp   Update bit
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci *
23462306a36Sopenharmony_ci * SJA1105 P/Q/R/S layout of the 8-byte SPI payload:
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci * 31    23    15    7     0     63    55    47    39    32
23762306a36Sopenharmony_ci * |     |     |     |     |     |     |     |     |     |
23862306a36Sopenharmony_ci *                         ^     +-----+-----+-----+-----+
23962306a36Sopenharmony_ci *                         |                 ^
24062306a36Sopenharmony_ci *                         |                 |
24162306a36Sopenharmony_ci *                    Update bit    32-bit timestamp
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * Notice that the update bit is in the same place.
24462306a36Sopenharmony_ci * To have common code for E/T and P/Q/R/S for reading the timestamp,
24562306a36Sopenharmony_ci * we need to juggle with the offset and the bit indices.
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistatic int sja1105_ptpegr_ts_poll(struct dsa_switch *ds, int port, u64 *ts)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
25062306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
25162306a36Sopenharmony_ci	int tstamp_bit_start, tstamp_bit_end;
25262306a36Sopenharmony_ci	int timeout = 10;
25362306a36Sopenharmony_ci	u8 packed_buf[8];
25462306a36Sopenharmony_ci	u64 update;
25562306a36Sopenharmony_ci	int rc;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	do {
25862306a36Sopenharmony_ci		rc = sja1105_xfer_buf(priv, SPI_READ, regs->ptpegr_ts[port],
25962306a36Sopenharmony_ci				      packed_buf, priv->info->ptpegr_ts_bytes);
26062306a36Sopenharmony_ci		if (rc < 0)
26162306a36Sopenharmony_ci			return rc;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		sja1105_unpack(packed_buf, &update, 0, 0,
26462306a36Sopenharmony_ci			       priv->info->ptpegr_ts_bytes);
26562306a36Sopenharmony_ci		if (update)
26662306a36Sopenharmony_ci			break;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		usleep_range(10, 50);
26962306a36Sopenharmony_ci	} while (--timeout);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (!timeout)
27262306a36Sopenharmony_ci		return -ETIMEDOUT;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Point the end bit to the second 32-bit word on P/Q/R/S,
27562306a36Sopenharmony_ci	 * no-op on E/T.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	tstamp_bit_end = (priv->info->ptpegr_ts_bytes - 4) * 8;
27862306a36Sopenharmony_ci	/* Shift the 24-bit timestamp on E/T to be collected from 31:8.
27962306a36Sopenharmony_ci	 * No-op on P/Q/R/S.
28062306a36Sopenharmony_ci	 */
28162306a36Sopenharmony_ci	tstamp_bit_end += 32 - priv->info->ptp_ts_bits;
28262306a36Sopenharmony_ci	tstamp_bit_start = tstamp_bit_end + priv->info->ptp_ts_bits - 1;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	*ts = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	sja1105_unpack(packed_buf, ts, tstamp_bit_start, tstamp_bit_end,
28762306a36Sopenharmony_ci		       priv->info->ptpegr_ts_bytes);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/* Caller must hold ptp_data->lock */
29362306a36Sopenharmony_cistatic int sja1105_ptpclkval_read(struct sja1105_private *priv, u64 *ticks,
29462306a36Sopenharmony_ci				  struct ptp_system_timestamp *ptp_sts)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return sja1105_xfer_u64(priv, SPI_READ, regs->ptpclkval, ticks,
29962306a36Sopenharmony_ci				ptp_sts);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/* Caller must hold ptp_data->lock */
30362306a36Sopenharmony_cistatic int sja1105_ptpclkval_write(struct sja1105_private *priv, u64 ticks,
30462306a36Sopenharmony_ci				   struct ptp_system_timestamp *ptp_sts)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return sja1105_xfer_u64(priv, SPI_WRITE, regs->ptpclkval, &ticks,
30962306a36Sopenharmony_ci				ptp_sts);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void sja1105_extts_poll(struct sja1105_private *priv)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
31562306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
31662306a36Sopenharmony_ci	struct ptp_clock_event event;
31762306a36Sopenharmony_ci	u64 ptpsyncts = 0;
31862306a36Sopenharmony_ci	int rc;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	rc = sja1105_xfer_u64(priv, SPI_READ, regs->ptpsyncts, &ptpsyncts,
32162306a36Sopenharmony_ci			      NULL);
32262306a36Sopenharmony_ci	if (rc < 0)
32362306a36Sopenharmony_ci		dev_err_ratelimited(priv->ds->dev,
32462306a36Sopenharmony_ci				    "Failed to read PTPSYNCTS: %d\n", rc);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (ptpsyncts && ptp_data->ptpsyncts != ptpsyncts) {
32762306a36Sopenharmony_ci		event.index = 0;
32862306a36Sopenharmony_ci		event.type = PTP_CLOCK_EXTTS;
32962306a36Sopenharmony_ci		event.timestamp = ns_to_ktime(sja1105_ticks_to_ns(ptpsyncts));
33062306a36Sopenharmony_ci		ptp_clock_event(ptp_data->clock, &event);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		ptp_data->ptpsyncts = ptpsyncts;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic long sja1105_rxtstamp_work(struct ptp_clock_info *ptp)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
33962306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
34062306a36Sopenharmony_ci	struct dsa_switch *ds = priv->ds;
34162306a36Sopenharmony_ci	struct sk_buff *skb;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	while ((skb = skb_dequeue(&ptp_data->skb_rxtstamp_queue)) != NULL) {
34662306a36Sopenharmony_ci		struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb);
34762306a36Sopenharmony_ci		u64 ticks, ts;
34862306a36Sopenharmony_ci		int rc;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		rc = sja1105_ptpclkval_read(priv, &ticks, NULL);
35162306a36Sopenharmony_ci		if (rc < 0) {
35262306a36Sopenharmony_ci			dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc);
35362306a36Sopenharmony_ci			kfree_skb(skb);
35462306a36Sopenharmony_ci			continue;
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		*shwt = (struct skb_shared_hwtstamps) {0};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		ts = SJA1105_SKB_CB(skb)->tstamp;
36062306a36Sopenharmony_ci		ts = sja1105_tstamp_reconstruct(ds, ticks, ts);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		shwt->hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(ts));
36362306a36Sopenharmony_ci		netif_rx(skb);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (ptp_data->extts_enabled)
36762306a36Sopenharmony_ci		sja1105_extts_poll(priv);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* Don't restart */
37262306a36Sopenharmony_ci	return -1;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cibool sja1105_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
37862306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!(priv->hwts_rx_en & BIT(port)))
38162306a36Sopenharmony_ci		return false;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* We need to read the full PTP clock to reconstruct the Rx
38462306a36Sopenharmony_ci	 * timestamp. For that we need a sleepable context.
38562306a36Sopenharmony_ci	 */
38662306a36Sopenharmony_ci	skb_queue_tail(&ptp_data->skb_rxtstamp_queue, skb);
38762306a36Sopenharmony_ci	ptp_schedule_worker(ptp_data->clock, 0);
38862306a36Sopenharmony_ci	return true;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cibool sja1110_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct skb_shared_hwtstamps *shwt = skb_hwtstamps(skb);
39462306a36Sopenharmony_ci	u64 ts = SJA1105_SKB_CB(skb)->tstamp;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	*shwt = (struct skb_shared_hwtstamps) {0};
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	shwt->hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(ts));
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* Don't defer */
40162306a36Sopenharmony_ci	return false;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* Called from dsa_skb_defer_rx_timestamp */
40562306a36Sopenharmony_cibool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,
40662306a36Sopenharmony_ci			   struct sk_buff *skb, unsigned int type)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return priv->info->rxtstamp(ds, port, skb);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_civoid sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, u8 ts_id,
41462306a36Sopenharmony_ci				 enum sja1110_meta_tstamp dir, u64 tstamp)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
41762306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
41862306a36Sopenharmony_ci	struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
41962306a36Sopenharmony_ci	struct skb_shared_hwtstamps shwt = {0};
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* We don't care about RX timestamps on the CPU port */
42262306a36Sopenharmony_ci	if (dir == SJA1110_META_TSTAMP_RX)
42362306a36Sopenharmony_ci		return;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	spin_lock(&ptp_data->skb_txtstamp_queue.lock);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	skb_queue_walk_safe(&ptp_data->skb_txtstamp_queue, skb, skb_tmp) {
42862306a36Sopenharmony_ci		if (SJA1105_SKB_CB(skb)->ts_id != ts_id)
42962306a36Sopenharmony_ci			continue;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		__skb_unlink(skb, &ptp_data->skb_txtstamp_queue);
43262306a36Sopenharmony_ci		skb_match = skb;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	spin_unlock(&ptp_data->skb_txtstamp_queue.lock);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (WARN_ON(!skb_match))
44062306a36Sopenharmony_ci		return;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(tstamp));
44362306a36Sopenharmony_ci	skb_complete_tx_timestamp(skb_match, &shwt);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/* In addition to cloning the skb which is done by the common
44762306a36Sopenharmony_ci * sja1105_port_txtstamp, we need to generate a timestamp ID and save the
44862306a36Sopenharmony_ci * packet to the TX timestamping queue.
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_civoid sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone;
45362306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
45462306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
45562306a36Sopenharmony_ci	u8 ts_id;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	spin_lock(&priv->ts_id_lock);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	ts_id = priv->ts_id;
46262306a36Sopenharmony_ci	/* Deal automatically with 8-bit wraparound */
46362306a36Sopenharmony_ci	priv->ts_id++;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	SJA1105_SKB_CB(clone)->ts_id = ts_id;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	spin_unlock(&priv->ts_id_lock);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	skb_queue_tail(&ptp_data->skb_txtstamp_queue, clone);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/* Called from dsa_skb_tx_timestamp. This callback is just to clone
47362306a36Sopenharmony_ci * the skb and have it available in SJA1105_SKB_CB in the .port_deferred_xmit
47462306a36Sopenharmony_ci * callback, where we will timestamp it synchronously.
47562306a36Sopenharmony_ci */
47662306a36Sopenharmony_civoid sja1105_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
47962306a36Sopenharmony_ci	struct sk_buff *clone;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!(priv->hwts_tx_en & BIT(port)))
48262306a36Sopenharmony_ci		return;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	clone = skb_clone_sk(skb);
48562306a36Sopenharmony_ci	if (!clone)
48662306a36Sopenharmony_ci		return;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	SJA1105_SKB_CB(skb)->clone = clone;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (priv->info->txtstamp)
49162306a36Sopenharmony_ci		priv->info->txtstamp(ds, port, skb);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic int sja1105_ptp_reset(struct dsa_switch *ds)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
49762306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
49862306a36Sopenharmony_ci	struct sja1105_ptp_cmd cmd = ptp_data->cmd;
49962306a36Sopenharmony_ci	int rc;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	cmd.resptp = 1;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	dev_dbg(ds->dev, "Resetting PTP clock\n");
50662306a36Sopenharmony_ci	rc = sja1105_ptp_commit(ds, &cmd, SPI_WRITE);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	sja1105_tas_clockstep(priv->ds);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	return rc;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci/* Caller must hold ptp_data->lock */
51662306a36Sopenharmony_ciint __sja1105_ptp_gettimex(struct dsa_switch *ds, u64 *ns,
51762306a36Sopenharmony_ci			   struct ptp_system_timestamp *ptp_sts)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
52062306a36Sopenharmony_ci	u64 ticks;
52162306a36Sopenharmony_ci	int rc;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	rc = sja1105_ptpclkval_read(priv, &ticks, ptp_sts);
52462306a36Sopenharmony_ci	if (rc < 0) {
52562306a36Sopenharmony_ci		dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc);
52662306a36Sopenharmony_ci		return rc;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	*ns = sja1105_ticks_to_ns(ticks);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return 0;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int sja1105_ptp_gettimex(struct ptp_clock_info *ptp,
53562306a36Sopenharmony_ci				struct timespec64 *ts,
53662306a36Sopenharmony_ci				struct ptp_system_timestamp *ptp_sts)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
53962306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
54062306a36Sopenharmony_ci	u64 now = 0;
54162306a36Sopenharmony_ci	int rc;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	rc = __sja1105_ptp_gettimex(priv->ds, &now, ptp_sts);
54662306a36Sopenharmony_ci	*ts = ns_to_timespec64(now);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return rc;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/* Caller must hold ptp_data->lock */
55462306a36Sopenharmony_cistatic int sja1105_ptp_mode_set(struct sja1105_private *priv,
55562306a36Sopenharmony_ci				enum sja1105_ptp_clk_mode mode)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (ptp_data->cmd.ptpclkadd == mode)
56062306a36Sopenharmony_ci		return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ptp_data->cmd.ptpclkadd = mode;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	return sja1105_ptp_commit(priv->ds, &ptp_data->cmd, SPI_WRITE);
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci/* Write to PTPCLKVAL while PTPCLKADD is 0 */
56862306a36Sopenharmony_ciint __sja1105_ptp_settime(struct dsa_switch *ds, u64 ns,
56962306a36Sopenharmony_ci			  struct ptp_system_timestamp *ptp_sts)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
57262306a36Sopenharmony_ci	u64 ticks = ns_to_sja1105_ticks(ns);
57362306a36Sopenharmony_ci	int rc;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	rc = sja1105_ptp_mode_set(priv, PTP_SET_MODE);
57662306a36Sopenharmony_ci	if (rc < 0) {
57762306a36Sopenharmony_ci		dev_err(priv->ds->dev, "Failed to put PTPCLK in set mode\n");
57862306a36Sopenharmony_ci		return rc;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	rc = sja1105_ptpclkval_write(priv, ticks, ptp_sts);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	sja1105_tas_clockstep(priv->ds);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return rc;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int sja1105_ptp_settime(struct ptp_clock_info *ptp,
58962306a36Sopenharmony_ci			       const struct timespec64 *ts)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
59262306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
59362306a36Sopenharmony_ci	u64 ns = timespec64_to_ns(ts);
59462306a36Sopenharmony_ci	int rc;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	rc = __sja1105_ptp_settime(priv->ds, ns, NULL);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	return rc;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic int sja1105_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
60862306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
60962306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
61062306a36Sopenharmony_ci	u32 clkrate32;
61162306a36Sopenharmony_ci	s64 clkrate;
61262306a36Sopenharmony_ci	int rc;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	clkrate = (s64)scaled_ppm * SJA1105_CC_MULT_NUM;
61562306a36Sopenharmony_ci	clkrate = div_s64(clkrate, SJA1105_CC_MULT_DEM);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	/* Take a +/- value and re-center it around 2^31. */
61862306a36Sopenharmony_ci	clkrate = SJA1105_CC_MULT + clkrate;
61962306a36Sopenharmony_ci	WARN_ON(abs(clkrate) >= GENMASK_ULL(31, 0));
62062306a36Sopenharmony_ci	clkrate32 = clkrate;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptpclkrate, &clkrate32,
62562306a36Sopenharmony_ci			      NULL);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	sja1105_tas_adjfreq(priv->ds);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return rc;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci/* Write to PTPCLKVAL while PTPCLKADD is 1 */
63562306a36Sopenharmony_ciint __sja1105_ptp_adjtime(struct dsa_switch *ds, s64 delta)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
63862306a36Sopenharmony_ci	s64 ticks = ns_to_sja1105_ticks(delta);
63962306a36Sopenharmony_ci	int rc;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	rc = sja1105_ptp_mode_set(priv, PTP_ADD_MODE);
64262306a36Sopenharmony_ci	if (rc < 0) {
64362306a36Sopenharmony_ci		dev_err(priv->ds->dev, "Failed to put PTPCLK in add mode\n");
64462306a36Sopenharmony_ci		return rc;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	rc = sja1105_ptpclkval_write(priv, ticks, NULL);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	sja1105_tas_clockstep(priv->ds);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return rc;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
65762306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
65862306a36Sopenharmony_ci	int rc;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	rc = __sja1105_ptp_adjtime(priv->ds, delta);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return rc;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic void sja1105_ptp_extts_setup_timer(struct sja1105_ptp_data *ptp_data)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	unsigned long expires = ((jiffies / SJA1105_EXTTS_INTERVAL) + 1) *
67262306a36Sopenharmony_ci				SJA1105_EXTTS_INTERVAL;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	mod_timer(&ptp_data->extts_timer, expires);
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void sja1105_ptp_extts_timer(struct timer_list *t)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = extts_to_data(t);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	ptp_schedule_worker(ptp_data->clock, 0);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	sja1105_ptp_extts_setup_timer(ptp_data);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int sja1105_change_ptp_clk_pin_func(struct sja1105_private *priv,
68762306a36Sopenharmony_ci					   enum ptp_pin_function func)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct sja1105_avb_params_entry *avb;
69062306a36Sopenharmony_ci	enum ptp_pin_function old_func;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	avb = priv->static_config.tables[BLK_IDX_AVB_PARAMS].entries;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (priv->info->device_id == SJA1105E_DEVICE_ID ||
69562306a36Sopenharmony_ci	    priv->info->device_id == SJA1105T_DEVICE_ID ||
69662306a36Sopenharmony_ci	    avb->cas_master)
69762306a36Sopenharmony_ci		old_func = PTP_PF_PEROUT;
69862306a36Sopenharmony_ci	else
69962306a36Sopenharmony_ci		old_func = PTP_PF_EXTTS;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (func == old_func)
70262306a36Sopenharmony_ci		return 0;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	avb->cas_master = (func == PTP_PF_PEROUT);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return sja1105_dynamic_config_write(priv, BLK_IDX_AVB_PARAMS, 0, avb,
70762306a36Sopenharmony_ci					    true);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/* The PTP_CLK pin may be configured to toggle with a 50% duty cycle and a
71162306a36Sopenharmony_ci * frequency f:
71262306a36Sopenharmony_ci *
71362306a36Sopenharmony_ci *           NSEC_PER_SEC
71462306a36Sopenharmony_ci * f = ----------------------
71562306a36Sopenharmony_ci *     (PTPPINDUR * 8 ns) * 2
71662306a36Sopenharmony_ci */
71762306a36Sopenharmony_cistatic int sja1105_per_out_enable(struct sja1105_private *priv,
71862306a36Sopenharmony_ci				  struct ptp_perout_request *perout,
71962306a36Sopenharmony_ci				  bool on)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
72262306a36Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
72362306a36Sopenharmony_ci	struct sja1105_ptp_cmd cmd = ptp_data->cmd;
72462306a36Sopenharmony_ci	int rc;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* We only support one channel */
72762306a36Sopenharmony_ci	if (perout->index != 0)
72862306a36Sopenharmony_ci		return -EOPNOTSUPP;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Reject requests with unsupported flags */
73162306a36Sopenharmony_ci	if (perout->flags)
73262306a36Sopenharmony_ci		return -EOPNOTSUPP;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_PEROUT);
73762306a36Sopenharmony_ci	if (rc)
73862306a36Sopenharmony_ci		goto out;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (on) {
74162306a36Sopenharmony_ci		struct timespec64 pin_duration_ts = {
74262306a36Sopenharmony_ci			.tv_sec = perout->period.sec,
74362306a36Sopenharmony_ci			.tv_nsec = perout->period.nsec,
74462306a36Sopenharmony_ci		};
74562306a36Sopenharmony_ci		struct timespec64 pin_start_ts = {
74662306a36Sopenharmony_ci			.tv_sec = perout->start.sec,
74762306a36Sopenharmony_ci			.tv_nsec = perout->start.nsec,
74862306a36Sopenharmony_ci		};
74962306a36Sopenharmony_ci		u64 pin_duration = timespec64_to_ns(&pin_duration_ts);
75062306a36Sopenharmony_ci		u64 pin_start = timespec64_to_ns(&pin_start_ts);
75162306a36Sopenharmony_ci		u32 pin_duration32;
75262306a36Sopenharmony_ci		u64 now;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		/* ptppindur: 32 bit register which holds the interval between
75562306a36Sopenharmony_ci		 * 2 edges on PTP_CLK. So check for truncation which happens
75662306a36Sopenharmony_ci		 * at periods larger than around 68.7 seconds.
75762306a36Sopenharmony_ci		 */
75862306a36Sopenharmony_ci		pin_duration = ns_to_sja1105_ticks(pin_duration / 2);
75962306a36Sopenharmony_ci		if (pin_duration > U32_MAX) {
76062306a36Sopenharmony_ci			rc = -ERANGE;
76162306a36Sopenharmony_ci			goto out;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci		pin_duration32 = pin_duration;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		/* ptppins: 64 bit register which needs to hold a PTP time
76662306a36Sopenharmony_ci		 * larger than the current time, otherwise the startptpcp
76762306a36Sopenharmony_ci		 * command won't do anything. So advance the current time
76862306a36Sopenharmony_ci		 * by a number of periods in a way that won't alter the
76962306a36Sopenharmony_ci		 * phase offset.
77062306a36Sopenharmony_ci		 */
77162306a36Sopenharmony_ci		rc = __sja1105_ptp_gettimex(priv->ds, &now, NULL);
77262306a36Sopenharmony_ci		if (rc < 0)
77362306a36Sopenharmony_ci			goto out;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		pin_start = future_base_time(pin_start, pin_duration,
77662306a36Sopenharmony_ci					     now + 1ull * NSEC_PER_SEC);
77762306a36Sopenharmony_ci		pin_start = ns_to_sja1105_ticks(pin_start);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		rc = sja1105_xfer_u64(priv, SPI_WRITE, regs->ptppinst,
78062306a36Sopenharmony_ci				      &pin_start, NULL);
78162306a36Sopenharmony_ci		if (rc < 0)
78262306a36Sopenharmony_ci			goto out;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptppindur,
78562306a36Sopenharmony_ci				      &pin_duration32, NULL);
78662306a36Sopenharmony_ci		if (rc < 0)
78762306a36Sopenharmony_ci			goto out;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (on)
79162306a36Sopenharmony_ci		cmd.startptpcp = true;
79262306a36Sopenharmony_ci	else
79362306a36Sopenharmony_ci		cmd.stopptpcp = true;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	rc = sja1105_ptp_commit(priv->ds, &cmd, SPI_WRITE);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ciout:
79862306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	return rc;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int sja1105_extts_enable(struct sja1105_private *priv,
80462306a36Sopenharmony_ci				struct ptp_extts_request *extts,
80562306a36Sopenharmony_ci				bool on)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	int rc;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* We only support one channel */
81062306a36Sopenharmony_ci	if (extts->index != 0)
81162306a36Sopenharmony_ci		return -EOPNOTSUPP;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/* Reject requests with unsupported flags */
81462306a36Sopenharmony_ci	if (extts->flags & ~(PTP_ENABLE_FEATURE |
81562306a36Sopenharmony_ci			     PTP_RISING_EDGE |
81662306a36Sopenharmony_ci			     PTP_FALLING_EDGE |
81762306a36Sopenharmony_ci			     PTP_STRICT_FLAGS))
81862306a36Sopenharmony_ci		return -EOPNOTSUPP;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* We can only enable time stamping on both edges, sadly. */
82162306a36Sopenharmony_ci	if ((extts->flags & PTP_STRICT_FLAGS) &&
82262306a36Sopenharmony_ci	    (extts->flags & PTP_ENABLE_FEATURE) &&
82362306a36Sopenharmony_ci	    (extts->flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES)
82462306a36Sopenharmony_ci		return -EOPNOTSUPP;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_EXTTS);
82762306a36Sopenharmony_ci	if (rc)
82862306a36Sopenharmony_ci		return rc;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	priv->ptp_data.extts_enabled = on;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (on)
83362306a36Sopenharmony_ci		sja1105_ptp_extts_setup_timer(&priv->ptp_data);
83462306a36Sopenharmony_ci	else
83562306a36Sopenharmony_ci		del_timer_sync(&priv->ptp_data.extts_timer);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return 0;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic int sja1105_ptp_enable(struct ptp_clock_info *ptp,
84162306a36Sopenharmony_ci			      struct ptp_clock_request *req, int on)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
84462306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
84562306a36Sopenharmony_ci	int rc = -EOPNOTSUPP;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (req->type == PTP_CLK_REQ_PEROUT)
84862306a36Sopenharmony_ci		rc = sja1105_per_out_enable(priv, &req->perout, on);
84962306a36Sopenharmony_ci	else if (req->type == PTP_CLK_REQ_EXTTS)
85062306a36Sopenharmony_ci		rc = sja1105_extts_enable(priv, &req->extts, on);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	return rc;
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int sja1105_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
85662306a36Sopenharmony_ci				  enum ptp_pin_function func, unsigned int chan)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
85962306a36Sopenharmony_ci	struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (chan != 0 || pin != 0)
86262306a36Sopenharmony_ci		return -1;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	switch (func) {
86562306a36Sopenharmony_ci	case PTP_PF_NONE:
86662306a36Sopenharmony_ci	case PTP_PF_PEROUT:
86762306a36Sopenharmony_ci		break;
86862306a36Sopenharmony_ci	case PTP_PF_EXTTS:
86962306a36Sopenharmony_ci		if (priv->info->device_id == SJA1105E_DEVICE_ID ||
87062306a36Sopenharmony_ci		    priv->info->device_id == SJA1105T_DEVICE_ID)
87162306a36Sopenharmony_ci			return -1;
87262306a36Sopenharmony_ci		break;
87362306a36Sopenharmony_ci	default:
87462306a36Sopenharmony_ci		return -1;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci	return 0;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic struct ptp_pin_desc sja1105_ptp_pin = {
88062306a36Sopenharmony_ci	.name = "ptp_clk",
88162306a36Sopenharmony_ci	.index = 0,
88262306a36Sopenharmony_ci	.func = PTP_PF_NONE,
88362306a36Sopenharmony_ci};
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ciint sja1105_ptp_clock_register(struct dsa_switch *ds)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
88862306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ptp_data->caps = (struct ptp_clock_info) {
89162306a36Sopenharmony_ci		.owner		= THIS_MODULE,
89262306a36Sopenharmony_ci		.name		= "SJA1105 PHC",
89362306a36Sopenharmony_ci		.adjfine	= sja1105_ptp_adjfine,
89462306a36Sopenharmony_ci		.adjtime	= sja1105_ptp_adjtime,
89562306a36Sopenharmony_ci		.gettimex64	= sja1105_ptp_gettimex,
89662306a36Sopenharmony_ci		.settime64	= sja1105_ptp_settime,
89762306a36Sopenharmony_ci		.enable		= sja1105_ptp_enable,
89862306a36Sopenharmony_ci		.verify		= sja1105_ptp_verify_pin,
89962306a36Sopenharmony_ci		.do_aux_work	= sja1105_rxtstamp_work,
90062306a36Sopenharmony_ci		.max_adj	= SJA1105_MAX_ADJ_PPB,
90162306a36Sopenharmony_ci		.pin_config	= &sja1105_ptp_pin,
90262306a36Sopenharmony_ci		.n_pins		= 1,
90362306a36Sopenharmony_ci		.n_ext_ts	= 1,
90462306a36Sopenharmony_ci		.n_per_out	= 1,
90562306a36Sopenharmony_ci	};
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* Only used on SJA1105 */
90862306a36Sopenharmony_ci	skb_queue_head_init(&ptp_data->skb_rxtstamp_queue);
90962306a36Sopenharmony_ci	/* Only used on SJA1110 */
91062306a36Sopenharmony_ci	skb_queue_head_init(&ptp_data->skb_txtstamp_queue);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev);
91362306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(ptp_data->clock))
91462306a36Sopenharmony_ci		return PTR_ERR(ptp_data->clock);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	ptp_data->cmd.corrclk4ts = true;
91762306a36Sopenharmony_ci	ptp_data->cmd.ptpclkadd = PTP_SET_MODE;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	timer_setup(&ptp_data->extts_timer, sja1105_ptp_extts_timer, 0);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return sja1105_ptp_reset(ds);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_civoid sja1105_ptp_clock_unregister(struct dsa_switch *ds)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
92762306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(ptp_data->clock))
93062306a36Sopenharmony_ci		return;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	del_timer_sync(&ptp_data->extts_timer);
93362306a36Sopenharmony_ci	ptp_cancel_worker_sync(ptp_data->clock);
93462306a36Sopenharmony_ci	skb_queue_purge(&ptp_data->skb_txtstamp_queue);
93562306a36Sopenharmony_ci	skb_queue_purge(&ptp_data->skb_rxtstamp_queue);
93662306a36Sopenharmony_ci	ptp_clock_unregister(ptp_data->clock);
93762306a36Sopenharmony_ci	ptp_data->clock = NULL;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_civoid sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int port,
94162306a36Sopenharmony_ci			      struct sk_buff *skb)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct sja1105_private *priv = ds->priv;
94462306a36Sopenharmony_ci	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
94562306a36Sopenharmony_ci	struct skb_shared_hwtstamps shwt = {0};
94662306a36Sopenharmony_ci	u64 ticks, ts;
94762306a36Sopenharmony_ci	int rc;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	mutex_lock(&ptp_data->lock);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	rc = sja1105_ptpegr_ts_poll(ds, port, &ts);
95462306a36Sopenharmony_ci	if (rc < 0) {
95562306a36Sopenharmony_ci		dev_err(ds->dev, "timed out polling for tstamp\n");
95662306a36Sopenharmony_ci		kfree_skb(skb);
95762306a36Sopenharmony_ci		goto out;
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	rc = sja1105_ptpclkval_read(priv, &ticks, NULL);
96162306a36Sopenharmony_ci	if (rc < 0) {
96262306a36Sopenharmony_ci		dev_err(ds->dev, "Failed to read PTP clock: %d\n", rc);
96362306a36Sopenharmony_ci		kfree_skb(skb);
96462306a36Sopenharmony_ci		goto out;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	ts = sja1105_tstamp_reconstruct(ds, ticks, ts);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(ts));
97062306a36Sopenharmony_ci	skb_complete_tx_timestamp(skb, &shwt);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ciout:
97362306a36Sopenharmony_ci	mutex_unlock(&ptp_data->lock);
97462306a36Sopenharmony_ci}
975