162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * The Sparx5 Chip Register Model can be browsed at this location: 762306a36Sopenharmony_ci * https://github.com/microchip-ung/sparx-5_reginfo 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "sparx5_main_regs.h" 1262306a36Sopenharmony_ci#include "sparx5_main.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define SPARX5_MAX_PTP_ID 512 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define TOD_ACC_PIN 0x4 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum { 1962306a36Sopenharmony_ci PTP_PIN_ACTION_IDLE = 0, 2062306a36Sopenharmony_ci PTP_PIN_ACTION_LOAD, 2162306a36Sopenharmony_ci PTP_PIN_ACTION_SAVE, 2262306a36Sopenharmony_ci PTP_PIN_ACTION_CLOCK, 2362306a36Sopenharmony_ci PTP_PIN_ACTION_DELTA, 2462306a36Sopenharmony_ci PTP_PIN_ACTION_TOD 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic u64 sparx5_ptp_get_1ppm(struct sparx5 *sparx5) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci /* Represents 1ppm adjustment in 2^59 format with 1.59687500000(625) 3062306a36Sopenharmony_ci * 1.99609375000(500), 3.99218750000(250) as reference 3162306a36Sopenharmony_ci * The value is calculated as following: 3262306a36Sopenharmony_ci * (1/1000000)/((2^-59)/X) 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci u64 res = 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci switch (sparx5->coreclock) { 3862306a36Sopenharmony_ci case SPX5_CORE_CLOCK_250MHZ: 3962306a36Sopenharmony_ci res = 2301339409586; 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case SPX5_CORE_CLOCK_500MHZ: 4262306a36Sopenharmony_ci res = 1150669704793; 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci case SPX5_CORE_CLOCK_625MHZ: 4562306a36Sopenharmony_ci res = 920535763834; 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci default: 4862306a36Sopenharmony_ci WARN(1, "Invalid core clock"); 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return res; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic u64 sparx5_ptp_get_nominal_value(struct sparx5 *sparx5) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci u64 res = 0; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci switch (sparx5->coreclock) { 6062306a36Sopenharmony_ci case SPX5_CORE_CLOCK_250MHZ: 6162306a36Sopenharmony_ci res = 0x1FF0000000000000; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case SPX5_CORE_CLOCK_500MHZ: 6462306a36Sopenharmony_ci res = 0x0FF8000000000000; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case SPX5_CORE_CLOCK_625MHZ: 6762306a36Sopenharmony_ci res = 0x0CC6666666666666; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci default: 7062306a36Sopenharmony_ci WARN(1, "Invalid core clock"); 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return res; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciint sparx5_ptp_hwtstamp_set(struct sparx5_port *port, 7862306a36Sopenharmony_ci struct kernel_hwtstamp_config *cfg, 7962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 8262306a36Sopenharmony_ci struct sparx5_phc *phc; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* For now don't allow to run ptp on ports that are part of a bridge, 8562306a36Sopenharmony_ci * because in case of transparent clock the HW will still forward the 8662306a36Sopenharmony_ci * frames, so there would be duplicate frames 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (test_bit(port->portno, sparx5->bridge_mask)) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci switch (cfg->tx_type) { 9362306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 9462306a36Sopenharmony_ci port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 9762306a36Sopenharmony_ci port->ptp_cmd = IFH_REW_OP_ONE_STEP_PTP; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 10062306a36Sopenharmony_ci port->ptp_cmd = IFH_REW_OP_NOOP; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci default: 10362306a36Sopenharmony_ci return -ERANGE; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci switch (cfg->rx_filter) { 10762306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 11062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 11162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 11262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 11362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 11462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 11562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 11662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 11762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 11862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 11962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 12062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 12162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 12262306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 12362306a36Sopenharmony_ci cfg->rx_filter = HWTSTAMP_FILTER_ALL; 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci default: 12662306a36Sopenharmony_ci return -ERANGE; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Commit back the result & save it */ 13062306a36Sopenharmony_ci mutex_lock(&sparx5->ptp_lock); 13162306a36Sopenharmony_ci phc = &sparx5->phc[SPARX5_PHC_PORT]; 13262306a36Sopenharmony_ci phc->hwtstamp_config = *cfg; 13362306a36Sopenharmony_ci mutex_unlock(&sparx5->ptp_lock); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_civoid sparx5_ptp_hwtstamp_get(struct sparx5_port *port, 13962306a36Sopenharmony_ci struct kernel_hwtstamp_config *cfg) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 14262306a36Sopenharmony_ci struct sparx5_phc *phc; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci phc = &sparx5->phc[SPARX5_PHC_PORT]; 14562306a36Sopenharmony_ci *cfg = phc->hwtstamp_config; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void sparx5_ptp_classify(struct sparx5_port *port, struct sk_buff *skb, 14962306a36Sopenharmony_ci u8 *rew_op, u8 *pdu_type, u8 *pdu_w16_offset) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct ptp_header *header; 15262306a36Sopenharmony_ci u8 msgtype; 15362306a36Sopenharmony_ci int type; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (port->ptp_cmd == IFH_REW_OP_NOOP) { 15662306a36Sopenharmony_ci *rew_op = IFH_REW_OP_NOOP; 15762306a36Sopenharmony_ci *pdu_type = IFH_PDU_TYPE_NONE; 15862306a36Sopenharmony_ci *pdu_w16_offset = 0; 15962306a36Sopenharmony_ci return; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci type = ptp_classify_raw(skb); 16362306a36Sopenharmony_ci if (type == PTP_CLASS_NONE) { 16462306a36Sopenharmony_ci *rew_op = IFH_REW_OP_NOOP; 16562306a36Sopenharmony_ci *pdu_type = IFH_PDU_TYPE_NONE; 16662306a36Sopenharmony_ci *pdu_w16_offset = 0; 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci header = ptp_parse_header(skb, type); 17162306a36Sopenharmony_ci if (!header) { 17262306a36Sopenharmony_ci *rew_op = IFH_REW_OP_NOOP; 17362306a36Sopenharmony_ci *pdu_type = IFH_PDU_TYPE_NONE; 17462306a36Sopenharmony_ci *pdu_w16_offset = 0; 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci *pdu_w16_offset = 7; 17962306a36Sopenharmony_ci if (type & PTP_CLASS_L2) 18062306a36Sopenharmony_ci *pdu_type = IFH_PDU_TYPE_PTP; 18162306a36Sopenharmony_ci if (type & PTP_CLASS_IPV4) 18262306a36Sopenharmony_ci *pdu_type = IFH_PDU_TYPE_IPV4_UDP_PTP; 18362306a36Sopenharmony_ci if (type & PTP_CLASS_IPV6) 18462306a36Sopenharmony_ci *pdu_type = IFH_PDU_TYPE_IPV6_UDP_PTP; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { 18762306a36Sopenharmony_ci *rew_op = IFH_REW_OP_TWO_STEP_PTP; 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* If it is sync and run 1 step then set the correct operation, 19262306a36Sopenharmony_ci * otherwise run as 2 step 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci msgtype = ptp_get_msgtype(header, type); 19562306a36Sopenharmony_ci if ((msgtype & 0xf) == 0) { 19662306a36Sopenharmony_ci *rew_op = IFH_REW_OP_ONE_STEP_PTP; 19762306a36Sopenharmony_ci return; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci *rew_op = IFH_REW_OP_TWO_STEP_PTP; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void sparx5_ptp_txtstamp_old_release(struct sparx5_port *port) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct sk_buff *skb, *skb_tmp; 20662306a36Sopenharmony_ci unsigned long flags; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci spin_lock_irqsave(&port->tx_skbs.lock, flags); 20962306a36Sopenharmony_ci skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { 21062306a36Sopenharmony_ci if time_after(SPARX5_SKB_CB(skb)->jiffies + SPARX5_PTP_TIMEOUT, 21162306a36Sopenharmony_ci jiffies) 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci __skb_unlink(skb, &port->tx_skbs); 21562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci spin_unlock_irqrestore(&port->tx_skbs.lock, flags); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciint sparx5_ptp_txtstamp_request(struct sparx5_port *port, 22162306a36Sopenharmony_ci struct sk_buff *skb) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 22462306a36Sopenharmony_ci u8 rew_op, pdu_type, pdu_w16_offset; 22562306a36Sopenharmony_ci unsigned long flags; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci sparx5_ptp_classify(port, skb, &rew_op, &pdu_type, &pdu_w16_offset); 22862306a36Sopenharmony_ci SPARX5_SKB_CB(skb)->rew_op = rew_op; 22962306a36Sopenharmony_ci SPARX5_SKB_CB(skb)->pdu_type = pdu_type; 23062306a36Sopenharmony_ci SPARX5_SKB_CB(skb)->pdu_w16_offset = pdu_w16_offset; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (rew_op != IFH_REW_OP_TWO_STEP_PTP) 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci sparx5_ptp_txtstamp_old_release(port); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); 23862306a36Sopenharmony_ci if (sparx5->ptp_skbs == SPARX5_MAX_PTP_ID) { 23962306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); 24062306a36Sopenharmony_ci return -EBUSY; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci skb_queue_tail(&port->tx_skbs, skb); 24662306a36Sopenharmony_ci SPARX5_SKB_CB(skb)->ts_id = port->ts_id; 24762306a36Sopenharmony_ci SPARX5_SKB_CB(skb)->jiffies = jiffies; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci sparx5->ptp_skbs++; 25062306a36Sopenharmony_ci port->ts_id++; 25162306a36Sopenharmony_ci if (port->ts_id == SPARX5_MAX_PTP_ID) 25262306a36Sopenharmony_ci port->ts_id = 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_civoid sparx5_ptp_txtstamp_release(struct sparx5_port *port, 26062306a36Sopenharmony_ci struct sk_buff *skb) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 26362306a36Sopenharmony_ci unsigned long flags; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags); 26662306a36Sopenharmony_ci port->ts_id--; 26762306a36Sopenharmony_ci sparx5->ptp_skbs--; 26862306a36Sopenharmony_ci skb_unlink(skb, &port->tx_skbs); 26962306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void sparx5_get_hwtimestamp(struct sparx5 *sparx5, 27362306a36Sopenharmony_ci struct timespec64 *ts, 27462306a36Sopenharmony_ci u32 nsec) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci /* Read current PTP time to get seconds */ 27762306a36Sopenharmony_ci unsigned long flags; 27862306a36Sopenharmony_ci u32 curr_nsec; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) | 28362306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(SPARX5_PHC_PORT) | 28462306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), 28562306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_ACTION | 28662306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM | 28762306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC, 28862306a36Sopenharmony_ci sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ts->tv_sec = spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN)); 29162306a36Sopenharmony_ci curr_nsec = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ts->tv_nsec = nsec; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Sec has incremented since the ts was registered */ 29662306a36Sopenharmony_ci if (curr_nsec < nsec) 29762306a36Sopenharmony_ci ts->tv_sec--; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ciirqreturn_t sparx5_ptp_irq_handler(int irq, void *args) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int budget = SPARX5_MAX_PTP_ID; 30562306a36Sopenharmony_ci struct sparx5 *sparx5 = args; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci while (budget--) { 30862306a36Sopenharmony_ci struct sk_buff *skb, *skb_tmp, *skb_match = NULL; 30962306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 31062306a36Sopenharmony_ci struct sparx5_port *port; 31162306a36Sopenharmony_ci struct timespec64 ts; 31262306a36Sopenharmony_ci unsigned long flags; 31362306a36Sopenharmony_ci u32 val, id, txport; 31462306a36Sopenharmony_ci u32 delay; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci val = spx5_rd(sparx5, REW_PTP_TWOSTEP_CTRL); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Check if a timestamp can be retrieved */ 31962306a36Sopenharmony_ci if (!(val & REW_PTP_TWOSTEP_CTRL_PTP_VLD)) 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci WARN_ON(val & REW_PTP_TWOSTEP_CTRL_PTP_OVFL); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!(val & REW_PTP_TWOSTEP_CTRL_STAMP_TX)) 32562306a36Sopenharmony_ci continue; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Retrieve the ts Tx port */ 32862306a36Sopenharmony_ci txport = REW_PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Retrieve its associated skb */ 33162306a36Sopenharmony_ci port = sparx5->ports[txport]; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Retrieve the delay */ 33462306a36Sopenharmony_ci delay = spx5_rd(sparx5, REW_PTP_TWOSTEP_STAMP); 33562306a36Sopenharmony_ci delay = REW_PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(delay); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Get next timestamp from fifo, which needs to be the 33862306a36Sopenharmony_ci * rx timestamp which represents the id of the frame 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci spx5_rmw(REW_PTP_TWOSTEP_CTRL_PTP_NXT_SET(1), 34162306a36Sopenharmony_ci REW_PTP_TWOSTEP_CTRL_PTP_NXT, 34262306a36Sopenharmony_ci sparx5, REW_PTP_TWOSTEP_CTRL); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci val = spx5_rd(sparx5, REW_PTP_TWOSTEP_CTRL); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Check if a timestamp can be retried */ 34762306a36Sopenharmony_ci if (!(val & REW_PTP_TWOSTEP_CTRL_PTP_VLD)) 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Read RX timestamping to get the ID */ 35162306a36Sopenharmony_ci id = spx5_rd(sparx5, REW_PTP_TWOSTEP_STAMP); 35262306a36Sopenharmony_ci id <<= 8; 35362306a36Sopenharmony_ci id |= spx5_rd(sparx5, REW_PTP_TWOSTEP_STAMP_SUBNS); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci spin_lock_irqsave(&port->tx_skbs.lock, flags); 35662306a36Sopenharmony_ci skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { 35762306a36Sopenharmony_ci if (SPARX5_SKB_CB(skb)->ts_id != id) 35862306a36Sopenharmony_ci continue; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci __skb_unlink(skb, &port->tx_skbs); 36162306a36Sopenharmony_ci skb_match = skb; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci spin_unlock_irqrestore(&port->tx_skbs.lock, flags); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Next ts */ 36762306a36Sopenharmony_ci spx5_rmw(REW_PTP_TWOSTEP_CTRL_PTP_NXT_SET(1), 36862306a36Sopenharmony_ci REW_PTP_TWOSTEP_CTRL_PTP_NXT, 36962306a36Sopenharmony_ci sparx5, REW_PTP_TWOSTEP_CTRL); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (WARN_ON(!skb_match)) 37262306a36Sopenharmony_ci continue; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci spin_lock(&sparx5->ptp_ts_id_lock); 37562306a36Sopenharmony_ci sparx5->ptp_skbs--; 37662306a36Sopenharmony_ci spin_unlock(&sparx5->ptp_ts_id_lock); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Get the h/w timestamp */ 37962306a36Sopenharmony_ci sparx5_get_hwtimestamp(sparx5, &ts, delay); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Set the timestamp into the skb */ 38262306a36Sopenharmony_ci shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 38362306a36Sopenharmony_ci skb_tstamp_tx(skb_match, &shhwtstamps); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci dev_kfree_skb_any(skb_match); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return IRQ_HANDLED; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int sparx5_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); 39462306a36Sopenharmony_ci struct sparx5 *sparx5 = phc->sparx5; 39562306a36Sopenharmony_ci unsigned long flags; 39662306a36Sopenharmony_ci bool neg_adj = 0; 39762306a36Sopenharmony_ci u64 tod_inc; 39862306a36Sopenharmony_ci u64 ref; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (!scaled_ppm) 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (scaled_ppm < 0) { 40462306a36Sopenharmony_ci neg_adj = 1; 40562306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci tod_inc = sparx5_ptp_get_nominal_value(sparx5); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* The multiplication is split in 2 separate additions because of 41162306a36Sopenharmony_ci * overflow issues. If scaled_ppm with 16bit fractional part was bigger 41262306a36Sopenharmony_ci * than 20ppm then we got overflow. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci ref = sparx5_ptp_get_1ppm(sparx5) * (scaled_ppm >> 16); 41562306a36Sopenharmony_ci ref += (sparx5_ptp_get_1ppm(sparx5) * (0xffff & scaled_ppm)) >> 16; 41662306a36Sopenharmony_ci tod_inc = neg_adj ? tod_inc - ref : tod_inc + ref; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(1 << BIT(phc->index)), 42162306a36Sopenharmony_ci PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, 42262306a36Sopenharmony_ci sparx5, PTP_PTP_DOM_CFG); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spx5_wr((u32)tod_inc & 0xFFFFFFFF, sparx5, 42562306a36Sopenharmony_ci PTP_CLK_PER_CFG(phc->index, 0)); 42662306a36Sopenharmony_ci spx5_wr((u32)(tod_inc >> 32), sparx5, 42762306a36Sopenharmony_ci PTP_CLK_PER_CFG(phc->index, 1)); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(0), 43062306a36Sopenharmony_ci PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, sparx5, 43162306a36Sopenharmony_ci PTP_PTP_DOM_CFG); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int sparx5_ptp_settime64(struct ptp_clock_info *ptp, 43962306a36Sopenharmony_ci const struct timespec64 *ts) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); 44262306a36Sopenharmony_ci struct sparx5 *sparx5 = phc->sparx5; 44362306a36Sopenharmony_ci unsigned long flags; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Must be in IDLE mode before the time can be loaded */ 44862306a36Sopenharmony_ci spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) | 44962306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | 45062306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), 45162306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_ACTION | 45262306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM | 45362306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC, 45462306a36Sopenharmony_ci sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Set new value */ 45762306a36Sopenharmony_ci spx5_wr(PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB_SET(upper_32_bits(ts->tv_sec)), 45862306a36Sopenharmony_ci sparx5, PTP_PTP_TOD_SEC_MSB(TOD_ACC_PIN)); 45962306a36Sopenharmony_ci spx5_wr(lower_32_bits(ts->tv_sec), 46062306a36Sopenharmony_ci sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN)); 46162306a36Sopenharmony_ci spx5_wr(ts->tv_nsec, sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Apply new values */ 46462306a36Sopenharmony_ci spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_LOAD) | 46562306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | 46662306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), 46762306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_ACTION | 46862306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM | 46962306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC, 47062306a36Sopenharmony_ci sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciint sparx5_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); 48062306a36Sopenharmony_ci struct sparx5 *sparx5 = phc->sparx5; 48162306a36Sopenharmony_ci unsigned long flags; 48262306a36Sopenharmony_ci time64_t s; 48362306a36Sopenharmony_ci s64 ns; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) | 48862306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | 48962306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), 49062306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_ACTION | 49162306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM | 49262306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC, 49362306a36Sopenharmony_ci sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci s = spx5_rd(sparx5, PTP_PTP_TOD_SEC_MSB(TOD_ACC_PIN)); 49662306a36Sopenharmony_ci s <<= 32; 49762306a36Sopenharmony_ci s |= spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN)); 49862306a36Sopenharmony_ci ns = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); 49962306a36Sopenharmony_ci ns &= PTP_PTP_TOD_NSEC_PTP_TOD_NSEC; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* Deal with negative values */ 50462306a36Sopenharmony_ci if ((ns & 0xFFFFFFF0) == 0x3FFFFFF0) { 50562306a36Sopenharmony_ci s--; 50662306a36Sopenharmony_ci ns &= 0xf; 50762306a36Sopenharmony_ci ns += 999999984; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci set_normalized_timespec64(ts, s, ns); 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int sparx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info); 51762306a36Sopenharmony_ci struct sparx5 *sparx5 = phc->sparx5; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) { 52062306a36Sopenharmony_ci unsigned long flags; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci spin_lock_irqsave(&sparx5->ptp_clock_lock, flags); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Must be in IDLE mode before the time can be loaded */ 52562306a36Sopenharmony_ci spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) | 52662306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | 52762306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), 52862306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_ACTION | 52962306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM | 53062306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC, 53162306a36Sopenharmony_ci sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci spx5_wr(PTP_PTP_TOD_NSEC_PTP_TOD_NSEC_SET(delta), 53462306a36Sopenharmony_ci sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN)); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Adjust time with the value of PTP_TOD_NSEC */ 53762306a36Sopenharmony_ci spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_DELTA) | 53862306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM_SET(phc->index) | 53962306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC_SET(0), 54062306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_ACTION | 54162306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_DOM | 54262306a36Sopenharmony_ci PTP_PTP_PIN_CFG_PTP_PIN_SYNC, 54362306a36Sopenharmony_ci sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN)); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags); 54662306a36Sopenharmony_ci } else { 54762306a36Sopenharmony_ci /* Fall back using sparx5_ptp_settime64 which is not exact */ 54862306a36Sopenharmony_ci struct timespec64 ts; 54962306a36Sopenharmony_ci u64 now; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci sparx5_ptp_gettime64(ptp, &ts); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci now = ktime_to_ns(timespec64_to_ktime(ts)); 55462306a36Sopenharmony_ci ts = ns_to_timespec64(now + delta); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci sparx5_ptp_settime64(ptp, &ts); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic struct ptp_clock_info sparx5_ptp_clock_info = { 56362306a36Sopenharmony_ci .owner = THIS_MODULE, 56462306a36Sopenharmony_ci .name = "sparx5 ptp", 56562306a36Sopenharmony_ci .max_adj = 200000, 56662306a36Sopenharmony_ci .gettime64 = sparx5_ptp_gettime64, 56762306a36Sopenharmony_ci .settime64 = sparx5_ptp_settime64, 56862306a36Sopenharmony_ci .adjtime = sparx5_ptp_adjtime, 56962306a36Sopenharmony_ci .adjfine = sparx5_ptp_adjfine, 57062306a36Sopenharmony_ci}; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int sparx5_ptp_phc_init(struct sparx5 *sparx5, 57362306a36Sopenharmony_ci int index, 57462306a36Sopenharmony_ci struct ptp_clock_info *clock_info) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct sparx5_phc *phc = &sparx5->phc[index]; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci phc->info = *clock_info; 57962306a36Sopenharmony_ci phc->clock = ptp_clock_register(&phc->info, sparx5->dev); 58062306a36Sopenharmony_ci if (IS_ERR(phc->clock)) 58162306a36Sopenharmony_ci return PTR_ERR(phc->clock); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci phc->index = index; 58462306a36Sopenharmony_ci phc->sparx5 = sparx5; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* PTP Rx stamping is always enabled. */ 58762306a36Sopenharmony_ci phc->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ciint sparx5_ptp_init(struct sparx5 *sparx5) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci u64 tod_adj = sparx5_ptp_get_nominal_value(sparx5); 59562306a36Sopenharmony_ci struct sparx5_port *port; 59662306a36Sopenharmony_ci int err, i; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (!sparx5->ptp) 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci for (i = 0; i < SPARX5_PHC_COUNT; ++i) { 60262306a36Sopenharmony_ci err = sparx5_ptp_phc_init(sparx5, i, &sparx5_ptp_clock_info); 60362306a36Sopenharmony_ci if (err) 60462306a36Sopenharmony_ci return err; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci spin_lock_init(&sparx5->ptp_clock_lock); 60862306a36Sopenharmony_ci spin_lock_init(&sparx5->ptp_ts_id_lock); 60962306a36Sopenharmony_ci mutex_init(&sparx5->ptp_lock); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* Disable master counters */ 61262306a36Sopenharmony_ci spx5_wr(PTP_PTP_DOM_CFG_PTP_ENA_SET(0), sparx5, PTP_PTP_DOM_CFG); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Configure the nominal TOD increment per clock cycle */ 61562306a36Sopenharmony_ci spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(0x7), 61662306a36Sopenharmony_ci PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, 61762306a36Sopenharmony_ci sparx5, PTP_PTP_DOM_CFG); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci for (i = 0; i < SPARX5_PHC_COUNT; ++i) { 62062306a36Sopenharmony_ci spx5_wr((u32)tod_adj & 0xFFFFFFFF, sparx5, 62162306a36Sopenharmony_ci PTP_CLK_PER_CFG(i, 0)); 62262306a36Sopenharmony_ci spx5_wr((u32)(tod_adj >> 32), sparx5, 62362306a36Sopenharmony_ci PTP_CLK_PER_CFG(i, 1)); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci spx5_rmw(PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS_SET(0), 62762306a36Sopenharmony_ci PTP_PTP_DOM_CFG_PTP_CLKCFG_DIS, 62862306a36Sopenharmony_ci sparx5, PTP_PTP_DOM_CFG); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* Enable master counters */ 63162306a36Sopenharmony_ci spx5_wr(PTP_PTP_DOM_CFG_PTP_ENA_SET(0x7), sparx5, PTP_PTP_DOM_CFG); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (i = 0; i < SPX5_PORTS; i++) { 63462306a36Sopenharmony_ci port = sparx5->ports[i]; 63562306a36Sopenharmony_ci if (!port) 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci skb_queue_head_init(&port->tx_skbs); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_civoid sparx5_ptp_deinit(struct sparx5 *sparx5) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct sparx5_port *port; 64762306a36Sopenharmony_ci int i; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci for (i = 0; i < SPX5_PORTS; i++) { 65062306a36Sopenharmony_ci port = sparx5->ports[i]; 65162306a36Sopenharmony_ci if (!port) 65262306a36Sopenharmony_ci continue; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci skb_queue_purge(&port->tx_skbs); 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci for (i = 0; i < SPARX5_PHC_COUNT; ++i) 65862306a36Sopenharmony_ci ptp_clock_unregister(sparx5->phc[i].clock); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_civoid sparx5_ptp_rxtstamp(struct sparx5 *sparx5, struct sk_buff *skb, 66262306a36Sopenharmony_ci u64 timestamp) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 66562306a36Sopenharmony_ci struct sparx5_phc *phc; 66662306a36Sopenharmony_ci struct timespec64 ts; 66762306a36Sopenharmony_ci u64 full_ts_in_ns; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!sparx5->ptp) 67062306a36Sopenharmony_ci return; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci phc = &sparx5->phc[SPARX5_PHC_PORT]; 67362306a36Sopenharmony_ci sparx5_ptp_gettime64(&phc->info, &ts); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (ts.tv_nsec < timestamp) 67662306a36Sopenharmony_ci ts.tv_sec--; 67762306a36Sopenharmony_ci ts.tv_nsec = timestamp; 67862306a36Sopenharmony_ci full_ts_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 68162306a36Sopenharmony_ci shhwtstamps->hwtstamp = full_ts_in_ns; 68262306a36Sopenharmony_ci} 683