drivers/rtc/rtc-brcmstb-waketimer.c

Source file repositories/reference/linux-study-clean/drivers/rtc/rtc-brcmstb-waketimer.c

File Facts

System
Linux kernel
Corpus path
drivers/rtc/rtc-brcmstb-waketimer.c
Extension
.c
Size
10611 bytes
Lines
433
Domain
Driver Families
Bucket
drivers/rtc
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 brcmstb_waketmr {
	struct rtc_device *rtc;
	struct device *dev;
	void __iomem *base;
	unsigned int wake_irq;
	unsigned int alarm_irq;
	struct notifier_block reboot_notifier;
	struct clk *clk;
	u32 rate;
	unsigned long rtc_alarm;
	bool alarm_en;
	bool alarm_expired;
};

#define BRCMSTB_WKTMR_EVENT		0x00
#define  WKTMR_ALARM_EVENT		BIT(0)
#define BRCMSTB_WKTMR_COUNTER		0x04
#define BRCMSTB_WKTMR_ALARM		0x08
#define BRCMSTB_WKTMR_PRESCALER		0x0C
#define BRCMSTB_WKTMR_PRESCALER_VAL	0x10

#define BRCMSTB_WKTMR_DEFAULT_FREQ	27000000

static inline bool brcmstb_waketmr_is_pending(struct brcmstb_waketmr *timer)
{
	u32 reg;

	reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
	return !!(reg & WKTMR_ALARM_EVENT);
}

static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)
{
	u32 reg;

	if (timer->alarm_en && timer->alarm_irq)
		disable_irq(timer->alarm_irq);
	timer->alarm_en = false;
	reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
	writel_relaxed(reg - 1, timer->base + BRCMSTB_WKTMR_ALARM);
	writel_relaxed(WKTMR_ALARM_EVENT, timer->base + BRCMSTB_WKTMR_EVENT);
	(void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
	if (timer->alarm_expired) {
		timer->alarm_expired = false;
		/* maintain call balance */
		enable_irq(timer->alarm_irq);
	}
}

static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
				      unsigned int secs)
{
	unsigned int now;

	brcmstb_waketmr_clear_alarm(timer);

	/* Make sure we are actually counting in seconds */
	writel_relaxed(timer->rate, timer->base + BRCMSTB_WKTMR_PRESCALER);

	writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM);
	now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);

	while ((int)(secs - now) <= 0 &&
		!brcmstb_waketmr_is_pending(timer)) {
		secs = now + 1;
		writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM);
		now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
	}
}

static irqreturn_t brcmstb_waketmr_irq(int irq, void *data)
{
	struct brcmstb_waketmr *timer = data;

	if (!timer->alarm_irq)
		pm_wakeup_event(timer->dev, 0);
	return IRQ_HANDLED;
}

static irqreturn_t brcmstb_alarm_irq(int irq, void *data)
{
	struct brcmstb_waketmr *timer = data;

	/* Ignore spurious interrupts */
	if (!brcmstb_waketmr_is_pending(timer))
		return IRQ_HANDLED;

	if (timer->alarm_en) {
		if (device_may_wakeup(timer->dev)) {
			disable_irq_nosync(irq);

Annotation

Implementation Notes