162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 562306a36Sopenharmony_ci#include <linux/clocksource.h> 662306a36Sopenharmony_ci#include <linux/timecounter.h> 762306a36Sopenharmony_ci#include <linux/spinlock.h> 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/rhashtable.h> 1062306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1162306a36Sopenharmony_ci#include <linux/if_ether.h> 1262306a36Sopenharmony_ci#include <linux/if_vlan.h> 1362306a36Sopenharmony_ci#include <linux/net_tstamp.h> 1462306a36Sopenharmony_ci#include <linux/refcount.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "spectrum.h" 1762306a36Sopenharmony_ci#include "spectrum_ptp.h" 1862306a36Sopenharmony_ci#include "core.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT 29 2162306a36Sopenharmony_ci#define MLXSW_SP1_PTP_CLOCK_FREQ_KHZ 156257 /* 6.4nSec */ 2262306a36Sopenharmony_ci#define MLXSW_SP1_PTP_CLOCK_MASK 64 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MLXSW_SP1_PTP_HT_GC_INTERVAL 500 /* ms */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* How long, approximately, should the unmatched entries stay in the hash table 2762306a36Sopenharmony_ci * before they are collected. Should be evenly divisible by the GC interval. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define MLXSW_SP1_PTP_HT_GC_TIMEOUT 1000 /* ms */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct mlxsw_sp_ptp_state { 3262306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct mlxsw_sp1_ptp_state { 3662306a36Sopenharmony_ci struct mlxsw_sp_ptp_state common; 3762306a36Sopenharmony_ci struct rhltable unmatched_ht; 3862306a36Sopenharmony_ci spinlock_t unmatched_lock; /* protects the HT */ 3962306a36Sopenharmony_ci struct delayed_work ht_gc_dw; 4062306a36Sopenharmony_ci u32 gc_cycle; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct mlxsw_sp2_ptp_state { 4462306a36Sopenharmony_ci struct mlxsw_sp_ptp_state common; 4562306a36Sopenharmony_ci refcount_t ptp_port_enabled_ref; /* Number of ports with time stamping 4662306a36Sopenharmony_ci * enabled. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci struct hwtstamp_config config; 4962306a36Sopenharmony_ci struct mutex lock; /* Protects 'config' and HW configuration. */ 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct mlxsw_sp1_ptp_key { 5362306a36Sopenharmony_ci u16 local_port; 5462306a36Sopenharmony_ci u8 message_type; 5562306a36Sopenharmony_ci u16 sequence_id; 5662306a36Sopenharmony_ci u8 domain_number; 5762306a36Sopenharmony_ci bool ingress; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct mlxsw_sp1_ptp_unmatched { 6162306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key; 6262306a36Sopenharmony_ci struct rhlist_head ht_node; 6362306a36Sopenharmony_ci struct rcu_head rcu; 6462306a36Sopenharmony_ci struct sk_buff *skb; 6562306a36Sopenharmony_ci u64 timestamp; 6662306a36Sopenharmony_ci u32 gc_cycle; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp1_ptp_unmatched_ht_params = { 7062306a36Sopenharmony_ci .key_len = sizeof_field(struct mlxsw_sp1_ptp_unmatched, key), 7162306a36Sopenharmony_ci .key_offset = offsetof(struct mlxsw_sp1_ptp_unmatched, key), 7262306a36Sopenharmony_ci .head_offset = offsetof(struct mlxsw_sp1_ptp_unmatched, ht_node), 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct mlxsw_sp_ptp_clock { 7662306a36Sopenharmony_ci struct mlxsw_core *core; 7762306a36Sopenharmony_ci struct ptp_clock *ptp; 7862306a36Sopenharmony_ci struct ptp_clock_info ptp_info; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct mlxsw_sp1_ptp_clock { 8262306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock common; 8362306a36Sopenharmony_ci spinlock_t lock; /* protect this structure */ 8462306a36Sopenharmony_ci struct cyclecounter cycles; 8562306a36Sopenharmony_ci struct timecounter tc; 8662306a36Sopenharmony_ci u32 nominal_c_mult; 8762306a36Sopenharmony_ci unsigned long overflow_period; 8862306a36Sopenharmony_ci struct delayed_work overflow_work; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct mlxsw_sp1_ptp_state * 9262306a36Sopenharmony_cimlxsw_sp1_ptp_state(struct mlxsw_sp *mlxsw_sp) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci return container_of(mlxsw_sp->ptp_state, struct mlxsw_sp1_ptp_state, 9562306a36Sopenharmony_ci common); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct mlxsw_sp2_ptp_state * 9962306a36Sopenharmony_cimlxsw_sp2_ptp_state(struct mlxsw_sp *mlxsw_sp) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return container_of(mlxsw_sp->ptp_state, struct mlxsw_sp2_ptp_state, 10262306a36Sopenharmony_ci common); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct mlxsw_sp1_ptp_clock * 10662306a36Sopenharmony_cimlxsw_sp1_ptp_clock(struct ptp_clock_info *ptp) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return container_of(ptp, struct mlxsw_sp1_ptp_clock, common.ptp_info); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic u64 __mlxsw_sp1_ptp_read_frc(struct mlxsw_sp1_ptp_clock *clock, 11262306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->common.core; 11562306a36Sopenharmony_ci u32 frc_h1, frc_h2, frc_l; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci frc_h1 = mlxsw_core_read_frc_h(mlxsw_core); 11862306a36Sopenharmony_ci ptp_read_system_prets(sts); 11962306a36Sopenharmony_ci frc_l = mlxsw_core_read_frc_l(mlxsw_core); 12062306a36Sopenharmony_ci ptp_read_system_postts(sts); 12162306a36Sopenharmony_ci frc_h2 = mlxsw_core_read_frc_h(mlxsw_core); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (frc_h1 != frc_h2) { 12462306a36Sopenharmony_ci /* wrap around */ 12562306a36Sopenharmony_ci ptp_read_system_prets(sts); 12662306a36Sopenharmony_ci frc_l = mlxsw_core_read_frc_l(mlxsw_core); 12762306a36Sopenharmony_ci ptp_read_system_postts(sts); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return (u64) frc_l | (u64) frc_h2 << 32; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic u64 mlxsw_sp1_ptp_read_frc(const struct cyclecounter *cc) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = 13662306a36Sopenharmony_ci container_of(cc, struct mlxsw_sp1_ptp_clock, cycles); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return __mlxsw_sp1_ptp_read_frc(clock, NULL) & cc->mask; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int 14262306a36Sopenharmony_cimlxsw_sp_ptp_phc_adjfreq(struct mlxsw_sp_ptp_clock *clock, int freq_adj) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 14562306a36Sopenharmony_ci char mtutc_pl[MLXSW_REG_MTUTC_LEN]; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mlxsw_reg_mtutc_pack(mtutc_pl, MLXSW_REG_MTUTC_OPERATION_ADJUST_FREQ, 14862306a36Sopenharmony_ci freq_adj, 0, 0, 0); 14962306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic u64 mlxsw_sp1_ptp_ns2cycles(const struct timecounter *tc, u64 nsec) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci u64 cycles = (u64) nsec; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci cycles <<= tc->cc->shift; 15762306a36Sopenharmony_ci cycles = div_u64(cycles, tc->cc->mult); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return cycles; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int 16362306a36Sopenharmony_cimlxsw_sp1_ptp_phc_settime(struct mlxsw_sp1_ptp_clock *clock, u64 nsec) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->common.core; 16662306a36Sopenharmony_ci u64 next_sec, next_sec_in_nsec, cycles; 16762306a36Sopenharmony_ci char mtutc_pl[MLXSW_REG_MTUTC_LEN]; 16862306a36Sopenharmony_ci char mtpps_pl[MLXSW_REG_MTPPS_LEN]; 16962306a36Sopenharmony_ci int err; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci next_sec = div_u64(nsec, NSEC_PER_SEC) + 1; 17262306a36Sopenharmony_ci next_sec_in_nsec = next_sec * NSEC_PER_SEC; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 17562306a36Sopenharmony_ci cycles = mlxsw_sp1_ptp_ns2cycles(&clock->tc, next_sec_in_nsec); 17662306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci mlxsw_reg_mtpps_vpin_pack(mtpps_pl, cycles); 17962306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtpps), mtpps_pl); 18062306a36Sopenharmony_ci if (err) 18162306a36Sopenharmony_ci return err; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci mlxsw_reg_mtutc_pack(mtutc_pl, 18462306a36Sopenharmony_ci MLXSW_REG_MTUTC_OPERATION_SET_TIME_AT_NEXT_SEC, 18562306a36Sopenharmony_ci 0, next_sec, 0, 0); 18662306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp); 19262306a36Sopenharmony_ci s32 ppb; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ppb = scaled_ppm_to_ppb(scaled_ppm); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 19762306a36Sopenharmony_ci timecounter_read(&clock->tc); 19862306a36Sopenharmony_ci clock->cycles.mult = adjust_by_scaled_ppm(clock->nominal_c_mult, 19962306a36Sopenharmony_ci scaled_ppm); 20062306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return mlxsw_sp_ptp_phc_adjfreq(&clock->common, ppb); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp); 20862306a36Sopenharmony_ci u64 nsec; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 21162306a36Sopenharmony_ci timecounter_adjtime(&clock->tc, delta); 21262306a36Sopenharmony_ci nsec = timecounter_read(&clock->tc); 21362306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return mlxsw_sp1_ptp_phc_settime(clock, nsec); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_gettimex(struct ptp_clock_info *ptp, 21962306a36Sopenharmony_ci struct timespec64 *ts, 22062306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp); 22362306a36Sopenharmony_ci u64 cycles, nsec; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 22662306a36Sopenharmony_ci cycles = __mlxsw_sp1_ptp_read_frc(clock, sts); 22762306a36Sopenharmony_ci nsec = timecounter_cyc2time(&clock->tc, cycles); 22862306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci *ts = ns_to_timespec64(nsec); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_settime(struct ptp_clock_info *ptp, 23662306a36Sopenharmony_ci const struct timespec64 *ts) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp); 23962306a36Sopenharmony_ci u64 nsec = timespec64_to_ns(ts); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 24262306a36Sopenharmony_ci timecounter_init(&clock->tc, &clock->cycles, nsec); 24362306a36Sopenharmony_ci nsec = timecounter_read(&clock->tc); 24462306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return mlxsw_sp1_ptp_phc_settime(clock, nsec); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic const struct ptp_clock_info mlxsw_sp1_ptp_clock_info = { 25062306a36Sopenharmony_ci .owner = THIS_MODULE, 25162306a36Sopenharmony_ci .name = "mlxsw_sp_clock", 25262306a36Sopenharmony_ci .max_adj = 100000000, 25362306a36Sopenharmony_ci .adjfine = mlxsw_sp1_ptp_adjfine, 25462306a36Sopenharmony_ci .adjtime = mlxsw_sp1_ptp_adjtime, 25562306a36Sopenharmony_ci .gettimex64 = mlxsw_sp1_ptp_gettimex, 25662306a36Sopenharmony_ci .settime64 = mlxsw_sp1_ptp_settime, 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_clock_overflow(struct work_struct *work) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 26262306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci clock = container_of(dwork, struct mlxsw_sp1_ptp_clock, overflow_work); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 26762306a36Sopenharmony_ci timecounter_read(&clock->tc); 26862306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 26962306a36Sopenharmony_ci mlxsw_core_schedule_dw(&clock->overflow_work, clock->overflow_period); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistruct mlxsw_sp_ptp_clock * 27362306a36Sopenharmony_cimlxsw_sp1_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci u64 overflow_cycles, nsec, frac = 0; 27662306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock; 27762306a36Sopenharmony_ci int err; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci clock = kzalloc(sizeof(*clock), GFP_KERNEL); 28062306a36Sopenharmony_ci if (!clock) 28162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci spin_lock_init(&clock->lock); 28462306a36Sopenharmony_ci clock->cycles.read = mlxsw_sp1_ptp_read_frc; 28562306a36Sopenharmony_ci clock->cycles.shift = MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT; 28662306a36Sopenharmony_ci clock->cycles.mult = clocksource_khz2mult(MLXSW_SP1_PTP_CLOCK_FREQ_KHZ, 28762306a36Sopenharmony_ci clock->cycles.shift); 28862306a36Sopenharmony_ci clock->nominal_c_mult = clock->cycles.mult; 28962306a36Sopenharmony_ci clock->cycles.mask = CLOCKSOURCE_MASK(MLXSW_SP1_PTP_CLOCK_MASK); 29062306a36Sopenharmony_ci clock->common.core = mlxsw_sp->core; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci timecounter_init(&clock->tc, &clock->cycles, 0); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Calculate period in seconds to call the overflow watchdog - to make 29562306a36Sopenharmony_ci * sure counter is checked at least twice every wrap around. 29662306a36Sopenharmony_ci * The period is calculated as the minimum between max HW cycles count 29762306a36Sopenharmony_ci * (The clock source mask) and max amount of cycles that can be 29862306a36Sopenharmony_ci * multiplied by clock multiplier where the result doesn't exceed 29962306a36Sopenharmony_ci * 64bits. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci overflow_cycles = div64_u64(~0ULL >> 1, clock->cycles.mult); 30262306a36Sopenharmony_ci overflow_cycles = min(overflow_cycles, div_u64(clock->cycles.mask, 3)); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci nsec = cyclecounter_cyc2ns(&clock->cycles, overflow_cycles, 0, &frac); 30562306a36Sopenharmony_ci clock->overflow_period = nsecs_to_jiffies(nsec); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci INIT_DELAYED_WORK(&clock->overflow_work, mlxsw_sp1_ptp_clock_overflow); 30862306a36Sopenharmony_ci mlxsw_core_schedule_dw(&clock->overflow_work, 0); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci clock->common.ptp_info = mlxsw_sp1_ptp_clock_info; 31162306a36Sopenharmony_ci clock->common.ptp = ptp_clock_register(&clock->common.ptp_info, dev); 31262306a36Sopenharmony_ci if (IS_ERR(clock->common.ptp)) { 31362306a36Sopenharmony_ci err = PTR_ERR(clock->common.ptp); 31462306a36Sopenharmony_ci dev_err(dev, "ptp_clock_register failed %d\n", err); 31562306a36Sopenharmony_ci goto err_ptp_clock_register; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return &clock->common; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cierr_ptp_clock_register: 32162306a36Sopenharmony_ci cancel_delayed_work_sync(&clock->overflow_work); 32262306a36Sopenharmony_ci kfree(clock); 32362306a36Sopenharmony_ci return ERR_PTR(err); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid mlxsw_sp1_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock_common) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = 32962306a36Sopenharmony_ci container_of(clock_common, struct mlxsw_sp1_ptp_clock, common); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ptp_clock_unregister(clock_common->ptp); 33262306a36Sopenharmony_ci cancel_delayed_work_sync(&clock->overflow_work); 33362306a36Sopenharmony_ci kfree(clock); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic u64 mlxsw_sp2_ptp_read_utc(struct mlxsw_sp_ptp_clock *clock, 33762306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 34062306a36Sopenharmony_ci u32 utc_sec1, utc_sec2, utc_nsec; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci utc_sec1 = mlxsw_core_read_utc_sec(mlxsw_core); 34362306a36Sopenharmony_ci ptp_read_system_prets(sts); 34462306a36Sopenharmony_ci utc_nsec = mlxsw_core_read_utc_nsec(mlxsw_core); 34562306a36Sopenharmony_ci ptp_read_system_postts(sts); 34662306a36Sopenharmony_ci utc_sec2 = mlxsw_core_read_utc_sec(mlxsw_core); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (utc_sec1 != utc_sec2) { 34962306a36Sopenharmony_ci /* Wrap around. */ 35062306a36Sopenharmony_ci ptp_read_system_prets(sts); 35162306a36Sopenharmony_ci utc_nsec = mlxsw_core_read_utc_nsec(mlxsw_core); 35262306a36Sopenharmony_ci ptp_read_system_postts(sts); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return (u64)utc_sec2 * NSEC_PER_SEC + utc_nsec; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int 35962306a36Sopenharmony_cimlxsw_sp2_ptp_phc_settime(struct mlxsw_sp_ptp_clock *clock, u64 nsec) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 36262306a36Sopenharmony_ci char mtutc_pl[MLXSW_REG_MTUTC_LEN]; 36362306a36Sopenharmony_ci u32 sec, nsec_rem; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci sec = div_u64_rem(nsec, NSEC_PER_SEC, &nsec_rem); 36662306a36Sopenharmony_ci mlxsw_reg_mtutc_pack(mtutc_pl, 36762306a36Sopenharmony_ci MLXSW_REG_MTUTC_OPERATION_SET_TIME_IMMEDIATE, 36862306a36Sopenharmony_ci 0, sec, nsec_rem, 0); 36962306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 37562306a36Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 37662306a36Sopenharmony_ci s32 ppb = scaled_ppm_to_ppb(scaled_ppm); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* In Spectrum-2 and newer ASICs, the frequency adjustment in MTUTC is 37962306a36Sopenharmony_ci * reversed, positive values mean to decrease the frequency. Adjust the 38062306a36Sopenharmony_ci * sign of PPB to this behavior. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci return mlxsw_sp_ptp_phc_adjfreq(clock, -ppb); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 38862306a36Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 38962306a36Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 39062306a36Sopenharmony_ci char mtutc_pl[MLXSW_REG_MTUTC_LEN]; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* HW time adjustment range is s16. If out of range, set time instead. */ 39362306a36Sopenharmony_ci if (delta < S16_MIN || delta > S16_MAX) { 39462306a36Sopenharmony_ci u64 nsec; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci nsec = mlxsw_sp2_ptp_read_utc(clock, NULL); 39762306a36Sopenharmony_ci nsec += delta; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return mlxsw_sp2_ptp_phc_settime(clock, nsec); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci mlxsw_reg_mtutc_pack(mtutc_pl, 40362306a36Sopenharmony_ci MLXSW_REG_MTUTC_OPERATION_ADJUST_TIME, 40462306a36Sopenharmony_ci 0, 0, 0, delta); 40562306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_gettimex(struct ptp_clock_info *ptp, 40962306a36Sopenharmony_ci struct timespec64 *ts, 41062306a36Sopenharmony_ci struct ptp_system_timestamp *sts) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 41362306a36Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 41462306a36Sopenharmony_ci u64 nsec; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci nsec = mlxsw_sp2_ptp_read_utc(clock, sts); 41762306a36Sopenharmony_ci *ts = ns_to_timespec64(nsec); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_settime(struct ptp_clock_info *ptp, 42362306a36Sopenharmony_ci const struct timespec64 *ts) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 42662306a36Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 42762306a36Sopenharmony_ci u64 nsec = timespec64_to_ns(ts); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return mlxsw_sp2_ptp_phc_settime(clock, nsec); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const struct ptp_clock_info mlxsw_sp2_ptp_clock_info = { 43362306a36Sopenharmony_ci .owner = THIS_MODULE, 43462306a36Sopenharmony_ci .name = "mlxsw_sp_clock", 43562306a36Sopenharmony_ci .max_adj = MLXSW_REG_MTUTC_MAX_FREQ_ADJ, 43662306a36Sopenharmony_ci .adjfine = mlxsw_sp2_ptp_adjfine, 43762306a36Sopenharmony_ci .adjtime = mlxsw_sp2_ptp_adjtime, 43862306a36Sopenharmony_ci .gettimex64 = mlxsw_sp2_ptp_gettimex, 43962306a36Sopenharmony_ci .settime64 = mlxsw_sp2_ptp_settime, 44062306a36Sopenharmony_ci}; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistruct mlxsw_sp_ptp_clock * 44362306a36Sopenharmony_cimlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock; 44662306a36Sopenharmony_ci int err; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci clock = kzalloc(sizeof(*clock), GFP_KERNEL); 44962306a36Sopenharmony_ci if (!clock) 45062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci clock->core = mlxsw_sp->core; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci clock->ptp_info = mlxsw_sp2_ptp_clock_info; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci err = mlxsw_sp2_ptp_phc_settime(clock, 0); 45762306a36Sopenharmony_ci if (err) { 45862306a36Sopenharmony_ci dev_err(dev, "setting UTC time failed %d\n", err); 45962306a36Sopenharmony_ci goto err_ptp_phc_settime; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci clock->ptp = ptp_clock_register(&clock->ptp_info, dev); 46362306a36Sopenharmony_ci if (IS_ERR(clock->ptp)) { 46462306a36Sopenharmony_ci err = PTR_ERR(clock->ptp); 46562306a36Sopenharmony_ci dev_err(dev, "ptp_clock_register failed %d\n", err); 46662306a36Sopenharmony_ci goto err_ptp_clock_register; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return clock; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cierr_ptp_clock_register: 47262306a36Sopenharmony_cierr_ptp_phc_settime: 47362306a36Sopenharmony_ci kfree(clock); 47462306a36Sopenharmony_ci return ERR_PTR(err); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_civoid mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci ptp_clock_unregister(clock->ptp); 48062306a36Sopenharmony_ci kfree(clock); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int mlxsw_sp_ptp_parse(struct sk_buff *skb, 48462306a36Sopenharmony_ci u8 *p_domain_number, 48562306a36Sopenharmony_ci u8 *p_message_type, 48662306a36Sopenharmony_ci u16 *p_sequence_id) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci unsigned int ptp_class; 48962306a36Sopenharmony_ci struct ptp_header *hdr; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ptp_class = ptp_classify_raw(skb); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci switch (ptp_class & PTP_CLASS_VMASK) { 49462306a36Sopenharmony_ci case PTP_CLASS_V1: 49562306a36Sopenharmony_ci case PTP_CLASS_V2: 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci default: 49862306a36Sopenharmony_ci return -ERANGE; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci hdr = ptp_parse_header(skb, ptp_class); 50262306a36Sopenharmony_ci if (!hdr) 50362306a36Sopenharmony_ci return -EINVAL; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci *p_message_type = ptp_get_msgtype(hdr, ptp_class); 50662306a36Sopenharmony_ci *p_domain_number = hdr->domain_number; 50762306a36Sopenharmony_ci *p_sequence_id = be16_to_cpu(hdr->sequence_id); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* Returns NULL on successful insertion, a pointer on conflict, or an ERR_PTR on 51362306a36Sopenharmony_ci * error. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_cistatic int 51662306a36Sopenharmony_cimlxsw_sp1_ptp_unmatched_save(struct mlxsw_sp *mlxsw_sp, 51762306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key, 51862306a36Sopenharmony_ci struct sk_buff *skb, 51962306a36Sopenharmony_ci u64 timestamp) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci int cycles = MLXSW_SP1_PTP_HT_GC_TIMEOUT / MLXSW_SP1_PTP_HT_GC_INTERVAL; 52262306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp); 52362306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched; 52462306a36Sopenharmony_ci int err; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci unmatched = kzalloc(sizeof(*unmatched), GFP_ATOMIC); 52762306a36Sopenharmony_ci if (!unmatched) 52862306a36Sopenharmony_ci return -ENOMEM; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci unmatched->key = key; 53162306a36Sopenharmony_ci unmatched->skb = skb; 53262306a36Sopenharmony_ci unmatched->timestamp = timestamp; 53362306a36Sopenharmony_ci unmatched->gc_cycle = ptp_state->gc_cycle + cycles; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci err = rhltable_insert(&ptp_state->unmatched_ht, &unmatched->ht_node, 53662306a36Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 53762306a36Sopenharmony_ci if (err) 53862306a36Sopenharmony_ci kfree(unmatched); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return err; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic struct mlxsw_sp1_ptp_unmatched * 54462306a36Sopenharmony_cimlxsw_sp1_ptp_unmatched_lookup(struct mlxsw_sp *mlxsw_sp, 54562306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key, int *p_length) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp); 54862306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched, *last = NULL; 54962306a36Sopenharmony_ci struct rhlist_head *tmp, *list; 55062306a36Sopenharmony_ci int length = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci list = rhltable_lookup(&ptp_state->unmatched_ht, &key, 55362306a36Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 55462306a36Sopenharmony_ci rhl_for_each_entry_rcu(unmatched, tmp, list, ht_node) { 55562306a36Sopenharmony_ci last = unmatched; 55662306a36Sopenharmony_ci length++; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci *p_length = length; 56062306a36Sopenharmony_ci return last; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int 56462306a36Sopenharmony_cimlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp, 56562306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return rhltable_remove(&ptp_state->unmatched_ht, 57062306a36Sopenharmony_ci &unmatched->ht_node, 57162306a36Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/* This function is called in the following scenarios: 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * 1) When a packet is matched with its timestamp. 57762306a36Sopenharmony_ci * 2) In several situation when it is necessary to immediately pass on 57862306a36Sopenharmony_ci * an SKB without a timestamp. 57962306a36Sopenharmony_ci * 3) From GC indirectly through mlxsw_sp1_ptp_unmatched_finish(). 58062306a36Sopenharmony_ci * This case is similar to 2) above. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp *mlxsw_sp, 58362306a36Sopenharmony_ci struct sk_buff *skb, u16 local_port, 58462306a36Sopenharmony_ci bool ingress, 58562306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* Between capturing the packet and finishing it, there is a window of 59062306a36Sopenharmony_ci * opportunity for the originating port to go away (e.g. due to a 59162306a36Sopenharmony_ci * split). Also make sure the SKB device reference is still valid. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 59462306a36Sopenharmony_ci if (!(mlxsw_sp_port && (!skb->dev || skb->dev == mlxsw_sp_port->dev))) { 59562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 59662306a36Sopenharmony_ci return; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (ingress) { 60062306a36Sopenharmony_ci if (hwtstamps) 60162306a36Sopenharmony_ci *skb_hwtstamps(skb) = *hwtstamps; 60262306a36Sopenharmony_ci mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp); 60362306a36Sopenharmony_ci } else { 60462306a36Sopenharmony_ci /* skb_tstamp_tx() allows hwtstamps to be NULL. */ 60562306a36Sopenharmony_ci skb_tstamp_tx(skb, hwtstamps); 60662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void mlxsw_sp1_packet_timestamp(struct mlxsw_sp *mlxsw_sp, 61162306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key, 61262306a36Sopenharmony_ci struct sk_buff *skb, 61362306a36Sopenharmony_ci u64 timestamp) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock_common = mlxsw_sp->clock; 61662306a36Sopenharmony_ci struct mlxsw_sp1_ptp_clock *clock = 61762306a36Sopenharmony_ci container_of(clock_common, struct mlxsw_sp1_ptp_clock, common); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci struct skb_shared_hwtstamps hwtstamps; 62062306a36Sopenharmony_ci u64 nsec; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci spin_lock_bh(&clock->lock); 62362306a36Sopenharmony_ci nsec = timecounter_cyc2time(&clock->tc, timestamp); 62462306a36Sopenharmony_ci spin_unlock_bh(&clock->lock); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci hwtstamps.hwtstamp = ns_to_ktime(nsec); 62762306a36Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, 62862306a36Sopenharmony_ci key.local_port, key.ingress, &hwtstamps); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void 63262306a36Sopenharmony_cimlxsw_sp1_ptp_unmatched_finish(struct mlxsw_sp *mlxsw_sp, 63362306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci if (unmatched->skb && unmatched->timestamp) 63662306a36Sopenharmony_ci mlxsw_sp1_packet_timestamp(mlxsw_sp, unmatched->key, 63762306a36Sopenharmony_ci unmatched->skb, 63862306a36Sopenharmony_ci unmatched->timestamp); 63962306a36Sopenharmony_ci else if (unmatched->skb) 64062306a36Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, unmatched->skb, 64162306a36Sopenharmony_ci unmatched->key.local_port, 64262306a36Sopenharmony_ci unmatched->key.ingress, NULL); 64362306a36Sopenharmony_ci kfree_rcu(unmatched, rcu); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_unmatched_free_fn(void *ptr, void *arg) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched = ptr; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* This is invoked at a point where the ports are gone already. Nothing 65162306a36Sopenharmony_ci * to do with whatever is left in the HT but to free it. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci if (unmatched->skb) 65462306a36Sopenharmony_ci dev_kfree_skb_any(unmatched->skb); 65562306a36Sopenharmony_ci kfree_rcu(unmatched, rcu); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_got_piece(struct mlxsw_sp *mlxsw_sp, 65962306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key, 66062306a36Sopenharmony_ci struct sk_buff *skb, u64 timestamp) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp); 66362306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched; 66462306a36Sopenharmony_ci int length; 66562306a36Sopenharmony_ci int err; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci rcu_read_lock(); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci spin_lock(&ptp_state->unmatched_lock); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci unmatched = mlxsw_sp1_ptp_unmatched_lookup(mlxsw_sp, key, &length); 67262306a36Sopenharmony_ci if (skb && unmatched && unmatched->timestamp) { 67362306a36Sopenharmony_ci unmatched->skb = skb; 67462306a36Sopenharmony_ci } else if (timestamp && unmatched && unmatched->skb) { 67562306a36Sopenharmony_ci unmatched->timestamp = timestamp; 67662306a36Sopenharmony_ci } else { 67762306a36Sopenharmony_ci /* Either there is no entry to match, or one that is there is 67862306a36Sopenharmony_ci * incompatible. 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci if (length < 100) 68162306a36Sopenharmony_ci err = mlxsw_sp1_ptp_unmatched_save(mlxsw_sp, key, 68262306a36Sopenharmony_ci skb, timestamp); 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci err = -E2BIG; 68562306a36Sopenharmony_ci if (err && skb) 68662306a36Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, 68762306a36Sopenharmony_ci key.local_port, 68862306a36Sopenharmony_ci key.ingress, NULL); 68962306a36Sopenharmony_ci unmatched = NULL; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (unmatched) { 69362306a36Sopenharmony_ci err = mlxsw_sp1_ptp_unmatched_remove(mlxsw_sp, unmatched); 69462306a36Sopenharmony_ci WARN_ON_ONCE(err); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_unlock(&ptp_state->unmatched_lock); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (unmatched) 70062306a36Sopenharmony_ci mlxsw_sp1_ptp_unmatched_finish(mlxsw_sp, unmatched); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci rcu_read_unlock(); 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_got_packet(struct mlxsw_sp *mlxsw_sp, 70662306a36Sopenharmony_ci struct sk_buff *skb, u16 local_port, 70762306a36Sopenharmony_ci bool ingress) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 71062306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key; 71162306a36Sopenharmony_ci u8 types; 71262306a36Sopenharmony_ci int err; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 71562306a36Sopenharmony_ci if (!mlxsw_sp_port) 71662306a36Sopenharmony_ci goto immediate; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci types = ingress ? mlxsw_sp_port->ptp.ing_types : 71962306a36Sopenharmony_ci mlxsw_sp_port->ptp.egr_types; 72062306a36Sopenharmony_ci if (!types) 72162306a36Sopenharmony_ci goto immediate; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci memset(&key, 0, sizeof(key)); 72462306a36Sopenharmony_ci key.local_port = local_port; 72562306a36Sopenharmony_ci key.ingress = ingress; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci err = mlxsw_sp_ptp_parse(skb, &key.domain_number, &key.message_type, 72862306a36Sopenharmony_ci &key.sequence_id); 72962306a36Sopenharmony_ci if (err) 73062306a36Sopenharmony_ci goto immediate; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* For packets whose timestamping was not enabled on this port, don't 73362306a36Sopenharmony_ci * bother trying to match the timestamp. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci if (!((1 << key.message_type) & types)) 73662306a36Sopenharmony_ci goto immediate; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci mlxsw_sp1_ptp_got_piece(mlxsw_sp, key, skb, 0); 73962306a36Sopenharmony_ci return; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ciimmediate: 74262306a36Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, local_port, ingress, NULL); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_civoid mlxsw_sp1_ptp_got_timestamp(struct mlxsw_sp *mlxsw_sp, bool ingress, 74662306a36Sopenharmony_ci u16 local_port, u8 message_type, 74762306a36Sopenharmony_ci u8 domain_number, u16 sequence_id, 74862306a36Sopenharmony_ci u64 timestamp) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 75162306a36Sopenharmony_ci struct mlxsw_sp1_ptp_key key; 75262306a36Sopenharmony_ci u8 types; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port))) 75562306a36Sopenharmony_ci return; 75662306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 75762306a36Sopenharmony_ci if (!mlxsw_sp_port) 75862306a36Sopenharmony_ci return; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci types = ingress ? mlxsw_sp_port->ptp.ing_types : 76162306a36Sopenharmony_ci mlxsw_sp_port->ptp.egr_types; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* For message types whose timestamping was not enabled on this port, 76462306a36Sopenharmony_ci * don't bother with the timestamp. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci if (!((1 << message_type) & types)) 76762306a36Sopenharmony_ci return; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci memset(&key, 0, sizeof(key)); 77062306a36Sopenharmony_ci key.local_port = local_port; 77162306a36Sopenharmony_ci key.domain_number = domain_number; 77262306a36Sopenharmony_ci key.message_type = message_type; 77362306a36Sopenharmony_ci key.sequence_id = sequence_id; 77462306a36Sopenharmony_ci key.ingress = ingress; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci mlxsw_sp1_ptp_got_piece(mlxsw_sp, key, NULL, timestamp); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_civoid mlxsw_sp1_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb, 78062306a36Sopenharmony_ci u16 local_port) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci skb_reset_mac_header(skb); 78362306a36Sopenharmony_ci mlxsw_sp1_ptp_got_packet(mlxsw_sp, skb, local_port, true); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_civoid mlxsw_sp1_ptp_transmitted(struct mlxsw_sp *mlxsw_sp, 78762306a36Sopenharmony_ci struct sk_buff *skb, u16 local_port) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci mlxsw_sp1_ptp_got_packet(mlxsw_sp, skb, local_port, false); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic void 79362306a36Sopenharmony_cimlxsw_sp1_ptp_ht_gc_collect(struct mlxsw_sp1_ptp_state *ptp_state, 79462306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = ptp_state->common.mlxsw_sp; 79762306a36Sopenharmony_ci struct mlxsw_sp_ptp_port_dir_stats *stats; 79862306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 79962306a36Sopenharmony_ci int err; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* If an unmatched entry has an SKB, it has to be handed over to the 80262306a36Sopenharmony_ci * networking stack. This is usually done from a trap handler, which is 80362306a36Sopenharmony_ci * invoked in a softirq context. Here we are going to do it in process 80462306a36Sopenharmony_ci * context. If that were to be interrupted by a softirq, it could cause 80562306a36Sopenharmony_ci * a deadlock when an attempt is made to take an already-taken lock 80662306a36Sopenharmony_ci * somewhere along the sending path. Disable softirqs to prevent this. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci local_bh_disable(); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci spin_lock(&ptp_state->unmatched_lock); 81162306a36Sopenharmony_ci err = rhltable_remove(&ptp_state->unmatched_ht, &unmatched->ht_node, 81262306a36Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 81362306a36Sopenharmony_ci spin_unlock(&ptp_state->unmatched_lock); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (err) 81662306a36Sopenharmony_ci /* The packet was matched with timestamp during the walk. */ 81762306a36Sopenharmony_ci goto out; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[unmatched->key.local_port]; 82062306a36Sopenharmony_ci if (mlxsw_sp_port) { 82162306a36Sopenharmony_ci stats = unmatched->key.ingress ? 82262306a36Sopenharmony_ci &mlxsw_sp_port->ptp.stats.rx_gcd : 82362306a36Sopenharmony_ci &mlxsw_sp_port->ptp.stats.tx_gcd; 82462306a36Sopenharmony_ci if (unmatched->skb) 82562306a36Sopenharmony_ci stats->packets++; 82662306a36Sopenharmony_ci else 82762306a36Sopenharmony_ci stats->timestamps++; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* mlxsw_sp1_ptp_unmatched_finish() invokes netif_receive_skb(). While 83162306a36Sopenharmony_ci * the comment at that function states that it can only be called in 83262306a36Sopenharmony_ci * soft IRQ context, this pattern of local_bh_disable() + 83362306a36Sopenharmony_ci * netif_receive_skb(), in process context, is seen elsewhere in the 83462306a36Sopenharmony_ci * kernel, notably in pktgen. 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_ci mlxsw_sp1_ptp_unmatched_finish(mlxsw_sp, unmatched); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ciout: 83962306a36Sopenharmony_ci local_bh_enable(); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic void mlxsw_sp1_ptp_ht_gc(struct work_struct *work) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 84562306a36Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched; 84662306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state; 84762306a36Sopenharmony_ci struct rhashtable_iter iter; 84862306a36Sopenharmony_ci u32 gc_cycle; 84962306a36Sopenharmony_ci void *obj; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ptp_state = container_of(dwork, struct mlxsw_sp1_ptp_state, ht_gc_dw); 85262306a36Sopenharmony_ci gc_cycle = ptp_state->gc_cycle++; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci rhltable_walk_enter(&ptp_state->unmatched_ht, &iter); 85562306a36Sopenharmony_ci rhashtable_walk_start(&iter); 85662306a36Sopenharmony_ci while ((obj = rhashtable_walk_next(&iter))) { 85762306a36Sopenharmony_ci if (IS_ERR(obj)) 85862306a36Sopenharmony_ci continue; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci unmatched = obj; 86162306a36Sopenharmony_ci if (unmatched->gc_cycle <= gc_cycle) 86262306a36Sopenharmony_ci mlxsw_sp1_ptp_ht_gc_collect(ptp_state, unmatched); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci rhashtable_walk_stop(&iter); 86562306a36Sopenharmony_ci rhashtable_walk_exit(&iter); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci mlxsw_core_schedule_dw(&ptp_state->ht_gc_dw, 86862306a36Sopenharmony_ci MLXSW_SP1_PTP_HT_GC_INTERVAL); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int mlxsw_sp_ptp_mtptpt_set(struct mlxsw_sp *mlxsw_sp, 87262306a36Sopenharmony_ci enum mlxsw_reg_mtptpt_trap_id trap_id, 87362306a36Sopenharmony_ci u16 message_type) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci char mtptpt_pl[MLXSW_REG_MTPTPT_LEN]; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci mlxsw_reg_mtptpt_pack(mtptpt_pl, trap_id, message_type); 87862306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtptpt), mtptpt_pl); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_set_fifo_clr_on_trap(struct mlxsw_sp *mlxsw_sp, 88262306a36Sopenharmony_ci bool clr) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci char mogcr_pl[MLXSW_REG_MOGCR_LEN] = {0}; 88562306a36Sopenharmony_ci int err; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); 88862306a36Sopenharmony_ci if (err) 88962306a36Sopenharmony_ci return err; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci mlxsw_reg_mogcr_ptp_iftc_set(mogcr_pl, clr); 89262306a36Sopenharmony_ci mlxsw_reg_mogcr_ptp_eftc_set(mogcr_pl, clr); 89362306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp *mlxsw_sp, 89762306a36Sopenharmony_ci u16 ing_types, u16 egr_types) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci char mtpppc_pl[MLXSW_REG_MTPPPC_LEN]; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci mlxsw_reg_mtpppc_pack(mtpppc_pl, ing_types, egr_types); 90262306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl); 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistruct mlxsw_sp1_ptp_shaper_params { 90662306a36Sopenharmony_ci u32 ethtool_speed; 90762306a36Sopenharmony_ci enum mlxsw_reg_qpsc_port_speed port_speed; 90862306a36Sopenharmony_ci u8 shaper_time_exp; 90962306a36Sopenharmony_ci u8 shaper_time_mantissa; 91062306a36Sopenharmony_ci u8 shaper_inc; 91162306a36Sopenharmony_ci u8 shaper_bs; 91262306a36Sopenharmony_ci u8 port_to_shaper_credits; 91362306a36Sopenharmony_ci int ing_timestamp_inc; 91462306a36Sopenharmony_ci int egr_timestamp_inc; 91562306a36Sopenharmony_ci}; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic const struct mlxsw_sp1_ptp_shaper_params 91862306a36Sopenharmony_cimlxsw_sp1_ptp_shaper_params[] = { 91962306a36Sopenharmony_ci { 92062306a36Sopenharmony_ci .ethtool_speed = SPEED_100, 92162306a36Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_100M, 92262306a36Sopenharmony_ci .shaper_time_exp = 4, 92362306a36Sopenharmony_ci .shaper_time_mantissa = 12, 92462306a36Sopenharmony_ci .shaper_inc = 9, 92562306a36Sopenharmony_ci .shaper_bs = 1, 92662306a36Sopenharmony_ci .port_to_shaper_credits = 1, 92762306a36Sopenharmony_ci .ing_timestamp_inc = -313, 92862306a36Sopenharmony_ci .egr_timestamp_inc = 313, 92962306a36Sopenharmony_ci }, 93062306a36Sopenharmony_ci { 93162306a36Sopenharmony_ci .ethtool_speed = SPEED_1000, 93262306a36Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_1G, 93362306a36Sopenharmony_ci .shaper_time_exp = 0, 93462306a36Sopenharmony_ci .shaper_time_mantissa = 12, 93562306a36Sopenharmony_ci .shaper_inc = 6, 93662306a36Sopenharmony_ci .shaper_bs = 0, 93762306a36Sopenharmony_ci .port_to_shaper_credits = 1, 93862306a36Sopenharmony_ci .ing_timestamp_inc = -35, 93962306a36Sopenharmony_ci .egr_timestamp_inc = 35, 94062306a36Sopenharmony_ci }, 94162306a36Sopenharmony_ci { 94262306a36Sopenharmony_ci .ethtool_speed = SPEED_10000, 94362306a36Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_10G, 94462306a36Sopenharmony_ci .shaper_time_exp = 0, 94562306a36Sopenharmony_ci .shaper_time_mantissa = 2, 94662306a36Sopenharmony_ci .shaper_inc = 14, 94762306a36Sopenharmony_ci .shaper_bs = 1, 94862306a36Sopenharmony_ci .port_to_shaper_credits = 1, 94962306a36Sopenharmony_ci .ing_timestamp_inc = -11, 95062306a36Sopenharmony_ci .egr_timestamp_inc = 11, 95162306a36Sopenharmony_ci }, 95262306a36Sopenharmony_ci { 95362306a36Sopenharmony_ci .ethtool_speed = SPEED_25000, 95462306a36Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_25G, 95562306a36Sopenharmony_ci .shaper_time_exp = 0, 95662306a36Sopenharmony_ci .shaper_time_mantissa = 0, 95762306a36Sopenharmony_ci .shaper_inc = 11, 95862306a36Sopenharmony_ci .shaper_bs = 1, 95962306a36Sopenharmony_ci .port_to_shaper_credits = 1, 96062306a36Sopenharmony_ci .ing_timestamp_inc = -14, 96162306a36Sopenharmony_ci .egr_timestamp_inc = 14, 96262306a36Sopenharmony_ci }, 96362306a36Sopenharmony_ci}; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci#define MLXSW_SP1_PTP_SHAPER_PARAMS_LEN ARRAY_SIZE(mlxsw_sp1_ptp_shaper_params) 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp *mlxsw_sp) 96862306a36Sopenharmony_ci{ 96962306a36Sopenharmony_ci const struct mlxsw_sp1_ptp_shaper_params *params; 97062306a36Sopenharmony_ci char qpsc_pl[MLXSW_REG_QPSC_LEN]; 97162306a36Sopenharmony_ci int i, err; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) { 97462306a36Sopenharmony_ci params = &mlxsw_sp1_ptp_shaper_params[i]; 97562306a36Sopenharmony_ci mlxsw_reg_qpsc_pack(qpsc_pl, params->port_speed, 97662306a36Sopenharmony_ci params->shaper_time_exp, 97762306a36Sopenharmony_ci params->shaper_time_mantissa, 97862306a36Sopenharmony_ci params->shaper_inc, params->shaper_bs, 97962306a36Sopenharmony_ci params->port_to_shaper_credits, 98062306a36Sopenharmony_ci params->ing_timestamp_inc, 98162306a36Sopenharmony_ci params->egr_timestamp_inc); 98262306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpsc), qpsc_pl); 98362306a36Sopenharmony_ci if (err) 98462306a36Sopenharmony_ci return err; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int mlxsw_sp_ptp_traps_set(struct mlxsw_sp *mlxsw_sp) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci u16 event_message_type; 99362306a36Sopenharmony_ci int err; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Deliver these message types as PTP0. */ 99662306a36Sopenharmony_ci event_message_type = BIT(PTP_MSGTYPE_SYNC) | 99762306a36Sopenharmony_ci BIT(PTP_MSGTYPE_DELAY_REQ) | 99862306a36Sopenharmony_ci BIT(PTP_MSGTYPE_PDELAY_REQ) | 99962306a36Sopenharmony_ci BIT(PTP_MSGTYPE_PDELAY_RESP); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci err = mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 100262306a36Sopenharmony_ci event_message_type); 100362306a36Sopenharmony_ci if (err) 100462306a36Sopenharmony_ci return err; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* Everything else is PTP1. */ 100762306a36Sopenharmony_ci err = mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 100862306a36Sopenharmony_ci ~event_message_type); 100962306a36Sopenharmony_ci if (err) 101062306a36Sopenharmony_ci goto err_mtptpt1_set; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return 0; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cierr_mtptpt1_set: 101562306a36Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0); 101662306a36Sopenharmony_ci return err; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic void mlxsw_sp_ptp_traps_unset(struct mlxsw_sp *mlxsw_sp) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 0); 102262306a36Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistruct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state; 102862306a36Sopenharmony_ci int err; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci err = mlxsw_sp1_ptp_shaper_params_set(mlxsw_sp); 103162306a36Sopenharmony_ci if (err) 103262306a36Sopenharmony_ci return ERR_PTR(err); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL); 103562306a36Sopenharmony_ci if (!ptp_state) 103662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 103762306a36Sopenharmony_ci ptp_state->common.mlxsw_sp = mlxsw_sp; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci spin_lock_init(&ptp_state->unmatched_lock); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci err = rhltable_init(&ptp_state->unmatched_ht, 104262306a36Sopenharmony_ci &mlxsw_sp1_ptp_unmatched_ht_params); 104362306a36Sopenharmony_ci if (err) 104462306a36Sopenharmony_ci goto err_hashtable_init; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci err = mlxsw_sp_ptp_traps_set(mlxsw_sp); 104762306a36Sopenharmony_ci if (err) 104862306a36Sopenharmony_ci goto err_ptp_traps_set; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci err = mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, true); 105162306a36Sopenharmony_ci if (err) 105262306a36Sopenharmony_ci goto err_fifo_clr; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci INIT_DELAYED_WORK(&ptp_state->ht_gc_dw, mlxsw_sp1_ptp_ht_gc); 105562306a36Sopenharmony_ci mlxsw_core_schedule_dw(&ptp_state->ht_gc_dw, 105662306a36Sopenharmony_ci MLXSW_SP1_PTP_HT_GC_INTERVAL); 105762306a36Sopenharmony_ci return &ptp_state->common; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cierr_fifo_clr: 106062306a36Sopenharmony_ci mlxsw_sp_ptp_traps_unset(mlxsw_sp); 106162306a36Sopenharmony_cierr_ptp_traps_set: 106262306a36Sopenharmony_ci rhltable_destroy(&ptp_state->unmatched_ht); 106362306a36Sopenharmony_cierr_hashtable_init: 106462306a36Sopenharmony_ci kfree(ptp_state); 106562306a36Sopenharmony_ci return ERR_PTR(err); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_civoid mlxsw_sp1_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state_common) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = ptp_state_common->mlxsw_sp; 107162306a36Sopenharmony_ci struct mlxsw_sp1_ptp_state *ptp_state; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci cancel_delayed_work_sync(&ptp_state->ht_gc_dw); 107662306a36Sopenharmony_ci mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp, 0, 0); 107762306a36Sopenharmony_ci mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, false); 107862306a36Sopenharmony_ci mlxsw_sp_ptp_traps_unset(mlxsw_sp); 107962306a36Sopenharmony_ci rhltable_free_and_destroy(&ptp_state->unmatched_ht, 108062306a36Sopenharmony_ci &mlxsw_sp1_ptp_unmatched_free_fn, NULL); 108162306a36Sopenharmony_ci kfree(ptp_state); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ciint mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port, 108562306a36Sopenharmony_ci struct hwtstamp_config *config) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci *config = mlxsw_sp_port->ptp.hwtstamp_config; 108862306a36Sopenharmony_ci return 0; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic int 109262306a36Sopenharmony_cimlxsw_sp1_ptp_get_message_types(const struct hwtstamp_config *config, 109362306a36Sopenharmony_ci u16 *p_ing_types, u16 *p_egr_types, 109462306a36Sopenharmony_ci enum hwtstamp_rx_filters *p_rx_filter) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci enum hwtstamp_rx_filters rx_filter = config->rx_filter; 109762306a36Sopenharmony_ci enum hwtstamp_tx_types tx_type = config->tx_type; 109862306a36Sopenharmony_ci u16 ing_types = 0x00; 109962306a36Sopenharmony_ci u16 egr_types = 0x00; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci switch (tx_type) { 110262306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 110362306a36Sopenharmony_ci egr_types = 0x00; 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 110662306a36Sopenharmony_ci egr_types = 0xff; 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 110962306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 111062306a36Sopenharmony_ci return -ERANGE; 111162306a36Sopenharmony_ci default: 111262306a36Sopenharmony_ci return -EINVAL; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci switch (rx_filter) { 111662306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 111762306a36Sopenharmony_ci ing_types = 0x00; 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 112062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 112162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 112262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 112362306a36Sopenharmony_ci ing_types = 0x01; 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 112662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 112762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 112862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 112962306a36Sopenharmony_ci ing_types = 0x02; 113062306a36Sopenharmony_ci break; 113162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 113262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 113362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 113462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 113562306a36Sopenharmony_ci ing_types = 0x0f; 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 113862306a36Sopenharmony_ci ing_types = 0xff; 113962306a36Sopenharmony_ci break; 114062306a36Sopenharmony_ci case HWTSTAMP_FILTER_SOME: 114162306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 114262306a36Sopenharmony_ci return -ERANGE; 114362306a36Sopenharmony_ci default: 114462306a36Sopenharmony_ci return -EINVAL; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci *p_ing_types = ing_types; 114862306a36Sopenharmony_ci *p_egr_types = egr_types; 114962306a36Sopenharmony_ci *p_rx_filter = rx_filter; 115062306a36Sopenharmony_ci return 0; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port, 115462306a36Sopenharmony_ci u16 ing_types, u16 egr_types) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 115762306a36Sopenharmony_ci struct mlxsw_sp_port *tmp; 115862306a36Sopenharmony_ci u16 orig_ing_types = 0; 115962306a36Sopenharmony_ci u16 orig_egr_types = 0; 116062306a36Sopenharmony_ci int err; 116162306a36Sopenharmony_ci int i; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* MTPPPC configures timestamping globally, not per port. Find the 116462306a36Sopenharmony_ci * configuration that contains all configured timestamping requests. 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_ci for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) { 116762306a36Sopenharmony_ci tmp = mlxsw_sp->ports[i]; 116862306a36Sopenharmony_ci if (tmp) { 116962306a36Sopenharmony_ci orig_ing_types |= tmp->ptp.ing_types; 117062306a36Sopenharmony_ci orig_egr_types |= tmp->ptp.egr_types; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci if (tmp && tmp != mlxsw_sp_port) { 117362306a36Sopenharmony_ci ing_types |= tmp->ptp.ing_types; 117462306a36Sopenharmony_ci egr_types |= tmp->ptp.egr_types; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if ((ing_types || egr_types) && !(orig_ing_types || orig_egr_types)) { 117962306a36Sopenharmony_ci err = mlxsw_sp_parsing_depth_inc(mlxsw_sp); 118062306a36Sopenharmony_ci if (err) { 118162306a36Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to increase parsing depth"); 118262306a36Sopenharmony_ci return err; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci if (!(ing_types || egr_types) && (orig_ing_types || orig_egr_types)) 118662306a36Sopenharmony_ci mlxsw_sp_parsing_depth_dec(mlxsw_sp); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp_port->mlxsw_sp, 118962306a36Sopenharmony_ci ing_types, egr_types); 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic bool mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port *mlxsw_sp_port) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci return mlxsw_sp_port->ptp.ing_types || mlxsw_sp_port->ptp.egr_types; 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic int 119862306a36Sopenharmony_cimlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 120162306a36Sopenharmony_ci char qeec_pl[MLXSW_REG_QEEC_LEN]; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci mlxsw_reg_qeec_ptps_pack(qeec_pl, mlxsw_sp_port->local_port, enable); 120462306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl); 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci bool ptps = false; 121062306a36Sopenharmony_ci int err, i; 121162306a36Sopenharmony_ci u32 speed; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port)) 121462306a36Sopenharmony_ci return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, false); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci err = mlxsw_sp_port_speed_get(mlxsw_sp_port, &speed); 121762306a36Sopenharmony_ci if (err) 121862306a36Sopenharmony_ci return err; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) { 122162306a36Sopenharmony_ci if (mlxsw_sp1_ptp_shaper_params[i].ethtool_speed == speed) { 122262306a36Sopenharmony_ci ptps = true; 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_civoid mlxsw_sp1_ptp_shaper_work(struct work_struct *work) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 123362306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 123462306a36Sopenharmony_ci int err; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port, 123762306a36Sopenharmony_ci ptp.shaper_dw); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port)) 124062306a36Sopenharmony_ci return; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port); 124362306a36Sopenharmony_ci if (err) 124462306a36Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to set up PTP shaper\n"); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ciint mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, 124862306a36Sopenharmony_ci struct hwtstamp_config *config) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci enum hwtstamp_rx_filters rx_filter; 125162306a36Sopenharmony_ci u16 ing_types; 125262306a36Sopenharmony_ci u16 egr_types; 125362306a36Sopenharmony_ci int err; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci err = mlxsw_sp1_ptp_get_message_types(config, &ing_types, &egr_types, 125662306a36Sopenharmony_ci &rx_filter); 125762306a36Sopenharmony_ci if (err) 125862306a36Sopenharmony_ci return err; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci err = mlxsw_sp1_ptp_mtpppc_update(mlxsw_sp_port, ing_types, egr_types); 126162306a36Sopenharmony_ci if (err) 126262306a36Sopenharmony_ci return err; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci mlxsw_sp_port->ptp.hwtstamp_config = *config; 126562306a36Sopenharmony_ci mlxsw_sp_port->ptp.ing_types = ing_types; 126662306a36Sopenharmony_ci mlxsw_sp_port->ptp.egr_types = egr_types; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port); 126962306a36Sopenharmony_ci if (err) 127062306a36Sopenharmony_ci return err; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci /* Notify the ioctl caller what we are actually timestamping. */ 127362306a36Sopenharmony_ci config->rx_filter = rx_filter; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci return 0; 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ciint mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, 127962306a36Sopenharmony_ci struct ethtool_ts_info *info) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci info->phc_index = ptp_clock_index(mlxsw_sp->clock->ptp); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 128462306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 128562306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci info->tx_types = BIT(HWTSTAMP_TX_OFF) | 128862306a36Sopenharmony_ci BIT(HWTSTAMP_TX_ON); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 129162306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_ALL); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci return 0; 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistruct mlxsw_sp_ptp_port_stat { 129762306a36Sopenharmony_ci char str[ETH_GSTRING_LEN]; 129862306a36Sopenharmony_ci ptrdiff_t offset; 129962306a36Sopenharmony_ci}; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci#define MLXSW_SP_PTP_PORT_STAT(NAME, FIELD) \ 130262306a36Sopenharmony_ci { \ 130362306a36Sopenharmony_ci .str = NAME, \ 130462306a36Sopenharmony_ci .offset = offsetof(struct mlxsw_sp_ptp_port_stats, \ 130562306a36Sopenharmony_ci FIELD), \ 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic const struct mlxsw_sp_ptp_port_stat mlxsw_sp_ptp_port_stats[] = { 130962306a36Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_rx_gcd_packets", rx_gcd.packets), 131062306a36Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_rx_gcd_timestamps", rx_gcd.timestamps), 131162306a36Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_tx_gcd_packets", tx_gcd.packets), 131262306a36Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_tx_gcd_timestamps", tx_gcd.timestamps), 131362306a36Sopenharmony_ci}; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci#undef MLXSW_SP_PTP_PORT_STAT 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci#define MLXSW_SP_PTP_PORT_STATS_LEN \ 131862306a36Sopenharmony_ci ARRAY_SIZE(mlxsw_sp_ptp_port_stats) 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ciint mlxsw_sp1_get_stats_count(void) 132162306a36Sopenharmony_ci{ 132262306a36Sopenharmony_ci return MLXSW_SP_PTP_PORT_STATS_LEN; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_civoid mlxsw_sp1_get_stats_strings(u8 **p) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci int i; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci for (i = 0; i < MLXSW_SP_PTP_PORT_STATS_LEN; i++) { 133062306a36Sopenharmony_ci memcpy(*p, mlxsw_sp_ptp_port_stats[i].str, 133162306a36Sopenharmony_ci ETH_GSTRING_LEN); 133262306a36Sopenharmony_ci *p += ETH_GSTRING_LEN; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_civoid mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 133762306a36Sopenharmony_ci u64 *data, int data_index) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci void *stats = &mlxsw_sp_port->ptp.stats; 134062306a36Sopenharmony_ci ptrdiff_t offset; 134162306a36Sopenharmony_ci int i; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci data += data_index; 134462306a36Sopenharmony_ci for (i = 0; i < MLXSW_SP_PTP_PORT_STATS_LEN; i++) { 134562306a36Sopenharmony_ci offset = mlxsw_sp_ptp_port_stats[i].offset; 134662306a36Sopenharmony_ci *data++ = *(u64 *)(stats + offset); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistruct mlxsw_sp_ptp_state *mlxsw_sp2_ptp_init(struct mlxsw_sp *mlxsw_sp) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state; 135362306a36Sopenharmony_ci int err; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL); 135662306a36Sopenharmony_ci if (!ptp_state) 135762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ptp_state->common.mlxsw_sp = mlxsw_sp; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci err = mlxsw_sp_ptp_traps_set(mlxsw_sp); 136262306a36Sopenharmony_ci if (err) 136362306a36Sopenharmony_ci goto err_ptp_traps_set; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci refcount_set(&ptp_state->ptp_port_enabled_ref, 0); 136662306a36Sopenharmony_ci mutex_init(&ptp_state->lock); 136762306a36Sopenharmony_ci return &ptp_state->common; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cierr_ptp_traps_set: 137062306a36Sopenharmony_ci kfree(ptp_state); 137162306a36Sopenharmony_ci return ERR_PTR(err); 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_civoid mlxsw_sp2_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state_common) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = ptp_state_common->mlxsw_sp; 137762306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci mutex_destroy(&ptp_state->lock); 138262306a36Sopenharmony_ci mlxsw_sp_ptp_traps_unset(mlxsw_sp); 138362306a36Sopenharmony_ci kfree(ptp_state); 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_cistatic u32 mlxsw_ptp_utc_time_stamp_sec_get(struct mlxsw_core *mlxsw_core, 138762306a36Sopenharmony_ci u8 cqe_ts_sec) 138862306a36Sopenharmony_ci{ 138962306a36Sopenharmony_ci u32 utc_sec = mlxsw_core_read_utc_sec(mlxsw_core); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (cqe_ts_sec > (utc_sec & 0xff)) 139262306a36Sopenharmony_ci /* Time stamp above the last bits of UTC (UTC & 0xff) means the 139362306a36Sopenharmony_ci * latter has wrapped after the time stamp was collected. 139462306a36Sopenharmony_ci */ 139562306a36Sopenharmony_ci utc_sec -= 256; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci utc_sec &= ~0xff; 139862306a36Sopenharmony_ci utc_sec |= cqe_ts_sec; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci return utc_sec; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic void mlxsw_sp2_ptp_hwtstamp_fill(struct mlxsw_core *mlxsw_core, 140462306a36Sopenharmony_ci const struct mlxsw_skb_cb *cb, 140562306a36Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci u64 ts_sec, ts_nsec, nsec; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci WARN_ON_ONCE(!cb->cqe_ts.sec && !cb->cqe_ts.nsec); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* The time stamp in the CQE is represented by 38 bits, which is a short 141262306a36Sopenharmony_ci * representation of UTC time. Software should create the full time 141362306a36Sopenharmony_ci * stamp using the global UTC clock. The seconds have only 8 bits in the 141462306a36Sopenharmony_ci * CQE, to create the full time stamp, use the current UTC time and fix 141562306a36Sopenharmony_ci * the seconds according to the relation between UTC seconds and CQE 141662306a36Sopenharmony_ci * seconds. 141762306a36Sopenharmony_ci */ 141862306a36Sopenharmony_ci ts_sec = mlxsw_ptp_utc_time_stamp_sec_get(mlxsw_core, cb->cqe_ts.sec); 141962306a36Sopenharmony_ci ts_nsec = cb->cqe_ts.nsec; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci nsec = ts_sec * NSEC_PER_SEC + ts_nsec; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci hwtstamps->hwtstamp = ns_to_ktime(nsec); 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_civoid mlxsw_sp2_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb, 142762306a36Sopenharmony_ci u16 local_port) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci struct skb_shared_hwtstamps hwtstamps; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci mlxsw_sp2_ptp_hwtstamp_fill(mlxsw_sp->core, mlxsw_skb_cb(skb), 143262306a36Sopenharmony_ci &hwtstamps); 143362306a36Sopenharmony_ci *skb_hwtstamps(skb) = hwtstamps; 143462306a36Sopenharmony_ci mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp); 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_civoid mlxsw_sp2_ptp_transmitted(struct mlxsw_sp *mlxsw_sp, 143862306a36Sopenharmony_ci struct sk_buff *skb, u16 local_port) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci struct skb_shared_hwtstamps hwtstamps; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci mlxsw_sp2_ptp_hwtstamp_fill(mlxsw_sp->core, mlxsw_skb_cb(skb), 144362306a36Sopenharmony_ci &hwtstamps); 144462306a36Sopenharmony_ci skb_tstamp_tx(skb, &hwtstamps); 144562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ciint mlxsw_sp2_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port, 144962306a36Sopenharmony_ci struct hwtstamp_config *config) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci mutex_lock(&ptp_state->lock); 145662306a36Sopenharmony_ci *config = ptp_state->config; 145762306a36Sopenharmony_ci mutex_unlock(&ptp_state->lock); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci return 0; 146062306a36Sopenharmony_ci} 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_cistatic int 146362306a36Sopenharmony_cimlxsw_sp2_ptp_get_message_types(const struct hwtstamp_config *config, 146462306a36Sopenharmony_ci u16 *p_ing_types, u16 *p_egr_types, 146562306a36Sopenharmony_ci enum hwtstamp_rx_filters *p_rx_filter) 146662306a36Sopenharmony_ci{ 146762306a36Sopenharmony_ci enum hwtstamp_rx_filters rx_filter = config->rx_filter; 146862306a36Sopenharmony_ci enum hwtstamp_tx_types tx_type = config->tx_type; 146962306a36Sopenharmony_ci u16 ing_types = 0x00; 147062306a36Sopenharmony_ci u16 egr_types = 0x00; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci *p_rx_filter = rx_filter; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci switch (rx_filter) { 147562306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 147662306a36Sopenharmony_ci ing_types = 0x00; 147762306a36Sopenharmony_ci break; 147862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 147962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 148062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 148162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 148262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 148362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 148462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 148562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 148662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 148762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 148862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 148962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 149062306a36Sopenharmony_ci /* In Spectrum-2 and above, all packets get time stamp by 149162306a36Sopenharmony_ci * default and the driver fill the time stamp only for event 149262306a36Sopenharmony_ci * packets. Return all event types even if only specific types 149362306a36Sopenharmony_ci * were required. 149462306a36Sopenharmony_ci */ 149562306a36Sopenharmony_ci ing_types = 0x0f; 149662306a36Sopenharmony_ci *p_rx_filter = HWTSTAMP_FILTER_SOME; 149762306a36Sopenharmony_ci break; 149862306a36Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 149962306a36Sopenharmony_ci case HWTSTAMP_FILTER_SOME: 150062306a36Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 150162306a36Sopenharmony_ci return -ERANGE; 150262306a36Sopenharmony_ci default: 150362306a36Sopenharmony_ci return -EINVAL; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci switch (tx_type) { 150762306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 150862306a36Sopenharmony_ci egr_types = 0x00; 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 151162306a36Sopenharmony_ci egr_types = 0x0f; 151262306a36Sopenharmony_ci break; 151362306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 151462306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 151562306a36Sopenharmony_ci return -ERANGE; 151662306a36Sopenharmony_ci default: 151762306a36Sopenharmony_ci return -EINVAL; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci if ((ing_types && !egr_types) || (!ing_types && egr_types)) 152162306a36Sopenharmony_ci return -EINVAL; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci *p_ing_types = ing_types; 152462306a36Sopenharmony_ci *p_egr_types = egr_types; 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_mtpcpc_set(struct mlxsw_sp *mlxsw_sp, bool ptp_trap_en, 152962306a36Sopenharmony_ci u16 ing_types, u16 egr_types) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci char mtpcpc_pl[MLXSW_REG_MTPCPC_LEN]; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci mlxsw_reg_mtpcpc_pack(mtpcpc_pl, false, 0, ptp_trap_en, ing_types, 153462306a36Sopenharmony_ci egr_types); 153562306a36Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpcpc), mtpcpc_pl); 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_enable(struct mlxsw_sp *mlxsw_sp, u16 ing_types, 153962306a36Sopenharmony_ci u16 egr_types, 154062306a36Sopenharmony_ci struct hwtstamp_config new_config) 154162306a36Sopenharmony_ci{ 154262306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp); 154362306a36Sopenharmony_ci int err; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci err = mlxsw_sp2_ptp_mtpcpc_set(mlxsw_sp, true, ing_types, egr_types); 154662306a36Sopenharmony_ci if (err) 154762306a36Sopenharmony_ci return err; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci ptp_state->config = new_config; 155062306a36Sopenharmony_ci return 0; 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_disable(struct mlxsw_sp *mlxsw_sp, 155462306a36Sopenharmony_ci struct hwtstamp_config new_config) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp); 155762306a36Sopenharmony_ci int err; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci err = mlxsw_sp2_ptp_mtpcpc_set(mlxsw_sp, false, 0, 0); 156062306a36Sopenharmony_ci if (err) 156162306a36Sopenharmony_ci return err; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci ptp_state->config = new_config; 156462306a36Sopenharmony_ci return 0; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_configure_port(struct mlxsw_sp_port *mlxsw_sp_port, 156862306a36Sopenharmony_ci u16 ing_types, u16 egr_types, 156962306a36Sopenharmony_ci struct hwtstamp_config new_config) 157062306a36Sopenharmony_ci{ 157162306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state; 157262306a36Sopenharmony_ci int err; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (refcount_inc_not_zero(&ptp_state->ptp_port_enabled_ref)) 157762306a36Sopenharmony_ci return 0; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci err = mlxsw_sp2_ptp_enable(mlxsw_sp_port->mlxsw_sp, ing_types, 158062306a36Sopenharmony_ci egr_types, new_config); 158162306a36Sopenharmony_ci if (err) 158262306a36Sopenharmony_ci return err; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci refcount_set(&ptp_state->ptp_port_enabled_ref, 1); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci return 0; 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic int mlxsw_sp2_ptp_deconfigure_port(struct mlxsw_sp_port *mlxsw_sp_port, 159062306a36Sopenharmony_ci struct hwtstamp_config new_config) 159162306a36Sopenharmony_ci{ 159262306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state; 159362306a36Sopenharmony_ci int err; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (!refcount_dec_and_test(&ptp_state->ptp_port_enabled_ref)) 159862306a36Sopenharmony_ci return 0; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci err = mlxsw_sp2_ptp_disable(mlxsw_sp_port->mlxsw_sp, new_config); 160162306a36Sopenharmony_ci if (err) 160262306a36Sopenharmony_ci goto err_ptp_disable; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci return 0; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cierr_ptp_disable: 160762306a36Sopenharmony_ci refcount_set(&ptp_state->ptp_port_enabled_ref, 1); 160862306a36Sopenharmony_ci return err; 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ciint mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, 161262306a36Sopenharmony_ci struct hwtstamp_config *config) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci struct mlxsw_sp2_ptp_state *ptp_state; 161562306a36Sopenharmony_ci enum hwtstamp_rx_filters rx_filter; 161662306a36Sopenharmony_ci struct hwtstamp_config new_config; 161762306a36Sopenharmony_ci u16 new_ing_types, new_egr_types; 161862306a36Sopenharmony_ci bool ptp_enabled; 161962306a36Sopenharmony_ci int err; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp); 162262306a36Sopenharmony_ci mutex_lock(&ptp_state->lock); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci err = mlxsw_sp2_ptp_get_message_types(config, &new_ing_types, 162562306a36Sopenharmony_ci &new_egr_types, &rx_filter); 162662306a36Sopenharmony_ci if (err) 162762306a36Sopenharmony_ci goto err_get_message_types; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci new_config.flags = config->flags; 163062306a36Sopenharmony_ci new_config.tx_type = config->tx_type; 163162306a36Sopenharmony_ci new_config.rx_filter = rx_filter; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci ptp_enabled = mlxsw_sp_port->ptp.ing_types || 163462306a36Sopenharmony_ci mlxsw_sp_port->ptp.egr_types; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci if ((new_ing_types || new_egr_types) && !ptp_enabled) { 163762306a36Sopenharmony_ci err = mlxsw_sp2_ptp_configure_port(mlxsw_sp_port, new_ing_types, 163862306a36Sopenharmony_ci new_egr_types, new_config); 163962306a36Sopenharmony_ci if (err) 164062306a36Sopenharmony_ci goto err_configure_port; 164162306a36Sopenharmony_ci } else if (!new_ing_types && !new_egr_types && ptp_enabled) { 164262306a36Sopenharmony_ci err = mlxsw_sp2_ptp_deconfigure_port(mlxsw_sp_port, new_config); 164362306a36Sopenharmony_ci if (err) 164462306a36Sopenharmony_ci goto err_deconfigure_port; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci mlxsw_sp_port->ptp.ing_types = new_ing_types; 164862306a36Sopenharmony_ci mlxsw_sp_port->ptp.egr_types = new_egr_types; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci /* Notify the ioctl caller what we are actually timestamping. */ 165162306a36Sopenharmony_ci config->rx_filter = rx_filter; 165262306a36Sopenharmony_ci mutex_unlock(&ptp_state->lock); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci return 0; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cierr_deconfigure_port: 165762306a36Sopenharmony_cierr_configure_port: 165862306a36Sopenharmony_cierr_get_message_types: 165962306a36Sopenharmony_ci mutex_unlock(&ptp_state->lock); 166062306a36Sopenharmony_ci return err; 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ciint mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, 166462306a36Sopenharmony_ci struct ethtool_ts_info *info) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci info->phc_index = ptp_clock_index(mlxsw_sp->clock->ptp); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 166962306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 167062306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci info->tx_types = BIT(HWTSTAMP_TX_OFF) | 167362306a36Sopenharmony_ci BIT(HWTSTAMP_TX_ON); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 167662306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | 167762306a36Sopenharmony_ci BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ciint mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core, 168362306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 168462306a36Sopenharmony_ci struct sk_buff *skb, 168562306a36Sopenharmony_ci const struct mlxsw_tx_info *tx_info) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci mlxsw_sp_txhdr_construct(skb, tx_info); 168862306a36Sopenharmony_ci return 0; 168962306a36Sopenharmony_ci} 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ciint mlxsw_sp2_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core, 169262306a36Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port, 169362306a36Sopenharmony_ci struct sk_buff *skb, 169462306a36Sopenharmony_ci const struct mlxsw_tx_info *tx_info) 169562306a36Sopenharmony_ci{ 169662306a36Sopenharmony_ci /* In Spectrum-2 and Spectrum-3, in order for PTP event packets to have 169762306a36Sopenharmony_ci * their correction field correctly set on the egress port they must be 169862306a36Sopenharmony_ci * transmitted as data packets. Such packets ingress the ASIC via the 169962306a36Sopenharmony_ci * CPU port and must have a VLAN tag, as the CPU port is not configured 170062306a36Sopenharmony_ci * with a PVID. Push the default VLAN (4095), which is configured as 170162306a36Sopenharmony_ci * egress untagged on all the ports. 170262306a36Sopenharmony_ci */ 170362306a36Sopenharmony_ci if (!skb_vlan_tagged(skb)) { 170462306a36Sopenharmony_ci skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q), 170562306a36Sopenharmony_ci MLXSW_SP_DEFAULT_VID); 170662306a36Sopenharmony_ci if (!skb) { 170762306a36Sopenharmony_ci this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); 170862306a36Sopenharmony_ci return -ENOMEM; 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci return mlxsw_sp_txhdr_ptp_data_construct(mlxsw_core, mlxsw_sp_port, skb, 171362306a36Sopenharmony_ci tx_info); 171462306a36Sopenharmony_ci} 1715