162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Marvell PTP driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2020 Marvell. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitfield.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/hrtimer.h> 1362306a36Sopenharmony_ci#include <linux/ktime.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "mbox.h" 1662306a36Sopenharmony_ci#include "ptp.h" 1762306a36Sopenharmony_ci#include "rvu.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DRV_NAME "Marvell PTP Driver" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PCI_DEVID_OCTEONTX2_PTP 0xA00C 2262306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_98xx_PTP 0xB100 2362306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_96XX_PTP 0xB200 2462306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XX_PTP 0xB300 2562306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XXN_PTP 0xB400 2662306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95MM_PTP 0xB500 2762306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP 0xB600 2862306a36Sopenharmony_ci#define PCI_DEVID_OCTEONTX2_RST 0xA085 2962306a36Sopenharmony_ci#define PCI_DEVID_CN10K_PTP 0xA09E 3062306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_CN10K_A_PTP 0xB900 3162306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_CNF10K_A_PTP 0xBA00 3262306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_CNF10K_B_PTP 0xBC00 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define PCI_PTP_BAR_NO 0 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PTP_CLOCK_CFG 0xF00ULL 3762306a36Sopenharmony_ci#define PTP_CLOCK_CFG_PTP_EN BIT_ULL(0) 3862306a36Sopenharmony_ci#define PTP_CLOCK_CFG_EXT_CLK_EN BIT_ULL(1) 3962306a36Sopenharmony_ci#define PTP_CLOCK_CFG_EXT_CLK_IN_MASK GENMASK_ULL(7, 2) 4062306a36Sopenharmony_ci#define PTP_CLOCK_CFG_TSTMP_EDGE BIT_ULL(9) 4162306a36Sopenharmony_ci#define PTP_CLOCK_CFG_TSTMP_EN BIT_ULL(8) 4262306a36Sopenharmony_ci#define PTP_CLOCK_CFG_TSTMP_IN_MASK GENMASK_ULL(15, 10) 4362306a36Sopenharmony_ci#define PTP_CLOCK_CFG_ATOMIC_OP_MASK GENMASK_ULL(28, 26) 4462306a36Sopenharmony_ci#define PTP_CLOCK_CFG_PPS_EN BIT_ULL(30) 4562306a36Sopenharmony_ci#define PTP_CLOCK_CFG_PPS_INV BIT_ULL(31) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define PTP_PPS_HI_INCR 0xF60ULL 4862306a36Sopenharmony_ci#define PTP_PPS_LO_INCR 0xF68ULL 4962306a36Sopenharmony_ci#define PTP_PPS_THRESH_HI 0xF58ULL 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define PTP_CLOCK_LO 0xF08ULL 5262306a36Sopenharmony_ci#define PTP_CLOCK_HI 0xF10ULL 5362306a36Sopenharmony_ci#define PTP_CLOCK_COMP 0xF18ULL 5462306a36Sopenharmony_ci#define PTP_TIMESTAMP 0xF20ULL 5562306a36Sopenharmony_ci#define PTP_CLOCK_SEC 0xFD0ULL 5662306a36Sopenharmony_ci#define PTP_SEC_ROLLOVER 0xFD8ULL 5762306a36Sopenharmony_ci/* Atomic update related CSRs */ 5862306a36Sopenharmony_ci#define PTP_FRNS_TIMESTAMP 0xFE0ULL 5962306a36Sopenharmony_ci#define PTP_NXT_ROLLOVER_SET 0xFE8ULL 6062306a36Sopenharmony_ci#define PTP_CURR_ROLLOVER_SET 0xFF0ULL 6162306a36Sopenharmony_ci#define PTP_NANO_TIMESTAMP 0xFF8ULL 6262306a36Sopenharmony_ci#define PTP_SEC_TIMESTAMP 0x1000ULL 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define CYCLE_MULT 1000 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define is_rev_A0(ptp) (((ptp)->pdev->revision & 0x0F) == 0x0) 6762306a36Sopenharmony_ci#define is_rev_A1(ptp) (((ptp)->pdev->revision & 0x0F) == 0x1) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* PTP atomic update operation type */ 7062306a36Sopenharmony_cienum atomic_opcode { 7162306a36Sopenharmony_ci ATOMIC_SET = 1, 7262306a36Sopenharmony_ci ATOMIC_INC = 3, 7362306a36Sopenharmony_ci ATOMIC_DEC = 4 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct ptp *first_ptp_block; 7762306a36Sopenharmony_cistatic const struct pci_device_id ptp_id_table[]; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic bool is_ptp_dev_cnf10ka(struct ptp *ptp) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci return ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10K_A_PTP; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic bool is_ptp_dev_cn10ka(struct ptp *ptp) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci return ptp->pdev->subsystem_device == PCI_SUBSYS_DEVID_CN10K_A_PTP; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic bool cn10k_ptp_errata(struct ptp *ptp) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if ((is_ptp_dev_cn10ka(ptp) || is_ptp_dev_cnf10ka(ptp)) && 9262306a36Sopenharmony_ci (is_rev_A0(ptp) || is_rev_A1(ptp))) 9362306a36Sopenharmony_ci return true; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return false; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic bool is_tstmp_atomic_update_supported(struct rvu *rvu) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct ptp *ptp = rvu->ptp; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (is_rvu_otx2(rvu)) 10362306a36Sopenharmony_ci return false; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* On older silicon variants of CN10K, atomic update feature 10662306a36Sopenharmony_ci * is not available. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci if ((is_ptp_dev_cn10ka(ptp) || is_ptp_dev_cnf10ka(ptp)) && 10962306a36Sopenharmony_ci (is_rev_A0(ptp) || is_rev_A1(ptp))) 11062306a36Sopenharmony_ci return false; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return true; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic enum hrtimer_restart ptp_reset_thresh(struct hrtimer *hrtimer) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct ptp *ptp = container_of(hrtimer, struct ptp, hrtimer); 11862306a36Sopenharmony_ci ktime_t curr_ts = ktime_get(); 11962306a36Sopenharmony_ci ktime_t delta_ns, period_ns; 12062306a36Sopenharmony_ci u64 ptp_clock_hi; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* calculate the elapsed time since last restart */ 12362306a36Sopenharmony_ci delta_ns = ktime_to_ns(ktime_sub(curr_ts, ptp->last_ts)); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* if the ptp clock value has crossed 0.5 seconds, 12662306a36Sopenharmony_ci * its too late to update pps threshold value, so 12762306a36Sopenharmony_ci * update threshold after 1 second. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI); 13062306a36Sopenharmony_ci if (ptp_clock_hi > 500000000) { 13162306a36Sopenharmony_ci period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - ptp_clock_hi)); 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci writeq(500000000, ptp->reg_base + PTP_PPS_THRESH_HI); 13462306a36Sopenharmony_ci period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - delta_ns)); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci hrtimer_forward_now(hrtimer, period_ns); 13862306a36Sopenharmony_ci ptp->last_ts = curr_ts; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return HRTIMER_RESTART; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void ptp_hrtimer_start(struct ptp *ptp, ktime_t start_ns) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci ktime_t period_ns; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci period_ns = ktime_set(0, (NSEC_PER_SEC + 100 - start_ns)); 14862306a36Sopenharmony_ci hrtimer_start(&ptp->hrtimer, period_ns, HRTIMER_MODE_REL); 14962306a36Sopenharmony_ci ptp->last_ts = ktime_get(); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic u64 read_ptp_tstmp_sec_nsec(struct ptp *ptp) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u64 sec, sec1, nsec; 15562306a36Sopenharmony_ci unsigned long flags; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci spin_lock_irqsave(&ptp->ptp_lock, flags); 15862306a36Sopenharmony_ci sec = readq(ptp->reg_base + PTP_CLOCK_SEC) & 0xFFFFFFFFUL; 15962306a36Sopenharmony_ci nsec = readq(ptp->reg_base + PTP_CLOCK_HI); 16062306a36Sopenharmony_ci sec1 = readq(ptp->reg_base + PTP_CLOCK_SEC) & 0xFFFFFFFFUL; 16162306a36Sopenharmony_ci /* check nsec rollover */ 16262306a36Sopenharmony_ci if (sec1 > sec) { 16362306a36Sopenharmony_ci nsec = readq(ptp->reg_base + PTP_CLOCK_HI); 16462306a36Sopenharmony_ci sec = sec1; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp->ptp_lock, flags); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return sec * NSEC_PER_SEC + nsec; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic u64 read_ptp_tstmp_nsec(struct ptp *ptp) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return readq(ptp->reg_base + PTP_CLOCK_HI); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic u64 ptp_calc_adjusted_comp(u64 ptp_clock_freq) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci u64 comp, adj = 0, cycles_per_sec, ns_drift = 0; 17962306a36Sopenharmony_ci u32 ptp_clock_nsec, cycle_time; 18062306a36Sopenharmony_ci int cycle; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* Errata: 18362306a36Sopenharmony_ci * Issue #1: At the time of 1 sec rollover of the nano-second counter, 18462306a36Sopenharmony_ci * the nano-second counter is set to 0. However, it should be set to 18562306a36Sopenharmony_ci * (existing counter_value - 10^9). 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * Issue #2: The nano-second counter rolls over at 0x3B9A_C9FF. 18862306a36Sopenharmony_ci * It should roll over at 0x3B9A_CA00. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* calculate ptp_clock_comp value */ 19262306a36Sopenharmony_ci comp = ((u64)1000000000ULL << 32) / ptp_clock_freq; 19362306a36Sopenharmony_ci /* use CYCLE_MULT to avoid accuracy loss due to integer arithmetic */ 19462306a36Sopenharmony_ci cycle_time = NSEC_PER_SEC * CYCLE_MULT / ptp_clock_freq; 19562306a36Sopenharmony_ci /* cycles per sec */ 19662306a36Sopenharmony_ci cycles_per_sec = ptp_clock_freq; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* check whether ptp nanosecond counter rolls over early */ 19962306a36Sopenharmony_ci cycle = cycles_per_sec - 1; 20062306a36Sopenharmony_ci ptp_clock_nsec = (cycle * comp) >> 32; 20162306a36Sopenharmony_ci while (ptp_clock_nsec < NSEC_PER_SEC) { 20262306a36Sopenharmony_ci if (ptp_clock_nsec == 0x3B9AC9FF) 20362306a36Sopenharmony_ci goto calc_adj_comp; 20462306a36Sopenharmony_ci cycle++; 20562306a36Sopenharmony_ci ptp_clock_nsec = (cycle * comp) >> 32; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci /* compute nanoseconds lost per second when nsec counter rolls over */ 20862306a36Sopenharmony_ci ns_drift = ptp_clock_nsec - NSEC_PER_SEC; 20962306a36Sopenharmony_ci /* calculate ptp_clock_comp adjustment */ 21062306a36Sopenharmony_ci if (ns_drift > 0) { 21162306a36Sopenharmony_ci adj = comp * ns_drift; 21262306a36Sopenharmony_ci adj = adj / 1000000000ULL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci /* speed up the ptp clock to account for nanoseconds lost */ 21562306a36Sopenharmony_ci comp += adj; 21662306a36Sopenharmony_ci return comp; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cicalc_adj_comp: 21962306a36Sopenharmony_ci /* slow down the ptp clock to not rollover early */ 22062306a36Sopenharmony_ci adj = comp * cycle_time; 22162306a36Sopenharmony_ci adj = adj / 1000000000ULL; 22262306a36Sopenharmony_ci adj = adj / CYCLE_MULT; 22362306a36Sopenharmony_ci comp -= adj; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return comp; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct ptp *ptp_get(void) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct ptp *ptp = first_ptp_block; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Check PTP block is present in hardware */ 23362306a36Sopenharmony_ci if (!pci_dev_present(ptp_id_table)) 23462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 23562306a36Sopenharmony_ci /* Check driver is bound to PTP block */ 23662306a36Sopenharmony_ci if (!ptp) 23762306a36Sopenharmony_ci ptp = ERR_PTR(-EPROBE_DEFER); 23862306a36Sopenharmony_ci else if (!IS_ERR(ptp)) 23962306a36Sopenharmony_ci pci_dev_get(ptp->pdev); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return ptp; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_civoid ptp_put(struct ptp *ptp) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (!ptp) 24762306a36Sopenharmony_ci return; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci pci_dev_put(ptp->pdev); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void ptp_atomic_update(struct ptp *ptp, u64 timestamp) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci u64 regval, curr_rollover_set, nxt_rollover_set; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* First setup NSECs and SECs */ 25762306a36Sopenharmony_ci writeq(timestamp, ptp->reg_base + PTP_NANO_TIMESTAMP); 25862306a36Sopenharmony_ci writeq(0, ptp->reg_base + PTP_FRNS_TIMESTAMP); 25962306a36Sopenharmony_ci writeq(timestamp / NSEC_PER_SEC, 26062306a36Sopenharmony_ci ptp->reg_base + PTP_SEC_TIMESTAMP); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci nxt_rollover_set = roundup(timestamp, NSEC_PER_SEC); 26362306a36Sopenharmony_ci curr_rollover_set = nxt_rollover_set - NSEC_PER_SEC; 26462306a36Sopenharmony_ci writeq(nxt_rollover_set, ptp->reg_base + PTP_NXT_ROLLOVER_SET); 26562306a36Sopenharmony_ci writeq(curr_rollover_set, ptp->reg_base + PTP_CURR_ROLLOVER_SET); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Now, initiate atomic update */ 26862306a36Sopenharmony_ci regval = readq(ptp->reg_base + PTP_CLOCK_CFG); 26962306a36Sopenharmony_ci regval &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK; 27062306a36Sopenharmony_ci regval |= (ATOMIC_SET << 26); 27162306a36Sopenharmony_ci writeq(regval, ptp->reg_base + PTP_CLOCK_CFG); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void ptp_atomic_adjtime(struct ptp *ptp, s64 delta) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci bool neg_adj = false, atomic_inc_dec = false; 27762306a36Sopenharmony_ci u64 regval, ptp_clock_hi; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (delta < 0) { 28062306a36Sopenharmony_ci delta = -delta; 28162306a36Sopenharmony_ci neg_adj = true; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* use atomic inc/dec when delta < 1 second */ 28562306a36Sopenharmony_ci if (delta < NSEC_PER_SEC) 28662306a36Sopenharmony_ci atomic_inc_dec = true; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!atomic_inc_dec) { 28962306a36Sopenharmony_ci ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI); 29062306a36Sopenharmony_ci if (neg_adj) { 29162306a36Sopenharmony_ci if (ptp_clock_hi > delta) 29262306a36Sopenharmony_ci ptp_clock_hi -= delta; 29362306a36Sopenharmony_ci else 29462306a36Sopenharmony_ci ptp_clock_hi = delta - ptp_clock_hi; 29562306a36Sopenharmony_ci } else { 29662306a36Sopenharmony_ci ptp_clock_hi += delta; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci ptp_atomic_update(ptp, ptp_clock_hi); 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci writeq(delta, ptp->reg_base + PTP_NANO_TIMESTAMP); 30162306a36Sopenharmony_ci writeq(0, ptp->reg_base + PTP_FRNS_TIMESTAMP); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* initiate atomic inc/dec */ 30462306a36Sopenharmony_ci regval = readq(ptp->reg_base + PTP_CLOCK_CFG); 30562306a36Sopenharmony_ci regval &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK; 30662306a36Sopenharmony_ci regval |= neg_adj ? (ATOMIC_DEC << 26) : (ATOMIC_INC << 26); 30762306a36Sopenharmony_ci writeq(regval, ptp->reg_base + PTP_CLOCK_CFG); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int ptp_adjfine(struct ptp *ptp, long scaled_ppm) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci bool neg_adj = false; 31462306a36Sopenharmony_ci u32 freq, freq_adj; 31562306a36Sopenharmony_ci u64 comp, adj; 31662306a36Sopenharmony_ci s64 ppb; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (scaled_ppm < 0) { 31962306a36Sopenharmony_ci neg_adj = true; 32062306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* The hardware adds the clock compensation value to the PTP clock 32462306a36Sopenharmony_ci * on every coprocessor clock cycle. Typical convention is that it 32562306a36Sopenharmony_ci * represent number of nanosecond betwen each cycle. In this 32662306a36Sopenharmony_ci * convention compensation value is in 64 bit fixed-point 32762306a36Sopenharmony_ci * representation where upper 32 bits are number of nanoseconds 32862306a36Sopenharmony_ci * and lower is fractions of nanosecond. 32962306a36Sopenharmony_ci * The scaled_ppm represent the ratio in "parts per million" by which 33062306a36Sopenharmony_ci * the compensation value should be corrected. 33162306a36Sopenharmony_ci * To calculate new compenstation value we use 64bit fixed point 33262306a36Sopenharmony_ci * arithmetic on following formula 33362306a36Sopenharmony_ci * comp = tbase + tbase * scaled_ppm / (1M * 2^16) 33462306a36Sopenharmony_ci * where tbase is the basic compensation value calculated 33562306a36Sopenharmony_ci * initialy in the probe function. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci /* convert scaled_ppm to ppb */ 33862306a36Sopenharmony_ci ppb = 1 + scaled_ppm; 33962306a36Sopenharmony_ci ppb *= 125; 34062306a36Sopenharmony_ci ppb >>= 13; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (cn10k_ptp_errata(ptp)) { 34362306a36Sopenharmony_ci /* calculate the new frequency based on ppb */ 34462306a36Sopenharmony_ci freq_adj = (ptp->clock_rate * ppb) / 1000000000ULL; 34562306a36Sopenharmony_ci freq = neg_adj ? ptp->clock_rate + freq_adj : ptp->clock_rate - freq_adj; 34662306a36Sopenharmony_ci comp = ptp_calc_adjusted_comp(freq); 34762306a36Sopenharmony_ci } else { 34862306a36Sopenharmony_ci comp = ((u64)1000000000ull << 32) / ptp->clock_rate; 34962306a36Sopenharmony_ci adj = comp * ppb; 35062306a36Sopenharmony_ci adj = div_u64(adj, 1000000000ull); 35162306a36Sopenharmony_ci comp = neg_adj ? comp - adj : comp + adj; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci writeq(comp, ptp->reg_base + PTP_CLOCK_COMP); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int ptp_get_clock(struct ptp *ptp, u64 *clk) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci /* Return the current PTP clock */ 36162306a36Sopenharmony_ci *clk = ptp->read_ptp_tstmp(ptp); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_civoid ptp_start(struct rvu *rvu, u64 sclk, u32 ext_clk_freq, u32 extts) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct ptp *ptp = rvu->ptp; 36962306a36Sopenharmony_ci struct pci_dev *pdev; 37062306a36Sopenharmony_ci u64 clock_comp; 37162306a36Sopenharmony_ci u64 clock_cfg; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (!ptp) 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci pdev = ptp->pdev; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (!sclk) { 37962306a36Sopenharmony_ci dev_err(&pdev->dev, "PTP input clock cannot be zero\n"); 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* sclk is in MHz */ 38462306a36Sopenharmony_ci ptp->clock_rate = sclk * 1000000; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Program the seconds rollover value to 1 second */ 38762306a36Sopenharmony_ci if (is_tstmp_atomic_update_supported(rvu)) { 38862306a36Sopenharmony_ci writeq(0, ptp->reg_base + PTP_NANO_TIMESTAMP); 38962306a36Sopenharmony_ci writeq(0, ptp->reg_base + PTP_FRNS_TIMESTAMP); 39062306a36Sopenharmony_ci writeq(0, ptp->reg_base + PTP_SEC_TIMESTAMP); 39162306a36Sopenharmony_ci writeq(0, ptp->reg_base + PTP_CURR_ROLLOVER_SET); 39262306a36Sopenharmony_ci writeq(0x3b9aca00, ptp->reg_base + PTP_NXT_ROLLOVER_SET); 39362306a36Sopenharmony_ci writeq(0x3b9aca00, ptp->reg_base + PTP_SEC_ROLLOVER); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Enable PTP clock */ 39762306a36Sopenharmony_ci clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (ext_clk_freq) { 40062306a36Sopenharmony_ci ptp->clock_rate = ext_clk_freq; 40162306a36Sopenharmony_ci /* Set GPIO as PTP clock source */ 40262306a36Sopenharmony_ci clock_cfg &= ~PTP_CLOCK_CFG_EXT_CLK_IN_MASK; 40362306a36Sopenharmony_ci clock_cfg |= PTP_CLOCK_CFG_EXT_CLK_EN; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (extts) { 40762306a36Sopenharmony_ci clock_cfg |= PTP_CLOCK_CFG_TSTMP_EDGE; 40862306a36Sopenharmony_ci /* Set GPIO as timestamping source */ 40962306a36Sopenharmony_ci clock_cfg &= ~PTP_CLOCK_CFG_TSTMP_IN_MASK; 41062306a36Sopenharmony_ci clock_cfg |= PTP_CLOCK_CFG_TSTMP_EN; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci clock_cfg |= PTP_CLOCK_CFG_PTP_EN; 41462306a36Sopenharmony_ci clock_cfg |= PTP_CLOCK_CFG_PPS_EN | PTP_CLOCK_CFG_PPS_INV; 41562306a36Sopenharmony_ci writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); 41662306a36Sopenharmony_ci clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); 41762306a36Sopenharmony_ci clock_cfg &= ~PTP_CLOCK_CFG_ATOMIC_OP_MASK; 41862306a36Sopenharmony_ci clock_cfg |= (ATOMIC_SET << 26); 41962306a36Sopenharmony_ci writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Set 50% duty cycle for 1Hz output */ 42262306a36Sopenharmony_ci writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_HI_INCR); 42362306a36Sopenharmony_ci writeq(0x1dcd650000000000, ptp->reg_base + PTP_PPS_LO_INCR); 42462306a36Sopenharmony_ci if (cn10k_ptp_errata(ptp)) { 42562306a36Sopenharmony_ci /* The ptp_clock_hi rollsover to zero once clock cycle before it 42662306a36Sopenharmony_ci * reaches one second boundary. so, program the pps_lo_incr in 42762306a36Sopenharmony_ci * such a way that the pps threshold value comparison at one 42862306a36Sopenharmony_ci * second boundary will succeed and pps edge changes. After each 42962306a36Sopenharmony_ci * one second boundary, the hrtimer handler will be invoked and 43062306a36Sopenharmony_ci * reprograms the pps threshold value. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci ptp->clock_period = NSEC_PER_SEC / ptp->clock_rate; 43362306a36Sopenharmony_ci writeq((0x1dcd6500ULL - ptp->clock_period) << 32, 43462306a36Sopenharmony_ci ptp->reg_base + PTP_PPS_LO_INCR); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (cn10k_ptp_errata(ptp)) 43862306a36Sopenharmony_ci clock_comp = ptp_calc_adjusted_comp(ptp->clock_rate); 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Initial compensation value to start the nanosecs counter */ 44362306a36Sopenharmony_ci writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int ptp_get_tstmp(struct ptp *ptp, u64 *clk) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci u64 timestamp; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (is_ptp_dev_cn10ka(ptp) || is_ptp_dev_cnf10ka(ptp)) { 45162306a36Sopenharmony_ci timestamp = readq(ptp->reg_base + PTP_TIMESTAMP); 45262306a36Sopenharmony_ci *clk = (timestamp >> 32) * NSEC_PER_SEC + (timestamp & 0xFFFFFFFF); 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci *clk = readq(ptp->reg_base + PTP_TIMESTAMP); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int ptp_set_thresh(struct ptp *ptp, u64 thresh) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci if (!cn10k_ptp_errata(ptp)) 46362306a36Sopenharmony_ci writeq(thresh, ptp->reg_base + PTP_PPS_THRESH_HI); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic int ptp_extts_on(struct ptp *ptp, int on) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci u64 ptp_clock_hi; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (cn10k_ptp_errata(ptp)) { 47362306a36Sopenharmony_ci if (on) { 47462306a36Sopenharmony_ci ptp_clock_hi = readq(ptp->reg_base + PTP_CLOCK_HI); 47562306a36Sopenharmony_ci ptp_hrtimer_start(ptp, (ktime_t)ptp_clock_hi); 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci if (hrtimer_active(&ptp->hrtimer)) 47862306a36Sopenharmony_ci hrtimer_cancel(&ptp->hrtimer); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int ptp_probe(struct pci_dev *pdev, 48662306a36Sopenharmony_ci const struct pci_device_id *ent) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct ptp *ptp; 48962306a36Sopenharmony_ci int err; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ptp = kzalloc(sizeof(*ptp), GFP_KERNEL); 49262306a36Sopenharmony_ci if (!ptp) { 49362306a36Sopenharmony_ci err = -ENOMEM; 49462306a36Sopenharmony_ci goto error; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ptp->pdev = pdev; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci err = pcim_enable_device(pdev); 50062306a36Sopenharmony_ci if (err) 50162306a36Sopenharmony_ci goto error_free; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev)); 50462306a36Sopenharmony_ci if (err) 50562306a36Sopenharmony_ci goto error_free; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ptp->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO]; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci pci_set_drvdata(pdev, ptp); 51062306a36Sopenharmony_ci if (!first_ptp_block) 51162306a36Sopenharmony_ci first_ptp_block = ptp; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci spin_lock_init(&ptp->ptp_lock); 51462306a36Sopenharmony_ci if (cn10k_ptp_errata(ptp)) { 51562306a36Sopenharmony_ci ptp->read_ptp_tstmp = &read_ptp_tstmp_sec_nsec; 51662306a36Sopenharmony_ci hrtimer_init(&ptp->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 51762306a36Sopenharmony_ci ptp->hrtimer.function = ptp_reset_thresh; 51862306a36Sopenharmony_ci } else { 51962306a36Sopenharmony_ci ptp->read_ptp_tstmp = &read_ptp_tstmp_nsec; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cierror_free: 52562306a36Sopenharmony_ci kfree(ptp); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cierror: 52862306a36Sopenharmony_ci /* For `ptp_get()` we need to differentiate between the case 52962306a36Sopenharmony_ci * when the core has not tried to probe this device and the case when 53062306a36Sopenharmony_ci * the probe failed. In the later case we keep the error in 53162306a36Sopenharmony_ci * `dev->driver_data`. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci pci_set_drvdata(pdev, ERR_PTR(err)); 53462306a36Sopenharmony_ci if (!first_ptp_block) 53562306a36Sopenharmony_ci first_ptp_block = ERR_PTR(err); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return err; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void ptp_remove(struct pci_dev *pdev) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct ptp *ptp = pci_get_drvdata(pdev); 54362306a36Sopenharmony_ci u64 clock_cfg; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ptp)) 54662306a36Sopenharmony_ci return; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (cn10k_ptp_errata(ptp) && hrtimer_active(&ptp->hrtimer)) 54962306a36Sopenharmony_ci hrtimer_cancel(&ptp->hrtimer); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Disable PTP clock */ 55262306a36Sopenharmony_ci clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); 55362306a36Sopenharmony_ci clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN; 55462306a36Sopenharmony_ci writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); 55562306a36Sopenharmony_ci kfree(ptp); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic const struct pci_device_id ptp_id_table[] = { 55962306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 56062306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 56162306a36Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) }, 56262306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 56362306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 56462306a36Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) }, 56562306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 56662306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 56762306a36Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) }, 56862306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 56962306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 57062306a36Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_95XXN_PTP) }, 57162306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 57262306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 57362306a36Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_95MM_PTP) }, 57462306a36Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 57562306a36Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 57662306a36Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_95XXO_PTP) }, 57762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_PTP) }, 57862306a36Sopenharmony_ci { 0, } 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistruct pci_driver ptp_driver = { 58262306a36Sopenharmony_ci .name = DRV_NAME, 58362306a36Sopenharmony_ci .id_table = ptp_id_table, 58462306a36Sopenharmony_ci .probe = ptp_probe, 58562306a36Sopenharmony_ci .remove = ptp_remove, 58662306a36Sopenharmony_ci}; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ciint rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req, 58962306a36Sopenharmony_ci struct ptp_rsp *rsp) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci int err = 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* This function is the PTP mailbox handler invoked when 59462306a36Sopenharmony_ci * called by AF consumers/netdev drivers via mailbox mechanism. 59562306a36Sopenharmony_ci * It is used by netdev driver to get the PTP clock and to set 59662306a36Sopenharmony_ci * frequency adjustments. Since mailbox can be called without 59762306a36Sopenharmony_ci * notion of whether the driver is bound to ptp device below 59862306a36Sopenharmony_ci * validation is needed as first step. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci if (!rvu->ptp) 60162306a36Sopenharmony_ci return -ENODEV; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci switch (req->op) { 60462306a36Sopenharmony_ci case PTP_OP_ADJFINE: 60562306a36Sopenharmony_ci err = ptp_adjfine(rvu->ptp, req->scaled_ppm); 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci case PTP_OP_GET_CLOCK: 60862306a36Sopenharmony_ci err = ptp_get_clock(rvu->ptp, &rsp->clk); 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci case PTP_OP_GET_TSTMP: 61162306a36Sopenharmony_ci err = ptp_get_tstmp(rvu->ptp, &rsp->clk); 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci case PTP_OP_SET_THRESH: 61462306a36Sopenharmony_ci err = ptp_set_thresh(rvu->ptp, req->thresh); 61562306a36Sopenharmony_ci break; 61662306a36Sopenharmony_ci case PTP_OP_EXTTS_ON: 61762306a36Sopenharmony_ci err = ptp_extts_on(rvu->ptp, req->extts_on); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case PTP_OP_ADJTIME: 62062306a36Sopenharmony_ci ptp_atomic_adjtime(rvu->ptp, req->delta); 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci case PTP_OP_SET_CLOCK: 62362306a36Sopenharmony_ci ptp_atomic_update(rvu->ptp, (u64)req->clk); 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci default: 62662306a36Sopenharmony_ci err = -EINVAL; 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return err; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ciint rvu_mbox_handler_ptp_get_cap(struct rvu *rvu, struct msg_req *req, 63462306a36Sopenharmony_ci struct ptp_get_cap_rsp *rsp) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci if (!rvu->ptp) 63762306a36Sopenharmony_ci return -ENODEV; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (is_tstmp_atomic_update_supported(rvu)) 64062306a36Sopenharmony_ci rsp->cap |= PTP_CAP_HW_ATOMIC_UPDATE; 64162306a36Sopenharmony_ci else 64262306a36Sopenharmony_ci rsp->cap &= ~BIT_ULL_MASK(0); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 646