drivers/watchdog/armada_37xx_wdt.c

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

File Facts

System
Linux kernel
Corpus path
drivers/watchdog/armada_37xx_wdt.c
Extension
.c
Size
9898 bytes
Lines
360
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 armada_37xx_watchdog {
	struct watchdog_device wdt;
	struct regmap *cpu_misc;
	void __iomem *reg;
	u64 timeout; /* in clock ticks */
	unsigned long clk_rate;
	struct clk *clk;
};

static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id)
{
	u64 val;

	/*
	 * when low is read, high is latched into flip-flops so that it can be
	 * read consistently without using software debouncing
	 */
	val = readl(dev->reg + CNTR_COUNT_LOW(id));
	val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32;

	return val;
}

static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val)
{
	writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id));
	writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id));
}

static void counter_enable(struct armada_37xx_watchdog *dev, int id)
{
	u32 reg;

	reg = readl(dev->reg + CNTR_CTRL(id));
	reg |= CNTR_CTRL_ENABLE;
	writel(reg, dev->reg + CNTR_CTRL(id));
}

static void counter_disable(struct armada_37xx_watchdog *dev, int id)
{
	u32 reg;

	reg = readl(dev->reg + CNTR_CTRL(id));
	reg &= ~CNTR_CTRL_ENABLE;
	writel(reg, dev->reg + CNTR_CTRL(id));
}

static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode,
			 u32 trig_src)
{
	u32 reg;

	reg = readl(dev->reg + CNTR_CTRL(id));

	reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
		 CNTR_CTRL_TRIG_SRC_MASK);

	/* set mode */
	reg |= mode & CNTR_CTRL_MODE_MASK;

	/* set prescaler to the min value */
	reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;

	/* set trigger source */
	reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK;

	writel(reg, dev->reg + CNTR_CTRL(id));
}

static int armada_37xx_wdt_ping(struct watchdog_device *wdt)
{
	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);

	/* counter 1 is retriggered by forcing end count on counter 0 */
	counter_disable(dev, CNTR_ID_RETRIGGER);
	counter_enable(dev, CNTR_ID_RETRIGGER);

	return 0;
}

static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt)
{
	struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
	u64 res;

	res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN;
	do_div(res, dev->clk_rate);

	return res;
}

Annotation

Implementation Notes