drivers/rtc/rtc-at91sam9.c

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

File Facts

System
Linux kernel
Corpus path
drivers/rtc/rtc-at91sam9.c
Extension
.c
Size
13465 bytes
Lines
547
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 sam9_rtc {
	void __iomem		*rtt;
	struct rtc_device	*rtcdev;
	u32			imr;
	struct regmap		*gpbr;
	unsigned int		gpbr_offset;
	int			irq;
	struct clk		*sclk;
	bool			suspended;
	unsigned long		events;
	spinlock_t		lock;
};

#define rtt_readl(rtc, field) \
	readl((rtc)->rtt + AT91_RTT_ ## field)
#define rtt_writel(rtc, field, val) \
	writel((val), (rtc)->rtt + AT91_RTT_ ## field)

static inline unsigned int gpbr_readl(struct sam9_rtc *rtc)
{
	unsigned int val;

	regmap_read(rtc->gpbr, rtc->gpbr_offset, &val);

	return val;
}

static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val)
{
	regmap_write(rtc->gpbr, rtc->gpbr_offset, val);
}

/*
 * Read current time and date in RTC
 */
static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
{
	struct sam9_rtc *rtc = dev_get_drvdata(dev);
	u32 secs, secs2;
	u32 offset;

	/* read current time offset */
	offset = gpbr_readl(rtc);
	if (offset == 0)
		return -EILSEQ;

	/* reread the counter to help sync the two clock domains */
	secs = rtt_readl(rtc, VR);
	secs2 = rtt_readl(rtc, VR);
	if (secs != secs2)
		secs = rtt_readl(rtc, VR);

	rtc_time64_to_tm(offset + secs, tm);

	dev_dbg(dev, "%s: %ptR\n", __func__, tm);

	return 0;
}

/*
 * Set current time and date in RTC
 */
static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
{
	struct sam9_rtc *rtc = dev_get_drvdata(dev);
	u32 offset, alarm, mr;
	unsigned long secs;

	dev_dbg(dev, "%s: %ptR\n", __func__, tm);

	secs = rtc_tm_to_time64(tm);

	mr = rtt_readl(rtc, MR);

	/* disable interrupts */
	rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));

	/* read current time offset */
	offset = gpbr_readl(rtc);

	/* store the new base time in a battery backup register */
	secs += 1;
	gpbr_writel(rtc, secs);

	/* adjust the alarm time for the new base */
	alarm = rtt_readl(rtc, AR);
	if (alarm != ALARM_DISABLED) {
		if (offset > secs) {
			/* time jumped backwards, increase time until alarm */
			alarm += (offset - secs);

Annotation

Implementation Notes