1992aa864SShalom Toledo // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2992aa864SShalom Toledo /* Copyright (c) 2019 Mellanox Technologies. All rights reserved */
3992aa864SShalom Toledo
4992aa864SShalom Toledo #include <linux/ptp_clock_kernel.h>
5992aa864SShalom Toledo #include <linux/clocksource.h>
6992aa864SShalom Toledo #include <linux/timecounter.h>
7992aa864SShalom Toledo #include <linux/spinlock.h>
8992aa864SShalom Toledo #include <linux/device.h>
9d92e4e6eSPetr Machata #include <linux/rhashtable.h>
10d92e4e6eSPetr Machata #include <linux/ptp_classify.h>
11d92e4e6eSPetr Machata #include <linux/if_ether.h>
12d92e4e6eSPetr Machata #include <linux/if_vlan.h>
1387486427SPetr Machata #include <linux/net_tstamp.h>
1408ef8bc8SDanielle Ratson #include <linux/refcount.h>
15992aa864SShalom Toledo
16810256ceSPetr Machata #include "spectrum.h"
17992aa864SShalom Toledo #include "spectrum_ptp.h"
18992aa864SShalom Toledo #include "core.h"
19*598f9574SAmit Cohen #include "txheader.h"
20992aa864SShalom Toledo
21992aa864SShalom Toledo #define MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT 29
22992aa864SShalom Toledo #define MLXSW_SP1_PTP_CLOCK_FREQ_KHZ 156257 /* 6.4nSec */
23992aa864SShalom Toledo #define MLXSW_SP1_PTP_CLOCK_MASK 64
24992aa864SShalom Toledo
255d23e415SPetr Machata #define MLXSW_SP1_PTP_HT_GC_INTERVAL 500 /* ms */
265d23e415SPetr Machata
275d23e415SPetr Machata /* How long, approximately, should the unmatched entries stay in the hash table
285d23e415SPetr Machata * before they are collected. Should be evenly divisible by the GC interval.
295d23e415SPetr Machata */
305d23e415SPetr Machata #define MLXSW_SP1_PTP_HT_GC_TIMEOUT 1000 /* ms */
315d23e415SPetr Machata
32810256ceSPetr Machata struct mlxsw_sp_ptp_state {
335d23e415SPetr Machata struct mlxsw_sp *mlxsw_sp;
34e8fea346SAmit Cohen };
35e8fea346SAmit Cohen
36e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state {
37e8fea346SAmit Cohen struct mlxsw_sp_ptp_state common;
388028ccdaSPetr Machata struct rhltable unmatched_ht;
39810256ceSPetr Machata spinlock_t unmatched_lock; /* protects the HT */
405d23e415SPetr Machata struct delayed_work ht_gc_dw;
415d23e415SPetr Machata u32 gc_cycle;
42810256ceSPetr Machata };
43810256ceSPetr Machata
44d25ff63aSDanielle Ratson struct mlxsw_sp2_ptp_state {
45d25ff63aSDanielle Ratson struct mlxsw_sp_ptp_state common;
4608ef8bc8SDanielle Ratson refcount_t ptp_port_enabled_ref; /* Number of ports with time stamping
4708ef8bc8SDanielle Ratson * enabled.
4808ef8bc8SDanielle Ratson */
4908ef8bc8SDanielle Ratson struct hwtstamp_config config;
50d72fdef2SAmit Cohen struct mutex lock; /* Protects 'config' and HW configuration. */
51d25ff63aSDanielle Ratson };
52d25ff63aSDanielle Ratson
53810256ceSPetr Machata struct mlxsw_sp1_ptp_key {
54c934757dSAmit Cohen u16 local_port;
55810256ceSPetr Machata u8 message_type;
56810256ceSPetr Machata u16 sequence_id;
57810256ceSPetr Machata u8 domain_number;
58810256ceSPetr Machata bool ingress;
59810256ceSPetr Machata };
60810256ceSPetr Machata
61810256ceSPetr Machata struct mlxsw_sp1_ptp_unmatched {
62810256ceSPetr Machata struct mlxsw_sp1_ptp_key key;
638028ccdaSPetr Machata struct rhlist_head ht_node;
64810256ceSPetr Machata struct rcu_head rcu;
65810256ceSPetr Machata struct sk_buff *skb;
66810256ceSPetr Machata u64 timestamp;
675d23e415SPetr Machata u32 gc_cycle;
68810256ceSPetr Machata };
69810256ceSPetr Machata
70810256ceSPetr Machata static const struct rhashtable_params mlxsw_sp1_ptp_unmatched_ht_params = {
71810256ceSPetr Machata .key_len = sizeof_field(struct mlxsw_sp1_ptp_unmatched, key),
72810256ceSPetr Machata .key_offset = offsetof(struct mlxsw_sp1_ptp_unmatched, key),
73810256ceSPetr Machata .head_offset = offsetof(struct mlxsw_sp1_ptp_unmatched, ht_node),
74810256ceSPetr Machata };
75810256ceSPetr Machata
76992aa864SShalom Toledo struct mlxsw_sp_ptp_clock {
77992aa864SShalom Toledo struct mlxsw_core *core;
789bfe3c16SAmit Cohen struct ptp_clock *ptp;
799bfe3c16SAmit Cohen struct ptp_clock_info ptp_info;
809bfe3c16SAmit Cohen };
819bfe3c16SAmit Cohen
829bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock {
839bfe3c16SAmit Cohen struct mlxsw_sp_ptp_clock common;
84992aa864SShalom Toledo spinlock_t lock; /* protect this structure */
85992aa864SShalom Toledo struct cyclecounter cycles;
86992aa864SShalom Toledo struct timecounter tc;
87992aa864SShalom Toledo u32 nominal_c_mult;
88992aa864SShalom Toledo unsigned long overflow_period;
89992aa864SShalom Toledo struct delayed_work overflow_work;
90992aa864SShalom Toledo };
91992aa864SShalom Toledo
92e8fea346SAmit Cohen static struct mlxsw_sp1_ptp_state *
mlxsw_sp1_ptp_state(struct mlxsw_sp * mlxsw_sp)93e8fea346SAmit Cohen mlxsw_sp1_ptp_state(struct mlxsw_sp *mlxsw_sp)
94e8fea346SAmit Cohen {
95e8fea346SAmit Cohen return container_of(mlxsw_sp->ptp_state, struct mlxsw_sp1_ptp_state,
96e8fea346SAmit Cohen common);
97e8fea346SAmit Cohen }
98e8fea346SAmit Cohen
99d25ff63aSDanielle Ratson static struct mlxsw_sp2_ptp_state *
mlxsw_sp2_ptp_state(struct mlxsw_sp * mlxsw_sp)100d25ff63aSDanielle Ratson mlxsw_sp2_ptp_state(struct mlxsw_sp *mlxsw_sp)
101d25ff63aSDanielle Ratson {
102d25ff63aSDanielle Ratson return container_of(mlxsw_sp->ptp_state, struct mlxsw_sp2_ptp_state,
103d25ff63aSDanielle Ratson common);
104d25ff63aSDanielle Ratson }
105d25ff63aSDanielle Ratson
1069bfe3c16SAmit Cohen static struct mlxsw_sp1_ptp_clock *
mlxsw_sp1_ptp_clock(struct ptp_clock_info * ptp)1079bfe3c16SAmit Cohen mlxsw_sp1_ptp_clock(struct ptp_clock_info *ptp)
1089bfe3c16SAmit Cohen {
1099bfe3c16SAmit Cohen return container_of(ptp, struct mlxsw_sp1_ptp_clock, common.ptp_info);
1109bfe3c16SAmit Cohen }
1119bfe3c16SAmit Cohen
__mlxsw_sp1_ptp_read_frc(struct mlxsw_sp1_ptp_clock * clock,struct ptp_system_timestamp * sts)1129bfe3c16SAmit Cohen static u64 __mlxsw_sp1_ptp_read_frc(struct mlxsw_sp1_ptp_clock *clock,
113992aa864SShalom Toledo struct ptp_system_timestamp *sts)
114992aa864SShalom Toledo {
1159bfe3c16SAmit Cohen struct mlxsw_core *mlxsw_core = clock->common.core;
116992aa864SShalom Toledo u32 frc_h1, frc_h2, frc_l;
117992aa864SShalom Toledo
118992aa864SShalom Toledo frc_h1 = mlxsw_core_read_frc_h(mlxsw_core);
119992aa864SShalom Toledo ptp_read_system_prets(sts);
120992aa864SShalom Toledo frc_l = mlxsw_core_read_frc_l(mlxsw_core);
121992aa864SShalom Toledo ptp_read_system_postts(sts);
122992aa864SShalom Toledo frc_h2 = mlxsw_core_read_frc_h(mlxsw_core);
123992aa864SShalom Toledo
124992aa864SShalom Toledo if (frc_h1 != frc_h2) {
125992aa864SShalom Toledo /* wrap around */
126992aa864SShalom Toledo ptp_read_system_prets(sts);
127992aa864SShalom Toledo frc_l = mlxsw_core_read_frc_l(mlxsw_core);
128992aa864SShalom Toledo ptp_read_system_postts(sts);
129992aa864SShalom Toledo }
130992aa864SShalom Toledo
131992aa864SShalom Toledo return (u64) frc_l | (u64) frc_h2 << 32;
132992aa864SShalom Toledo }
133992aa864SShalom Toledo
mlxsw_sp1_ptp_read_frc(const struct cyclecounter * cc)134992aa864SShalom Toledo static u64 mlxsw_sp1_ptp_read_frc(const struct cyclecounter *cc)
135992aa864SShalom Toledo {
1369bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock =
1379bfe3c16SAmit Cohen container_of(cc, struct mlxsw_sp1_ptp_clock, cycles);
138992aa864SShalom Toledo
139992aa864SShalom Toledo return __mlxsw_sp1_ptp_read_frc(clock, NULL) & cc->mask;
140992aa864SShalom Toledo }
141992aa864SShalom Toledo
142992aa864SShalom Toledo static int
mlxsw_sp_ptp_phc_adjfreq(struct mlxsw_sp_ptp_clock * clock,int freq_adj)143a168e13fSAmit Cohen mlxsw_sp_ptp_phc_adjfreq(struct mlxsw_sp_ptp_clock *clock, int freq_adj)
144992aa864SShalom Toledo {
145992aa864SShalom Toledo struct mlxsw_core *mlxsw_core = clock->core;
146992aa864SShalom Toledo char mtutc_pl[MLXSW_REG_MTUTC_LEN];
147992aa864SShalom Toledo
148992aa864SShalom Toledo mlxsw_reg_mtutc_pack(mtutc_pl, MLXSW_REG_MTUTC_OPERATION_ADJUST_FREQ,
14997b05cfbSDanielle Ratson freq_adj, 0, 0, 0);
150992aa864SShalom Toledo return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl);
151992aa864SShalom Toledo }
152992aa864SShalom Toledo
mlxsw_sp1_ptp_ns2cycles(const struct timecounter * tc,u64 nsec)153992aa864SShalom Toledo static u64 mlxsw_sp1_ptp_ns2cycles(const struct timecounter *tc, u64 nsec)
154992aa864SShalom Toledo {
155992aa864SShalom Toledo u64 cycles = (u64) nsec;
156992aa864SShalom Toledo
157992aa864SShalom Toledo cycles <<= tc->cc->shift;
158992aa864SShalom Toledo cycles = div_u64(cycles, tc->cc->mult);
159992aa864SShalom Toledo
160992aa864SShalom Toledo return cycles;
161992aa864SShalom Toledo }
162992aa864SShalom Toledo
163992aa864SShalom Toledo static int
mlxsw_sp1_ptp_phc_settime(struct mlxsw_sp1_ptp_clock * clock,u64 nsec)1649bfe3c16SAmit Cohen mlxsw_sp1_ptp_phc_settime(struct mlxsw_sp1_ptp_clock *clock, u64 nsec)
165992aa864SShalom Toledo {
1669bfe3c16SAmit Cohen struct mlxsw_core *mlxsw_core = clock->common.core;
167cd4bb2a3SShalom Toledo u64 next_sec, next_sec_in_nsec, cycles;
168992aa864SShalom Toledo char mtutc_pl[MLXSW_REG_MTUTC_LEN];
169992aa864SShalom Toledo char mtpps_pl[MLXSW_REG_MTPPS_LEN];
170992aa864SShalom Toledo int err;
171992aa864SShalom Toledo
172cd4bb2a3SShalom Toledo next_sec = div_u64(nsec, NSEC_PER_SEC) + 1;
173992aa864SShalom Toledo next_sec_in_nsec = next_sec * NSEC_PER_SEC;
174992aa864SShalom Toledo
17589e602eeSPetr Machata spin_lock_bh(&clock->lock);
176992aa864SShalom Toledo cycles = mlxsw_sp1_ptp_ns2cycles(&clock->tc, next_sec_in_nsec);
17789e602eeSPetr Machata spin_unlock_bh(&clock->lock);
178992aa864SShalom Toledo
179992aa864SShalom Toledo mlxsw_reg_mtpps_vpin_pack(mtpps_pl, cycles);
180992aa864SShalom Toledo err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtpps), mtpps_pl);
181992aa864SShalom Toledo if (err)
182992aa864SShalom Toledo return err;
183992aa864SShalom Toledo
184992aa864SShalom Toledo mlxsw_reg_mtutc_pack(mtutc_pl,
185992aa864SShalom Toledo MLXSW_REG_MTUTC_OPERATION_SET_TIME_AT_NEXT_SEC,
18697b05cfbSDanielle Ratson 0, next_sec, 0, 0);
187992aa864SShalom Toledo return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl);
188992aa864SShalom Toledo }
189992aa864SShalom Toledo
mlxsw_sp1_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)190992aa864SShalom Toledo static int mlxsw_sp1_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
191992aa864SShalom Toledo {
1929bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp);
193992aa864SShalom Toledo s32 ppb;
194992aa864SShalom Toledo
195992aa864SShalom Toledo ppb = scaled_ppm_to_ppb(scaled_ppm);
196992aa864SShalom Toledo
19789e602eeSPetr Machata spin_lock_bh(&clock->lock);
198992aa864SShalom Toledo timecounter_read(&clock->tc);
199d82303dfSJacob Keller clock->cycles.mult = adjust_by_scaled_ppm(clock->nominal_c_mult,
200d82303dfSJacob Keller scaled_ppm);
20189e602eeSPetr Machata spin_unlock_bh(&clock->lock);
202992aa864SShalom Toledo
203d82303dfSJacob Keller return mlxsw_sp_ptp_phc_adjfreq(&clock->common, ppb);
204992aa864SShalom Toledo }
205992aa864SShalom Toledo
mlxsw_sp1_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)206992aa864SShalom Toledo static int mlxsw_sp1_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
207992aa864SShalom Toledo {
2089bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp);
209992aa864SShalom Toledo u64 nsec;
210992aa864SShalom Toledo
21189e602eeSPetr Machata spin_lock_bh(&clock->lock);
212992aa864SShalom Toledo timecounter_adjtime(&clock->tc, delta);
213992aa864SShalom Toledo nsec = timecounter_read(&clock->tc);
21489e602eeSPetr Machata spin_unlock_bh(&clock->lock);
215992aa864SShalom Toledo
216992aa864SShalom Toledo return mlxsw_sp1_ptp_phc_settime(clock, nsec);
217992aa864SShalom Toledo }
218992aa864SShalom Toledo
mlxsw_sp1_ptp_gettimex(struct ptp_clock_info * ptp,struct timespec64 * ts,struct ptp_system_timestamp * sts)219992aa864SShalom Toledo static int mlxsw_sp1_ptp_gettimex(struct ptp_clock_info *ptp,
220992aa864SShalom Toledo struct timespec64 *ts,
221992aa864SShalom Toledo struct ptp_system_timestamp *sts)
222992aa864SShalom Toledo {
2239bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp);
224992aa864SShalom Toledo u64 cycles, nsec;
225992aa864SShalom Toledo
22689e602eeSPetr Machata spin_lock_bh(&clock->lock);
227992aa864SShalom Toledo cycles = __mlxsw_sp1_ptp_read_frc(clock, sts);
228992aa864SShalom Toledo nsec = timecounter_cyc2time(&clock->tc, cycles);
22989e602eeSPetr Machata spin_unlock_bh(&clock->lock);
230992aa864SShalom Toledo
231992aa864SShalom Toledo *ts = ns_to_timespec64(nsec);
232992aa864SShalom Toledo
233992aa864SShalom Toledo return 0;
234992aa864SShalom Toledo }
235992aa864SShalom Toledo
mlxsw_sp1_ptp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)236992aa864SShalom Toledo static int mlxsw_sp1_ptp_settime(struct ptp_clock_info *ptp,
237992aa864SShalom Toledo const struct timespec64 *ts)
238992aa864SShalom Toledo {
2399bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock = mlxsw_sp1_ptp_clock(ptp);
240992aa864SShalom Toledo u64 nsec = timespec64_to_ns(ts);
241992aa864SShalom Toledo
24289e602eeSPetr Machata spin_lock_bh(&clock->lock);
243992aa864SShalom Toledo timecounter_init(&clock->tc, &clock->cycles, nsec);
244992aa864SShalom Toledo nsec = timecounter_read(&clock->tc);
24589e602eeSPetr Machata spin_unlock_bh(&clock->lock);
246992aa864SShalom Toledo
247992aa864SShalom Toledo return mlxsw_sp1_ptp_phc_settime(clock, nsec);
248992aa864SShalom Toledo }
249992aa864SShalom Toledo
250992aa864SShalom Toledo static const struct ptp_clock_info mlxsw_sp1_ptp_clock_info = {
251992aa864SShalom Toledo .owner = THIS_MODULE,
252992aa864SShalom Toledo .name = "mlxsw_sp_clock",
253992aa864SShalom Toledo .max_adj = 100000000,
254992aa864SShalom Toledo .adjfine = mlxsw_sp1_ptp_adjfine,
255992aa864SShalom Toledo .adjtime = mlxsw_sp1_ptp_adjtime,
256992aa864SShalom Toledo .gettimex64 = mlxsw_sp1_ptp_gettimex,
257992aa864SShalom Toledo .settime64 = mlxsw_sp1_ptp_settime,
258992aa864SShalom Toledo };
259992aa864SShalom Toledo
mlxsw_sp1_ptp_clock_overflow(struct work_struct * work)260992aa864SShalom Toledo static void mlxsw_sp1_ptp_clock_overflow(struct work_struct *work)
261992aa864SShalom Toledo {
262992aa864SShalom Toledo struct delayed_work *dwork = to_delayed_work(work);
2639bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock;
264992aa864SShalom Toledo
2659bfe3c16SAmit Cohen clock = container_of(dwork, struct mlxsw_sp1_ptp_clock, overflow_work);
266992aa864SShalom Toledo
26789e602eeSPetr Machata spin_lock_bh(&clock->lock);
268992aa864SShalom Toledo timecounter_read(&clock->tc);
26989e602eeSPetr Machata spin_unlock_bh(&clock->lock);
270992aa864SShalom Toledo mlxsw_core_schedule_dw(&clock->overflow_work, clock->overflow_period);
271992aa864SShalom Toledo }
272992aa864SShalom Toledo
273992aa864SShalom Toledo struct mlxsw_sp_ptp_clock *
mlxsw_sp1_ptp_clock_init(struct mlxsw_sp * mlxsw_sp,struct device * dev)274992aa864SShalom Toledo mlxsw_sp1_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev)
275992aa864SShalom Toledo {
276992aa864SShalom Toledo u64 overflow_cycles, nsec, frac = 0;
2779bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock;
278992aa864SShalom Toledo int err;
279992aa864SShalom Toledo
280992aa864SShalom Toledo clock = kzalloc(sizeof(*clock), GFP_KERNEL);
281992aa864SShalom Toledo if (!clock)
282992aa864SShalom Toledo return ERR_PTR(-ENOMEM);
283992aa864SShalom Toledo
284992aa864SShalom Toledo spin_lock_init(&clock->lock);
285992aa864SShalom Toledo clock->cycles.read = mlxsw_sp1_ptp_read_frc;
286992aa864SShalom Toledo clock->cycles.shift = MLXSW_SP1_PTP_CLOCK_CYCLES_SHIFT;
287992aa864SShalom Toledo clock->cycles.mult = clocksource_khz2mult(MLXSW_SP1_PTP_CLOCK_FREQ_KHZ,
288992aa864SShalom Toledo clock->cycles.shift);
289992aa864SShalom Toledo clock->nominal_c_mult = clock->cycles.mult;
290992aa864SShalom Toledo clock->cycles.mask = CLOCKSOURCE_MASK(MLXSW_SP1_PTP_CLOCK_MASK);
2919bfe3c16SAmit Cohen clock->common.core = mlxsw_sp->core;
292992aa864SShalom Toledo
29322d950b7SAmit Cohen timecounter_init(&clock->tc, &clock->cycles, 0);
294992aa864SShalom Toledo
295992aa864SShalom Toledo /* Calculate period in seconds to call the overflow watchdog - to make
296992aa864SShalom Toledo * sure counter is checked at least twice every wrap around.
297992aa864SShalom Toledo * The period is calculated as the minimum between max HW cycles count
298992aa864SShalom Toledo * (The clock source mask) and max amount of cycles that can be
299992aa864SShalom Toledo * multiplied by clock multiplier where the result doesn't exceed
300992aa864SShalom Toledo * 64bits.
301992aa864SShalom Toledo */
302992aa864SShalom Toledo overflow_cycles = div64_u64(~0ULL >> 1, clock->cycles.mult);
303992aa864SShalom Toledo overflow_cycles = min(overflow_cycles, div_u64(clock->cycles.mask, 3));
304992aa864SShalom Toledo
305992aa864SShalom Toledo nsec = cyclecounter_cyc2ns(&clock->cycles, overflow_cycles, 0, &frac);
306992aa864SShalom Toledo clock->overflow_period = nsecs_to_jiffies(nsec);
307992aa864SShalom Toledo
308992aa864SShalom Toledo INIT_DELAYED_WORK(&clock->overflow_work, mlxsw_sp1_ptp_clock_overflow);
309992aa864SShalom Toledo mlxsw_core_schedule_dw(&clock->overflow_work, 0);
310992aa864SShalom Toledo
3119bfe3c16SAmit Cohen clock->common.ptp_info = mlxsw_sp1_ptp_clock_info;
3129bfe3c16SAmit Cohen clock->common.ptp = ptp_clock_register(&clock->common.ptp_info, dev);
3139bfe3c16SAmit Cohen if (IS_ERR(clock->common.ptp)) {
3149bfe3c16SAmit Cohen err = PTR_ERR(clock->common.ptp);
315992aa864SShalom Toledo dev_err(dev, "ptp_clock_register failed %d\n", err);
316992aa864SShalom Toledo goto err_ptp_clock_register;
317992aa864SShalom Toledo }
318992aa864SShalom Toledo
3199bfe3c16SAmit Cohen return &clock->common;
320992aa864SShalom Toledo
321992aa864SShalom Toledo err_ptp_clock_register:
322992aa864SShalom Toledo cancel_delayed_work_sync(&clock->overflow_work);
323992aa864SShalom Toledo kfree(clock);
324992aa864SShalom Toledo return ERR_PTR(err);
325992aa864SShalom Toledo }
326992aa864SShalom Toledo
mlxsw_sp1_ptp_clock_fini(struct mlxsw_sp_ptp_clock * clock_common)3279bfe3c16SAmit Cohen void mlxsw_sp1_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock_common)
328992aa864SShalom Toledo {
3299bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock =
3309bfe3c16SAmit Cohen container_of(clock_common, struct mlxsw_sp1_ptp_clock, common);
3319bfe3c16SAmit Cohen
3329bfe3c16SAmit Cohen ptp_clock_unregister(clock_common->ptp);
333992aa864SShalom Toledo cancel_delayed_work_sync(&clock->overflow_work);
334992aa864SShalom Toledo kfree(clock);
335992aa864SShalom Toledo }
336aed4b572SPetr Machata
mlxsw_sp2_ptp_read_utc(struct mlxsw_sp_ptp_clock * clock,struct ptp_system_timestamp * sts)337a5bf8e5eSDanielle Ratson static u64 mlxsw_sp2_ptp_read_utc(struct mlxsw_sp_ptp_clock *clock,
338a5bf8e5eSDanielle Ratson struct ptp_system_timestamp *sts)
339a5bf8e5eSDanielle Ratson {
340a5bf8e5eSDanielle Ratson struct mlxsw_core *mlxsw_core = clock->core;
341a5bf8e5eSDanielle Ratson u32 utc_sec1, utc_sec2, utc_nsec;
342a5bf8e5eSDanielle Ratson
343a5bf8e5eSDanielle Ratson utc_sec1 = mlxsw_core_read_utc_sec(mlxsw_core);
344a5bf8e5eSDanielle Ratson ptp_read_system_prets(sts);
345a5bf8e5eSDanielle Ratson utc_nsec = mlxsw_core_read_utc_nsec(mlxsw_core);
346a5bf8e5eSDanielle Ratson ptp_read_system_postts(sts);
347a5bf8e5eSDanielle Ratson utc_sec2 = mlxsw_core_read_utc_sec(mlxsw_core);
348a5bf8e5eSDanielle Ratson
349a5bf8e5eSDanielle Ratson if (utc_sec1 != utc_sec2) {
350a5bf8e5eSDanielle Ratson /* Wrap around. */
351a5bf8e5eSDanielle Ratson ptp_read_system_prets(sts);
352a5bf8e5eSDanielle Ratson utc_nsec = mlxsw_core_read_utc_nsec(mlxsw_core);
353a5bf8e5eSDanielle Ratson ptp_read_system_postts(sts);
354a5bf8e5eSDanielle Ratson }
355a5bf8e5eSDanielle Ratson
356a5bf8e5eSDanielle Ratson return (u64)utc_sec2 * NSEC_PER_SEC + utc_nsec;
357a5bf8e5eSDanielle Ratson }
358a5bf8e5eSDanielle Ratson
359a5bf8e5eSDanielle Ratson static int
mlxsw_sp2_ptp_phc_settime(struct mlxsw_sp_ptp_clock * clock,u64 nsec)360a5bf8e5eSDanielle Ratson mlxsw_sp2_ptp_phc_settime(struct mlxsw_sp_ptp_clock *clock, u64 nsec)
361a5bf8e5eSDanielle Ratson {
362a5bf8e5eSDanielle Ratson struct mlxsw_core *mlxsw_core = clock->core;
363a5bf8e5eSDanielle Ratson char mtutc_pl[MLXSW_REG_MTUTC_LEN];
364a5bf8e5eSDanielle Ratson u32 sec, nsec_rem;
365a5bf8e5eSDanielle Ratson
366a5bf8e5eSDanielle Ratson sec = div_u64_rem(nsec, NSEC_PER_SEC, &nsec_rem);
367a5bf8e5eSDanielle Ratson mlxsw_reg_mtutc_pack(mtutc_pl,
368a5bf8e5eSDanielle Ratson MLXSW_REG_MTUTC_OPERATION_SET_TIME_IMMEDIATE,
369a5bf8e5eSDanielle Ratson 0, sec, nsec_rem, 0);
370a5bf8e5eSDanielle Ratson return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl);
371a5bf8e5eSDanielle Ratson }
372a5bf8e5eSDanielle Ratson
mlxsw_sp2_ptp_adjfine(struct ptp_clock_info * ptp,long scaled_ppm)373a5bf8e5eSDanielle Ratson static int mlxsw_sp2_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
374a5bf8e5eSDanielle Ratson {
375a5bf8e5eSDanielle Ratson struct mlxsw_sp_ptp_clock *clock =
376a5bf8e5eSDanielle Ratson container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
377a5bf8e5eSDanielle Ratson s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
378a5bf8e5eSDanielle Ratson
379a5bf8e5eSDanielle Ratson /* In Spectrum-2 and newer ASICs, the frequency adjustment in MTUTC is
380a5bf8e5eSDanielle Ratson * reversed, positive values mean to decrease the frequency. Adjust the
381a5bf8e5eSDanielle Ratson * sign of PPB to this behavior.
382a5bf8e5eSDanielle Ratson */
383a5bf8e5eSDanielle Ratson return mlxsw_sp_ptp_phc_adjfreq(clock, -ppb);
384a5bf8e5eSDanielle Ratson }
385a5bf8e5eSDanielle Ratson
mlxsw_sp2_ptp_adjtime(struct ptp_clock_info * ptp,s64 delta)386a5bf8e5eSDanielle Ratson static int mlxsw_sp2_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
387a5bf8e5eSDanielle Ratson {
388a5bf8e5eSDanielle Ratson struct mlxsw_sp_ptp_clock *clock =
389a5bf8e5eSDanielle Ratson container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
390a5bf8e5eSDanielle Ratson struct mlxsw_core *mlxsw_core = clock->core;
391a5bf8e5eSDanielle Ratson char mtutc_pl[MLXSW_REG_MTUTC_LEN];
392a5bf8e5eSDanielle Ratson
393a5bf8e5eSDanielle Ratson /* HW time adjustment range is s16. If out of range, set time instead. */
394a5bf8e5eSDanielle Ratson if (delta < S16_MIN || delta > S16_MAX) {
395a5bf8e5eSDanielle Ratson u64 nsec;
396a5bf8e5eSDanielle Ratson
397a5bf8e5eSDanielle Ratson nsec = mlxsw_sp2_ptp_read_utc(clock, NULL);
398a5bf8e5eSDanielle Ratson nsec += delta;
399a5bf8e5eSDanielle Ratson
400a5bf8e5eSDanielle Ratson return mlxsw_sp2_ptp_phc_settime(clock, nsec);
401a5bf8e5eSDanielle Ratson }
402a5bf8e5eSDanielle Ratson
403a5bf8e5eSDanielle Ratson mlxsw_reg_mtutc_pack(mtutc_pl,
404a5bf8e5eSDanielle Ratson MLXSW_REG_MTUTC_OPERATION_ADJUST_TIME,
405a5bf8e5eSDanielle Ratson 0, 0, 0, delta);
406a5bf8e5eSDanielle Ratson return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl);
407a5bf8e5eSDanielle Ratson }
408a5bf8e5eSDanielle Ratson
mlxsw_sp2_ptp_gettimex(struct ptp_clock_info * ptp,struct timespec64 * ts,struct ptp_system_timestamp * sts)409a5bf8e5eSDanielle Ratson static int mlxsw_sp2_ptp_gettimex(struct ptp_clock_info *ptp,
410a5bf8e5eSDanielle Ratson struct timespec64 *ts,
411a5bf8e5eSDanielle Ratson struct ptp_system_timestamp *sts)
412a5bf8e5eSDanielle Ratson {
413a5bf8e5eSDanielle Ratson struct mlxsw_sp_ptp_clock *clock =
414a5bf8e5eSDanielle Ratson container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
415a5bf8e5eSDanielle Ratson u64 nsec;
416a5bf8e5eSDanielle Ratson
417a5bf8e5eSDanielle Ratson nsec = mlxsw_sp2_ptp_read_utc(clock, sts);
418a5bf8e5eSDanielle Ratson *ts = ns_to_timespec64(nsec);
419a5bf8e5eSDanielle Ratson
420a5bf8e5eSDanielle Ratson return 0;
421a5bf8e5eSDanielle Ratson }
422a5bf8e5eSDanielle Ratson
mlxsw_sp2_ptp_settime(struct ptp_clock_info * ptp,const struct timespec64 * ts)423a5bf8e5eSDanielle Ratson static int mlxsw_sp2_ptp_settime(struct ptp_clock_info *ptp,
424a5bf8e5eSDanielle Ratson const struct timespec64 *ts)
425a5bf8e5eSDanielle Ratson {
426a5bf8e5eSDanielle Ratson struct mlxsw_sp_ptp_clock *clock =
427a5bf8e5eSDanielle Ratson container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
428a5bf8e5eSDanielle Ratson u64 nsec = timespec64_to_ns(ts);
429a5bf8e5eSDanielle Ratson
430a5bf8e5eSDanielle Ratson return mlxsw_sp2_ptp_phc_settime(clock, nsec);
431a5bf8e5eSDanielle Ratson }
432a5bf8e5eSDanielle Ratson
433a5bf8e5eSDanielle Ratson static const struct ptp_clock_info mlxsw_sp2_ptp_clock_info = {
434a5bf8e5eSDanielle Ratson .owner = THIS_MODULE,
435a5bf8e5eSDanielle Ratson .name = "mlxsw_sp_clock",
436a5bf8e5eSDanielle Ratson .max_adj = MLXSW_REG_MTUTC_MAX_FREQ_ADJ,
437a5bf8e5eSDanielle Ratson .adjfine = mlxsw_sp2_ptp_adjfine,
438a5bf8e5eSDanielle Ratson .adjtime = mlxsw_sp2_ptp_adjtime,
439a5bf8e5eSDanielle Ratson .gettimex64 = mlxsw_sp2_ptp_gettimex,
440a5bf8e5eSDanielle Ratson .settime64 = mlxsw_sp2_ptp_settime,
441a5bf8e5eSDanielle Ratson };
442a5bf8e5eSDanielle Ratson
443a5bf8e5eSDanielle Ratson struct mlxsw_sp_ptp_clock *
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp * mlxsw_sp,struct device * dev)444a5bf8e5eSDanielle Ratson mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev)
445a5bf8e5eSDanielle Ratson {
446a5bf8e5eSDanielle Ratson struct mlxsw_sp_ptp_clock *clock;
447a5bf8e5eSDanielle Ratson int err;
448a5bf8e5eSDanielle Ratson
449a5bf8e5eSDanielle Ratson clock = kzalloc(sizeof(*clock), GFP_KERNEL);
450a5bf8e5eSDanielle Ratson if (!clock)
451a5bf8e5eSDanielle Ratson return ERR_PTR(-ENOMEM);
452a5bf8e5eSDanielle Ratson
453a5bf8e5eSDanielle Ratson clock->core = mlxsw_sp->core;
454a5bf8e5eSDanielle Ratson
455a5bf8e5eSDanielle Ratson clock->ptp_info = mlxsw_sp2_ptp_clock_info;
456a5bf8e5eSDanielle Ratson
457a5bf8e5eSDanielle Ratson err = mlxsw_sp2_ptp_phc_settime(clock, 0);
458a5bf8e5eSDanielle Ratson if (err) {
459a5bf8e5eSDanielle Ratson dev_err(dev, "setting UTC time failed %d\n", err);
460a5bf8e5eSDanielle Ratson goto err_ptp_phc_settime;
461a5bf8e5eSDanielle Ratson }
462a5bf8e5eSDanielle Ratson
463a5bf8e5eSDanielle Ratson clock->ptp = ptp_clock_register(&clock->ptp_info, dev);
464a5bf8e5eSDanielle Ratson if (IS_ERR(clock->ptp)) {
465a5bf8e5eSDanielle Ratson err = PTR_ERR(clock->ptp);
466a5bf8e5eSDanielle Ratson dev_err(dev, "ptp_clock_register failed %d\n", err);
467a5bf8e5eSDanielle Ratson goto err_ptp_clock_register;
468a5bf8e5eSDanielle Ratson }
469a5bf8e5eSDanielle Ratson
470a5bf8e5eSDanielle Ratson return clock;
471a5bf8e5eSDanielle Ratson
472a5bf8e5eSDanielle Ratson err_ptp_clock_register:
473a5bf8e5eSDanielle Ratson err_ptp_phc_settime:
474a5bf8e5eSDanielle Ratson kfree(clock);
475a5bf8e5eSDanielle Ratson return ERR_PTR(err);
476a5bf8e5eSDanielle Ratson }
477a5bf8e5eSDanielle Ratson
mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock * clock)478a5bf8e5eSDanielle Ratson void mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock)
479a5bf8e5eSDanielle Ratson {
480a5bf8e5eSDanielle Ratson ptp_clock_unregister(clock->ptp);
481a5bf8e5eSDanielle Ratson kfree(clock);
482a5bf8e5eSDanielle Ratson }
483a5bf8e5eSDanielle Ratson
mlxsw_sp_ptp_parse(struct sk_buff * skb,u8 * p_domain_number,u8 * p_message_type,u16 * p_sequence_id)484d92e4e6eSPetr Machata static int mlxsw_sp_ptp_parse(struct sk_buff *skb,
485d92e4e6eSPetr Machata u8 *p_domain_number,
486d92e4e6eSPetr Machata u8 *p_message_type,
487d92e4e6eSPetr Machata u16 *p_sequence_id)
488d92e4e6eSPetr Machata {
489d92e4e6eSPetr Machata unsigned int ptp_class;
4907b2b28c6SKurt Kanzenbach struct ptp_header *hdr;
491d92e4e6eSPetr Machata
492d92e4e6eSPetr Machata ptp_class = ptp_classify_raw(skb);
493d92e4e6eSPetr Machata
494d92e4e6eSPetr Machata switch (ptp_class & PTP_CLASS_VMASK) {
495d92e4e6eSPetr Machata case PTP_CLASS_V1:
496d92e4e6eSPetr Machata case PTP_CLASS_V2:
497d92e4e6eSPetr Machata break;
498d92e4e6eSPetr Machata default:
499d92e4e6eSPetr Machata return -ERANGE;
500d92e4e6eSPetr Machata }
501d92e4e6eSPetr Machata
5027b2b28c6SKurt Kanzenbach hdr = ptp_parse_header(skb, ptp_class);
5037b2b28c6SKurt Kanzenbach if (!hdr)
504d92e4e6eSPetr Machata return -EINVAL;
505d92e4e6eSPetr Machata
5067b2b28c6SKurt Kanzenbach *p_message_type = ptp_get_msgtype(hdr, ptp_class);
5077b2b28c6SKurt Kanzenbach *p_domain_number = hdr->domain_number;
5087b2b28c6SKurt Kanzenbach *p_sequence_id = be16_to_cpu(hdr->sequence_id);
5097b2b28c6SKurt Kanzenbach
510d92e4e6eSPetr Machata return 0;
511d92e4e6eSPetr Machata }
512d92e4e6eSPetr Machata
513d92e4e6eSPetr Machata /* Returns NULL on successful insertion, a pointer on conflict, or an ERR_PTR on
514d92e4e6eSPetr Machata * error.
515d92e4e6eSPetr Machata */
5168028ccdaSPetr Machata static int
mlxsw_sp1_ptp_unmatched_save(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp1_ptp_key key,struct sk_buff * skb,u64 timestamp)517d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_save(struct mlxsw_sp *mlxsw_sp,
518d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_key key,
519d92e4e6eSPetr Machata struct sk_buff *skb,
520d92e4e6eSPetr Machata u64 timestamp)
521d92e4e6eSPetr Machata {
5225d23e415SPetr Machata int cycles = MLXSW_SP1_PTP_HT_GC_TIMEOUT / MLXSW_SP1_PTP_HT_GC_INTERVAL;
523e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp);
524d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched;
5258028ccdaSPetr Machata int err;
526d92e4e6eSPetr Machata
527d92e4e6eSPetr Machata unmatched = kzalloc(sizeof(*unmatched), GFP_ATOMIC);
528d92e4e6eSPetr Machata if (!unmatched)
5298028ccdaSPetr Machata return -ENOMEM;
530d92e4e6eSPetr Machata
531d92e4e6eSPetr Machata unmatched->key = key;
532d92e4e6eSPetr Machata unmatched->skb = skb;
533d92e4e6eSPetr Machata unmatched->timestamp = timestamp;
534e8fea346SAmit Cohen unmatched->gc_cycle = ptp_state->gc_cycle + cycles;
535d92e4e6eSPetr Machata
5368028ccdaSPetr Machata err = rhltable_insert(&ptp_state->unmatched_ht, &unmatched->ht_node,
537d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_ht_params);
5388028ccdaSPetr Machata if (err)
539d92e4e6eSPetr Machata kfree(unmatched);
540d92e4e6eSPetr Machata
5418028ccdaSPetr Machata return err;
542d92e4e6eSPetr Machata }
543d92e4e6eSPetr Machata
544d92e4e6eSPetr Machata static struct mlxsw_sp1_ptp_unmatched *
mlxsw_sp1_ptp_unmatched_lookup(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp1_ptp_key key,int * p_length)545d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_lookup(struct mlxsw_sp *mlxsw_sp,
5468028ccdaSPetr Machata struct mlxsw_sp1_ptp_key key, int *p_length)
547d92e4e6eSPetr Machata {
548e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp);
5498028ccdaSPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched, *last = NULL;
5508028ccdaSPetr Machata struct rhlist_head *tmp, *list;
5518028ccdaSPetr Machata int length = 0;
5528028ccdaSPetr Machata
553e8fea346SAmit Cohen list = rhltable_lookup(&ptp_state->unmatched_ht, &key,
554d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_ht_params);
5558028ccdaSPetr Machata rhl_for_each_entry_rcu(unmatched, tmp, list, ht_node) {
5568028ccdaSPetr Machata last = unmatched;
5578028ccdaSPetr Machata length++;
5588028ccdaSPetr Machata }
5598028ccdaSPetr Machata
5608028ccdaSPetr Machata *p_length = length;
5618028ccdaSPetr Machata return last;
562d92e4e6eSPetr Machata }
563d92e4e6eSPetr Machata
564d92e4e6eSPetr Machata static int
mlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp1_ptp_unmatched * unmatched)565d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp,
566d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched)
567d92e4e6eSPetr Machata {
568e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp);
569e8fea346SAmit Cohen
570e8fea346SAmit Cohen return rhltable_remove(&ptp_state->unmatched_ht,
571d92e4e6eSPetr Machata &unmatched->ht_node,
572d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_ht_params);
573d92e4e6eSPetr Machata }
574d92e4e6eSPetr Machata
575d92e4e6eSPetr Machata /* This function is called in the following scenarios:
576d92e4e6eSPetr Machata *
577d92e4e6eSPetr Machata * 1) When a packet is matched with its timestamp.
578d92e4e6eSPetr Machata * 2) In several situation when it is necessary to immediately pass on
579d92e4e6eSPetr Machata * an SKB without a timestamp.
5805d23e415SPetr Machata * 3) From GC indirectly through mlxsw_sp1_ptp_unmatched_finish().
5815d23e415SPetr Machata * This case is similar to 2) above.
582d92e4e6eSPetr Machata */
mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u16 local_port,bool ingress,struct skb_shared_hwtstamps * hwtstamps)583d92e4e6eSPetr Machata static void mlxsw_sp1_ptp_packet_finish(struct mlxsw_sp *mlxsw_sp,
584c934757dSAmit Cohen struct sk_buff *skb, u16 local_port,
585d92e4e6eSPetr Machata bool ingress,
586d92e4e6eSPetr Machata struct skb_shared_hwtstamps *hwtstamps)
587d92e4e6eSPetr Machata {
588d92e4e6eSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port;
589d92e4e6eSPetr Machata
590d92e4e6eSPetr Machata /* Between capturing the packet and finishing it, there is a window of
591d92e4e6eSPetr Machata * opportunity for the originating port to go away (e.g. due to a
592d92e4e6eSPetr Machata * split). Also make sure the SKB device reference is still valid.
593d92e4e6eSPetr Machata */
594d92e4e6eSPetr Machata mlxsw_sp_port = mlxsw_sp->ports[local_port];
595dbcdb61aSPetr Machata if (!(mlxsw_sp_port && (!skb->dev || skb->dev == mlxsw_sp_port->dev))) {
596d92e4e6eSPetr Machata dev_kfree_skb_any(skb);
597d92e4e6eSPetr Machata return;
598d92e4e6eSPetr Machata }
599d92e4e6eSPetr Machata
600d92e4e6eSPetr Machata if (ingress) {
601d92e4e6eSPetr Machata if (hwtstamps)
602d92e4e6eSPetr Machata *skb_hwtstamps(skb) = *hwtstamps;
603d92e4e6eSPetr Machata mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp);
604d92e4e6eSPetr Machata } else {
605d92e4e6eSPetr Machata /* skb_tstamp_tx() allows hwtstamps to be NULL. */
606d92e4e6eSPetr Machata skb_tstamp_tx(skb, hwtstamps);
607d92e4e6eSPetr Machata dev_kfree_skb_any(skb);
608d92e4e6eSPetr Machata }
609d92e4e6eSPetr Machata }
610d92e4e6eSPetr Machata
mlxsw_sp1_packet_timestamp(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp1_ptp_key key,struct sk_buff * skb,u64 timestamp)611d92e4e6eSPetr Machata static void mlxsw_sp1_packet_timestamp(struct mlxsw_sp *mlxsw_sp,
612d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_key key,
613d92e4e6eSPetr Machata struct sk_buff *skb,
614d92e4e6eSPetr Machata u64 timestamp)
615d92e4e6eSPetr Machata {
6169bfe3c16SAmit Cohen struct mlxsw_sp_ptp_clock *clock_common = mlxsw_sp->clock;
6179bfe3c16SAmit Cohen struct mlxsw_sp1_ptp_clock *clock =
6189bfe3c16SAmit Cohen container_of(clock_common, struct mlxsw_sp1_ptp_clock, common);
6199bfe3c16SAmit Cohen
620d92e4e6eSPetr Machata struct skb_shared_hwtstamps hwtstamps;
621d92e4e6eSPetr Machata u64 nsec;
622d92e4e6eSPetr Machata
6239bfe3c16SAmit Cohen spin_lock_bh(&clock->lock);
6249bfe3c16SAmit Cohen nsec = timecounter_cyc2time(&clock->tc, timestamp);
6259bfe3c16SAmit Cohen spin_unlock_bh(&clock->lock);
626d92e4e6eSPetr Machata
627d92e4e6eSPetr Machata hwtstamps.hwtstamp = ns_to_ktime(nsec);
628d92e4e6eSPetr Machata mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb,
629d92e4e6eSPetr Machata key.local_port, key.ingress, &hwtstamps);
630d92e4e6eSPetr Machata }
631d92e4e6eSPetr Machata
632d92e4e6eSPetr Machata static void
mlxsw_sp1_ptp_unmatched_finish(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp1_ptp_unmatched * unmatched)633d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_finish(struct mlxsw_sp *mlxsw_sp,
634d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched)
635d92e4e6eSPetr Machata {
636d92e4e6eSPetr Machata if (unmatched->skb && unmatched->timestamp)
637d92e4e6eSPetr Machata mlxsw_sp1_packet_timestamp(mlxsw_sp, unmatched->key,
638d92e4e6eSPetr Machata unmatched->skb,
639d92e4e6eSPetr Machata unmatched->timestamp);
640d92e4e6eSPetr Machata else if (unmatched->skb)
641d92e4e6eSPetr Machata mlxsw_sp1_ptp_packet_finish(mlxsw_sp, unmatched->skb,
642d92e4e6eSPetr Machata unmatched->key.local_port,
643d92e4e6eSPetr Machata unmatched->key.ingress, NULL);
644d92e4e6eSPetr Machata kfree_rcu(unmatched, rcu);
645d92e4e6eSPetr Machata }
646d92e4e6eSPetr Machata
mlxsw_sp1_ptp_unmatched_free_fn(void * ptr,void * arg)647810256ceSPetr Machata static void mlxsw_sp1_ptp_unmatched_free_fn(void *ptr, void *arg)
648810256ceSPetr Machata {
649810256ceSPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched = ptr;
650810256ceSPetr Machata
651810256ceSPetr Machata /* This is invoked at a point where the ports are gone already. Nothing
652810256ceSPetr Machata * to do with whatever is left in the HT but to free it.
653810256ceSPetr Machata */
654810256ceSPetr Machata if (unmatched->skb)
655810256ceSPetr Machata dev_kfree_skb_any(unmatched->skb);
656810256ceSPetr Machata kfree_rcu(unmatched, rcu);
657810256ceSPetr Machata }
658810256ceSPetr Machata
mlxsw_sp1_ptp_got_piece(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp1_ptp_key key,struct sk_buff * skb,u64 timestamp)659d92e4e6eSPetr Machata static void mlxsw_sp1_ptp_got_piece(struct mlxsw_sp *mlxsw_sp,
660d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_key key,
661d92e4e6eSPetr Machata struct sk_buff *skb, u64 timestamp)
662d92e4e6eSPetr Machata {
663e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp);
6648028ccdaSPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched;
6658028ccdaSPetr Machata int length;
666d92e4e6eSPetr Machata int err;
667d92e4e6eSPetr Machata
668d92e4e6eSPetr Machata rcu_read_lock();
669d92e4e6eSPetr Machata
670e8fea346SAmit Cohen spin_lock(&ptp_state->unmatched_lock);
671d92e4e6eSPetr Machata
6728028ccdaSPetr Machata unmatched = mlxsw_sp1_ptp_unmatched_lookup(mlxsw_sp, key, &length);
673d92e4e6eSPetr Machata if (skb && unmatched && unmatched->timestamp) {
674d92e4e6eSPetr Machata unmatched->skb = skb;
675d92e4e6eSPetr Machata } else if (timestamp && unmatched && unmatched->skb) {
676d92e4e6eSPetr Machata unmatched->timestamp = timestamp;
6778028ccdaSPetr Machata } else {
6788028ccdaSPetr Machata /* Either there is no entry to match, or one that is there is
6798028ccdaSPetr Machata * incompatible.
680d92e4e6eSPetr Machata */
6818028ccdaSPetr Machata if (length < 100)
6828028ccdaSPetr Machata err = mlxsw_sp1_ptp_unmatched_save(mlxsw_sp, key,
683d92e4e6eSPetr Machata skb, timestamp);
6848028ccdaSPetr Machata else
6858028ccdaSPetr Machata err = -E2BIG;
6868028ccdaSPetr Machata if (err && skb)
687d92e4e6eSPetr Machata mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb,
688d92e4e6eSPetr Machata key.local_port,
689d92e4e6eSPetr Machata key.ingress, NULL);
6908028ccdaSPetr Machata unmatched = NULL;
691d92e4e6eSPetr Machata }
6928028ccdaSPetr Machata
6938028ccdaSPetr Machata if (unmatched) {
6948028ccdaSPetr Machata err = mlxsw_sp1_ptp_unmatched_remove(mlxsw_sp, unmatched);
6958028ccdaSPetr Machata WARN_ON_ONCE(err);
696d92e4e6eSPetr Machata }
697d92e4e6eSPetr Machata
698e8fea346SAmit Cohen spin_unlock(&ptp_state->unmatched_lock);
699d92e4e6eSPetr Machata
700d92e4e6eSPetr Machata if (unmatched)
701d92e4e6eSPetr Machata mlxsw_sp1_ptp_unmatched_finish(mlxsw_sp, unmatched);
702d92e4e6eSPetr Machata
703d92e4e6eSPetr Machata rcu_read_unlock();
704d92e4e6eSPetr Machata }
705d92e4e6eSPetr Machata
mlxsw_sp1_ptp_got_packet(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u16 local_port,bool ingress)706d92e4e6eSPetr Machata static void mlxsw_sp1_ptp_got_packet(struct mlxsw_sp *mlxsw_sp,
707c934757dSAmit Cohen struct sk_buff *skb, u16 local_port,
708d92e4e6eSPetr Machata bool ingress)
709d92e4e6eSPetr Machata {
710d92e4e6eSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port;
711d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_key key;
712d92e4e6eSPetr Machata u8 types;
713d92e4e6eSPetr Machata int err;
714d92e4e6eSPetr Machata
715d92e4e6eSPetr Machata mlxsw_sp_port = mlxsw_sp->ports[local_port];
716d92e4e6eSPetr Machata if (!mlxsw_sp_port)
717d92e4e6eSPetr Machata goto immediate;
718d92e4e6eSPetr Machata
719d92e4e6eSPetr Machata types = ingress ? mlxsw_sp_port->ptp.ing_types :
720d92e4e6eSPetr Machata mlxsw_sp_port->ptp.egr_types;
721d92e4e6eSPetr Machata if (!types)
722d92e4e6eSPetr Machata goto immediate;
723d92e4e6eSPetr Machata
724d92e4e6eSPetr Machata memset(&key, 0, sizeof(key));
725d92e4e6eSPetr Machata key.local_port = local_port;
726d92e4e6eSPetr Machata key.ingress = ingress;
727d92e4e6eSPetr Machata
728d92e4e6eSPetr Machata err = mlxsw_sp_ptp_parse(skb, &key.domain_number, &key.message_type,
729d92e4e6eSPetr Machata &key.sequence_id);
730d92e4e6eSPetr Machata if (err)
731d92e4e6eSPetr Machata goto immediate;
732d92e4e6eSPetr Machata
733d92e4e6eSPetr Machata /* For packets whose timestamping was not enabled on this port, don't
734d92e4e6eSPetr Machata * bother trying to match the timestamp.
735d92e4e6eSPetr Machata */
736d92e4e6eSPetr Machata if (!((1 << key.message_type) & types))
737d92e4e6eSPetr Machata goto immediate;
738d92e4e6eSPetr Machata
739d92e4e6eSPetr Machata mlxsw_sp1_ptp_got_piece(mlxsw_sp, key, skb, 0);
740d92e4e6eSPetr Machata return;
741d92e4e6eSPetr Machata
742d92e4e6eSPetr Machata immediate:
743d92e4e6eSPetr Machata mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, local_port, ingress, NULL);
744d92e4e6eSPetr Machata }
745d92e4e6eSPetr Machata
mlxsw_sp1_ptp_got_timestamp(struct mlxsw_sp * mlxsw_sp,bool ingress,u16 local_port,u8 message_type,u8 domain_number,u16 sequence_id,u64 timestamp)746d92e4e6eSPetr Machata void mlxsw_sp1_ptp_got_timestamp(struct mlxsw_sp *mlxsw_sp, bool ingress,
747c934757dSAmit Cohen u16 local_port, u8 message_type,
748d92e4e6eSPetr Machata u8 domain_number, u16 sequence_id,
749d92e4e6eSPetr Machata u64 timestamp)
750d92e4e6eSPetr Machata {
751d92e4e6eSPetr Machata struct mlxsw_sp_port *mlxsw_sp_port;
752d92e4e6eSPetr Machata struct mlxsw_sp1_ptp_key key;
753d92e4e6eSPetr Machata u8 types;
754d92e4e6eSPetr Machata
755bcdfd615SAmit Cohen if (WARN_ON_ONCE(!mlxsw_sp_local_port_is_valid(mlxsw_sp, local_port)))
756837ec05cSDanielle Ratson return;
757d92e4e6eSPetr Machata mlxsw_sp_port = mlxsw_sp->ports[local_port];
758d92e4e6eSPetr Machata if (!mlxsw_sp_port)
759d92e4e6eSPetr Machata return;
760d92e4e6eSPetr Machata
761d92e4e6eSPetr Machata types = ingress ? mlxsw_sp_port->ptp.ing_types :
762d92e4e6eSPetr Machata mlxsw_sp_port->ptp.egr_types;
763d92e4e6eSPetr Machata
764d92e4e6eSPetr Machata /* For message types whose timestamping was not enabled on this port,
765d92e4e6eSPetr Machata * don't bother with the timestamp.
766d92e4e6eSPetr Machata */
767d92e4e6eSPetr Machata if (!((1 << message_type) & types))
768d92e4e6eSPetr Machata return;
769d92e4e6eSPetr Machata
770d92e4e6eSPetr Machata memset(&key, 0, sizeof(key));
771d92e4e6eSPetr Machata key.local_port = local_port;
772d92e4e6eSPetr Machata key.domain_number = domain_number;
773d92e4e6eSPetr Machata key.message_type = message_type;
774d92e4e6eSPetr Machata key.sequence_id = sequence_id;
775d92e4e6eSPetr Machata key.ingress = ingress;
776d92e4e6eSPetr Machata
777d92e4e6eSPetr Machata mlxsw_sp1_ptp_got_piece(mlxsw_sp, key, NULL, timestamp);
778d92e4e6eSPetr Machata }
779d92e4e6eSPetr Machata
mlxsw_sp1_ptp_receive(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u16 local_port)780aed4b572SPetr Machata void mlxsw_sp1_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
781c934757dSAmit Cohen u16 local_port)
782aed4b572SPetr Machata {
783d92e4e6eSPetr Machata skb_reset_mac_header(skb);
784d92e4e6eSPetr Machata mlxsw_sp1_ptp_got_packet(mlxsw_sp, skb, local_port, true);
785aed4b572SPetr Machata }
7860714256cSPetr Machata
mlxsw_sp1_ptp_transmitted(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u16 local_port)7870714256cSPetr Machata void mlxsw_sp1_ptp_transmitted(struct mlxsw_sp *mlxsw_sp,
788c934757dSAmit Cohen struct sk_buff *skb, u16 local_port)
7890714256cSPetr Machata {
790d92e4e6eSPetr Machata mlxsw_sp1_ptp_got_packet(mlxsw_sp, skb, local_port, false);
7910714256cSPetr Machata }
792810256ceSPetr Machata
7935d23e415SPetr Machata static void
mlxsw_sp1_ptp_ht_gc_collect(struct mlxsw_sp1_ptp_state * ptp_state,struct mlxsw_sp1_ptp_unmatched * unmatched)794e8fea346SAmit Cohen mlxsw_sp1_ptp_ht_gc_collect(struct mlxsw_sp1_ptp_state *ptp_state,
7955d23e415SPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched)
7965d23e415SPetr Machata {
797e8fea346SAmit Cohen struct mlxsw_sp *mlxsw_sp = ptp_state->common.mlxsw_sp;
798dc4f3eb0SPetr Machata struct mlxsw_sp_ptp_port_dir_stats *stats;
799dc4f3eb0SPetr Machata struct mlxsw_sp_port *mlxsw_sp_port;
8005d23e415SPetr Machata int err;
8015d23e415SPetr Machata
8025d23e415SPetr Machata /* If an unmatched entry has an SKB, it has to be handed over to the
8035d23e415SPetr Machata * networking stack. This is usually done from a trap handler, which is
8045d23e415SPetr Machata * invoked in a softirq context. Here we are going to do it in process
8055d23e415SPetr Machata * context. If that were to be interrupted by a softirq, it could cause
8065d23e415SPetr Machata * a deadlock when an attempt is made to take an already-taken lock
8075d23e415SPetr Machata * somewhere along the sending path. Disable softirqs to prevent this.
8085d23e415SPetr Machata */
8095d23e415SPetr Machata local_bh_disable();
8105d23e415SPetr Machata
8115d23e415SPetr Machata spin_lock(&ptp_state->unmatched_lock);
8128028ccdaSPetr Machata err = rhltable_remove(&ptp_state->unmatched_ht, &unmatched->ht_node,
8135d23e415SPetr Machata mlxsw_sp1_ptp_unmatched_ht_params);
8145d23e415SPetr Machata spin_unlock(&ptp_state->unmatched_lock);
8155d23e415SPetr Machata
8165d23e415SPetr Machata if (err)
8175d23e415SPetr Machata /* The packet was matched with timestamp during the walk. */
8185d23e415SPetr Machata goto out;
8195d23e415SPetr Machata
820e8fea346SAmit Cohen mlxsw_sp_port = mlxsw_sp->ports[unmatched->key.local_port];
821dc4f3eb0SPetr Machata if (mlxsw_sp_port) {
822dc4f3eb0SPetr Machata stats = unmatched->key.ingress ?
823dc4f3eb0SPetr Machata &mlxsw_sp_port->ptp.stats.rx_gcd :
824dc4f3eb0SPetr Machata &mlxsw_sp_port->ptp.stats.tx_gcd;
825dc4f3eb0SPetr Machata if (unmatched->skb)
826dc4f3eb0SPetr Machata stats->packets++;
827dc4f3eb0SPetr Machata else
828dc4f3eb0SPetr Machata stats->timestamps++;
829dc4f3eb0SPetr Machata }
830dc4f3eb0SPetr Machata
8315d23e415SPetr Machata /* mlxsw_sp1_ptp_unmatched_finish() invokes netif_receive_skb(). While
8325d23e415SPetr Machata * the comment at that function states that it can only be called in
8335d23e415SPetr Machata * soft IRQ context, this pattern of local_bh_disable() +
8345d23e415SPetr Machata * netif_receive_skb(), in process context, is seen elsewhere in the
8355d23e415SPetr Machata * kernel, notably in pktgen.
8365d23e415SPetr Machata */
837e8fea346SAmit Cohen mlxsw_sp1_ptp_unmatched_finish(mlxsw_sp, unmatched);
8385d23e415SPetr Machata
8395d23e415SPetr Machata out:
8405d23e415SPetr Machata local_bh_enable();
8415d23e415SPetr Machata }
8425d23e415SPetr Machata
mlxsw_sp1_ptp_ht_gc(struct work_struct * work)8435d23e415SPetr Machata static void mlxsw_sp1_ptp_ht_gc(struct work_struct *work)
8445d23e415SPetr Machata {
8455d23e415SPetr Machata struct delayed_work *dwork = to_delayed_work(work);
8465d23e415SPetr Machata struct mlxsw_sp1_ptp_unmatched *unmatched;
847e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state;
8485d23e415SPetr Machata struct rhashtable_iter iter;
8495d23e415SPetr Machata u32 gc_cycle;
8505d23e415SPetr Machata void *obj;
8515d23e415SPetr Machata
852e8fea346SAmit Cohen ptp_state = container_of(dwork, struct mlxsw_sp1_ptp_state, ht_gc_dw);
8535d23e415SPetr Machata gc_cycle = ptp_state->gc_cycle++;
8545d23e415SPetr Machata
8558028ccdaSPetr Machata rhltable_walk_enter(&ptp_state->unmatched_ht, &iter);
8565d23e415SPetr Machata rhashtable_walk_start(&iter);
8575d23e415SPetr Machata while ((obj = rhashtable_walk_next(&iter))) {
8585d23e415SPetr Machata if (IS_ERR(obj))
8595d23e415SPetr Machata continue;
8605d23e415SPetr Machata
8615d23e415SPetr Machata unmatched = obj;
8625d23e415SPetr Machata if (unmatched->gc_cycle <= gc_cycle)
8635d23e415SPetr Machata mlxsw_sp1_ptp_ht_gc_collect(ptp_state, unmatched);
8645d23e415SPetr Machata }
8655d23e415SPetr Machata rhashtable_walk_stop(&iter);
8665d23e415SPetr Machata rhashtable_walk_exit(&iter);
8675d23e415SPetr Machata
8685d23e415SPetr Machata mlxsw_core_schedule_dw(&ptp_state->ht_gc_dw,
8695d23e415SPetr Machata MLXSW_SP1_PTP_HT_GC_INTERVAL);
8705d23e415SPetr Machata }
8715d23e415SPetr Machata
mlxsw_sp_ptp_mtptpt_set(struct mlxsw_sp * mlxsw_sp,enum mlxsw_reg_mtptpt_trap_id trap_id,u16 message_type)872a773c76cSPetr Machata static int mlxsw_sp_ptp_mtptpt_set(struct mlxsw_sp *mlxsw_sp,
873a773c76cSPetr Machata enum mlxsw_reg_mtptpt_trap_id trap_id,
874a773c76cSPetr Machata u16 message_type)
875a773c76cSPetr Machata {
876a773c76cSPetr Machata char mtptpt_pl[MLXSW_REG_MTPTPT_LEN];
877a773c76cSPetr Machata
8781c358fedSDanielle Ratson mlxsw_reg_mtptpt_pack(mtptpt_pl, trap_id, message_type);
879a773c76cSPetr Machata return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtptpt), mtptpt_pl);
880a773c76cSPetr Machata }
881a773c76cSPetr Machata
mlxsw_sp1_ptp_set_fifo_clr_on_trap(struct mlxsw_sp * mlxsw_sp,bool clr)882a773c76cSPetr Machata static int mlxsw_sp1_ptp_set_fifo_clr_on_trap(struct mlxsw_sp *mlxsw_sp,
883a773c76cSPetr Machata bool clr)
884a773c76cSPetr Machata {
885a773c76cSPetr Machata char mogcr_pl[MLXSW_REG_MOGCR_LEN] = {0};
886a773c76cSPetr Machata int err;
887a773c76cSPetr Machata
888a773c76cSPetr Machata err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
889a773c76cSPetr Machata if (err)
890a773c76cSPetr Machata return err;
891a773c76cSPetr Machata
892a773c76cSPetr Machata mlxsw_reg_mogcr_ptp_iftc_set(mogcr_pl, clr);
893a773c76cSPetr Machata mlxsw_reg_mogcr_ptp_eftc_set(mogcr_pl, clr);
894a773c76cSPetr Machata return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mogcr), mogcr_pl);
895a773c76cSPetr Machata }
896a773c76cSPetr Machata
mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp * mlxsw_sp,u16 ing_types,u16 egr_types)89787486427SPetr Machata static int mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp *mlxsw_sp,
89887486427SPetr Machata u16 ing_types, u16 egr_types)
89987486427SPetr Machata {
90087486427SPetr Machata char mtpppc_pl[MLXSW_REG_MTPPPC_LEN];
90187486427SPetr Machata
90287486427SPetr Machata mlxsw_reg_mtpppc_pack(mtpppc_pl, ing_types, egr_types);
90387486427SPetr Machata return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl);
90487486427SPetr Machata }
90587486427SPetr Machata
906399569cbSShalom Toledo struct mlxsw_sp1_ptp_shaper_params {
907399569cbSShalom Toledo u32 ethtool_speed;
908399569cbSShalom Toledo enum mlxsw_reg_qpsc_port_speed port_speed;
909399569cbSShalom Toledo u8 shaper_time_exp;
910399569cbSShalom Toledo u8 shaper_time_mantissa;
911399569cbSShalom Toledo u8 shaper_inc;
912399569cbSShalom Toledo u8 shaper_bs;
913399569cbSShalom Toledo u8 port_to_shaper_credits;
914399569cbSShalom Toledo int ing_timestamp_inc;
915399569cbSShalom Toledo int egr_timestamp_inc;
916399569cbSShalom Toledo };
917399569cbSShalom Toledo
918399569cbSShalom Toledo static const struct mlxsw_sp1_ptp_shaper_params
919399569cbSShalom Toledo mlxsw_sp1_ptp_shaper_params[] = {
92072458e27SShalom Toledo {
92172458e27SShalom Toledo .ethtool_speed = SPEED_100,
92272458e27SShalom Toledo .port_speed = MLXSW_REG_QPSC_PORT_SPEED_100M,
92372458e27SShalom Toledo .shaper_time_exp = 4,
92472458e27SShalom Toledo .shaper_time_mantissa = 12,
92572458e27SShalom Toledo .shaper_inc = 9,
92672458e27SShalom Toledo .shaper_bs = 1,
92772458e27SShalom Toledo .port_to_shaper_credits = 1,
92872458e27SShalom Toledo .ing_timestamp_inc = -313,
92972458e27SShalom Toledo .egr_timestamp_inc = 313,
93072458e27SShalom Toledo },
93172458e27SShalom Toledo {
93272458e27SShalom Toledo .ethtool_speed = SPEED_1000,
93372458e27SShalom Toledo .port_speed = MLXSW_REG_QPSC_PORT_SPEED_1G,
93472458e27SShalom Toledo .shaper_time_exp = 0,
93572458e27SShalom Toledo .shaper_time_mantissa = 12,
93672458e27SShalom Toledo .shaper_inc = 6,
93772458e27SShalom Toledo .shaper_bs = 0,
93872458e27SShalom Toledo .port_to_shaper_credits = 1,
93972458e27SShalom Toledo .ing_timestamp_inc = -35,
94072458e27SShalom Toledo .egr_timestamp_inc = 35,
94172458e27SShalom Toledo },
94272458e27SShalom Toledo {
94372458e27SShalom Toledo .ethtool_speed = SPEED_10000,
94472458e27SShalom Toledo .port_speed = MLXSW_REG_QPSC_PORT_SPEED_10G,
94572458e27SShalom Toledo .shaper_time_exp = 0,
94672458e27SShalom Toledo .shaper_time_mantissa = 2,
94772458e27SShalom Toledo .shaper_inc = 14,
94872458e27SShalom Toledo .shaper_bs = 1,
94972458e27SShalom Toledo .port_to_shaper_credits = 1,
95072458e27SShalom Toledo .ing_timestamp_inc = -11,
95172458e27SShalom Toledo .egr_timestamp_inc = 11,
95272458e27SShalom Toledo },
95372458e27SShalom Toledo {
95472458e27SShalom Toledo .ethtool_speed = SPEED_25000,
95572458e27SShalom Toledo .port_speed = MLXSW_REG_QPSC_PORT_SPEED_25G,
95672458e27SShalom Toledo .shaper_time_exp = 0,
95772458e27SShalom Toledo .shaper_time_mantissa = 0,
95872458e27SShalom Toledo .shaper_inc = 11,
95972458e27SShalom Toledo .shaper_bs = 1,
96072458e27SShalom Toledo .port_to_shaper_credits = 1,
96172458e27SShalom Toledo .ing_timestamp_inc = -14,
96272458e27SShalom Toledo .egr_timestamp_inc = 14,
96372458e27SShalom Toledo },
964399569cbSShalom Toledo };
965399569cbSShalom Toledo
966399569cbSShalom Toledo #define MLXSW_SP1_PTP_SHAPER_PARAMS_LEN ARRAY_SIZE(mlxsw_sp1_ptp_shaper_params)
967399569cbSShalom Toledo
mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp * mlxsw_sp)968399569cbSShalom Toledo static int mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp *mlxsw_sp)
969399569cbSShalom Toledo {
970399569cbSShalom Toledo const struct mlxsw_sp1_ptp_shaper_params *params;
971399569cbSShalom Toledo char qpsc_pl[MLXSW_REG_QPSC_LEN];
972399569cbSShalom Toledo int i, err;
973399569cbSShalom Toledo
974399569cbSShalom Toledo for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) {
975399569cbSShalom Toledo params = &mlxsw_sp1_ptp_shaper_params[i];
976399569cbSShalom Toledo mlxsw_reg_qpsc_pack(qpsc_pl, params->port_speed,
977399569cbSShalom Toledo params->shaper_time_exp,
978399569cbSShalom Toledo params->shaper_time_mantissa,
979399569cbSShalom Toledo params->shaper_inc, params->shaper_bs,
980399569cbSShalom Toledo params->port_to_shaper_credits,
981399569cbSShalom Toledo params->ing_timestamp_inc,
982399569cbSShalom Toledo params->egr_timestamp_inc);
983399569cbSShalom Toledo err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpsc), qpsc_pl);
984399569cbSShalom Toledo if (err)
985399569cbSShalom Toledo return err;
986399569cbSShalom Toledo }
987399569cbSShalom Toledo
988399569cbSShalom Toledo return 0;
989399569cbSShalom Toledo }
990399569cbSShalom Toledo
mlxsw_sp_ptp_traps_set(struct mlxsw_sp * mlxsw_sp)99137b62b28SAmit Cohen static int mlxsw_sp_ptp_traps_set(struct mlxsw_sp *mlxsw_sp)
99237b62b28SAmit Cohen {
99337b62b28SAmit Cohen u16 event_message_type;
99437b62b28SAmit Cohen int err;
99537b62b28SAmit Cohen
99637b62b28SAmit Cohen /* Deliver these message types as PTP0. */
99737b62b28SAmit Cohen event_message_type = BIT(PTP_MSGTYPE_SYNC) |
99837b62b28SAmit Cohen BIT(PTP_MSGTYPE_DELAY_REQ) |
99937b62b28SAmit Cohen BIT(PTP_MSGTYPE_PDELAY_REQ) |
100037b62b28SAmit Cohen BIT(PTP_MSGTYPE_PDELAY_RESP);
100137b62b28SAmit Cohen
100237b62b28SAmit Cohen err = mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0,
100337b62b28SAmit Cohen event_message_type);
100437b62b28SAmit Cohen if (err)
100537b62b28SAmit Cohen return err;
100637b62b28SAmit Cohen
100737b62b28SAmit Cohen /* Everything else is PTP1. */
100837b62b28SAmit Cohen err = mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1,
100937b62b28SAmit Cohen ~event_message_type);
101037b62b28SAmit Cohen if (err)
101137b62b28SAmit Cohen goto err_mtptpt1_set;
101237b62b28SAmit Cohen
101337b62b28SAmit Cohen return 0;
101437b62b28SAmit Cohen
101537b62b28SAmit Cohen err_mtptpt1_set:
101637b62b28SAmit Cohen mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0);
101737b62b28SAmit Cohen return err;
101837b62b28SAmit Cohen }
101937b62b28SAmit Cohen
mlxsw_sp_ptp_traps_unset(struct mlxsw_sp * mlxsw_sp)102037b62b28SAmit Cohen static void mlxsw_sp_ptp_traps_unset(struct mlxsw_sp *mlxsw_sp)
102137b62b28SAmit Cohen {
102237b62b28SAmit Cohen mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 0);
102337b62b28SAmit Cohen mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0);
102437b62b28SAmit Cohen }
102537b62b28SAmit Cohen
mlxsw_sp1_ptp_init(struct mlxsw_sp * mlxsw_sp)1026810256ceSPetr Machata struct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp)
1027810256ceSPetr Machata {
1028e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state;
1029810256ceSPetr Machata int err;
1030810256ceSPetr Machata
1031399569cbSShalom Toledo err = mlxsw_sp1_ptp_shaper_params_set(mlxsw_sp);
1032399569cbSShalom Toledo if (err)
1033399569cbSShalom Toledo return ERR_PTR(err);
1034399569cbSShalom Toledo
1035810256ceSPetr Machata ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL);
1036810256ceSPetr Machata if (!ptp_state)
1037810256ceSPetr Machata return ERR_PTR(-ENOMEM);
1038e8fea346SAmit Cohen ptp_state->common.mlxsw_sp = mlxsw_sp;
1039810256ceSPetr Machata
1040810256ceSPetr Machata spin_lock_init(&ptp_state->unmatched_lock);
1041810256ceSPetr Machata
10428028ccdaSPetr Machata err = rhltable_init(&ptp_state->unmatched_ht,
1043810256ceSPetr Machata &mlxsw_sp1_ptp_unmatched_ht_params);
1044810256ceSPetr Machata if (err)
1045810256ceSPetr Machata goto err_hashtable_init;
1046810256ceSPetr Machata
104737b62b28SAmit Cohen err = mlxsw_sp_ptp_traps_set(mlxsw_sp);
1048a773c76cSPetr Machata if (err)
104937b62b28SAmit Cohen goto err_ptp_traps_set;
1050a773c76cSPetr Machata
1051a773c76cSPetr Machata err = mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, true);
1052a773c76cSPetr Machata if (err)
1053a773c76cSPetr Machata goto err_fifo_clr;
1054a773c76cSPetr Machata
10555d23e415SPetr Machata INIT_DELAYED_WORK(&ptp_state->ht_gc_dw, mlxsw_sp1_ptp_ht_gc);
10565d23e415SPetr Machata mlxsw_core_schedule_dw(&ptp_state->ht_gc_dw,
10575d23e415SPetr Machata MLXSW_SP1_PTP_HT_GC_INTERVAL);
1058e8fea346SAmit Cohen return &ptp_state->common;
1059810256ceSPetr Machata
1060a773c76cSPetr Machata err_fifo_clr:
106137b62b28SAmit Cohen mlxsw_sp_ptp_traps_unset(mlxsw_sp);
106237b62b28SAmit Cohen err_ptp_traps_set:
10638028ccdaSPetr Machata rhltable_destroy(&ptp_state->unmatched_ht);
1064810256ceSPetr Machata err_hashtable_init:
1065810256ceSPetr Machata kfree(ptp_state);
1066810256ceSPetr Machata return ERR_PTR(err);
1067810256ceSPetr Machata }
1068810256ceSPetr Machata
mlxsw_sp1_ptp_fini(struct mlxsw_sp_ptp_state * ptp_state_common)1069e8fea346SAmit Cohen void mlxsw_sp1_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state_common)
1070810256ceSPetr Machata {
1071e8fea346SAmit Cohen struct mlxsw_sp *mlxsw_sp = ptp_state_common->mlxsw_sp;
1072e8fea346SAmit Cohen struct mlxsw_sp1_ptp_state *ptp_state;
1073e8fea346SAmit Cohen
1074e8fea346SAmit Cohen ptp_state = mlxsw_sp1_ptp_state(mlxsw_sp);
1075a773c76cSPetr Machata
10765d23e415SPetr Machata cancel_delayed_work_sync(&ptp_state->ht_gc_dw);
107787486427SPetr Machata mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp, 0, 0);
1078a773c76cSPetr Machata mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, false);
107937b62b28SAmit Cohen mlxsw_sp_ptp_traps_unset(mlxsw_sp);
10808028ccdaSPetr Machata rhltable_free_and_destroy(&ptp_state->unmatched_ht,
1081810256ceSPetr Machata &mlxsw_sp1_ptp_unmatched_free_fn, NULL);
1082810256ceSPetr Machata kfree(ptp_state);
1083810256ceSPetr Machata }
108487486427SPetr Machata
mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port * mlxsw_sp_port,struct hwtstamp_config * config)108587486427SPetr Machata int mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port,
108687486427SPetr Machata struct hwtstamp_config *config)
108787486427SPetr Machata {
108887486427SPetr Machata *config = mlxsw_sp_port->ptp.hwtstamp_config;
108987486427SPetr Machata return 0;
109087486427SPetr Machata }
109187486427SPetr Machata
10924017d929SAmit Cohen static int
mlxsw_sp1_ptp_get_message_types(const struct hwtstamp_config * config,u16 * p_ing_types,u16 * p_egr_types,enum hwtstamp_rx_filters * p_rx_filter)10934017d929SAmit Cohen mlxsw_sp1_ptp_get_message_types(const struct hwtstamp_config *config,
109487486427SPetr Machata u16 *p_ing_types, u16 *p_egr_types,
109587486427SPetr Machata enum hwtstamp_rx_filters *p_rx_filter)
109687486427SPetr Machata {
109787486427SPetr Machata enum hwtstamp_rx_filters rx_filter = config->rx_filter;
109887486427SPetr Machata enum hwtstamp_tx_types tx_type = config->tx_type;
109987486427SPetr Machata u16 ing_types = 0x00;
110087486427SPetr Machata u16 egr_types = 0x00;
110187486427SPetr Machata
110287486427SPetr Machata switch (tx_type) {
110387486427SPetr Machata case HWTSTAMP_TX_OFF:
110487486427SPetr Machata egr_types = 0x00;
110587486427SPetr Machata break;
110687486427SPetr Machata case HWTSTAMP_TX_ON:
110787486427SPetr Machata egr_types = 0xff;
110887486427SPetr Machata break;
110987486427SPetr Machata case HWTSTAMP_TX_ONESTEP_SYNC:
1110b6fd7b96SRichard Cochran case HWTSTAMP_TX_ONESTEP_P2P:
111187486427SPetr Machata return -ERANGE;
1112ea315c55SIdo Schimmel default:
1113ea315c55SIdo Schimmel return -EINVAL;
111487486427SPetr Machata }
111587486427SPetr Machata
111687486427SPetr Machata switch (rx_filter) {
111787486427SPetr Machata case HWTSTAMP_FILTER_NONE:
111887486427SPetr Machata ing_types = 0x00;
111987486427SPetr Machata break;
112087486427SPetr Machata case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
112187486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
112287486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
112387486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_SYNC:
112487486427SPetr Machata ing_types = 0x01;
112587486427SPetr Machata break;
112687486427SPetr Machata case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
112787486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
112887486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
112987486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
113087486427SPetr Machata ing_types = 0x02;
113187486427SPetr Machata break;
113287486427SPetr Machata case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
113387486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
113487486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
113587486427SPetr Machata case HWTSTAMP_FILTER_PTP_V2_EVENT:
113687486427SPetr Machata ing_types = 0x0f;
113787486427SPetr Machata break;
113887486427SPetr Machata case HWTSTAMP_FILTER_ALL:
113987486427SPetr Machata ing_types = 0xff;
114087486427SPetr Machata break;
114187486427SPetr Machata case HWTSTAMP_FILTER_SOME:
114287486427SPetr Machata case HWTSTAMP_FILTER_NTP_ALL:
114387486427SPetr Machata return -ERANGE;
1144ea315c55SIdo Schimmel default:
1145ea315c55SIdo Schimmel return -EINVAL;
114687486427SPetr Machata }
114787486427SPetr Machata
114887486427SPetr Machata *p_ing_types = ing_types;
114987486427SPetr Machata *p_egr_types = egr_types;
115087486427SPetr Machata *p_rx_filter = rx_filter;
115187486427SPetr Machata return 0;
115287486427SPetr Machata }
115387486427SPetr Machata
mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port * mlxsw_sp_port,u16 ing_types,u16 egr_types)115487486427SPetr Machata static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port,
115587486427SPetr Machata u16 ing_types, u16 egr_types)
115687486427SPetr Machata {
115787486427SPetr Machata struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
115887486427SPetr Machata struct mlxsw_sp_port *tmp;
1159c6b36bddSPetr Machata u16 orig_ing_types = 0;
1160c6b36bddSPetr Machata u16 orig_egr_types = 0;
1161c6b36bddSPetr Machata int err;
116287486427SPetr Machata int i;
116387486427SPetr Machata
116487486427SPetr Machata /* MTPPPC configures timestamping globally, not per port. Find the
116587486427SPetr Machata * configuration that contains all configured timestamping requests.
116687486427SPetr Machata */
116787486427SPetr Machata for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) {
116887486427SPetr Machata tmp = mlxsw_sp->ports[i];
1169c6b36bddSPetr Machata if (tmp) {
1170c6b36bddSPetr Machata orig_ing_types |= tmp->ptp.ing_types;
1171c6b36bddSPetr Machata orig_egr_types |= tmp->ptp.egr_types;
1172c6b36bddSPetr Machata }
117387486427SPetr Machata if (tmp && tmp != mlxsw_sp_port) {
117487486427SPetr Machata ing_types |= tmp->ptp.ing_types;
117587486427SPetr Machata egr_types |= tmp->ptp.egr_types;
117687486427SPetr Machata }
117787486427SPetr Machata }
117887486427SPetr Machata
11792ad07086SColin Ian King if ((ing_types || egr_types) && !(orig_ing_types || orig_egr_types)) {
11800071e7cdSAmit Cohen err = mlxsw_sp_parsing_depth_inc(mlxsw_sp);
1181c6b36bddSPetr Machata if (err) {
1182c6b36bddSPetr Machata netdev_err(mlxsw_sp_port->dev, "Failed to increase parsing depth");
1183c6b36bddSPetr Machata return err;
1184c6b36bddSPetr Machata }
1185c6b36bddSPetr Machata }
11862ad07086SColin Ian King if (!(ing_types || egr_types) && (orig_ing_types || orig_egr_types))
11870071e7cdSAmit Cohen mlxsw_sp_parsing_depth_dec(mlxsw_sp);
1188c6b36bddSPetr Machata
118987486427SPetr Machata return mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp_port->mlxsw_sp,
119087486427SPetr Machata ing_types, egr_types);
119187486427SPetr Machata }
119287486427SPetr Machata
mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port * mlxsw_sp_port)1193eceed3b1SShalom Toledo static bool mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port *mlxsw_sp_port)
1194eceed3b1SShalom Toledo {
1195eceed3b1SShalom Toledo return mlxsw_sp_port->ptp.ing_types || mlxsw_sp_port->ptp.egr_types;
1196eceed3b1SShalom Toledo }
1197eceed3b1SShalom Toledo
1198eceed3b1SShalom Toledo static int
mlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port * mlxsw_sp_port,bool enable)1199eceed3b1SShalom Toledo mlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
1200eceed3b1SShalom Toledo {
1201eceed3b1SShalom Toledo struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1202eceed3b1SShalom Toledo char qeec_pl[MLXSW_REG_QEEC_LEN];
1203eceed3b1SShalom Toledo
1204eceed3b1SShalom Toledo mlxsw_reg_qeec_ptps_pack(qeec_pl, mlxsw_sp_port->local_port, enable);
1205eceed3b1SShalom Toledo return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
1206eceed3b1SShalom Toledo }
1207eceed3b1SShalom Toledo
mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port * mlxsw_sp_port)1208eceed3b1SShalom Toledo static int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port)
1209eceed3b1SShalom Toledo {
1210eceed3b1SShalom Toledo bool ptps = false;
1211eceed3b1SShalom Toledo int err, i;
1212ac9cc4e2SJiri Pirko u32 speed;
1213eceed3b1SShalom Toledo
1214eceed3b1SShalom Toledo if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port))
1215eceed3b1SShalom Toledo return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, false);
1216eceed3b1SShalom Toledo
1217ac9cc4e2SJiri Pirko err = mlxsw_sp_port_speed_get(mlxsw_sp_port, &speed);
1218eceed3b1SShalom Toledo if (err)
1219eceed3b1SShalom Toledo return err;
1220eceed3b1SShalom Toledo
1221eceed3b1SShalom Toledo for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) {
1222eceed3b1SShalom Toledo if (mlxsw_sp1_ptp_shaper_params[i].ethtool_speed == speed) {
1223eceed3b1SShalom Toledo ptps = true;
1224eceed3b1SShalom Toledo break;
1225eceed3b1SShalom Toledo }
1226eceed3b1SShalom Toledo }
1227eceed3b1SShalom Toledo
1228eceed3b1SShalom Toledo return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps);
1229eceed3b1SShalom Toledo }
1230eceed3b1SShalom Toledo
mlxsw_sp1_ptp_shaper_work(struct work_struct * work)12315fc17338SShalom Toledo void mlxsw_sp1_ptp_shaper_work(struct work_struct *work)
12325fc17338SShalom Toledo {
12335fc17338SShalom Toledo struct delayed_work *dwork = to_delayed_work(work);
12345fc17338SShalom Toledo struct mlxsw_sp_port *mlxsw_sp_port;
12355fc17338SShalom Toledo int err;
12365fc17338SShalom Toledo
12375fc17338SShalom Toledo mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port,
12385fc17338SShalom Toledo ptp.shaper_dw);
12395fc17338SShalom Toledo
12405fc17338SShalom Toledo if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port))
12415fc17338SShalom Toledo return;
12425fc17338SShalom Toledo
12435fc17338SShalom Toledo err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port);
12445fc17338SShalom Toledo if (err)
12455fc17338SShalom Toledo netdev_err(mlxsw_sp_port->dev, "Failed to set up PTP shaper\n");
12465fc17338SShalom Toledo }
12475fc17338SShalom Toledo
mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port * mlxsw_sp_port,struct hwtstamp_config * config)124887486427SPetr Machata int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
124987486427SPetr Machata struct hwtstamp_config *config)
125087486427SPetr Machata {
125187486427SPetr Machata enum hwtstamp_rx_filters rx_filter;
125287486427SPetr Machata u16 ing_types;
125387486427SPetr Machata u16 egr_types;
125487486427SPetr Machata int err;
125587486427SPetr Machata
12564017d929SAmit Cohen err = mlxsw_sp1_ptp_get_message_types(config, &ing_types, &egr_types,
125787486427SPetr Machata &rx_filter);
125887486427SPetr Machata if (err)
125987486427SPetr Machata return err;
126087486427SPetr Machata
126187486427SPetr Machata err = mlxsw_sp1_ptp_mtpppc_update(mlxsw_sp_port, ing_types, egr_types);
126287486427SPetr Machata if (err)
126387486427SPetr Machata return err;
126487486427SPetr Machata
126587486427SPetr Machata mlxsw_sp_port->ptp.hwtstamp_config = *config;
126687486427SPetr Machata mlxsw_sp_port->ptp.ing_types = ing_types;
126787486427SPetr Machata mlxsw_sp_port->ptp.egr_types = egr_types;
126887486427SPetr Machata
1269eceed3b1SShalom Toledo err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port);
1270eceed3b1SShalom Toledo if (err)
1271eceed3b1SShalom Toledo return err;
1272eceed3b1SShalom Toledo
127387486427SPetr Machata /* Notify the ioctl caller what we are actually timestamping. */
127487486427SPetr Machata config->rx_filter = rx_filter;
127587486427SPetr Machata
127687486427SPetr Machata return 0;
127787486427SPetr Machata }
127887ee07f8SPetr Machata
mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp * mlxsw_sp,struct ethtool_ts_info * info)127987ee07f8SPetr Machata int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
128087ee07f8SPetr Machata struct ethtool_ts_info *info)
128187ee07f8SPetr Machata {
128287ee07f8SPetr Machata info->phc_index = ptp_clock_index(mlxsw_sp->clock->ptp);
128387ee07f8SPetr Machata
128487ee07f8SPetr Machata info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
128587ee07f8SPetr Machata SOF_TIMESTAMPING_RX_HARDWARE |
128687ee07f8SPetr Machata SOF_TIMESTAMPING_RAW_HARDWARE;
128787ee07f8SPetr Machata
128887ee07f8SPetr Machata info->tx_types = BIT(HWTSTAMP_TX_OFF) |
128987ee07f8SPetr Machata BIT(HWTSTAMP_TX_ON);
129087ee07f8SPetr Machata
129187ee07f8SPetr Machata info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
129287ee07f8SPetr Machata BIT(HWTSTAMP_FILTER_ALL);
129387ee07f8SPetr Machata
129487ee07f8SPetr Machata return 0;
129587ee07f8SPetr Machata }
1296dc4f3eb0SPetr Machata
1297dc4f3eb0SPetr Machata struct mlxsw_sp_ptp_port_stat {
1298dc4f3eb0SPetr Machata char str[ETH_GSTRING_LEN];
1299dc4f3eb0SPetr Machata ptrdiff_t offset;
1300dc4f3eb0SPetr Machata };
1301dc4f3eb0SPetr Machata
1302dc4f3eb0SPetr Machata #define MLXSW_SP_PTP_PORT_STAT(NAME, FIELD) \
1303dc4f3eb0SPetr Machata { \
1304dc4f3eb0SPetr Machata .str = NAME, \
1305dc4f3eb0SPetr Machata .offset = offsetof(struct mlxsw_sp_ptp_port_stats, \
1306dc4f3eb0SPetr Machata FIELD), \
1307dc4f3eb0SPetr Machata }
1308dc4f3eb0SPetr Machata
1309dc4f3eb0SPetr Machata static const struct mlxsw_sp_ptp_port_stat mlxsw_sp_ptp_port_stats[] = {
1310dc4f3eb0SPetr Machata MLXSW_SP_PTP_PORT_STAT("ptp_rx_gcd_packets", rx_gcd.packets),
1311dc4f3eb0SPetr Machata MLXSW_SP_PTP_PORT_STAT("ptp_rx_gcd_timestamps", rx_gcd.timestamps),
1312dc4f3eb0SPetr Machata MLXSW_SP_PTP_PORT_STAT("ptp_tx_gcd_packets", tx_gcd.packets),
1313dc4f3eb0SPetr Machata MLXSW_SP_PTP_PORT_STAT("ptp_tx_gcd_timestamps", tx_gcd.timestamps),
1314dc4f3eb0SPetr Machata };
1315dc4f3eb0SPetr Machata
1316dc4f3eb0SPetr Machata #undef MLXSW_SP_PTP_PORT_STAT
1317dc4f3eb0SPetr Machata
1318dc4f3eb0SPetr Machata #define MLXSW_SP_PTP_PORT_STATS_LEN \
1319dc4f3eb0SPetr Machata ARRAY_SIZE(mlxsw_sp_ptp_port_stats)
1320dc4f3eb0SPetr Machata
mlxsw_sp1_get_stats_count(void)1321dc4f3eb0SPetr Machata int mlxsw_sp1_get_stats_count(void)
1322dc4f3eb0SPetr Machata {
1323dc4f3eb0SPetr Machata return MLXSW_SP_PTP_PORT_STATS_LEN;
1324dc4f3eb0SPetr Machata }
1325dc4f3eb0SPetr Machata
mlxsw_sp1_get_stats_strings(u8 ** p)1326dc4f3eb0SPetr Machata void mlxsw_sp1_get_stats_strings(u8 **p)
1327dc4f3eb0SPetr Machata {
1328dc4f3eb0SPetr Machata int i;
1329dc4f3eb0SPetr Machata
1330dc4f3eb0SPetr Machata for (i = 0; i < MLXSW_SP_PTP_PORT_STATS_LEN; i++) {
1331dc4f3eb0SPetr Machata memcpy(*p, mlxsw_sp_ptp_port_stats[i].str,
1332dc4f3eb0SPetr Machata ETH_GSTRING_LEN);
1333dc4f3eb0SPetr Machata *p += ETH_GSTRING_LEN;
1334dc4f3eb0SPetr Machata }
1335dc4f3eb0SPetr Machata }
1336dc4f3eb0SPetr Machata
mlxsw_sp1_get_stats(struct mlxsw_sp_port * mlxsw_sp_port,u64 * data,int data_index)1337dc4f3eb0SPetr Machata void mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
1338dc4f3eb0SPetr Machata u64 *data, int data_index)
1339dc4f3eb0SPetr Machata {
1340dc4f3eb0SPetr Machata void *stats = &mlxsw_sp_port->ptp.stats;
1341dc4f3eb0SPetr Machata ptrdiff_t offset;
1342dc4f3eb0SPetr Machata int i;
1343dc4f3eb0SPetr Machata
1344dc4f3eb0SPetr Machata data += data_index;
1345dc4f3eb0SPetr Machata for (i = 0; i < MLXSW_SP_PTP_PORT_STATS_LEN; i++) {
1346dc4f3eb0SPetr Machata offset = mlxsw_sp_ptp_port_stats[i].offset;
1347dc4f3eb0SPetr Machata *data++ = *(u64 *)(stats + offset);
1348dc4f3eb0SPetr Machata }
1349dc4f3eb0SPetr Machata }
1350d25ff63aSDanielle Ratson
mlxsw_sp2_ptp_init(struct mlxsw_sp * mlxsw_sp)1351d25ff63aSDanielle Ratson struct mlxsw_sp_ptp_state *mlxsw_sp2_ptp_init(struct mlxsw_sp *mlxsw_sp)
1352d25ff63aSDanielle Ratson {
1353d25ff63aSDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state;
1354d25ff63aSDanielle Ratson int err;
1355d25ff63aSDanielle Ratson
1356d25ff63aSDanielle Ratson ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL);
1357d25ff63aSDanielle Ratson if (!ptp_state)
1358d25ff63aSDanielle Ratson return ERR_PTR(-ENOMEM);
1359d25ff63aSDanielle Ratson
1360d25ff63aSDanielle Ratson ptp_state->common.mlxsw_sp = mlxsw_sp;
1361d25ff63aSDanielle Ratson
1362d25ff63aSDanielle Ratson err = mlxsw_sp_ptp_traps_set(mlxsw_sp);
1363d25ff63aSDanielle Ratson if (err)
1364d25ff63aSDanielle Ratson goto err_ptp_traps_set;
1365d25ff63aSDanielle Ratson
136608ef8bc8SDanielle Ratson refcount_set(&ptp_state->ptp_port_enabled_ref, 0);
1367d72fdef2SAmit Cohen mutex_init(&ptp_state->lock);
1368d25ff63aSDanielle Ratson return &ptp_state->common;
1369d25ff63aSDanielle Ratson
1370d25ff63aSDanielle Ratson err_ptp_traps_set:
1371d25ff63aSDanielle Ratson kfree(ptp_state);
1372d25ff63aSDanielle Ratson return ERR_PTR(err);
1373d25ff63aSDanielle Ratson }
1374d25ff63aSDanielle Ratson
mlxsw_sp2_ptp_fini(struct mlxsw_sp_ptp_state * ptp_state_common)1375d25ff63aSDanielle Ratson void mlxsw_sp2_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state_common)
1376d25ff63aSDanielle Ratson {
1377d25ff63aSDanielle Ratson struct mlxsw_sp *mlxsw_sp = ptp_state_common->mlxsw_sp;
1378d25ff63aSDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state;
1379d25ff63aSDanielle Ratson
1380d25ff63aSDanielle Ratson ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp);
1381d25ff63aSDanielle Ratson
1382d72fdef2SAmit Cohen mutex_destroy(&ptp_state->lock);
1383d25ff63aSDanielle Ratson mlxsw_sp_ptp_traps_unset(mlxsw_sp);
1384d25ff63aSDanielle Ratson kfree(ptp_state);
1385d25ff63aSDanielle Ratson }
138624157bc6SDanielle Ratson
mlxsw_ptp_utc_time_stamp_sec_get(struct mlxsw_core * mlxsw_core,u8 cqe_ts_sec)1387382ad0d9SDanielle Ratson static u32 mlxsw_ptp_utc_time_stamp_sec_get(struct mlxsw_core *mlxsw_core,
1388382ad0d9SDanielle Ratson u8 cqe_ts_sec)
1389382ad0d9SDanielle Ratson {
1390382ad0d9SDanielle Ratson u32 utc_sec = mlxsw_core_read_utc_sec(mlxsw_core);
1391382ad0d9SDanielle Ratson
1392382ad0d9SDanielle Ratson if (cqe_ts_sec > (utc_sec & 0xff))
1393382ad0d9SDanielle Ratson /* Time stamp above the last bits of UTC (UTC & 0xff) means the
1394382ad0d9SDanielle Ratson * latter has wrapped after the time stamp was collected.
1395382ad0d9SDanielle Ratson */
1396382ad0d9SDanielle Ratson utc_sec -= 256;
1397382ad0d9SDanielle Ratson
1398382ad0d9SDanielle Ratson utc_sec &= ~0xff;
1399382ad0d9SDanielle Ratson utc_sec |= cqe_ts_sec;
1400382ad0d9SDanielle Ratson
1401382ad0d9SDanielle Ratson return utc_sec;
1402382ad0d9SDanielle Ratson }
1403382ad0d9SDanielle Ratson
mlxsw_sp2_ptp_hwtstamp_fill(struct mlxsw_core * mlxsw_core,const struct mlxsw_skb_cb * cb,struct skb_shared_hwtstamps * hwtstamps)1404382ad0d9SDanielle Ratson static void mlxsw_sp2_ptp_hwtstamp_fill(struct mlxsw_core *mlxsw_core,
1405382ad0d9SDanielle Ratson const struct mlxsw_skb_cb *cb,
1406382ad0d9SDanielle Ratson struct skb_shared_hwtstamps *hwtstamps)
1407382ad0d9SDanielle Ratson {
1408382ad0d9SDanielle Ratson u64 ts_sec, ts_nsec, nsec;
1409382ad0d9SDanielle Ratson
1410382ad0d9SDanielle Ratson WARN_ON_ONCE(!cb->cqe_ts.sec && !cb->cqe_ts.nsec);
1411382ad0d9SDanielle Ratson
1412382ad0d9SDanielle Ratson /* The time stamp in the CQE is represented by 38 bits, which is a short
1413382ad0d9SDanielle Ratson * representation of UTC time. Software should create the full time
1414382ad0d9SDanielle Ratson * stamp using the global UTC clock. The seconds have only 8 bits in the
1415382ad0d9SDanielle Ratson * CQE, to create the full time stamp, use the current UTC time and fix
1416382ad0d9SDanielle Ratson * the seconds according to the relation between UTC seconds and CQE
1417382ad0d9SDanielle Ratson * seconds.
1418382ad0d9SDanielle Ratson */
1419382ad0d9SDanielle Ratson ts_sec = mlxsw_ptp_utc_time_stamp_sec_get(mlxsw_core, cb->cqe_ts.sec);
1420382ad0d9SDanielle Ratson ts_nsec = cb->cqe_ts.nsec;
1421382ad0d9SDanielle Ratson
1422382ad0d9SDanielle Ratson nsec = ts_sec * NSEC_PER_SEC + ts_nsec;
1423382ad0d9SDanielle Ratson
1424382ad0d9SDanielle Ratson hwtstamps->hwtstamp = ns_to_ktime(nsec);
1425382ad0d9SDanielle Ratson }
1426382ad0d9SDanielle Ratson
mlxsw_sp2_ptp_receive(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u16 local_port)1427382ad0d9SDanielle Ratson void mlxsw_sp2_ptp_receive(struct mlxsw_sp *mlxsw_sp, struct sk_buff *skb,
1428382ad0d9SDanielle Ratson u16 local_port)
1429382ad0d9SDanielle Ratson {
1430382ad0d9SDanielle Ratson struct skb_shared_hwtstamps hwtstamps;
1431382ad0d9SDanielle Ratson
1432382ad0d9SDanielle Ratson mlxsw_sp2_ptp_hwtstamp_fill(mlxsw_sp->core, mlxsw_skb_cb(skb),
1433382ad0d9SDanielle Ratson &hwtstamps);
1434382ad0d9SDanielle Ratson *skb_hwtstamps(skb) = hwtstamps;
1435382ad0d9SDanielle Ratson mlxsw_sp_rx_listener_no_mark_func(skb, local_port, mlxsw_sp);
1436382ad0d9SDanielle Ratson }
1437382ad0d9SDanielle Ratson
mlxsw_sp2_ptp_transmitted(struct mlxsw_sp * mlxsw_sp,struct sk_buff * skb,u16 local_port)1438382ad0d9SDanielle Ratson void mlxsw_sp2_ptp_transmitted(struct mlxsw_sp *mlxsw_sp,
1439382ad0d9SDanielle Ratson struct sk_buff *skb, u16 local_port)
1440382ad0d9SDanielle Ratson {
1441382ad0d9SDanielle Ratson struct skb_shared_hwtstamps hwtstamps;
1442382ad0d9SDanielle Ratson
1443382ad0d9SDanielle Ratson mlxsw_sp2_ptp_hwtstamp_fill(mlxsw_sp->core, mlxsw_skb_cb(skb),
1444382ad0d9SDanielle Ratson &hwtstamps);
1445382ad0d9SDanielle Ratson skb_tstamp_tx(skb, &hwtstamps);
1446382ad0d9SDanielle Ratson dev_kfree_skb_any(skb);
1447382ad0d9SDanielle Ratson }
1448382ad0d9SDanielle Ratson
mlxsw_sp2_ptp_hwtstamp_get(struct mlxsw_sp_port * mlxsw_sp_port,struct hwtstamp_config * config)144908ef8bc8SDanielle Ratson int mlxsw_sp2_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port,
145008ef8bc8SDanielle Ratson struct hwtstamp_config *config)
145108ef8bc8SDanielle Ratson {
145208ef8bc8SDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state;
145308ef8bc8SDanielle Ratson
145408ef8bc8SDanielle Ratson ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp);
145508ef8bc8SDanielle Ratson
1456d72fdef2SAmit Cohen mutex_lock(&ptp_state->lock);
145708ef8bc8SDanielle Ratson *config = ptp_state->config;
1458d72fdef2SAmit Cohen mutex_unlock(&ptp_state->lock);
1459d72fdef2SAmit Cohen
146008ef8bc8SDanielle Ratson return 0;
146108ef8bc8SDanielle Ratson }
146208ef8bc8SDanielle Ratson
146308ef8bc8SDanielle Ratson static int
mlxsw_sp2_ptp_get_message_types(const struct hwtstamp_config * config,u16 * p_ing_types,u16 * p_egr_types,enum hwtstamp_rx_filters * p_rx_filter)146408ef8bc8SDanielle Ratson mlxsw_sp2_ptp_get_message_types(const struct hwtstamp_config *config,
146508ef8bc8SDanielle Ratson u16 *p_ing_types, u16 *p_egr_types,
146608ef8bc8SDanielle Ratson enum hwtstamp_rx_filters *p_rx_filter)
146708ef8bc8SDanielle Ratson {
146808ef8bc8SDanielle Ratson enum hwtstamp_rx_filters rx_filter = config->rx_filter;
146908ef8bc8SDanielle Ratson enum hwtstamp_tx_types tx_type = config->tx_type;
147008ef8bc8SDanielle Ratson u16 ing_types = 0x00;
147108ef8bc8SDanielle Ratson u16 egr_types = 0x00;
147208ef8bc8SDanielle Ratson
147308ef8bc8SDanielle Ratson *p_rx_filter = rx_filter;
147408ef8bc8SDanielle Ratson
147508ef8bc8SDanielle Ratson switch (rx_filter) {
147608ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_NONE:
147708ef8bc8SDanielle Ratson ing_types = 0x00;
147808ef8bc8SDanielle Ratson break;
147908ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
148008ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
148108ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
148208ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_SYNC:
148308ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
148408ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
148508ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
148608ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
148708ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
148808ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
148908ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
149008ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_PTP_V2_EVENT:
149108ef8bc8SDanielle Ratson /* In Spectrum-2 and above, all packets get time stamp by
149208ef8bc8SDanielle Ratson * default and the driver fill the time stamp only for event
149308ef8bc8SDanielle Ratson * packets. Return all event types even if only specific types
149408ef8bc8SDanielle Ratson * were required.
149508ef8bc8SDanielle Ratson */
149608ef8bc8SDanielle Ratson ing_types = 0x0f;
149708ef8bc8SDanielle Ratson *p_rx_filter = HWTSTAMP_FILTER_SOME;
149808ef8bc8SDanielle Ratson break;
149908ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_ALL:
150008ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_SOME:
150108ef8bc8SDanielle Ratson case HWTSTAMP_FILTER_NTP_ALL:
150208ef8bc8SDanielle Ratson return -ERANGE;
150308ef8bc8SDanielle Ratson default:
150408ef8bc8SDanielle Ratson return -EINVAL;
150508ef8bc8SDanielle Ratson }
150608ef8bc8SDanielle Ratson
150708ef8bc8SDanielle Ratson switch (tx_type) {
150808ef8bc8SDanielle Ratson case HWTSTAMP_TX_OFF:
150908ef8bc8SDanielle Ratson egr_types = 0x00;
151008ef8bc8SDanielle Ratson break;
151108ef8bc8SDanielle Ratson case HWTSTAMP_TX_ON:
151208ef8bc8SDanielle Ratson egr_types = 0x0f;
151308ef8bc8SDanielle Ratson break;
151408ef8bc8SDanielle Ratson case HWTSTAMP_TX_ONESTEP_SYNC:
151508ef8bc8SDanielle Ratson case HWTSTAMP_TX_ONESTEP_P2P:
151608ef8bc8SDanielle Ratson return -ERANGE;
151708ef8bc8SDanielle Ratson default:
151808ef8bc8SDanielle Ratson return -EINVAL;
151908ef8bc8SDanielle Ratson }
152008ef8bc8SDanielle Ratson
1521e01885c3SAmit Cohen if ((ing_types && !egr_types) || (!ing_types && egr_types))
1522e01885c3SAmit Cohen return -EINVAL;
1523e01885c3SAmit Cohen
152408ef8bc8SDanielle Ratson *p_ing_types = ing_types;
152508ef8bc8SDanielle Ratson *p_egr_types = egr_types;
152608ef8bc8SDanielle Ratson return 0;
152708ef8bc8SDanielle Ratson }
152808ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_mtpcpc_set(struct mlxsw_sp * mlxsw_sp,bool ptp_trap_en,u16 ing_types,u16 egr_types)152908ef8bc8SDanielle Ratson static int mlxsw_sp2_ptp_mtpcpc_set(struct mlxsw_sp *mlxsw_sp, bool ptp_trap_en,
153008ef8bc8SDanielle Ratson u16 ing_types, u16 egr_types)
153108ef8bc8SDanielle Ratson {
153208ef8bc8SDanielle Ratson char mtpcpc_pl[MLXSW_REG_MTPCPC_LEN];
153308ef8bc8SDanielle Ratson
153408ef8bc8SDanielle Ratson mlxsw_reg_mtpcpc_pack(mtpcpc_pl, false, 0, ptp_trap_en, ing_types,
153508ef8bc8SDanielle Ratson egr_types);
153608ef8bc8SDanielle Ratson return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpcpc), mtpcpc_pl);
153708ef8bc8SDanielle Ratson }
153808ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_enable(struct mlxsw_sp * mlxsw_sp,u16 ing_types,u16 egr_types,struct hwtstamp_config new_config)153908ef8bc8SDanielle Ratson static int mlxsw_sp2_ptp_enable(struct mlxsw_sp *mlxsw_sp, u16 ing_types,
154008ef8bc8SDanielle Ratson u16 egr_types,
154108ef8bc8SDanielle Ratson struct hwtstamp_config new_config)
154208ef8bc8SDanielle Ratson {
154308ef8bc8SDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp);
154408ef8bc8SDanielle Ratson int err;
154508ef8bc8SDanielle Ratson
154608ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_mtpcpc_set(mlxsw_sp, true, ing_types, egr_types);
154708ef8bc8SDanielle Ratson if (err)
154808ef8bc8SDanielle Ratson return err;
154908ef8bc8SDanielle Ratson
155008ef8bc8SDanielle Ratson ptp_state->config = new_config;
155108ef8bc8SDanielle Ratson return 0;
155208ef8bc8SDanielle Ratson }
155308ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_disable(struct mlxsw_sp * mlxsw_sp,struct hwtstamp_config new_config)155408ef8bc8SDanielle Ratson static int mlxsw_sp2_ptp_disable(struct mlxsw_sp *mlxsw_sp,
155508ef8bc8SDanielle Ratson struct hwtstamp_config new_config)
155608ef8bc8SDanielle Ratson {
155708ef8bc8SDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp);
155808ef8bc8SDanielle Ratson int err;
155908ef8bc8SDanielle Ratson
156008ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_mtpcpc_set(mlxsw_sp, false, 0, 0);
156108ef8bc8SDanielle Ratson if (err)
156208ef8bc8SDanielle Ratson return err;
156308ef8bc8SDanielle Ratson
156408ef8bc8SDanielle Ratson ptp_state->config = new_config;
156508ef8bc8SDanielle Ratson return 0;
156608ef8bc8SDanielle Ratson }
156708ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_configure_port(struct mlxsw_sp_port * mlxsw_sp_port,u16 ing_types,u16 egr_types,struct hwtstamp_config new_config)156808ef8bc8SDanielle Ratson static int mlxsw_sp2_ptp_configure_port(struct mlxsw_sp_port *mlxsw_sp_port,
156908ef8bc8SDanielle Ratson u16 ing_types, u16 egr_types,
157008ef8bc8SDanielle Ratson struct hwtstamp_config new_config)
157108ef8bc8SDanielle Ratson {
157208ef8bc8SDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state;
157308ef8bc8SDanielle Ratson int err;
157408ef8bc8SDanielle Ratson
157508ef8bc8SDanielle Ratson ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp);
157608ef8bc8SDanielle Ratson
157708ef8bc8SDanielle Ratson if (refcount_inc_not_zero(&ptp_state->ptp_port_enabled_ref))
157808ef8bc8SDanielle Ratson return 0;
157908ef8bc8SDanielle Ratson
158008ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_enable(mlxsw_sp_port->mlxsw_sp, ing_types,
158108ef8bc8SDanielle Ratson egr_types, new_config);
158208ef8bc8SDanielle Ratson if (err)
158308ef8bc8SDanielle Ratson return err;
158408ef8bc8SDanielle Ratson
158508ef8bc8SDanielle Ratson refcount_set(&ptp_state->ptp_port_enabled_ref, 1);
158608ef8bc8SDanielle Ratson
158708ef8bc8SDanielle Ratson return 0;
158808ef8bc8SDanielle Ratson }
158908ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_deconfigure_port(struct mlxsw_sp_port * mlxsw_sp_port,struct hwtstamp_config new_config)159008ef8bc8SDanielle Ratson static int mlxsw_sp2_ptp_deconfigure_port(struct mlxsw_sp_port *mlxsw_sp_port,
159108ef8bc8SDanielle Ratson struct hwtstamp_config new_config)
159208ef8bc8SDanielle Ratson {
159308ef8bc8SDanielle Ratson struct mlxsw_sp2_ptp_state *ptp_state;
159408ef8bc8SDanielle Ratson int err;
159508ef8bc8SDanielle Ratson
159608ef8bc8SDanielle Ratson ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp);
159708ef8bc8SDanielle Ratson
159808ef8bc8SDanielle Ratson if (!refcount_dec_and_test(&ptp_state->ptp_port_enabled_ref))
159908ef8bc8SDanielle Ratson return 0;
160008ef8bc8SDanielle Ratson
160108ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_disable(mlxsw_sp_port->mlxsw_sp, new_config);
160208ef8bc8SDanielle Ratson if (err)
160308ef8bc8SDanielle Ratson goto err_ptp_disable;
160408ef8bc8SDanielle Ratson
160508ef8bc8SDanielle Ratson return 0;
160608ef8bc8SDanielle Ratson
160708ef8bc8SDanielle Ratson err_ptp_disable:
160808ef8bc8SDanielle Ratson refcount_set(&ptp_state->ptp_port_enabled_ref, 1);
160908ef8bc8SDanielle Ratson return err;
161008ef8bc8SDanielle Ratson }
161108ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port * mlxsw_sp_port,struct hwtstamp_config * config)161208ef8bc8SDanielle Ratson int mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
161308ef8bc8SDanielle Ratson struct hwtstamp_config *config)
161408ef8bc8SDanielle Ratson {
1615d72fdef2SAmit Cohen struct mlxsw_sp2_ptp_state *ptp_state;
161608ef8bc8SDanielle Ratson enum hwtstamp_rx_filters rx_filter;
161708ef8bc8SDanielle Ratson struct hwtstamp_config new_config;
161808ef8bc8SDanielle Ratson u16 new_ing_types, new_egr_types;
161908ef8bc8SDanielle Ratson bool ptp_enabled;
162008ef8bc8SDanielle Ratson int err;
162108ef8bc8SDanielle Ratson
1622d72fdef2SAmit Cohen ptp_state = mlxsw_sp2_ptp_state(mlxsw_sp_port->mlxsw_sp);
1623d72fdef2SAmit Cohen mutex_lock(&ptp_state->lock);
1624d72fdef2SAmit Cohen
162508ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_get_message_types(config, &new_ing_types,
162608ef8bc8SDanielle Ratson &new_egr_types, &rx_filter);
162708ef8bc8SDanielle Ratson if (err)
1628d72fdef2SAmit Cohen goto err_get_message_types;
162908ef8bc8SDanielle Ratson
163008ef8bc8SDanielle Ratson new_config.flags = config->flags;
163108ef8bc8SDanielle Ratson new_config.tx_type = config->tx_type;
163208ef8bc8SDanielle Ratson new_config.rx_filter = rx_filter;
163308ef8bc8SDanielle Ratson
163408ef8bc8SDanielle Ratson ptp_enabled = mlxsw_sp_port->ptp.ing_types ||
163508ef8bc8SDanielle Ratson mlxsw_sp_port->ptp.egr_types;
163608ef8bc8SDanielle Ratson
163708ef8bc8SDanielle Ratson if ((new_ing_types || new_egr_types) && !ptp_enabled) {
163808ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_configure_port(mlxsw_sp_port, new_ing_types,
163908ef8bc8SDanielle Ratson new_egr_types, new_config);
164008ef8bc8SDanielle Ratson if (err)
1641d72fdef2SAmit Cohen goto err_configure_port;
164208ef8bc8SDanielle Ratson } else if (!new_ing_types && !new_egr_types && ptp_enabled) {
164308ef8bc8SDanielle Ratson err = mlxsw_sp2_ptp_deconfigure_port(mlxsw_sp_port, new_config);
164408ef8bc8SDanielle Ratson if (err)
1645d72fdef2SAmit Cohen goto err_deconfigure_port;
164608ef8bc8SDanielle Ratson }
164708ef8bc8SDanielle Ratson
164808ef8bc8SDanielle Ratson mlxsw_sp_port->ptp.ing_types = new_ing_types;
164908ef8bc8SDanielle Ratson mlxsw_sp_port->ptp.egr_types = new_egr_types;
165008ef8bc8SDanielle Ratson
165108ef8bc8SDanielle Ratson /* Notify the ioctl caller what we are actually timestamping. */
165208ef8bc8SDanielle Ratson config->rx_filter = rx_filter;
1653d72fdef2SAmit Cohen mutex_unlock(&ptp_state->lock);
165408ef8bc8SDanielle Ratson
165508ef8bc8SDanielle Ratson return 0;
1656d72fdef2SAmit Cohen
1657d72fdef2SAmit Cohen err_deconfigure_port:
1658d72fdef2SAmit Cohen err_configure_port:
1659d72fdef2SAmit Cohen err_get_message_types:
1660d72fdef2SAmit Cohen mutex_unlock(&ptp_state->lock);
1661d72fdef2SAmit Cohen return err;
166208ef8bc8SDanielle Ratson }
166308ef8bc8SDanielle Ratson
mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp * mlxsw_sp,struct ethtool_ts_info * info)1664eba28aafSDanielle Ratson int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
1665eba28aafSDanielle Ratson struct ethtool_ts_info *info)
1666eba28aafSDanielle Ratson {
1667eba28aafSDanielle Ratson info->phc_index = ptp_clock_index(mlxsw_sp->clock->ptp);
1668eba28aafSDanielle Ratson
1669eba28aafSDanielle Ratson info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
1670eba28aafSDanielle Ratson SOF_TIMESTAMPING_RX_HARDWARE |
1671eba28aafSDanielle Ratson SOF_TIMESTAMPING_RAW_HARDWARE;
1672eba28aafSDanielle Ratson
1673eba28aafSDanielle Ratson info->tx_types = BIT(HWTSTAMP_TX_OFF) |
1674eba28aafSDanielle Ratson BIT(HWTSTAMP_TX_ON);
1675eba28aafSDanielle Ratson
1676eba28aafSDanielle Ratson info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
1677eba28aafSDanielle Ratson BIT(HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
1678eba28aafSDanielle Ratson BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
1679eba28aafSDanielle Ratson
1680eba28aafSDanielle Ratson return 0;
1681eba28aafSDanielle Ratson }
1682eba28aafSDanielle Ratson
mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core * mlxsw_core,struct mlxsw_sp_port * mlxsw_sp_port,struct sk_buff * skb,const struct mlxsw_tx_info * tx_info)168324157bc6SDanielle Ratson int mlxsw_sp_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
168424157bc6SDanielle Ratson struct mlxsw_sp_port *mlxsw_sp_port,
168524157bc6SDanielle Ratson struct sk_buff *skb,
168624157bc6SDanielle Ratson const struct mlxsw_tx_info *tx_info)
168724157bc6SDanielle Ratson {
1688*598f9574SAmit Cohen if (skb_cow_head(skb, MLXSW_TXHDR_LEN)) {
1689*598f9574SAmit Cohen this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
1690*598f9574SAmit Cohen dev_kfree_skb_any(skb);
1691*598f9574SAmit Cohen return -ENOMEM;
1692*598f9574SAmit Cohen }
1693*598f9574SAmit Cohen
169424157bc6SDanielle Ratson mlxsw_sp_txhdr_construct(skb, tx_info);
169524157bc6SDanielle Ratson return 0;
169624157bc6SDanielle Ratson }
169724157bc6SDanielle Ratson
mlxsw_sp2_ptp_txhdr_construct(struct mlxsw_core * mlxsw_core,struct mlxsw_sp_port * mlxsw_sp_port,struct sk_buff * skb,const struct mlxsw_tx_info * tx_info)169824157bc6SDanielle Ratson int mlxsw_sp2_ptp_txhdr_construct(struct mlxsw_core *mlxsw_core,
169924157bc6SDanielle Ratson struct mlxsw_sp_port *mlxsw_sp_port,
170024157bc6SDanielle Ratson struct sk_buff *skb,
170124157bc6SDanielle Ratson const struct mlxsw_tx_info *tx_info)
170224157bc6SDanielle Ratson {
170324157bc6SDanielle Ratson /* In Spectrum-2 and Spectrum-3, in order for PTP event packets to have
170424157bc6SDanielle Ratson * their correction field correctly set on the egress port they must be
170524157bc6SDanielle Ratson * transmitted as data packets. Such packets ingress the ASIC via the
170624157bc6SDanielle Ratson * CPU port and must have a VLAN tag, as the CPU port is not configured
170724157bc6SDanielle Ratson * with a PVID. Push the default VLAN (4095), which is configured as
170824157bc6SDanielle Ratson * egress untagged on all the ports.
170924157bc6SDanielle Ratson */
171024157bc6SDanielle Ratson if (!skb_vlan_tagged(skb)) {
171124157bc6SDanielle Ratson skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q),
171224157bc6SDanielle Ratson MLXSW_SP_DEFAULT_VID);
171324157bc6SDanielle Ratson if (!skb) {
171424157bc6SDanielle Ratson this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped);
171524157bc6SDanielle Ratson return -ENOMEM;
171624157bc6SDanielle Ratson }
171724157bc6SDanielle Ratson }
171824157bc6SDanielle Ratson
171924157bc6SDanielle Ratson return mlxsw_sp_txhdr_ptp_data_construct(mlxsw_core, mlxsw_sp_port, skb,
172024157bc6SDanielle Ratson tx_info);
172124157bc6SDanielle Ratson }
1722