18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019 Mellanox Technologies. All rights reserved */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 68c2ecf20Sopenharmony_ci#include <linux/timecounter.h> 78c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 108c2ecf20Sopenharmony_ci#include <linux/ptp_classify.h> 118c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 128c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 138c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "spectrum.h" 168c2ecf20Sopenharmony_ci#include "spectrum_ptp.h" 178c2ecf20Sopenharmony_ci#include "core.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT 29 208c2ecf20Sopenharmony_ci#define MLXSW_SP1_PTP_CLOCK_FREQ_KHZ 156257 /* 6.4nSec */ 218c2ecf20Sopenharmony_ci#define MLXSW_SP1_PTP_CLOCK_MASK 64 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MLXSW_SP1_PTP_HT_GC_INTERVAL 500 /* ms */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* How long, approximately, should the unmatched entries stay in the hash table 268c2ecf20Sopenharmony_ci * before they are collected. Should be evenly divisible by the GC interval. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define MLXSW_SP1_PTP_HT_GC_TIMEOUT 1000 /* ms */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct mlxsw_sp_ptp_state { 318c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp; 328c2ecf20Sopenharmony_ci struct rhltable unmatched_ht; 338c2ecf20Sopenharmony_ci spinlock_t unmatched_lock; /* protects the HT */ 348c2ecf20Sopenharmony_ci struct delayed_work ht_gc_dw; 358c2ecf20Sopenharmony_ci u32 gc_cycle; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct mlxsw_sp1_ptp_key { 398c2ecf20Sopenharmony_ci u8 local_port; 408c2ecf20Sopenharmony_ci u8 message_type; 418c2ecf20Sopenharmony_ci u16 sequence_id; 428c2ecf20Sopenharmony_ci u8 domain_number; 438c2ecf20Sopenharmony_ci bool ingress; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct mlxsw_sp1_ptp_unmatched { 478c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key; 488c2ecf20Sopenharmony_ci struct rhlist_head ht_node; 498c2ecf20Sopenharmony_ci struct rcu_head rcu; 508c2ecf20Sopenharmony_ci struct sk_buff *skb; 518c2ecf20Sopenharmony_ci u64 timestamp; 528c2ecf20Sopenharmony_ci u32 gc_cycle; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp1_ptp_unmatched_ht_params = { 568c2ecf20Sopenharmony_ci .key_len = sizeof_field(struct mlxsw_sp1_ptp_unmatched, key), 578c2ecf20Sopenharmony_ci .key_offset = offsetof(struct mlxsw_sp1_ptp_unmatched, key), 588c2ecf20Sopenharmony_ci .head_offset = offsetof(struct mlxsw_sp1_ptp_unmatched, ht_node), 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct mlxsw_sp_ptp_clock { 628c2ecf20Sopenharmony_ci struct mlxsw_core *core; 638c2ecf20Sopenharmony_ci spinlock_t lock; /* protect this structure */ 648c2ecf20Sopenharmony_ci struct cyclecounter cycles; 658c2ecf20Sopenharmony_ci struct timecounter tc; 668c2ecf20Sopenharmony_ci u32 nominal_c_mult; 678c2ecf20Sopenharmony_ci struct ptp_clock *ptp; 688c2ecf20Sopenharmony_ci struct ptp_clock_info ptp_info; 698c2ecf20Sopenharmony_ci unsigned long overflow_period; 708c2ecf20Sopenharmony_ci struct delayed_work overflow_work; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic u64 __mlxsw_sp1_ptp_read_frc(struct mlxsw_sp_ptp_clock *clock, 748c2ecf20Sopenharmony_ci struct ptp_system_timestamp *sts) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 778c2ecf20Sopenharmony_ci u32 frc_h1, frc_h2, frc_l; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci frc_h1 = mlxsw_core_read_frc_h(mlxsw_core); 808c2ecf20Sopenharmony_ci ptp_read_system_prets(sts); 818c2ecf20Sopenharmony_ci frc_l = mlxsw_core_read_frc_l(mlxsw_core); 828c2ecf20Sopenharmony_ci ptp_read_system_postts(sts); 838c2ecf20Sopenharmony_ci frc_h2 = mlxsw_core_read_frc_h(mlxsw_core); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (frc_h1 != frc_h2) { 868c2ecf20Sopenharmony_ci /* wrap around */ 878c2ecf20Sopenharmony_ci ptp_read_system_prets(sts); 888c2ecf20Sopenharmony_ci frc_l = mlxsw_core_read_frc_l(mlxsw_core); 898c2ecf20Sopenharmony_ci ptp_read_system_postts(sts); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return (u64) frc_l | (u64) frc_h2 << 32; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic u64 mlxsw_sp1_ptp_read_frc(const struct cyclecounter *cc) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 988c2ecf20Sopenharmony_ci container_of(cc, struct mlxsw_sp_ptp_clock, cycles); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return __mlxsw_sp1_ptp_read_frc(clock, NULL) & cc->mask; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int 1048c2ecf20Sopenharmony_cimlxsw_sp1_ptp_phc_adjfreq(struct mlxsw_sp_ptp_clock *clock, int freq_adj) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 1078c2ecf20Sopenharmony_ci char mtutc_pl[MLXSW_REG_MTUTC_LEN]; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci mlxsw_reg_mtutc_pack(mtutc_pl, MLXSW_REG_MTUTC_OPERATION_ADJUST_FREQ, 1108c2ecf20Sopenharmony_ci freq_adj, 0); 1118c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic u64 mlxsw_sp1_ptp_ns2cycles(const struct timecounter *tc, u64 nsec) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci u64 cycles = (u64) nsec; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci cycles <<= tc->cc->shift; 1198c2ecf20Sopenharmony_ci cycles = div_u64(cycles, tc->cc->mult); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return cycles; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int 1258c2ecf20Sopenharmony_cimlxsw_sp1_ptp_phc_settime(struct mlxsw_sp_ptp_clock *clock, u64 nsec) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct mlxsw_core *mlxsw_core = clock->core; 1288c2ecf20Sopenharmony_ci u64 next_sec, next_sec_in_nsec, cycles; 1298c2ecf20Sopenharmony_ci char mtutc_pl[MLXSW_REG_MTUTC_LEN]; 1308c2ecf20Sopenharmony_ci char mtpps_pl[MLXSW_REG_MTPPS_LEN]; 1318c2ecf20Sopenharmony_ci int err; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci next_sec = div_u64(nsec, NSEC_PER_SEC) + 1; 1348c2ecf20Sopenharmony_ci next_sec_in_nsec = next_sec * NSEC_PER_SEC; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_lock_bh(&clock->lock); 1378c2ecf20Sopenharmony_ci cycles = mlxsw_sp1_ptp_ns2cycles(&clock->tc, next_sec_in_nsec); 1388c2ecf20Sopenharmony_ci spin_unlock_bh(&clock->lock); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci mlxsw_reg_mtpps_vpin_pack(mtpps_pl, cycles); 1418c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtpps), mtpps_pl); 1428c2ecf20Sopenharmony_ci if (err) 1438c2ecf20Sopenharmony_ci return err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci mlxsw_reg_mtutc_pack(mtutc_pl, 1468c2ecf20Sopenharmony_ci MLXSW_REG_MTUTC_OPERATION_SET_TIME_AT_NEXT_SEC, 1478c2ecf20Sopenharmony_ci 0, next_sec); 1488c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 1548c2ecf20Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 1558c2ecf20Sopenharmony_ci int neg_adj = 0; 1568c2ecf20Sopenharmony_ci u32 diff; 1578c2ecf20Sopenharmony_ci u64 adj; 1588c2ecf20Sopenharmony_ci s32 ppb; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ppb = scaled_ppm_to_ppb(scaled_ppm); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (ppb < 0) { 1638c2ecf20Sopenharmony_ci neg_adj = 1; 1648c2ecf20Sopenharmony_ci ppb = -ppb; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci adj = clock->nominal_c_mult; 1688c2ecf20Sopenharmony_ci adj *= ppb; 1698c2ecf20Sopenharmony_ci diff = div_u64(adj, NSEC_PER_SEC); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci spin_lock_bh(&clock->lock); 1728c2ecf20Sopenharmony_ci timecounter_read(&clock->tc); 1738c2ecf20Sopenharmony_ci clock->cycles.mult = neg_adj ? clock->nominal_c_mult - diff : 1748c2ecf20Sopenharmony_ci clock->nominal_c_mult + diff; 1758c2ecf20Sopenharmony_ci spin_unlock_bh(&clock->lock); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return mlxsw_sp1_ptp_phc_adjfreq(clock, neg_adj ? -ppb : ppb); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 1838c2ecf20Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 1848c2ecf20Sopenharmony_ci u64 nsec; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci spin_lock_bh(&clock->lock); 1878c2ecf20Sopenharmony_ci timecounter_adjtime(&clock->tc, delta); 1888c2ecf20Sopenharmony_ci nsec = timecounter_read(&clock->tc); 1898c2ecf20Sopenharmony_ci spin_unlock_bh(&clock->lock); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return mlxsw_sp1_ptp_phc_settime(clock, nsec); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_gettimex(struct ptp_clock_info *ptp, 1958c2ecf20Sopenharmony_ci struct timespec64 *ts, 1968c2ecf20Sopenharmony_ci struct ptp_system_timestamp *sts) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 1998c2ecf20Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 2008c2ecf20Sopenharmony_ci u64 cycles, nsec; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock_bh(&clock->lock); 2038c2ecf20Sopenharmony_ci cycles = __mlxsw_sp1_ptp_read_frc(clock, sts); 2048c2ecf20Sopenharmony_ci nsec = timecounter_cyc2time(&clock->tc, cycles); 2058c2ecf20Sopenharmony_ci spin_unlock_bh(&clock->lock); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci *ts = ns_to_timespec64(nsec); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_settime(struct ptp_clock_info *ptp, 2138c2ecf20Sopenharmony_ci const struct timespec64 *ts) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock = 2168c2ecf20Sopenharmony_ci container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info); 2178c2ecf20Sopenharmony_ci u64 nsec = timespec64_to_ns(ts); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci spin_lock_bh(&clock->lock); 2208c2ecf20Sopenharmony_ci timecounter_init(&clock->tc, &clock->cycles, nsec); 2218c2ecf20Sopenharmony_ci nsec = timecounter_read(&clock->tc); 2228c2ecf20Sopenharmony_ci spin_unlock_bh(&clock->lock); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return mlxsw_sp1_ptp_phc_settime(clock, nsec); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct ptp_clock_info mlxsw_sp1_ptp_clock_info = { 2288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2298c2ecf20Sopenharmony_ci .name = "mlxsw_sp_clock", 2308c2ecf20Sopenharmony_ci .max_adj = 100000000, 2318c2ecf20Sopenharmony_ci .adjfine = mlxsw_sp1_ptp_adjfine, 2328c2ecf20Sopenharmony_ci .adjtime = mlxsw_sp1_ptp_adjtime, 2338c2ecf20Sopenharmony_ci .gettimex64 = mlxsw_sp1_ptp_gettimex, 2348c2ecf20Sopenharmony_ci .settime64 = mlxsw_sp1_ptp_settime, 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void mlxsw_sp1_ptp_clock_overflow(struct work_struct *work) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 2408c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci clock = container_of(dwork, struct mlxsw_sp_ptp_clock, overflow_work); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci spin_lock_bh(&clock->lock); 2458c2ecf20Sopenharmony_ci timecounter_read(&clock->tc); 2468c2ecf20Sopenharmony_ci spin_unlock_bh(&clock->lock); 2478c2ecf20Sopenharmony_ci mlxsw_core_schedule_dw(&clock->overflow_work, clock->overflow_period); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistruct mlxsw_sp_ptp_clock * 2518c2ecf20Sopenharmony_cimlxsw_sp1_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u64 overflow_cycles, nsec, frac = 0; 2548c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_clock *clock; 2558c2ecf20Sopenharmony_ci int err; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci clock = kzalloc(sizeof(*clock), GFP_KERNEL); 2588c2ecf20Sopenharmony_ci if (!clock) 2598c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci spin_lock_init(&clock->lock); 2628c2ecf20Sopenharmony_ci clock->cycles.read = mlxsw_sp1_ptp_read_frc; 2638c2ecf20Sopenharmony_ci clock->cycles.shift = MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT; 2648c2ecf20Sopenharmony_ci clock->cycles.mult = clocksource_khz2mult(MLXSW_SP1_PTP_CLOCK_FREQ_KHZ, 2658c2ecf20Sopenharmony_ci clock->cycles.shift); 2668c2ecf20Sopenharmony_ci clock->nominal_c_mult = clock->cycles.mult; 2678c2ecf20Sopenharmony_ci clock->cycles.mask = CLOCKSOURCE_MASK(MLXSW_SP1_PTP_CLOCK_MASK); 2688c2ecf20Sopenharmony_ci clock->core = mlxsw_sp->core; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci timecounter_init(&clock->tc, &clock->cycles, 2718c2ecf20Sopenharmony_ci ktime_to_ns(ktime_get_real())); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Calculate period in seconds to call the overflow watchdog - to make 2748c2ecf20Sopenharmony_ci * sure counter is checked at least twice every wrap around. 2758c2ecf20Sopenharmony_ci * The period is calculated as the minimum between max HW cycles count 2768c2ecf20Sopenharmony_ci * (The clock source mask) and max amount of cycles that can be 2778c2ecf20Sopenharmony_ci * multiplied by clock multiplier where the result doesn't exceed 2788c2ecf20Sopenharmony_ci * 64bits. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci overflow_cycles = div64_u64(~0ULL >> 1, clock->cycles.mult); 2818c2ecf20Sopenharmony_ci overflow_cycles = min(overflow_cycles, div_u64(clock->cycles.mask, 3)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci nsec = cyclecounter_cyc2ns(&clock->cycles, overflow_cycles, 0, &frac); 2848c2ecf20Sopenharmony_ci clock->overflow_period = nsecs_to_jiffies(nsec); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&clock->overflow_work, mlxsw_sp1_ptp_clock_overflow); 2878c2ecf20Sopenharmony_ci mlxsw_core_schedule_dw(&clock->overflow_work, 0); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci clock->ptp_info = mlxsw_sp1_ptp_clock_info; 2908c2ecf20Sopenharmony_ci clock->ptp = ptp_clock_register(&clock->ptp_info, dev); 2918c2ecf20Sopenharmony_ci if (IS_ERR(clock->ptp)) { 2928c2ecf20Sopenharmony_ci err = PTR_ERR(clock->ptp); 2938c2ecf20Sopenharmony_ci dev_err(dev, "ptp_clock_register failed %d\n", err); 2948c2ecf20Sopenharmony_ci goto err_ptp_clock_register; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return clock; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cierr_ptp_clock_register: 3008c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&clock->overflow_work); 3018c2ecf20Sopenharmony_ci kfree(clock); 3028c2ecf20Sopenharmony_ci return ERR_PTR(err); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_civoid mlxsw_sp1_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci ptp_clock_unregister(clock->ptp); 3088c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&clock->overflow_work); 3098c2ecf20Sopenharmony_ci kfree(clock); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int mlxsw_sp_ptp_parse(struct sk_buff *skb, 3138c2ecf20Sopenharmony_ci u8 *p_domain_number, 3148c2ecf20Sopenharmony_ci u8 *p_message_type, 3158c2ecf20Sopenharmony_ci u16 *p_sequence_id) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci unsigned int ptp_class; 3188c2ecf20Sopenharmony_ci struct ptp_header *hdr; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ptp_class = ptp_classify_raw(skb); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci switch (ptp_class & PTP_CLASS_VMASK) { 3238c2ecf20Sopenharmony_ci case PTP_CLASS_V1: 3248c2ecf20Sopenharmony_ci case PTP_CLASS_V2: 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci default: 3278c2ecf20Sopenharmony_ci return -ERANGE; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci hdr = ptp_parse_header(skb, ptp_class); 3318c2ecf20Sopenharmony_ci if (!hdr) 3328c2ecf20Sopenharmony_ci return -EINVAL; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci *p_message_type = ptp_get_msgtype(hdr, ptp_class); 3358c2ecf20Sopenharmony_ci *p_domain_number = hdr->domain_number; 3368c2ecf20Sopenharmony_ci *p_sequence_id = be16_to_cpu(hdr->sequence_id); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* Returns NULL on successful insertion, a pointer on conflict, or an ERR_PTR on 3428c2ecf20Sopenharmony_ci * error. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_cistatic int 3458c2ecf20Sopenharmony_cimlxsw_sp1_ptp_unmatched_save(struct mlxsw_sp *mlxsw_sp, 3468c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key, 3478c2ecf20Sopenharmony_ci struct sk_buff *skb, 3488c2ecf20Sopenharmony_ci u64 timestamp) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci int cycles = MLXSW_SP1_PTP_HT_GC_TIMEOUT / MLXSW_SP1_PTP_HT_GC_INTERVAL; 3518c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_state *ptp_state = mlxsw_sp->ptp_state; 3528c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched; 3538c2ecf20Sopenharmony_ci int err; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci unmatched = kzalloc(sizeof(*unmatched), GFP_ATOMIC); 3568c2ecf20Sopenharmony_ci if (!unmatched) 3578c2ecf20Sopenharmony_ci return -ENOMEM; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci unmatched->key = key; 3608c2ecf20Sopenharmony_ci unmatched->skb = skb; 3618c2ecf20Sopenharmony_ci unmatched->timestamp = timestamp; 3628c2ecf20Sopenharmony_ci unmatched->gc_cycle = mlxsw_sp->ptp_state->gc_cycle + cycles; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci err = rhltable_insert(&ptp_state->unmatched_ht, &unmatched->ht_node, 3658c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 3668c2ecf20Sopenharmony_ci if (err) 3678c2ecf20Sopenharmony_ci kfree(unmatched); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return err; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct mlxsw_sp1_ptp_unmatched * 3738c2ecf20Sopenharmony_cimlxsw_sp1_ptp_unmatched_lookup(struct mlxsw_sp *mlxsw_sp, 3748c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key, int *p_length) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched, *last = NULL; 3778c2ecf20Sopenharmony_ci struct rhlist_head *tmp, *list; 3788c2ecf20Sopenharmony_ci int length = 0; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci list = rhltable_lookup(&mlxsw_sp->ptp_state->unmatched_ht, &key, 3818c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 3828c2ecf20Sopenharmony_ci rhl_for_each_entry_rcu(unmatched, tmp, list, ht_node) { 3838c2ecf20Sopenharmony_ci last = unmatched; 3848c2ecf20Sopenharmony_ci length++; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci *p_length = length; 3888c2ecf20Sopenharmony_ci return last; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int 3928c2ecf20Sopenharmony_cimlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp, 3938c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci return rhltable_remove(&mlxsw_sp->ptp_state->unmatched_ht, 3968c2ecf20Sopenharmony_ci &unmatched->ht_node, 3978c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci/* This function is called in the following scenarios: 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * 1) When a packet is matched with its timestamp. 4038c2ecf20Sopenharmony_ci * 2) In several situation when it is necessary to immediately pass on 4048c2ecf20Sopenharmony_ci * an SKB without a timestamp. 4058c2ecf20Sopenharmony_ci * 3) From GC indirectly through mlxsw_sp1_ptp_unmatched_finish(). 4068c2ecf20Sopenharmony_ci * This case is similar to 2) above. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_cistatic void mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp *mlxsw_sp, 4098c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 local_port, 4108c2ecf20Sopenharmony_ci bool ingress, 4118c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *hwtstamps) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Between capturing the packet and finishing it, there is a window of 4168c2ecf20Sopenharmony_ci * opportunity for the originating port to go away (e.g. due to a 4178c2ecf20Sopenharmony_ci * split). Also make sure the SKB device reference is still valid. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 4208c2ecf20Sopenharmony_ci if (!(mlxsw_sp_port && (!skb->dev || skb->dev == mlxsw_sp_port->dev))) { 4218c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4228c2ecf20Sopenharmony_ci return; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (ingress) { 4268c2ecf20Sopenharmony_ci if (hwtstamps) 4278c2ecf20Sopenharmony_ci *skb_hwtstamps(skb) = *hwtstamps; 4288c2ecf20Sopenharmony_ci mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp); 4298c2ecf20Sopenharmony_ci } else { 4308c2ecf20Sopenharmony_ci /* skb_tstamp_tx() allows hwtstamps to be NULL. */ 4318c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, hwtstamps); 4328c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void mlxsw_sp1_packet_timestamp(struct mlxsw_sp *mlxsw_sp, 4378c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key, 4388c2ecf20Sopenharmony_ci struct sk_buff *skb, 4398c2ecf20Sopenharmony_ci u64 timestamp) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps hwtstamps; 4428c2ecf20Sopenharmony_ci u64 nsec; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci spin_lock_bh(&mlxsw_sp->clock->lock); 4458c2ecf20Sopenharmony_ci nsec = timecounter_cyc2time(&mlxsw_sp->clock->tc, timestamp); 4468c2ecf20Sopenharmony_ci spin_unlock_bh(&mlxsw_sp->clock->lock); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci hwtstamps.hwtstamp = ns_to_ktime(nsec); 4498c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, 4508c2ecf20Sopenharmony_ci key.local_port, key.ingress, &hwtstamps); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void 4548c2ecf20Sopenharmony_cimlxsw_sp1_ptp_unmatched_finish(struct mlxsw_sp *mlxsw_sp, 4558c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci if (unmatched->skb && unmatched->timestamp) 4588c2ecf20Sopenharmony_ci mlxsw_sp1_packet_timestamp(mlxsw_sp, unmatched->key, 4598c2ecf20Sopenharmony_ci unmatched->skb, 4608c2ecf20Sopenharmony_ci unmatched->timestamp); 4618c2ecf20Sopenharmony_ci else if (unmatched->skb) 4628c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, unmatched->skb, 4638c2ecf20Sopenharmony_ci unmatched->key.local_port, 4648c2ecf20Sopenharmony_ci unmatched->key.ingress, NULL); 4658c2ecf20Sopenharmony_ci kfree_rcu(unmatched, rcu); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void mlxsw_sp1_ptp_unmatched_free_fn(void *ptr, void *arg) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched = ptr; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* This is invoked at a point where the ports are gone already. Nothing 4738c2ecf20Sopenharmony_ci * to do with whatever is left in the HT but to free it. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci if (unmatched->skb) 4768c2ecf20Sopenharmony_ci dev_kfree_skb_any(unmatched->skb); 4778c2ecf20Sopenharmony_ci kfree_rcu(unmatched, rcu); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void mlxsw_sp1_ptp_got_piece(struct mlxsw_sp *mlxsw_sp, 4818c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key, 4828c2ecf20Sopenharmony_ci struct sk_buff *skb, u64 timestamp) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched; 4858c2ecf20Sopenharmony_ci int length; 4868c2ecf20Sopenharmony_ci int err; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci rcu_read_lock(); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci spin_lock(&mlxsw_sp->ptp_state->unmatched_lock); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci unmatched = mlxsw_sp1_ptp_unmatched_lookup(mlxsw_sp, key, &length); 4938c2ecf20Sopenharmony_ci if (skb && unmatched && unmatched->timestamp) { 4948c2ecf20Sopenharmony_ci unmatched->skb = skb; 4958c2ecf20Sopenharmony_ci } else if (timestamp && unmatched && unmatched->skb) { 4968c2ecf20Sopenharmony_ci unmatched->timestamp = timestamp; 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci /* Either there is no entry to match, or one that is there is 4998c2ecf20Sopenharmony_ci * incompatible. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci if (length < 100) 5028c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_unmatched_save(mlxsw_sp, key, 5038c2ecf20Sopenharmony_ci skb, timestamp); 5048c2ecf20Sopenharmony_ci else 5058c2ecf20Sopenharmony_ci err = -E2BIG; 5068c2ecf20Sopenharmony_ci if (err && skb) 5078c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, 5088c2ecf20Sopenharmony_ci key.local_port, 5098c2ecf20Sopenharmony_ci key.ingress, NULL); 5108c2ecf20Sopenharmony_ci unmatched = NULL; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (unmatched) { 5148c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_unmatched_remove(mlxsw_sp, unmatched); 5158c2ecf20Sopenharmony_ci WARN_ON_ONCE(err); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci spin_unlock(&mlxsw_sp->ptp_state->unmatched_lock); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (unmatched) 5218c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_unmatched_finish(mlxsw_sp, unmatched); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci rcu_read_unlock(); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void mlxsw_sp1_ptp_got_packet(struct mlxsw_sp *mlxsw_sp, 5278c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 local_port, 5288c2ecf20Sopenharmony_ci bool ingress) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 5318c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key; 5328c2ecf20Sopenharmony_ci u8 types; 5338c2ecf20Sopenharmony_ci int err; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 5368c2ecf20Sopenharmony_ci if (!mlxsw_sp_port) 5378c2ecf20Sopenharmony_ci goto immediate; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci types = ingress ? mlxsw_sp_port->ptp.ing_types : 5408c2ecf20Sopenharmony_ci mlxsw_sp_port->ptp.egr_types; 5418c2ecf20Sopenharmony_ci if (!types) 5428c2ecf20Sopenharmony_ci goto immediate; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci memset(&key, 0, sizeof(key)); 5458c2ecf20Sopenharmony_ci key.local_port = local_port; 5468c2ecf20Sopenharmony_ci key.ingress = ingress; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci err = mlxsw_sp_ptp_parse(skb, &key.domain_number, &key.message_type, 5498c2ecf20Sopenharmony_ci &key.sequence_id); 5508c2ecf20Sopenharmony_ci if (err) 5518c2ecf20Sopenharmony_ci goto immediate; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* For packets whose timestamping was not enabled on this port, don't 5548c2ecf20Sopenharmony_ci * bother trying to match the timestamp. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci if (!((1 << key.message_type) & types)) 5578c2ecf20Sopenharmony_ci goto immediate; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_got_piece(mlxsw_sp, key, skb, 0); 5608c2ecf20Sopenharmony_ci return; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ciimmediate: 5638c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, local_port, ingress, NULL); 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_civoid mlxsw_sp1_ptp_got_timestamp(struct mlxsw_sp *mlxsw_sp, bool ingress, 5678c2ecf20Sopenharmony_ci u8 local_port, u8 message_type, 5688c2ecf20Sopenharmony_ci u8 domain_number, u16 sequence_id, 5698c2ecf20Sopenharmony_ci u64 timestamp) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); 5728c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 5738c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_key key; 5748c2ecf20Sopenharmony_ci u8 types; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(local_port >= max_ports)) 5778c2ecf20Sopenharmony_ci return; 5788c2ecf20Sopenharmony_ci mlxsw_sp_port = mlxsw_sp->ports[local_port]; 5798c2ecf20Sopenharmony_ci if (!mlxsw_sp_port) 5808c2ecf20Sopenharmony_ci return; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci types = ingress ? mlxsw_sp_port->ptp.ing_types : 5838c2ecf20Sopenharmony_ci mlxsw_sp_port->ptp.egr_types; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* For message types whose timestamping was not enabled on this port, 5868c2ecf20Sopenharmony_ci * don't bother with the timestamp. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (!((1 << message_type) & types)) 5898c2ecf20Sopenharmony_ci return; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci memset(&key, 0, sizeof(key)); 5928c2ecf20Sopenharmony_ci key.local_port = local_port; 5938c2ecf20Sopenharmony_ci key.domain_number = domain_number; 5948c2ecf20Sopenharmony_ci key.message_type = message_type; 5958c2ecf20Sopenharmony_ci key.sequence_id = sequence_id; 5968c2ecf20Sopenharmony_ci key.ingress = ingress; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_got_piece(mlxsw_sp, key, NULL, timestamp); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_civoid mlxsw_sp1_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb, 6028c2ecf20Sopenharmony_ci u8 local_port) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 6058c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_got_packet(mlxsw_sp, skb, local_port, true); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_civoid mlxsw_sp1_ptp_transmitted(struct mlxsw_sp *mlxsw_sp, 6098c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 local_port) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_got_packet(mlxsw_sp, skb, local_port, false); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic void 6158c2ecf20Sopenharmony_cimlxsw_sp1_ptp_ht_gc_collect(struct mlxsw_sp_ptp_state *ptp_state, 6168c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_port_dir_stats *stats; 6198c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 6208c2ecf20Sopenharmony_ci int err; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* If an unmatched entry has an SKB, it has to be handed over to the 6238c2ecf20Sopenharmony_ci * networking stack. This is usually done from a trap handler, which is 6248c2ecf20Sopenharmony_ci * invoked in a softirq context. Here we are going to do it in process 6258c2ecf20Sopenharmony_ci * context. If that were to be interrupted by a softirq, it could cause 6268c2ecf20Sopenharmony_ci * a deadlock when an attempt is made to take an already-taken lock 6278c2ecf20Sopenharmony_ci * somewhere along the sending path. Disable softirqs to prevent this. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_ci local_bh_disable(); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci spin_lock(&ptp_state->unmatched_lock); 6328c2ecf20Sopenharmony_ci err = rhltable_remove(&ptp_state->unmatched_ht, &unmatched->ht_node, 6338c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_unmatched_ht_params); 6348c2ecf20Sopenharmony_ci spin_unlock(&ptp_state->unmatched_lock); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (err) 6378c2ecf20Sopenharmony_ci /* The packet was matched with timestamp during the walk. */ 6388c2ecf20Sopenharmony_ci goto out; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci mlxsw_sp_port = ptp_state->mlxsw_sp->ports[unmatched->key.local_port]; 6418c2ecf20Sopenharmony_ci if (mlxsw_sp_port) { 6428c2ecf20Sopenharmony_ci stats = unmatched->key.ingress ? 6438c2ecf20Sopenharmony_ci &mlxsw_sp_port->ptp.stats.rx_gcd : 6448c2ecf20Sopenharmony_ci &mlxsw_sp_port->ptp.stats.tx_gcd; 6458c2ecf20Sopenharmony_ci if (unmatched->skb) 6468c2ecf20Sopenharmony_ci stats->packets++; 6478c2ecf20Sopenharmony_ci else 6488c2ecf20Sopenharmony_ci stats->timestamps++; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* mlxsw_sp1_ptp_unmatched_finish() invokes netif_receive_skb(). While 6528c2ecf20Sopenharmony_ci * the comment at that function states that it can only be called in 6538c2ecf20Sopenharmony_ci * soft IRQ context, this pattern of local_bh_disable() + 6548c2ecf20Sopenharmony_ci * netif_receive_skb(), in process context, is seen elsewhere in the 6558c2ecf20Sopenharmony_ci * kernel, notably in pktgen. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_unmatched_finish(ptp_state->mlxsw_sp, unmatched); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ciout: 6608c2ecf20Sopenharmony_ci local_bh_enable(); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic void mlxsw_sp1_ptp_ht_gc(struct work_struct *work) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 6668c2ecf20Sopenharmony_ci struct mlxsw_sp1_ptp_unmatched *unmatched; 6678c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_state *ptp_state; 6688c2ecf20Sopenharmony_ci struct rhashtable_iter iter; 6698c2ecf20Sopenharmony_ci u32 gc_cycle; 6708c2ecf20Sopenharmony_ci void *obj; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci ptp_state = container_of(dwork, struct mlxsw_sp_ptp_state, ht_gc_dw); 6738c2ecf20Sopenharmony_ci gc_cycle = ptp_state->gc_cycle++; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci rhltable_walk_enter(&ptp_state->unmatched_ht, &iter); 6768c2ecf20Sopenharmony_ci rhashtable_walk_start(&iter); 6778c2ecf20Sopenharmony_ci while ((obj = rhashtable_walk_next(&iter))) { 6788c2ecf20Sopenharmony_ci if (IS_ERR(obj)) 6798c2ecf20Sopenharmony_ci continue; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci unmatched = obj; 6828c2ecf20Sopenharmony_ci if (unmatched->gc_cycle <= gc_cycle) 6838c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_ht_gc_collect(ptp_state, unmatched); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci rhashtable_walk_stop(&iter); 6868c2ecf20Sopenharmony_ci rhashtable_walk_exit(&iter); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci mlxsw_core_schedule_dw(&ptp_state->ht_gc_dw, 6898c2ecf20Sopenharmony_ci MLXSW_SP1_PTP_HT_GC_INTERVAL); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int mlxsw_sp_ptp_mtptpt_set(struct mlxsw_sp *mlxsw_sp, 6938c2ecf20Sopenharmony_ci enum mlxsw_reg_mtptpt_trap_id trap_id, 6948c2ecf20Sopenharmony_ci u16 message_type) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci char mtptpt_pl[MLXSW_REG_MTPTPT_LEN]; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci mlxsw_reg_mtptptp_pack(mtptpt_pl, trap_id, message_type); 6998c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtptpt), mtptpt_pl); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_set_fifo_clr_on_trap(struct mlxsw_sp *mlxsw_sp, 7038c2ecf20Sopenharmony_ci bool clr) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci char mogcr_pl[MLXSW_REG_MOGCR_LEN] = {0}; 7068c2ecf20Sopenharmony_ci int err; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); 7098c2ecf20Sopenharmony_ci if (err) 7108c2ecf20Sopenharmony_ci return err; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci mlxsw_reg_mogcr_ptp_iftc_set(mogcr_pl, clr); 7138c2ecf20Sopenharmony_ci mlxsw_reg_mogcr_ptp_eftc_set(mogcr_pl, clr); 7148c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp *mlxsw_sp, 7188c2ecf20Sopenharmony_ci u16 ing_types, u16 egr_types) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci char mtpppc_pl[MLXSW_REG_MTPPPC_LEN]; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci mlxsw_reg_mtpppc_pack(mtpppc_pl, ing_types, egr_types); 7238c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistruct mlxsw_sp1_ptp_shaper_params { 7278c2ecf20Sopenharmony_ci u32 ethtool_speed; 7288c2ecf20Sopenharmony_ci enum mlxsw_reg_qpsc_port_speed port_speed; 7298c2ecf20Sopenharmony_ci u8 shaper_time_exp; 7308c2ecf20Sopenharmony_ci u8 shaper_time_mantissa; 7318c2ecf20Sopenharmony_ci u8 shaper_inc; 7328c2ecf20Sopenharmony_ci u8 shaper_bs; 7338c2ecf20Sopenharmony_ci u8 port_to_shaper_credits; 7348c2ecf20Sopenharmony_ci int ing_timestamp_inc; 7358c2ecf20Sopenharmony_ci int egr_timestamp_inc; 7368c2ecf20Sopenharmony_ci}; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic const struct mlxsw_sp1_ptp_shaper_params 7398c2ecf20Sopenharmony_cimlxsw_sp1_ptp_shaper_params[] = { 7408c2ecf20Sopenharmony_ci { 7418c2ecf20Sopenharmony_ci .ethtool_speed = SPEED_100, 7428c2ecf20Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_100M, 7438c2ecf20Sopenharmony_ci .shaper_time_exp = 4, 7448c2ecf20Sopenharmony_ci .shaper_time_mantissa = 12, 7458c2ecf20Sopenharmony_ci .shaper_inc = 9, 7468c2ecf20Sopenharmony_ci .shaper_bs = 1, 7478c2ecf20Sopenharmony_ci .port_to_shaper_credits = 1, 7488c2ecf20Sopenharmony_ci .ing_timestamp_inc = -313, 7498c2ecf20Sopenharmony_ci .egr_timestamp_inc = 313, 7508c2ecf20Sopenharmony_ci }, 7518c2ecf20Sopenharmony_ci { 7528c2ecf20Sopenharmony_ci .ethtool_speed = SPEED_1000, 7538c2ecf20Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_1G, 7548c2ecf20Sopenharmony_ci .shaper_time_exp = 0, 7558c2ecf20Sopenharmony_ci .shaper_time_mantissa = 12, 7568c2ecf20Sopenharmony_ci .shaper_inc = 6, 7578c2ecf20Sopenharmony_ci .shaper_bs = 0, 7588c2ecf20Sopenharmony_ci .port_to_shaper_credits = 1, 7598c2ecf20Sopenharmony_ci .ing_timestamp_inc = -35, 7608c2ecf20Sopenharmony_ci .egr_timestamp_inc = 35, 7618c2ecf20Sopenharmony_ci }, 7628c2ecf20Sopenharmony_ci { 7638c2ecf20Sopenharmony_ci .ethtool_speed = SPEED_10000, 7648c2ecf20Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_10G, 7658c2ecf20Sopenharmony_ci .shaper_time_exp = 0, 7668c2ecf20Sopenharmony_ci .shaper_time_mantissa = 2, 7678c2ecf20Sopenharmony_ci .shaper_inc = 14, 7688c2ecf20Sopenharmony_ci .shaper_bs = 1, 7698c2ecf20Sopenharmony_ci .port_to_shaper_credits = 1, 7708c2ecf20Sopenharmony_ci .ing_timestamp_inc = -11, 7718c2ecf20Sopenharmony_ci .egr_timestamp_inc = 11, 7728c2ecf20Sopenharmony_ci }, 7738c2ecf20Sopenharmony_ci { 7748c2ecf20Sopenharmony_ci .ethtool_speed = SPEED_25000, 7758c2ecf20Sopenharmony_ci .port_speed = MLXSW_REG_QPSC_PORT_SPEED_25G, 7768c2ecf20Sopenharmony_ci .shaper_time_exp = 0, 7778c2ecf20Sopenharmony_ci .shaper_time_mantissa = 0, 7788c2ecf20Sopenharmony_ci .shaper_inc = 11, 7798c2ecf20Sopenharmony_ci .shaper_bs = 1, 7808c2ecf20Sopenharmony_ci .port_to_shaper_credits = 1, 7818c2ecf20Sopenharmony_ci .ing_timestamp_inc = -14, 7828c2ecf20Sopenharmony_ci .egr_timestamp_inc = 14, 7838c2ecf20Sopenharmony_ci }, 7848c2ecf20Sopenharmony_ci}; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci#define MLXSW_SP1_PTP_SHAPER_PARAMS_LEN ARRAY_SIZE(mlxsw_sp1_ptp_shaper_params) 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp *mlxsw_sp) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci const struct mlxsw_sp1_ptp_shaper_params *params; 7918c2ecf20Sopenharmony_ci char qpsc_pl[MLXSW_REG_QPSC_LEN]; 7928c2ecf20Sopenharmony_ci int i, err; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) { 7958c2ecf20Sopenharmony_ci params = &mlxsw_sp1_ptp_shaper_params[i]; 7968c2ecf20Sopenharmony_ci mlxsw_reg_qpsc_pack(qpsc_pl, params->port_speed, 7978c2ecf20Sopenharmony_ci params->shaper_time_exp, 7988c2ecf20Sopenharmony_ci params->shaper_time_mantissa, 7998c2ecf20Sopenharmony_ci params->shaper_inc, params->shaper_bs, 8008c2ecf20Sopenharmony_ci params->port_to_shaper_credits, 8018c2ecf20Sopenharmony_ci params->ing_timestamp_inc, 8028c2ecf20Sopenharmony_ci params->egr_timestamp_inc); 8038c2ecf20Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpsc), qpsc_pl); 8048c2ecf20Sopenharmony_ci if (err) 8058c2ecf20Sopenharmony_ci return err; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistruct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct mlxsw_sp_ptp_state *ptp_state; 8148c2ecf20Sopenharmony_ci u16 message_type; 8158c2ecf20Sopenharmony_ci int err; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_shaper_params_set(mlxsw_sp); 8188c2ecf20Sopenharmony_ci if (err) 8198c2ecf20Sopenharmony_ci return ERR_PTR(err); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL); 8228c2ecf20Sopenharmony_ci if (!ptp_state) 8238c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8248c2ecf20Sopenharmony_ci ptp_state->mlxsw_sp = mlxsw_sp; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci spin_lock_init(&ptp_state->unmatched_lock); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci err = rhltable_init(&ptp_state->unmatched_ht, 8298c2ecf20Sopenharmony_ci &mlxsw_sp1_ptp_unmatched_ht_params); 8308c2ecf20Sopenharmony_ci if (err) 8318c2ecf20Sopenharmony_ci goto err_hashtable_init; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Delive these message types as PTP0. */ 8348c2ecf20Sopenharmony_ci message_type = BIT(MLXSW_SP_PTP_MESSAGE_TYPE_SYNC) | 8358c2ecf20Sopenharmony_ci BIT(MLXSW_SP_PTP_MESSAGE_TYPE_DELAY_REQ) | 8368c2ecf20Sopenharmony_ci BIT(MLXSW_SP_PTP_MESSAGE_TYPE_PDELAY_REQ) | 8378c2ecf20Sopenharmony_ci BIT(MLXSW_SP_PTP_MESSAGE_TYPE_PDELAY_RESP); 8388c2ecf20Sopenharmony_ci err = mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 8398c2ecf20Sopenharmony_ci message_type); 8408c2ecf20Sopenharmony_ci if (err) 8418c2ecf20Sopenharmony_ci goto err_mtptpt_set; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* Everything else is PTP1. */ 8448c2ecf20Sopenharmony_ci message_type = ~message_type; 8458c2ecf20Sopenharmony_ci err = mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 8468c2ecf20Sopenharmony_ci message_type); 8478c2ecf20Sopenharmony_ci if (err) 8488c2ecf20Sopenharmony_ci goto err_mtptpt1_set; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, true); 8518c2ecf20Sopenharmony_ci if (err) 8528c2ecf20Sopenharmony_ci goto err_fifo_clr; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ptp_state->ht_gc_dw, mlxsw_sp1_ptp_ht_gc); 8558c2ecf20Sopenharmony_ci mlxsw_core_schedule_dw(&ptp_state->ht_gc_dw, 8568c2ecf20Sopenharmony_ci MLXSW_SP1_PTP_HT_GC_INTERVAL); 8578c2ecf20Sopenharmony_ci return ptp_state; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cierr_fifo_clr: 8608c2ecf20Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 0); 8618c2ecf20Sopenharmony_cierr_mtptpt1_set: 8628c2ecf20Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0); 8638c2ecf20Sopenharmony_cierr_mtptpt_set: 8648c2ecf20Sopenharmony_ci rhltable_destroy(&ptp_state->unmatched_ht); 8658c2ecf20Sopenharmony_cierr_hashtable_init: 8668c2ecf20Sopenharmony_ci kfree(ptp_state); 8678c2ecf20Sopenharmony_ci return ERR_PTR(err); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_civoid mlxsw_sp1_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = ptp_state->mlxsw_sp; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ptp_state->ht_gc_dw); 8758c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp, 0, 0); 8768c2ecf20Sopenharmony_ci mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, false); 8778c2ecf20Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 0); 8788c2ecf20Sopenharmony_ci mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0); 8798c2ecf20Sopenharmony_ci rhltable_free_and_destroy(&ptp_state->unmatched_ht, 8808c2ecf20Sopenharmony_ci &mlxsw_sp1_ptp_unmatched_free_fn, NULL); 8818c2ecf20Sopenharmony_ci kfree(ptp_state); 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ciint mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port, 8858c2ecf20Sopenharmony_ci struct hwtstamp_config *config) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci *config = mlxsw_sp_port->ptp.hwtstamp_config; 8888c2ecf20Sopenharmony_ci return 0; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic int mlxsw_sp_ptp_get_message_types(const struct hwtstamp_config *config, 8928c2ecf20Sopenharmony_ci u16 *p_ing_types, u16 *p_egr_types, 8938c2ecf20Sopenharmony_ci enum hwtstamp_rx_filters *p_rx_filter) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci enum hwtstamp_rx_filters rx_filter = config->rx_filter; 8968c2ecf20Sopenharmony_ci enum hwtstamp_tx_types tx_type = config->tx_type; 8978c2ecf20Sopenharmony_ci u16 ing_types = 0x00; 8988c2ecf20Sopenharmony_ci u16 egr_types = 0x00; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci switch (tx_type) { 9018c2ecf20Sopenharmony_ci case HWTSTAMP_TX_OFF: 9028c2ecf20Sopenharmony_ci egr_types = 0x00; 9038c2ecf20Sopenharmony_ci break; 9048c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ON: 9058c2ecf20Sopenharmony_ci egr_types = 0xff; 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 9088c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_P2P: 9098c2ecf20Sopenharmony_ci return -ERANGE; 9108c2ecf20Sopenharmony_ci default: 9118c2ecf20Sopenharmony_ci return -EINVAL; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci switch (rx_filter) { 9158c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 9168c2ecf20Sopenharmony_ci ing_types = 0x00; 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 9198c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 9208c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 9218c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 9228c2ecf20Sopenharmony_ci ing_types = 0x01; 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 9258c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 9268c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 9278c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 9288c2ecf20Sopenharmony_ci ing_types = 0x02; 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 9318c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 9328c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 9338c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 9348c2ecf20Sopenharmony_ci ing_types = 0x0f; 9358c2ecf20Sopenharmony_ci break; 9368c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 9378c2ecf20Sopenharmony_ci ing_types = 0xff; 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_SOME: 9408c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 9418c2ecf20Sopenharmony_ci return -ERANGE; 9428c2ecf20Sopenharmony_ci default: 9438c2ecf20Sopenharmony_ci return -EINVAL; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci *p_ing_types = ing_types; 9478c2ecf20Sopenharmony_ci *p_egr_types = egr_types; 9488c2ecf20Sopenharmony_ci *p_rx_filter = rx_filter; 9498c2ecf20Sopenharmony_ci return 0; 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port, 9538c2ecf20Sopenharmony_ci u16 ing_types, u16 egr_types) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 9568c2ecf20Sopenharmony_ci struct mlxsw_sp_port *tmp; 9578c2ecf20Sopenharmony_ci u16 orig_ing_types = 0; 9588c2ecf20Sopenharmony_ci u16 orig_egr_types = 0; 9598c2ecf20Sopenharmony_ci int err; 9608c2ecf20Sopenharmony_ci int i; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* MTPPPC configures timestamping globally, not per port. Find the 9638c2ecf20Sopenharmony_ci * configuration that contains all configured timestamping requests. 9648c2ecf20Sopenharmony_ci */ 9658c2ecf20Sopenharmony_ci for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) { 9668c2ecf20Sopenharmony_ci tmp = mlxsw_sp->ports[i]; 9678c2ecf20Sopenharmony_ci if (tmp) { 9688c2ecf20Sopenharmony_ci orig_ing_types |= tmp->ptp.ing_types; 9698c2ecf20Sopenharmony_ci orig_egr_types |= tmp->ptp.egr_types; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci if (tmp && tmp != mlxsw_sp_port) { 9728c2ecf20Sopenharmony_ci ing_types |= tmp->ptp.ing_types; 9738c2ecf20Sopenharmony_ci egr_types |= tmp->ptp.egr_types; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if ((ing_types || egr_types) && !(orig_ing_types || orig_egr_types)) { 9788c2ecf20Sopenharmony_ci err = mlxsw_sp_nve_inc_parsing_depth_get(mlxsw_sp); 9798c2ecf20Sopenharmony_ci if (err) { 9808c2ecf20Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to increase parsing depth"); 9818c2ecf20Sopenharmony_ci return err; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci if (!(ing_types || egr_types) && (orig_ing_types || orig_egr_types)) 9858c2ecf20Sopenharmony_ci mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp_port->mlxsw_sp, 9888c2ecf20Sopenharmony_ci ing_types, egr_types); 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic bool mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port *mlxsw_sp_port) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci return mlxsw_sp_port->ptp.ing_types || mlxsw_sp_port->ptp.egr_types; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int 9978c2ecf20Sopenharmony_cimlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 10008c2ecf20Sopenharmony_ci char qeec_pl[MLXSW_REG_QEEC_LEN]; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci mlxsw_reg_qeec_ptps_pack(qeec_pl, mlxsw_sp_port->local_port, enable); 10038c2ecf20Sopenharmony_ci return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl); 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci bool ptps = false; 10098c2ecf20Sopenharmony_ci int err, i; 10108c2ecf20Sopenharmony_ci u32 speed; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port)) 10138c2ecf20Sopenharmony_ci return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, false); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci err = mlxsw_sp_port_speed_get(mlxsw_sp_port, &speed); 10168c2ecf20Sopenharmony_ci if (err) 10178c2ecf20Sopenharmony_ci return err; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) { 10208c2ecf20Sopenharmony_ci if (mlxsw_sp1_ptp_shaper_params[i].ethtool_speed == speed) { 10218c2ecf20Sopenharmony_ci ptps = true; 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps); 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_civoid mlxsw_sp1_ptp_shaper_work(struct work_struct *work) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct delayed_work *dwork = to_delayed_work(work); 10328c2ecf20Sopenharmony_ci struct mlxsw_sp_port *mlxsw_sp_port; 10338c2ecf20Sopenharmony_ci int err; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port, 10368c2ecf20Sopenharmony_ci ptp.shaper_dw); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port)) 10398c2ecf20Sopenharmony_ci return; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port); 10428c2ecf20Sopenharmony_ci if (err) 10438c2ecf20Sopenharmony_ci netdev_err(mlxsw_sp_port->dev, "Failed to set up PTP shaper\n"); 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ciint mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, 10478c2ecf20Sopenharmony_ci struct hwtstamp_config *config) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci enum hwtstamp_rx_filters rx_filter; 10508c2ecf20Sopenharmony_ci u16 ing_types; 10518c2ecf20Sopenharmony_ci u16 egr_types; 10528c2ecf20Sopenharmony_ci int err; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci err = mlxsw_sp_ptp_get_message_types(config, &ing_types, &egr_types, 10558c2ecf20Sopenharmony_ci &rx_filter); 10568c2ecf20Sopenharmony_ci if (err) 10578c2ecf20Sopenharmony_ci return err; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_mtpppc_update(mlxsw_sp_port, ing_types, egr_types); 10608c2ecf20Sopenharmony_ci if (err) 10618c2ecf20Sopenharmony_ci return err; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci mlxsw_sp_port->ptp.hwtstamp_config = *config; 10648c2ecf20Sopenharmony_ci mlxsw_sp_port->ptp.ing_types = ing_types; 10658c2ecf20Sopenharmony_ci mlxsw_sp_port->ptp.egr_types = egr_types; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port); 10688c2ecf20Sopenharmony_ci if (err) 10698c2ecf20Sopenharmony_ci return err; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* Notify the ioctl caller what we are actually timestamping. */ 10728c2ecf20Sopenharmony_ci config->rx_filter = rx_filter; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci return 0; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ciint mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, 10788c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci info->phc_index = ptp_clock_index(mlxsw_sp->clock->ptp); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 10838c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 10848c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci info->tx_types = BIT(HWTSTAMP_TX_OFF) | 10878c2ecf20Sopenharmony_ci BIT(HWTSTAMP_TX_ON); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 10908c2ecf20Sopenharmony_ci BIT(HWTSTAMP_FILTER_ALL); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return 0; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistruct mlxsw_sp_ptp_port_stat { 10968c2ecf20Sopenharmony_ci char str[ETH_GSTRING_LEN]; 10978c2ecf20Sopenharmony_ci ptrdiff_t offset; 10988c2ecf20Sopenharmony_ci}; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci#define MLXSW_SP_PTP_PORT_STAT(NAME, FIELD) \ 11018c2ecf20Sopenharmony_ci { \ 11028c2ecf20Sopenharmony_ci .str = NAME, \ 11038c2ecf20Sopenharmony_ci .offset = offsetof(struct mlxsw_sp_ptp_port_stats, \ 11048c2ecf20Sopenharmony_ci FIELD), \ 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_ptp_port_stat mlxsw_sp_ptp_port_stats[] = { 11088c2ecf20Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_rx_gcd_packets", rx_gcd.packets), 11098c2ecf20Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_rx_gcd_timestamps", rx_gcd.timestamps), 11108c2ecf20Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_tx_gcd_packets", tx_gcd.packets), 11118c2ecf20Sopenharmony_ci MLXSW_SP_PTP_PORT_STAT("ptp_tx_gcd_timestamps", tx_gcd.timestamps), 11128c2ecf20Sopenharmony_ci}; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci#undef MLXSW_SP_PTP_PORT_STAT 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci#define MLXSW_SP_PTP_PORT_STATS_LEN \ 11178c2ecf20Sopenharmony_ci ARRAY_SIZE(mlxsw_sp_ptp_port_stats) 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ciint mlxsw_sp1_get_stats_count(void) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci return MLXSW_SP_PTP_PORT_STATS_LEN; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_civoid mlxsw_sp1_get_stats_strings(u8 **p) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci int i; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci for (i = 0; i < MLXSW_SP_PTP_PORT_STATS_LEN; i++) { 11298c2ecf20Sopenharmony_ci memcpy(*p, mlxsw_sp_ptp_port_stats[i].str, 11308c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 11318c2ecf20Sopenharmony_ci *p += ETH_GSTRING_LEN; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_civoid mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port, 11368c2ecf20Sopenharmony_ci u64 *data, int data_index) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci void *stats = &mlxsw_sp_port->ptp.stats; 11398c2ecf20Sopenharmony_ci ptrdiff_t offset; 11408c2ecf20Sopenharmony_ci int i; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci data += data_index; 11438c2ecf20Sopenharmony_ci for (i = 0; i < MLXSW_SP_PTP_PORT_STATS_LEN; i++) { 11448c2ecf20Sopenharmony_ci offset = mlxsw_sp_ptp_port_stats[i].offset; 11458c2ecf20Sopenharmony_ci *data++ = *(u64 *)(stats + offset); 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci} 1148