drivers/clocksource/arm_arch_timer_mmio.c

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

File Facts

System
Linux kernel
Corpus path
drivers/clocksource/arm_arch_timer_mmio.c
Extension
.c
Size
11074 bytes
Lines
443
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 arch_timer {
	struct clock_event_device	evt;
	struct clocksource		cs;
	struct arch_timer_mem		*gt_block;
	void __iomem			*base;
	enum arch_timer_access		access;
	u32				rate;
};

#define evt_to_arch_timer(e) container_of(e, struct arch_timer, evt)
#define cs_to_arch_timer(c) container_of(c, struct arch_timer, cs)

static void arch_timer_mmio_write(struct arch_timer *timer,
				  enum arch_timer_reg reg, u64 val)
{
	switch (timer->access) {
	case PHYS_ACCESS:
		switch (reg) {
		case ARCH_TIMER_REG_CTRL:
			writel_relaxed((u32)val, timer->base + CNTP_CTL);
			return;
		case ARCH_TIMER_REG_CVAL:
			/*
			 * Not guaranteed to be atomic, so the timer
			 * must be disabled at this point.
			 */
			writeq_relaxed(val, timer->base + CNTP_CVAL_LO);
			return;
		}
		break;
	case VIRT_ACCESS:
		switch (reg) {
		case ARCH_TIMER_REG_CTRL:
			writel_relaxed((u32)val, timer->base + CNTV_CTL);
			return;
		case ARCH_TIMER_REG_CVAL:
			/* Same restriction as above */
			writeq_relaxed(val, timer->base + CNTV_CVAL_LO);
			return;
		}
		break;
	}

	/* Should never be here */
	WARN_ON_ONCE(1);
}

static u32 arch_timer_mmio_read(struct arch_timer *timer, enum arch_timer_reg reg)
{
	switch (timer->access) {
	case PHYS_ACCESS:
		switch (reg) {
		case ARCH_TIMER_REG_CTRL:
			return readl_relaxed(timer->base + CNTP_CTL);
		default:
			break;
		}
		break;
	case VIRT_ACCESS:
		switch (reg) {
		case ARCH_TIMER_REG_CTRL:
			return readl_relaxed(timer->base + CNTV_CTL);
		default:
			break;
		}
		break;
	}

	/* Should never be here */
	WARN_ON_ONCE(1);
	return 0;
}

static noinstr u64 arch_counter_mmio_get_cnt(struct arch_timer *t)
{
	int offset_lo = t->access == VIRT_ACCESS ? CNTVCT_LO : CNTPCT_LO;
	u32 cnt_lo, cnt_hi, tmp_hi;

	do {
		cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4));
		cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo));
		tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(t->base + offset_lo + 4));
	} while (cnt_hi != tmp_hi);

	return ((u64) cnt_hi << 32) | cnt_lo;
}

static u64 arch_mmio_counter_read(struct clocksource *cs)
{
	struct arch_timer *at = cs_to_arch_timer(cs);

Annotation

Implementation Notes