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