Skip to content

Commit 278d240

Browse files
Luwei Zhoudavem330
authored andcommitted
net: fec: ptp: Enable PPS output based on ptp clock
FEC ptp timer has 4 channel compare/trigger function. It can be used to enable pps output. The pulse would be ouput high exactly on N second. The pulse ouput high on compare event mode is used to produce pulse per second. The pulse width would be one cycle based on ptp timer clock source.Since 31-bit ptp hardware timer is used, the timer will wrap more than 2 seconds. We need to reload the compare compare event about every 1 second. Signed-off-by: Luwei Zhou <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 89bddcd commit 278d240

File tree

3 files changed

+205
-1
lines changed

3 files changed

+205
-1
lines changed

drivers/net/ethernet/freescale/fec.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,12 +487,19 @@ struct fec_enet_private {
487487

488488
/* ptp clock period in ns*/
489489
unsigned int ptp_inc;
490+
491+
/* pps */
492+
int pps_channel;
493+
unsigned int reload_period;
494+
int pps_enable;
495+
unsigned int next_counter;
490496
};
491497

492498
void fec_ptp_init(struct platform_device *pdev);
493499
void fec_ptp_start_cyclecounter(struct net_device *ndev);
494500
int fec_ptp_set(struct net_device *ndev, struct ifreq *ifr);
495501
int fec_ptp_get(struct net_device *ndev, struct ifreq *ifr);
502+
uint fec_ptp_check_pps_event(struct fec_enet_private *fep);
496503

497504
/****************************************************************************/
498505
#endif /* FEC_H */

drivers/net/ethernet/freescale/fec_main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,8 @@ fec_enet_interrupt(int irq, void *dev_id)
16221622
complete(&fep->mdio_done);
16231623
}
16241624

1625+
fec_ptp_check_pps_event(fep);
1626+
16251627
return ret;
16261628
}
16271629

drivers/net/ethernet/freescale/fec_ptp.c

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,24 @@
6161
#define FEC_T_INC_CORR_MASK 0x00007f00
6262
#define FEC_T_INC_CORR_OFFSET 8
6363

64+
#define FEC_T_CTRL_PINPER 0x00000080
65+
#define FEC_T_TF0_MASK 0x00000001
66+
#define FEC_T_TF0_OFFSET 0
67+
#define FEC_T_TF1_MASK 0x00000002
68+
#define FEC_T_TF1_OFFSET 1
69+
#define FEC_T_TF2_MASK 0x00000004
70+
#define FEC_T_TF2_OFFSET 2
71+
#define FEC_T_TF3_MASK 0x00000008
72+
#define FEC_T_TF3_OFFSET 3
73+
#define FEC_T_TDRE_MASK 0x00000001
74+
#define FEC_T_TDRE_OFFSET 0
75+
#define FEC_T_TMODE_MASK 0x0000003C
76+
#define FEC_T_TMODE_OFFSET 2
77+
#define FEC_T_TIE_MASK 0x00000040
78+
#define FEC_T_TIE_OFFSET 6
79+
#define FEC_T_TF_MASK 0x00000080
80+
#define FEC_T_TF_OFFSET 7
81+
6482
#define FEC_ATIME_CTRL 0x400
6583
#define FEC_ATIME 0x404
6684
#define FEC_ATIME_EVT_OFFSET 0x408
@@ -69,8 +87,143 @@
6987
#define FEC_ATIME_INC 0x414
7088
#define FEC_TS_TIMESTAMP 0x418
7189

90+
#define FEC_TGSR 0x604
91+
#define FEC_TCSR(n) (0x608 + n * 0x08)
92+
#define FEC_TCCR(n) (0x60C + n * 0x08)
93+
#define MAX_TIMER_CHANNEL 3
94+
#define FEC_TMODE_TOGGLE 0x05
95+
#define FEC_HIGH_PULSE 0x0F
96+
7297
#define FEC_CC_MULT (1 << 31)
7398
#define FEC_COUNTER_PERIOD (1 << 31)
99+
#define PPS_OUPUT_RELOAD_PERIOD NSEC_PER_SEC
100+
#define FEC_CHANNLE_0 0
101+
#define DEFAULT_PPS_CHANNEL FEC_CHANNLE_0
102+
103+
/**
104+
* fec_ptp_enable_pps
105+
* @fep: the fec_enet_private structure handle
106+
* @enable: enable the channel pps output
107+
*
108+
* This function enble the PPS ouput on the timer channel.
109+
*/
110+
static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable)
111+
{
112+
unsigned long flags;
113+
u32 val, tempval;
114+
int inc;
115+
struct timespec ts;
116+
u64 ns;
117+
u32 remainder;
118+
val = 0;
119+
120+
if (!(fep->hwts_tx_en || fep->hwts_rx_en)) {
121+
dev_err(&fep->pdev->dev, "No ptp stack is running\n");
122+
return -EINVAL;
123+
}
124+
125+
if (fep->pps_enable == enable)
126+
return 0;
127+
128+
fep->pps_channel = DEFAULT_PPS_CHANNEL;
129+
fep->reload_period = PPS_OUPUT_RELOAD_PERIOD;
130+
inc = fep->ptp_inc;
131+
132+
spin_lock_irqsave(&fep->tmreg_lock, flags);
133+
134+
if (enable) {
135+
/* clear capture or output compare interrupt status if have.
136+
*/
137+
writel(FEC_T_TF_MASK, fep->hwp + FEC_TCSR(fep->pps_channel));
138+
139+
/* It is recommended to doulbe check the TMODE field in the
140+
* TCSR register to be cleared before the first compare counter
141+
* is written into TCCR register. Just add a double check.
142+
*/
143+
val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
144+
do {
145+
val &= ~(FEC_T_TMODE_MASK);
146+
writel(val, fep->hwp + FEC_TCSR(fep->pps_channel));
147+
val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
148+
} while (val & FEC_T_TMODE_MASK);
149+
150+
/* Dummy read counter to update the counter */
151+
timecounter_read(&fep->tc);
152+
/* We want to find the first compare event in the next
153+
* second point. So we need to know what the ptp time
154+
* is now and how many nanoseconds is ahead to get next second.
155+
* The remaining nanosecond ahead before the next second would be
156+
* NSEC_PER_SEC - ts.tv_nsec. Add the remaining nanoseconds
157+
* to current timer would be next second.
158+
*/
159+
tempval = readl(fep->hwp + FEC_ATIME_CTRL);
160+
tempval |= FEC_T_CTRL_CAPTURE;
161+
writel(tempval, fep->hwp + FEC_ATIME_CTRL);
162+
163+
tempval = readl(fep->hwp + FEC_ATIME);
164+
/* Convert the ptp local counter to 1588 timestamp */
165+
ns = timecounter_cyc2time(&fep->tc, tempval);
166+
ts.tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder);
167+
ts.tv_nsec = remainder;
168+
169+
/* The tempval is less than 3 seconds, and so val is less than
170+
* 4 seconds. No overflow for 32bit calculation.
171+
*/
172+
val = NSEC_PER_SEC - (u32)ts.tv_nsec + tempval;
173+
174+
/* Need to consider the situation that the current time is
175+
* very close to the second point, which means NSEC_PER_SEC
176+
* - ts.tv_nsec is close to be zero(For example 20ns); Since the timer
177+
* is still running when we calculate the first compare event, it is
178+
* possible that the remaining nanoseonds run out before the compare
179+
* counter is calculated and written into TCCR register. To avoid
180+
* this possibility, we will set the compare event to be the next
181+
* of next second. The current setting is 31-bit timer and wrap
182+
* around over 2 seconds. So it is okay to set the next of next
183+
* seond for the timer.
184+
*/
185+
val += NSEC_PER_SEC;
186+
187+
/* We add (2 * NSEC_PER_SEC - (u32)ts.tv_nsec) to current
188+
* ptp counter, which maybe cause 32-bit wrap. Since the
189+
* (NSEC_PER_SEC - (u32)ts.tv_nsec) is less than 2 second.
190+
* We can ensure the wrap will not cause issue. If the offset
191+
* is bigger than fep->cc.mask would be a error.
192+
*/
193+
val &= fep->cc.mask;
194+
writel(val, fep->hwp + FEC_TCCR(fep->pps_channel));
195+
196+
/* Calculate the second the compare event timestamp */
197+
fep->next_counter = (val + fep->reload_period) & fep->cc.mask;
198+
199+
/* * Enable compare event when overflow */
200+
val = readl(fep->hwp + FEC_ATIME_CTRL);
201+
val |= FEC_T_CTRL_PINPER;
202+
writel(val, fep->hwp + FEC_ATIME_CTRL);
203+
204+
/* Compare channel setting. */
205+
val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
206+
val |= (1 << FEC_T_TF_OFFSET | 1 << FEC_T_TIE_OFFSET);
207+
val &= ~(1 << FEC_T_TDRE_OFFSET);
208+
val &= ~(FEC_T_TMODE_MASK);
209+
val |= (FEC_HIGH_PULSE << FEC_T_TMODE_OFFSET);
210+
writel(val, fep->hwp + FEC_TCSR(fep->pps_channel));
211+
212+
/* Write the second compare event timestamp and calculate
213+
* the third timestamp. Refer the TCCR register detail in the spec.
214+
*/
215+
writel(fep->next_counter, fep->hwp + FEC_TCCR(fep->pps_channel));
216+
fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask;
217+
} else {
218+
writel(0, fep->hwp + FEC_TCSR(fep->pps_channel));
219+
}
220+
221+
fep->pps_enable = enable;
222+
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
223+
224+
return 0;
225+
}
226+
74227
/**
75228
* fec_ptp_read - read raw cycle counter (to be used by time counter)
76229
* @cc: the cyclecounter structure
@@ -314,6 +467,15 @@ static int fec_ptp_settime(struct ptp_clock_info *ptp,
314467
static int fec_ptp_enable(struct ptp_clock_info *ptp,
315468
struct ptp_clock_request *rq, int on)
316469
{
470+
struct fec_enet_private *fep =
471+
container_of(ptp, struct fec_enet_private, ptp_caps);
472+
int ret = 0;
473+
474+
if (rq->type == PTP_CLK_REQ_PPS) {
475+
ret = fec_ptp_enable_pps(fep, on);
476+
477+
return ret;
478+
}
317479
return -EOPNOTSUPP;
318480
}
319481

@@ -428,7 +590,7 @@ void fec_ptp_init(struct platform_device *pdev)
428590
fep->ptp_caps.n_ext_ts = 0;
429591
fep->ptp_caps.n_per_out = 0;
430592
fep->ptp_caps.n_pins = 0;
431-
fep->ptp_caps.pps = 0;
593+
fep->ptp_caps.pps = 1;
432594
fep->ptp_caps.adjfreq = fec_ptp_adjfreq;
433595
fep->ptp_caps.adjtime = fec_ptp_adjtime;
434596
fep->ptp_caps.gettime = fec_ptp_gettime;
@@ -452,3 +614,36 @@ void fec_ptp_init(struct platform_device *pdev)
452614

453615
schedule_delayed_work(&fep->time_keep, HZ);
454616
}
617+
618+
/**
619+
* fec_ptp_check_pps_event
620+
* @fep: the fec_enet_private structure handle
621+
*
622+
* This function check the pps event and reload the timer compare counter.
623+
*/
624+
uint fec_ptp_check_pps_event(struct fec_enet_private *fep)
625+
{
626+
u32 val;
627+
u8 channel = fep->pps_channel;
628+
struct ptp_clock_event event;
629+
630+
val = readl(fep->hwp + FEC_TCSR(channel));
631+
if (val & FEC_T_TF_MASK) {
632+
/* Write the next next compare(not the next according the spec)
633+
* value to the register
634+
*/
635+
writel(fep->next_counter, fep->hwp + FEC_TCCR(channel));
636+
do {
637+
writel(val, fep->hwp + FEC_TCSR(channel));
638+
} while (readl(fep->hwp + FEC_TCSR(channel)) & FEC_T_TF_MASK);
639+
640+
/* Update the counter; */
641+
fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask;
642+
643+
event.type = PTP_CLOCK_PPS;
644+
ptp_clock_event(fep->ptp_clock, &event);
645+
return 1;
646+
}
647+
648+
return 0;
649+
}

0 commit comments

Comments
 (0)