if (fep->pps_enable == enable) {
spin_unlock_irqrestore(&fep->tmreg_lock, flags); return0;
}
if (enable) { /* clear capture or output compare interrupt status if have.
*/
writel(FEC_T_TF_MASK, fep->hwp + FEC_TCSR(fep->pps_channel));
/* It is recommended to double check the TMODE field in the *TCSRregistertobeclearedbeforethefirstcomparecounter *iswrittenintoTCCRregister.Justaddadoublecheck.
*/
val = readl(fep->hwp + FEC_TCSR(fep->pps_channel)); do {
val &= ~(FEC_T_TMODE_MASK);
writel(val, fep->hwp + FEC_TCSR(fep->pps_channel));
val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
} while (val & FEC_T_TMODE_MASK);
/* Dummy read counter to update the counter */
timecounter_read(&fep->tc); /* We want to find the first compare event in the next *secondpoint.Soweneedtoknowwhattheptptime *isnowandhowmanynanosecondsisaheadtogetnextsecond. *Theremainingnanosecondaheadbeforethenextsecondwouldbe *NSEC_PER_SEC-ts.tv_nsec.Addtheremainingnanoseconds *tocurrenttimerwouldbenextsecond.
*/
tempval = fec_ptp_read(&fep->cc); /* Convert the ptp local counter to 1588 timestamp */
ns = timecounter_cyc2time(&fep->tc, tempval);
ts = ns_to_timespec64(ns);
/* The tempval is less than 3 seconds, and so val is less than *4seconds.Nooverflowfor32bitcalculation.
*/
val = NSEC_PER_SEC - (u32)ts.tv_nsec + tempval;
/* Need to consider the situation that the current time is *veryclosetothesecondpoint,whichmeansNSEC_PER_SEC *-ts.tv_nsecisclosetobezero(Forexample20ns);Sincethetimer *isstillrunningwhenwecalculatethefirstcompareevent,itis *possiblethattheremainingnanosecondsrunoutbeforethecompare *counteriscalculatedandwrittenintoTCCRregister.Toavoid *thispossibility,wewillsetthecompareeventtobethenext *ofnextsecond.Thecurrentsettingis31-bittimerandwrap *aroundover2seconds.Soitisokaytosetthenextofnext *seondforthetimer.
*/
val += NSEC_PER_SEC;
/* We add (2 * NSEC_PER_SEC - (u32)ts.tv_nsec) to current *ptpcounter,whichmaybecause32-bitwrap.Sincethe *(NSEC_PER_SEC-(u32)ts.tv_nsec)islessthan2second. *Wecanensurethewrapwillnotcauseissue.Iftheoffset *isbiggerthanfep->cc.maskwouldbeaerror.
*/
val &= fep->cc.mask;
writel(val, fep->hwp + FEC_TCCR(fep->pps_channel));
/* Calculate the second the compare event timestamp */
fep->next_counter = (val + fep->reload_period) & fep->cc.mask;
/* * Enable compare event when overflow */
val = readl(fep->hwp + FEC_ATIME_CTRL);
val |= FEC_T_CTRL_PINPER;
writel(val, fep->hwp + FEC_ATIME_CTRL);
/* Compare channel setting. */
val = readl(fep->hwp + FEC_TCSR(fep->pps_channel));
val |= (1 << FEC_T_TF_OFFSET | 1 << FEC_T_TIE_OFFSET);
val &= ~(1 << FEC_T_TDRE_OFFSET);
val &= ~(FEC_T_TMODE_MASK);
val |= (FEC_HIGH_PULSE << FEC_T_TMODE_OFFSET);
writel(val, fep->hwp + FEC_TCSR(fep->pps_channel));
/* Write the second compare event timestamp and calculate *thethirdtimestamp.RefertheTCCRregisterdetailinthespec.
*/
writel(fep->next_counter, fep->hwp + FEC_TCCR(fep->pps_channel));
fep->next_counter = (fep->next_counter + fep->reload_period) & fep->cc.mask;
} else {
writel(0, fep->hwp + FEC_TCSR(fep->pps_channel));
}
/* Update time counter */
timecounter_read(&fep->tc);
/* Get the current ptp hardware time counter */
ptp_hc = fec_ptp_read(&fep->cc);
/* Convert the ptp local counter to 1588 timestamp */
curr_time = timecounter_cyc2time(&fep->tc, ptp_hc);
/* If the pps start time less than current time add 100ms, just return. *Becausethesoftwaremightnotabletosetthecomparisontimeinto *theFEC_TCCRregisterintimeandmissedthestarttime.
*/ if (fep->perout_stime < curr_time + 100 * NSEC_PER_MSEC) {
dev_err(&fep->pdev->dev, "Current time is too close to the start time!\n");
spin_unlock_irqrestore(&fep->tmreg_lock, flags); return -1;
}
/* FEC PTP timer only has 31 bits, so if the period exceed *4sisnotsupported.
*/ if (period_ns > FEC_PTP_MAX_NSEC_PERIOD) {
dev_err(&fep->pdev->dev, "The period must equal to or less than 4s!\n"); return -EOPNOTSUPP;
}
fep->reload_period = div_u64(period_ns, 2); if (on && fep->reload_period) { /* Convert 1588 timestamp to ns*/
start_time.tv_sec = rq->perout.start.sec;
start_time.tv_nsec = rq->perout.start.nsec;
fep->perout_stime = timespec64_to_ns(&start_time);
mutex_lock(&fep->ptp_clk_mutex); if (!fep->ptp_clk_on) {
dev_err(&fep->pdev->dev, "Error: PTP clock is closed!\n");
mutex_unlock(&fep->ptp_clk_mutex); return -EOPNOTSUPP;
}
spin_lock_irqsave(&fep->tmreg_lock, flags); /* Read current timestamp */
curr_time = timecounter_read(&fep->tc);
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
mutex_unlock(&fep->ptp_clk_mutex);
/* Calculate time difference */
delta = fep->perout_stime - curr_time;
if (fep->perout_stime <= curr_time) {
dev_err(&fep->pdev->dev, "Start time must larger than current time!\n"); return -EINVAL;
}
/* Because the timer counter of FEC only has 31-bits, correspondingly, *thetimecomparisonregisterFEC_TCCRalsoonlylow31bitscanbe *set.Ifthestarttimeofppssignalexceedscurrenttimemorethan *0x80000000ns,asoftwaretimerisusedandthetimerexpiresabout *1secondbeforethestarttimetobeabletosetFEC_TCCR.
*/ if (delta > FEC_PTP_MAX_NSEC_COUNTER) {
timeout = ns_to_ktime(delta - NSEC_PER_SEC);
hrtimer_start(&fep->perout_timer, timeout, HRTIMER_MODE_REL);
} else { return fec_ptp_pps_perout(fep);
}
} else {
fec_ptp_pps_disable(fep, fep->pps_channel);
}
/* This function checks the pps event and reloads the timer compare counter. */ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id)
{ struct net_device *ndev = dev_id; struct fec_enet_private *fep = netdev_priv(ndev);
u32 val;
u8 channel = fep->pps_channel; struct ptp_clock_event event;
val = readl(fep->hwp + FEC_TCSR(channel)); if (val & FEC_T_TF_MASK) { /* Write the next next compare(not the next according the spec) *valuetotheregister
*/
writel(fep->next_counter, fep->hwp + FEC_TCCR(channel)); do {
writel(val, fep->hwp + FEC_TCSR(channel));
} while (readl(fep->hwp + FEC_TCSR(channel)) & FEC_T_TF_MASK);
irq = platform_get_irq_byname_optional(pdev, "pps"); if (irq < 0)
irq = platform_get_irq_optional(pdev, irq_idx); /* Failure to get an irq is not fatal, *onlythePTP_CLOCK_PPSclockeventsshouldstop
*/ if (irq >= 0) {
ret = devm_request_irq(&pdev->dev, irq, fec_pps_interrupt, 0, pdev->name, ndev); if (ret < 0)
dev_warn(&pdev->dev, "request for pps irq failed(%d)\n",
ret);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.