drivers/rtc/rtc-tegra.c

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

File Facts

System
Linux kernel
Corpus path
drivers/rtc/rtc-tegra.c
Extension
.c
Size
10769 bytes
Lines
404
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 tegra_rtc_info {
	struct platform_device *pdev;
	struct rtc_device *rtc;
	void __iomem *base; /* NULL if not initialized */
	struct clk *clk;
	int irq; /* alarm and periodic IRQ */
	spinlock_t lock;
};

/*
 * RTC hardware is busy when it is updating its values over AHB once every
 * eight 32 kHz clocks (~250 us). Outside of these updates the CPU is free to
 * write. CPU is always free to read.
 */
static inline u32 tegra_rtc_check_busy(struct tegra_rtc_info *info)
{
	return readl(info->base + TEGRA_RTC_REG_BUSY) & 1;
}

/*
 * Wait for hardware to be ready for writing. This function tries to maximize
 * the amount of time before the next update. It does this by waiting for the
 * RTC to become busy with its periodic update, then returning once the RTC
 * first becomes not busy.
 *
 * This periodic update (where the seconds and milliseconds are copied to the
 * AHB side) occurs every eight 32 kHz clocks (~250 us). The behavior of this
 * function allows us to make some assumptions without introducing a race,
 * because 250 us is plenty of time to read/write a value.
 */
static int tegra_rtc_wait_while_busy(struct device *dev)
{
	struct tegra_rtc_info *info = dev_get_drvdata(dev);
	int retries = 500; /* ~490 us is the worst case, ~250 us is best */

	/*
	 * First wait for the RTC to become busy. This is when it posts its
	 * updated seconds+msec registers to AHB side.
	 */
	while (tegra_rtc_check_busy(info)) {
		if (!retries--)
			goto retry_failed;

		udelay(1);
	}

	/* now we have about 250 us to manipulate registers */
	return 0;

retry_failed:
	dev_err(dev, "write failed: retry count exceeded\n");
	return -ETIMEDOUT;
}

static int tegra_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	struct tegra_rtc_info *info = dev_get_drvdata(dev);
	unsigned long flags;
	u32 sec;

	/*
	 * RTC hardware copies seconds to shadow seconds when a read of
	 * milliseconds occurs. use a lock to keep other threads out.
	 */
	spin_lock_irqsave(&info->lock, flags);

	readl(info->base + TEGRA_RTC_REG_MILLI_SECONDS);
	sec = readl(info->base + TEGRA_RTC_REG_SHADOW_SECONDS);

	spin_unlock_irqrestore(&info->lock, flags);

	rtc_time64_to_tm(sec, tm);

	dev_vdbg(dev, "time read as %u, %ptR\n", sec, tm);

	return 0;
}

static int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	struct tegra_rtc_info *info = dev_get_drvdata(dev);
	u32 sec;
	int ret;

	/* convert tm to seconds */
	sec = rtc_tm_to_time64(tm);

	dev_vdbg(dev, "time set to %u, %ptR\n", sec, tm);

	/* seconds only written if wait succeeded */

Annotation

Implementation Notes