xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c (revision 5f8b7d4b2e9604d03ae06f1a2dd5a1f34c33e533)
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