drivers/ptp/ptp_dfl_tod.c

Source file repositories/reference/linux-study-clean/drivers/ptp/ptp_dfl_tod.c

File Facts

System
Linux kernel
Corpus path
drivers/ptp/ptp_dfl_tod.c
Extension
.c
Size
9704 bytes
Lines
333
Domain
Driver Families
Bucket
drivers/ptp
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct dfl_tod {
	struct ptp_clock_info ptp_clock_ops;
	struct device *dev;
	struct ptp_clock *ptp_clock;

	/* 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.
 */
static int 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.
 */
static int 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;

	if (delta == 0)
		return 0;

	nanosec = readl(base + TOD_NANOSEC);
	seconds_lsb = readl(base + TOD_SECONDSL);
	seconds_msb = readl(base + TOD_SECONDSH);

	/* Calculate new time */
	seconds = CAL_SECONDS(seconds_msb, seconds_lsb);
	now = seconds * NSEC_PER_SEC + nanosec + delta;

	seconds = div_u64_rem(now, NSEC_PER_SEC, &nanosec);
	seconds_msb = FIELD_GET(SECONDS_MSB, seconds);
	seconds_lsb = FIELD_GET(SECONDS_LSB, seconds);

	writel(seconds_msb, base + TOD_SECONDSH);
	writel(seconds_lsb, base + TOD_SECONDSL);
	writel(nanosec, base + TOD_NANOSEC);

	return 0;
}

static int dfl_tod_adjust_fine(struct ptp_clock_info *ptp, long scaled_ppm)
{
	struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops);
	u32 tod_period, tod_rem, tod_drift_adjust_fns, tod_drift_adjust_rate;
	void __iomem *base = dt->tod_ctrl;
	unsigned long flags, rate;
	u64 ppb;

	/* Get the clock rate from clock frequency register offset */
	rate = readl(base + TOD_CLK_FREQ);

	/* add GIGA as nominal ppb */
	ppb = scaled_ppm_to_ppb(scaled_ppm) + GIGA;

	tod_period = div_u64_rem(ppb << PERIOD_FRAC_OFFSET, rate, &tod_rem);
	if (tod_period > TOD_PERIOD_MAX)
		return -ERANGE;

	/*
	 * 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);

	while ((tod_drift_adjust_fns > TOD_DRIFT_ADJUST_FNS_MAX) ||

Annotation

Implementation Notes