drivers/clocksource/timer-sun5i.c

Source file repositories/reference/linux-study-clean/drivers/clocksource/timer-sun5i.c

File Facts

System
Linux kernel
Corpus path
drivers/clocksource/timer-sun5i.c
Extension
.c
Size
9883 bytes
Lines
380
Domain
Driver Families
Bucket
drivers/clocksource
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 sunxi_timer_quirks {
	u32 from_ctl_base_offset;
};

struct sun5i_timer {
	void __iomem		*base;
	struct clk		*clk;
	struct notifier_block	clk_rate_cb;
	u32			ticks_per_jiffy;
	struct clocksource	clksrc;
	struct clock_event_device	clkevt;
	const struct sunxi_timer_quirks *quirks;
};

#define nb_to_sun5i_timer(x) \
	container_of(x, struct sun5i_timer, clk_rate_cb)
#define clksrc_to_sun5i_timer(x) \
	container_of(x, struct sun5i_timer, clksrc)
#define clkevt_to_sun5i_timer(x) \
	container_of(x, struct sun5i_timer, clkevt)

/*
 * When we disable a timer, we need to wait at least for 2 cycles of
 * the timer source clock. We will use for that the clocksource timer
 * that is already setup and runs at the same frequency than the other
 * timers, and we never will be disabled.
 */
static void sun5i_clkevt_sync(struct sun5i_timer *ce)
{
	u32 offset = ce->quirks->from_ctl_base_offset;
	u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1, offset));

	while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1, offset))) <
	       TIMER_SYNC_TICKS)
		cpu_relax();
}

static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer)
{
	u32 offset = ce->quirks->from_ctl_base_offset;
	u32 val = readl(ce->base + TIMER_CTL_REG(timer, offset));

	writel(val & ~TIMER_CTL_ENABLE,
	       ce->base + TIMER_CTL_REG(timer, offset));

	sun5i_clkevt_sync(ce);
}

static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay)
{
	u32 offset = ce->quirks->from_ctl_base_offset;

	writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer, offset));
}

static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic)
{
	u32 offset = ce->quirks->from_ctl_base_offset;
	u32 val = readl(ce->base + TIMER_CTL_REG(timer, offset));

	if (periodic)
		val &= ~TIMER_CTL_ONESHOT;
	else
		val |= TIMER_CTL_ONESHOT;

	writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
	       ce->base + TIMER_CTL_REG(timer, offset));
}

static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt)
{
	struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);

	sun5i_clkevt_time_stop(ce, 0);
	return 0;
}

static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt)
{
	struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);

	sun5i_clkevt_time_stop(ce, 0);
	sun5i_clkevt_time_start(ce, 0, false);
	return 0;
}

static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt)
{
	struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt);

Annotation

Implementation Notes