/* ToD clock register space. */ #define TOD_CLK_FREQ 0x038
/* * The read sequence of ToD timestamp registers: TOD_NANOSEC, TOD_SECONDSL and * TOD_SECONDSH, because there is a hardware snapshot whenever the TOD_NANOSEC * register is read. * * The ToD IP requires writing registers in the reverse order to the read sequence. * The timestamp is corrected when the TOD_NANOSEC register is written, so the * sequence of write TOD registers: TOD_SECONDSH, TOD_SECONDSL and TOD_NANOSEC.
*/ #define TOD_SECONDSH 0x100 #define TOD_SECONDSL 0x104 #define TOD_NANOSEC 0x108 #define TOD_PERIOD 0x110 #define TOD_ADJUST_PERIOD 0x114 #define TOD_ADJUST_COUNT 0x118 #define TOD_DRIFT_ADJUST 0x11c #define TOD_DRIFT_ADJUST_RATE 0x120 #define PERIOD_FRAC_OFFSET 16 #define SECONDS_MSB GENMASK_ULL(47, 32) #define SECONDS_LSB GENMASK_ULL(31, 0) #define TOD_SECONDSH_SEC_MSB GENMASK_ULL(15, 0)
/* ToD Clock address space */ void __iomem *tod_ctrl;
/* ToD clock registers protection */
spinlock_t tod_lock;
};
/* * A fine ToD HW clock offset adjustment. To perform the fine offset adjustment, the * adjust_period and adjust_count argument are used to update the TOD_ADJUST_PERIOD * and TOD_ADJUST_COUNT register for in hardware. The dt->tod_lock spinlock must be * held when calling this function.
*/ staticint fine_adjust_tod_clock(struct dfl_tod *dt, u32 adjust_period,
u32 adjust_count)
{ void __iomem *base = dt->tod_ctrl;
u32 val;
writel(adjust_period, base + TOD_ADJUST_PERIOD);
writel(adjust_count, base + TOD_ADJUST_COUNT);
/* Wait for present offset adjustment update to complete */ return readl_poll_timeout_atomic(base + TOD_ADJUST_COUNT, val, !val, TOD_ADJUST_INTERVAL_US,
TOD_ADJUST_MAX_US);
}
/* * A coarse ToD HW clock offset adjustment. The coarse time adjustment performs by * adding or subtracting the delta value from the current ToD HW clock time.
*/ staticint coarse_adjust_tod_clock(struct dfl_tod *dt, s64 delta)
{
u32 seconds_msb, seconds_lsb, nanosec; void __iomem *base = dt->tod_ctrl;
u64 seconds, now;
/* * The drift of ToD adjusted periodically by adding a drift_adjust_fns * correction value every drift_adjust_rate count of clock cycles.
*/
tod_drift_adjust_fns = tod_rem / gcd(tod_rem, rate);
tod_drift_adjust_rate = rate / gcd(tod_rem, rate);
if (tod_drift_adjust_fns == 0)
tod_drift_adjust_rate = 0;
spin_lock_irqsave(&dt->tod_lock, flags);
writel(tod_period, base + TOD_PERIOD);
writel(0, base + TOD_ADJUST_PERIOD);
writel(0, base + TOD_ADJUST_COUNT);
writel(tod_drift_adjust_fns, base + TOD_DRIFT_ADJUST);
writel(tod_drift_adjust_rate, base + TOD_DRIFT_ADJUST_RATE);
spin_unlock_irqrestore(&dt->tod_lock, flags);
/* * Get the maximum possible value of the Period register offset * adjustment in nanoseconds scale. This depends on the current * Period register setting and the maximum and minimum possible * values of the Period register.
*/
period = readl(base + TOD_PERIOD);
if (count > TOD_ADJUST_COUNT_MAX) {
ret = coarse_adjust_tod_clock(dt, delta);
} else { /* Adjust the period by count cycles to adjust the time */ if (count)
ret = fine_adjust_tod_clock(dt, adj_period, count);
/* If there is a remainder, adjust the period for an additional cycle */ if (rem)
ret = fine_adjust_tod_clock(dt, rem_period, 1);
}
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.