88
88
#define FEC_CHANNLE_0 0
89
89
#define DEFAULT_PPS_CHANNEL FEC_CHANNLE_0
90
90
91
+ #define FEC_PTP_MAX_NSEC_PERIOD 4000000000ULL
92
+ #define FEC_PTP_MAX_NSEC_COUNTER 0x80000000ULL
93
+
91
94
/**
92
95
* fec_ptp_enable_pps
93
96
* @fep: the fec_enet_private structure handle
@@ -198,6 +201,78 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable)
198
201
return 0 ;
199
202
}
200
203
204
+ static int fec_ptp_pps_perout (struct fec_enet_private * fep )
205
+ {
206
+ u32 compare_val , ptp_hc , temp_val ;
207
+ u64 curr_time ;
208
+ unsigned long flags ;
209
+
210
+ spin_lock_irqsave (& fep -> tmreg_lock , flags );
211
+
212
+ /* Update time counter */
213
+ timecounter_read (& fep -> tc );
214
+
215
+ /* Get the current ptp hardware time counter */
216
+ temp_val = readl (fep -> hwp + FEC_ATIME_CTRL );
217
+ temp_val |= FEC_T_CTRL_CAPTURE ;
218
+ writel (temp_val , fep -> hwp + FEC_ATIME_CTRL );
219
+ if (fep -> quirks & FEC_QUIRK_BUG_CAPTURE )
220
+ udelay (1 );
221
+
222
+ ptp_hc = readl (fep -> hwp + FEC_ATIME );
223
+
224
+ /* Convert the ptp local counter to 1588 timestamp */
225
+ curr_time = timecounter_cyc2time (& fep -> tc , ptp_hc );
226
+
227
+ /* If the pps start time less than current time add 100ms, just return.
228
+ * Because the software might not able to set the comparison time into
229
+ * the FEC_TCCR register in time and missed the start time.
230
+ */
231
+ if (fep -> perout_stime < curr_time + 100 * NSEC_PER_MSEC ) {
232
+ dev_err (& fep -> pdev -> dev , "Current time is too close to the start time!\n" );
233
+ spin_unlock_irqrestore (& fep -> tmreg_lock , flags );
234
+ return -1 ;
235
+ }
236
+
237
+ compare_val = fep -> perout_stime - curr_time + ptp_hc ;
238
+ compare_val &= fep -> cc .mask ;
239
+
240
+ writel (compare_val , fep -> hwp + FEC_TCCR (fep -> pps_channel ));
241
+ fep -> next_counter = (compare_val + fep -> reload_period ) & fep -> cc .mask ;
242
+
243
+ /* Enable compare event when overflow */
244
+ temp_val = readl (fep -> hwp + FEC_ATIME_CTRL );
245
+ temp_val |= FEC_T_CTRL_PINPER ;
246
+ writel (temp_val , fep -> hwp + FEC_ATIME_CTRL );
247
+
248
+ /* Compare channel setting. */
249
+ temp_val = readl (fep -> hwp + FEC_TCSR (fep -> pps_channel ));
250
+ temp_val |= (1 << FEC_T_TF_OFFSET | 1 << FEC_T_TIE_OFFSET );
251
+ temp_val &= ~(1 << FEC_T_TDRE_OFFSET );
252
+ temp_val &= ~(FEC_T_TMODE_MASK );
253
+ temp_val |= (FEC_TMODE_TOGGLE << FEC_T_TMODE_OFFSET );
254
+ writel (temp_val , fep -> hwp + FEC_TCSR (fep -> pps_channel ));
255
+
256
+ /* Write the second compare event timestamp and calculate
257
+ * the third timestamp. Refer the TCCR register detail in the spec.
258
+ */
259
+ writel (fep -> next_counter , fep -> hwp + FEC_TCCR (fep -> pps_channel ));
260
+ fep -> next_counter = (fep -> next_counter + fep -> reload_period ) & fep -> cc .mask ;
261
+ spin_unlock_irqrestore (& fep -> tmreg_lock , flags );
262
+
263
+ return 0 ;
264
+ }
265
+
266
+ static enum hrtimer_restart fec_ptp_pps_perout_handler (struct hrtimer * timer )
267
+ {
268
+ struct fec_enet_private * fep = container_of (timer ,
269
+ struct fec_enet_private , perout_timer );
270
+
271
+ fec_ptp_pps_perout (fep );
272
+
273
+ return HRTIMER_NORESTART ;
274
+ }
275
+
201
276
/**
202
277
* fec_ptp_read - read raw cycle counter (to be used by time counter)
203
278
* @cc: the cyclecounter structure
@@ -425,6 +500,17 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
425
500
return 0 ;
426
501
}
427
502
503
+ static int fec_ptp_pps_disable (struct fec_enet_private * fep , uint channel )
504
+ {
505
+ unsigned long flags ;
506
+
507
+ spin_lock_irqsave (& fep -> tmreg_lock , flags );
508
+ writel (0 , fep -> hwp + FEC_TCSR (channel ));
509
+ spin_unlock_irqrestore (& fep -> tmreg_lock , flags );
510
+
511
+ return 0 ;
512
+ }
513
+
428
514
/**
429
515
* fec_ptp_enable
430
516
* @ptp: the ptp clock structure
@@ -437,14 +523,84 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
437
523
{
438
524
struct fec_enet_private * fep =
439
525
container_of (ptp , struct fec_enet_private , ptp_caps );
526
+ ktime_t timeout ;
527
+ struct timespec64 start_time , period ;
528
+ u64 curr_time , delta , period_ns ;
529
+ unsigned long flags ;
440
530
int ret = 0 ;
441
531
442
532
if (rq -> type == PTP_CLK_REQ_PPS ) {
443
533
ret = fec_ptp_enable_pps (fep , on );
444
534
445
535
return ret ;
536
+ } else if (rq -> type == PTP_CLK_REQ_PEROUT ) {
537
+ /* Reject requests with unsupported flags */
538
+ if (rq -> perout .flags )
539
+ return - EOPNOTSUPP ;
540
+
541
+ if (rq -> perout .index != DEFAULT_PPS_CHANNEL )
542
+ return - EOPNOTSUPP ;
543
+
544
+ fep -> pps_channel = DEFAULT_PPS_CHANNEL ;
545
+ period .tv_sec = rq -> perout .period .sec ;
546
+ period .tv_nsec = rq -> perout .period .nsec ;
547
+ period_ns = timespec64_to_ns (& period );
548
+
549
+ /* FEC PTP timer only has 31 bits, so if the period exceed
550
+ * 4s is not supported.
551
+ */
552
+ if (period_ns > FEC_PTP_MAX_NSEC_PERIOD ) {
553
+ dev_err (& fep -> pdev -> dev , "The period must equal to or less than 4s!\n" );
554
+ return - EOPNOTSUPP ;
555
+ }
556
+
557
+ fep -> reload_period = div_u64 (period_ns , 2 );
558
+ if (on && fep -> reload_period ) {
559
+ /* Convert 1588 timestamp to ns*/
560
+ start_time .tv_sec = rq -> perout .start .sec ;
561
+ start_time .tv_nsec = rq -> perout .start .nsec ;
562
+ fep -> perout_stime = timespec64_to_ns (& start_time );
563
+
564
+ mutex_lock (& fep -> ptp_clk_mutex );
565
+ if (!fep -> ptp_clk_on ) {
566
+ dev_err (& fep -> pdev -> dev , "Error: PTP clock is closed!\n" );
567
+ mutex_unlock (& fep -> ptp_clk_mutex );
568
+ return - EOPNOTSUPP ;
569
+ }
570
+ spin_lock_irqsave (& fep -> tmreg_lock , flags );
571
+ /* Read current timestamp */
572
+ curr_time = timecounter_read (& fep -> tc );
573
+ spin_unlock_irqrestore (& fep -> tmreg_lock , flags );
574
+ mutex_unlock (& fep -> ptp_clk_mutex );
575
+
576
+ /* Calculate time difference */
577
+ delta = fep -> perout_stime - curr_time ;
578
+
579
+ if (fep -> perout_stime <= curr_time ) {
580
+ dev_err (& fep -> pdev -> dev , "Start time must larger than current time!\n" );
581
+ return - EINVAL ;
582
+ }
583
+
584
+ /* Because the timer counter of FEC only has 31-bits, correspondingly,
585
+ * the time comparison register FEC_TCCR also only low 31 bits can be
586
+ * set. If the start time of pps signal exceeds current time more than
587
+ * 0x80000000 ns, a software timer is used and the timer expires about
588
+ * 1 second before the start time to be able to set FEC_TCCR.
589
+ */
590
+ if (delta > FEC_PTP_MAX_NSEC_COUNTER ) {
591
+ timeout = ns_to_ktime (delta - NSEC_PER_SEC );
592
+ hrtimer_start (& fep -> perout_timer , timeout , HRTIMER_MODE_REL );
593
+ } else {
594
+ return fec_ptp_pps_perout (fep );
595
+ }
596
+ } else {
597
+ fec_ptp_pps_disable (fep , fep -> pps_channel );
598
+ }
599
+
600
+ return 0 ;
601
+ } else {
602
+ return - EOPNOTSUPP ;
446
603
}
447
- return - EOPNOTSUPP ;
448
604
}
449
605
450
606
/**
@@ -583,7 +739,7 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx)
583
739
fep -> ptp_caps .max_adj = 250000000 ;
584
740
fep -> ptp_caps .n_alarm = 0 ;
585
741
fep -> ptp_caps .n_ext_ts = 0 ;
586
- fep -> ptp_caps .n_per_out = 0 ;
742
+ fep -> ptp_caps .n_per_out = 1 ;
587
743
fep -> ptp_caps .n_pins = 0 ;
588
744
fep -> ptp_caps .pps = 1 ;
589
745
fep -> ptp_caps .adjfreq = fec_ptp_adjfreq ;
@@ -605,6 +761,9 @@ void fec_ptp_init(struct platform_device *pdev, int irq_idx)
605
761
606
762
INIT_DELAYED_WORK (& fep -> time_keep , fec_time_keep );
607
763
764
+ hrtimer_init (& fep -> perout_timer , CLOCK_REALTIME , HRTIMER_MODE_REL );
765
+ fep -> perout_timer .function = fec_ptp_pps_perout_handler ;
766
+
608
767
irq = platform_get_irq_byname_optional (pdev , "pps" );
609
768
if (irq < 0 )
610
769
irq = platform_get_irq_optional (pdev , irq_idx );
@@ -634,6 +793,7 @@ void fec_ptp_stop(struct platform_device *pdev)
634
793
struct fec_enet_private * fep = netdev_priv (ndev );
635
794
636
795
cancel_delayed_work_sync (& fep -> time_keep );
796
+ hrtimer_cancel (& fep -> perout_timer );
637
797
if (fep -> ptp_clock )
638
798
ptp_clock_unregister (fep -> ptp_clock );
639
799
}
0 commit comments