drivers/watchdog/lpc18xx_wdt.c

Source file repositories/reference/linux-study-clean/drivers/watchdog/lpc18xx_wdt.c

File Facts

System
Linux kernel
Corpus path
drivers/watchdog/lpc18xx_wdt.c
Extension
.c
Size
8342 bytes
Lines
292
Domain
Driver Families
Bucket
drivers/watchdog
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 lpc18xx_wdt_dev {
	struct watchdog_device	wdt_dev;
	struct clk		*reg_clk;
	struct clk		*wdt_clk;
	unsigned long		clk_rate;
	void __iomem		*base;
	struct timer_list	timer;
	spinlock_t		lock;
};

static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev)
{
	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
	unsigned long flags;

	/*
	 * An abort condition will occur if an interrupt happens during the feed
	 * sequence.
	 */
	spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
	writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
	writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
	spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);

	return 0;
}

static void lpc18xx_wdt_timer_feed(struct timer_list *t)
{
	struct lpc18xx_wdt_dev *lpc18xx_wdt = timer_container_of(lpc18xx_wdt,
								 t, timer);
	struct watchdog_device *wdt_dev = &lpc18xx_wdt->wdt_dev;

	lpc18xx_wdt_feed(wdt_dev);

	/* Use safe value (1/2 of real timeout) */
	mod_timer(&lpc18xx_wdt->timer, jiffies +
		  msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2));
}

/*
 * Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding
 * it with a timer until userspace watchdog software takes over.
 */
static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev)
{
	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);

	lpc18xx_wdt_timer_feed(&lpc18xx_wdt->timer);

	return 0;
}

static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt)
{
	unsigned int val;

	val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate,
			   LPC18XX_WDT_CLK_DIV);
	writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC);
}

static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev,
				   unsigned int new_timeout)
{
	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);

	lpc18xx_wdt->wdt_dev.timeout = new_timeout;
	__lpc18xx_wdt_set_timeout(lpc18xx_wdt);

	return 0;
}

static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev)
{
	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
	unsigned int val;

	val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV);
	return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
}

static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
{
	struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
	unsigned int val;

	if (timer_pending(&lpc18xx_wdt->timer))
		timer_delete(&lpc18xx_wdt->timer);

Annotation

Implementation Notes