xref: /openbmc/linux/drivers/clocksource/timer-tegra186.c (revision 07385a6055a8649593052703b1bfd6aef49db02a)
142cee19aSThierry Reding // SPDX-License-Identifier: GPL-2.0-only
242cee19aSThierry Reding /*
342cee19aSThierry Reding  * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
442cee19aSThierry Reding  */
542cee19aSThierry Reding 
642cee19aSThierry Reding #include <linux/clocksource.h>
742cee19aSThierry Reding #include <linux/module.h>
842cee19aSThierry Reding #include <linux/interrupt.h>
942cee19aSThierry Reding #include <linux/io.h>
1042cee19aSThierry Reding #include <linux/of.h>
1142cee19aSThierry Reding #include <linux/of_device.h>
1242cee19aSThierry Reding #include <linux/platform_device.h>
1342cee19aSThierry Reding #include <linux/pm.h>
1442cee19aSThierry Reding #include <linux/watchdog.h>
1542cee19aSThierry Reding 
1642cee19aSThierry Reding /* shared registers */
1742cee19aSThierry Reding #define TKETSC0 0x000
1842cee19aSThierry Reding #define TKETSC1 0x004
1942cee19aSThierry Reding #define TKEUSEC 0x008
2042cee19aSThierry Reding #define TKEOSC  0x00c
2142cee19aSThierry Reding 
2242cee19aSThierry Reding #define TKEIE(x) (0x100 + ((x) * 4))
2342cee19aSThierry Reding #define  TKEIE_WDT_MASK(x, y) ((y) << (16 + 4 * (x)))
2442cee19aSThierry Reding 
2542cee19aSThierry Reding /* timer registers */
2642cee19aSThierry Reding #define TMRCR 0x000
2742cee19aSThierry Reding #define  TMRCR_ENABLE BIT(31)
2842cee19aSThierry Reding #define  TMRCR_PERIODIC BIT(30)
2942cee19aSThierry Reding #define  TMRCR_PTV(x) ((x) & 0x0fffffff)
3042cee19aSThierry Reding 
3142cee19aSThierry Reding #define TMRSR 0x004
3242cee19aSThierry Reding #define  TMRSR_INTR_CLR BIT(30)
3342cee19aSThierry Reding 
3442cee19aSThierry Reding #define TMRCSSR 0x008
3542cee19aSThierry Reding #define  TMRCSSR_SRC_USEC (0 << 0)
3642cee19aSThierry Reding 
3742cee19aSThierry Reding /* watchdog registers */
3842cee19aSThierry Reding #define WDTCR 0x000
3942cee19aSThierry Reding #define  WDTCR_SYSTEM_POR_RESET_ENABLE BIT(16)
4042cee19aSThierry Reding #define  WDTCR_SYSTEM_DEBUG_RESET_ENABLE BIT(15)
4142cee19aSThierry Reding #define  WDTCR_REMOTE_INT_ENABLE BIT(14)
4242cee19aSThierry Reding #define  WDTCR_LOCAL_FIQ_ENABLE BIT(13)
4342cee19aSThierry Reding #define  WDTCR_LOCAL_INT_ENABLE BIT(12)
4442cee19aSThierry Reding #define  WDTCR_PERIOD_MASK (0xff << 4)
4542cee19aSThierry Reding #define  WDTCR_PERIOD(x) (((x) & 0xff) << 4)
4642cee19aSThierry Reding #define  WDTCR_TIMER_SOURCE_MASK 0xf
4742cee19aSThierry Reding #define  WDTCR_TIMER_SOURCE(x) ((x) & 0xf)
4842cee19aSThierry Reding 
4942cee19aSThierry Reding #define WDTCMDR 0x008
5042cee19aSThierry Reding #define  WDTCMDR_DISABLE_COUNTER BIT(1)
5142cee19aSThierry Reding #define  WDTCMDR_START_COUNTER BIT(0)
5242cee19aSThierry Reding 
5342cee19aSThierry Reding #define WDTUR 0x00c
5442cee19aSThierry Reding #define  WDTUR_UNLOCK_PATTERN 0x0000c45a
5542cee19aSThierry Reding 
5642cee19aSThierry Reding struct tegra186_timer_soc {
5742cee19aSThierry Reding 	unsigned int num_timers;
5842cee19aSThierry Reding 	unsigned int num_wdts;
5942cee19aSThierry Reding };
6042cee19aSThierry Reding 
6142cee19aSThierry Reding struct tegra186_tmr {
6242cee19aSThierry Reding 	struct tegra186_timer *parent;
6342cee19aSThierry Reding 	void __iomem *regs;
6442cee19aSThierry Reding 	unsigned int index;
6542cee19aSThierry Reding 	unsigned int hwirq;
6642cee19aSThierry Reding };
6742cee19aSThierry Reding 
6842cee19aSThierry Reding struct tegra186_wdt {
6942cee19aSThierry Reding 	struct watchdog_device base;
7042cee19aSThierry Reding 
7142cee19aSThierry Reding 	void __iomem *regs;
7242cee19aSThierry Reding 	unsigned int index;
7342cee19aSThierry Reding 	bool locked;
7442cee19aSThierry Reding 
7542cee19aSThierry Reding 	struct tegra186_tmr *tmr;
7642cee19aSThierry Reding };
7742cee19aSThierry Reding 
7842cee19aSThierry Reding static inline struct tegra186_wdt *to_tegra186_wdt(struct watchdog_device *wdd)
7942cee19aSThierry Reding {
8042cee19aSThierry Reding 	return container_of(wdd, struct tegra186_wdt, base);
8142cee19aSThierry Reding }
8242cee19aSThierry Reding 
8342cee19aSThierry Reding struct tegra186_timer {
8442cee19aSThierry Reding 	const struct tegra186_timer_soc *soc;
8542cee19aSThierry Reding 	struct device *dev;
8642cee19aSThierry Reding 	void __iomem *regs;
8742cee19aSThierry Reding 
8842cee19aSThierry Reding 	struct tegra186_wdt *wdt;
8942cee19aSThierry Reding 	struct clocksource usec;
9042cee19aSThierry Reding 	struct clocksource tsc;
9142cee19aSThierry Reding 	struct clocksource osc;
9242cee19aSThierry Reding };
9342cee19aSThierry Reding 
9442cee19aSThierry Reding static void tmr_writel(struct tegra186_tmr *tmr, u32 value, unsigned int offset)
9542cee19aSThierry Reding {
9642cee19aSThierry Reding 	writel_relaxed(value, tmr->regs + offset);
9742cee19aSThierry Reding }
9842cee19aSThierry Reding 
9942cee19aSThierry Reding static void wdt_writel(struct tegra186_wdt *wdt, u32 value, unsigned int offset)
10042cee19aSThierry Reding {
10142cee19aSThierry Reding 	writel_relaxed(value, wdt->regs + offset);
10242cee19aSThierry Reding }
10342cee19aSThierry Reding 
10442cee19aSThierry Reding static u32 wdt_readl(struct tegra186_wdt *wdt, unsigned int offset)
10542cee19aSThierry Reding {
10642cee19aSThierry Reding 	return readl_relaxed(wdt->regs + offset);
10742cee19aSThierry Reding }
10842cee19aSThierry Reding 
10942cee19aSThierry Reding static struct tegra186_tmr *tegra186_tmr_create(struct tegra186_timer *tegra,
11042cee19aSThierry Reding 						unsigned int index)
11142cee19aSThierry Reding {
11242cee19aSThierry Reding 	unsigned int offset = 0x10000 + index * 0x10000;
11342cee19aSThierry Reding 	struct tegra186_tmr *tmr;
11442cee19aSThierry Reding 
11542cee19aSThierry Reding 	tmr = devm_kzalloc(tegra->dev, sizeof(*tmr), GFP_KERNEL);
11642cee19aSThierry Reding 	if (!tmr)
11742cee19aSThierry Reding 		return ERR_PTR(-ENOMEM);
11842cee19aSThierry Reding 
11942cee19aSThierry Reding 	tmr->parent = tegra;
12042cee19aSThierry Reding 	tmr->regs = tegra->regs + offset;
12142cee19aSThierry Reding 	tmr->index = index;
12242cee19aSThierry Reding 	tmr->hwirq = 0;
12342cee19aSThierry Reding 
12442cee19aSThierry Reding 	return tmr;
12542cee19aSThierry Reding }
12642cee19aSThierry Reding 
12742cee19aSThierry Reding static const struct watchdog_info tegra186_wdt_info = {
12842cee19aSThierry Reding 	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
12942cee19aSThierry Reding 	.identity = "NVIDIA Tegra186 WDT",
13042cee19aSThierry Reding };
13142cee19aSThierry Reding 
13242cee19aSThierry Reding static void tegra186_wdt_disable(struct tegra186_wdt *wdt)
13342cee19aSThierry Reding {
13442cee19aSThierry Reding 	/* unlock and disable the watchdog */
13542cee19aSThierry Reding 	wdt_writel(wdt, WDTUR_UNLOCK_PATTERN, WDTUR);
13642cee19aSThierry Reding 	wdt_writel(wdt, WDTCMDR_DISABLE_COUNTER, WDTCMDR);
13742cee19aSThierry Reding 
13842cee19aSThierry Reding 	/* disable timer */
13942cee19aSThierry Reding 	tmr_writel(wdt->tmr, 0, TMRCR);
14042cee19aSThierry Reding }
14142cee19aSThierry Reding 
14242cee19aSThierry Reding static void tegra186_wdt_enable(struct tegra186_wdt *wdt)
14342cee19aSThierry Reding {
14442cee19aSThierry Reding 	struct tegra186_timer *tegra = wdt->tmr->parent;
14542cee19aSThierry Reding 	u32 value;
14642cee19aSThierry Reding 
14742cee19aSThierry Reding 	/* unmask hardware IRQ, this may have been lost across powergate */
14842cee19aSThierry Reding 	value = TKEIE_WDT_MASK(wdt->index, 1);
14942cee19aSThierry Reding 	writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq));
15042cee19aSThierry Reding 
15142cee19aSThierry Reding 	/* clear interrupt */
15242cee19aSThierry Reding 	tmr_writel(wdt->tmr, TMRSR_INTR_CLR, TMRSR);
15342cee19aSThierry Reding 
15442cee19aSThierry Reding 	/* select microsecond source */
15542cee19aSThierry Reding 	tmr_writel(wdt->tmr, TMRCSSR_SRC_USEC, TMRCSSR);
15642cee19aSThierry Reding 
15742cee19aSThierry Reding 	/* configure timer (system reset happens on the fifth expiration) */
15842cee19aSThierry Reding 	value = TMRCR_PTV(wdt->base.timeout * USEC_PER_SEC / 5) |
15942cee19aSThierry Reding 		TMRCR_PERIODIC | TMRCR_ENABLE;
16042cee19aSThierry Reding 	tmr_writel(wdt->tmr, value, TMRCR);
16142cee19aSThierry Reding 
16242cee19aSThierry Reding 	if (!wdt->locked) {
16342cee19aSThierry Reding 		value = wdt_readl(wdt, WDTCR);
16442cee19aSThierry Reding 
16542cee19aSThierry Reding 		/* select the proper timer source */
16642cee19aSThierry Reding 		value &= ~WDTCR_TIMER_SOURCE_MASK;
16742cee19aSThierry Reding 		value |= WDTCR_TIMER_SOURCE(wdt->tmr->index);
16842cee19aSThierry Reding 
16942cee19aSThierry Reding 		/* single timer period since that's already configured */
17042cee19aSThierry Reding 		value &= ~WDTCR_PERIOD_MASK;
17142cee19aSThierry Reding 		value |= WDTCR_PERIOD(1);
17242cee19aSThierry Reding 
17342cee19aSThierry Reding 		/* enable local interrupt for WDT petting */
17442cee19aSThierry Reding 		value |= WDTCR_LOCAL_INT_ENABLE;
17542cee19aSThierry Reding 
17642cee19aSThierry Reding 		/* enable local FIQ and remote interrupt for debug dump */
17742cee19aSThierry Reding 		if (0)
17842cee19aSThierry Reding 			value |= WDTCR_REMOTE_INT_ENABLE |
17942cee19aSThierry Reding 				 WDTCR_LOCAL_FIQ_ENABLE;
18042cee19aSThierry Reding 
18142cee19aSThierry Reding 		/* enable system debug reset (doesn't properly reboot) */
18242cee19aSThierry Reding 		if (0)
18342cee19aSThierry Reding 			value |= WDTCR_SYSTEM_DEBUG_RESET_ENABLE;
18442cee19aSThierry Reding 
18542cee19aSThierry Reding 		/* enable system POR reset */
18642cee19aSThierry Reding 		value |= WDTCR_SYSTEM_POR_RESET_ENABLE;
18742cee19aSThierry Reding 
18842cee19aSThierry Reding 		wdt_writel(wdt, value, WDTCR);
18942cee19aSThierry Reding 	}
19042cee19aSThierry Reding 
19142cee19aSThierry Reding 	wdt_writel(wdt, WDTCMDR_START_COUNTER, WDTCMDR);
19242cee19aSThierry Reding }
19342cee19aSThierry Reding 
19442cee19aSThierry Reding static int tegra186_wdt_start(struct watchdog_device *wdd)
19542cee19aSThierry Reding {
19642cee19aSThierry Reding 	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
19742cee19aSThierry Reding 
19842cee19aSThierry Reding 	tegra186_wdt_enable(wdt);
19942cee19aSThierry Reding 
20042cee19aSThierry Reding 	return 0;
20142cee19aSThierry Reding }
20242cee19aSThierry Reding 
20342cee19aSThierry Reding static int tegra186_wdt_stop(struct watchdog_device *wdd)
20442cee19aSThierry Reding {
20542cee19aSThierry Reding 	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
20642cee19aSThierry Reding 
20742cee19aSThierry Reding 	tegra186_wdt_disable(wdt);
20842cee19aSThierry Reding 
20942cee19aSThierry Reding 	return 0;
21042cee19aSThierry Reding }
21142cee19aSThierry Reding 
21242cee19aSThierry Reding static int tegra186_wdt_ping(struct watchdog_device *wdd)
21342cee19aSThierry Reding {
21442cee19aSThierry Reding 	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
21542cee19aSThierry Reding 
21642cee19aSThierry Reding 	tegra186_wdt_disable(wdt);
21742cee19aSThierry Reding 	tegra186_wdt_enable(wdt);
21842cee19aSThierry Reding 
21942cee19aSThierry Reding 	return 0;
22042cee19aSThierry Reding }
22142cee19aSThierry Reding 
22242cee19aSThierry Reding static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,
22342cee19aSThierry Reding 				    unsigned int timeout)
22442cee19aSThierry Reding {
22542cee19aSThierry Reding 	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
22642cee19aSThierry Reding 
22742cee19aSThierry Reding 	if (watchdog_active(&wdt->base))
22842cee19aSThierry Reding 		tegra186_wdt_disable(wdt);
22942cee19aSThierry Reding 
23042cee19aSThierry Reding 	wdt->base.timeout = timeout;
23142cee19aSThierry Reding 
23242cee19aSThierry Reding 	if (watchdog_active(&wdt->base))
23342cee19aSThierry Reding 		tegra186_wdt_enable(wdt);
23442cee19aSThierry Reding 
23542cee19aSThierry Reding 	return 0;
23642cee19aSThierry Reding }
23742cee19aSThierry Reding 
23842cee19aSThierry Reding static const struct watchdog_ops tegra186_wdt_ops = {
23942cee19aSThierry Reding 	.owner = THIS_MODULE,
24042cee19aSThierry Reding 	.start = tegra186_wdt_start,
24142cee19aSThierry Reding 	.stop = tegra186_wdt_stop,
24242cee19aSThierry Reding 	.ping = tegra186_wdt_ping,
24342cee19aSThierry Reding 	.set_timeout = tegra186_wdt_set_timeout,
24442cee19aSThierry Reding };
24542cee19aSThierry Reding 
24642cee19aSThierry Reding static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra,
24742cee19aSThierry Reding 						unsigned int index)
24842cee19aSThierry Reding {
24942cee19aSThierry Reding 	unsigned int offset = 0x10000, source;
25042cee19aSThierry Reding 	struct tegra186_wdt *wdt;
25142cee19aSThierry Reding 	u32 value;
25242cee19aSThierry Reding 	int err;
25342cee19aSThierry Reding 
25442cee19aSThierry Reding 	offset += tegra->soc->num_timers * 0x10000 + index * 0x10000;
25542cee19aSThierry Reding 
25642cee19aSThierry Reding 	wdt = devm_kzalloc(tegra->dev, sizeof(*wdt), GFP_KERNEL);
25742cee19aSThierry Reding 	if (!wdt)
25842cee19aSThierry Reding 		return ERR_PTR(-ENOMEM);
25942cee19aSThierry Reding 
26042cee19aSThierry Reding 	wdt->regs = tegra->regs + offset;
26142cee19aSThierry Reding 	wdt->index = index;
26242cee19aSThierry Reding 
26342cee19aSThierry Reding 	/* read the watchdog configuration since it might be locked down */
26442cee19aSThierry Reding 	value = wdt_readl(wdt, WDTCR);
26542cee19aSThierry Reding 
26642cee19aSThierry Reding 	if (value & WDTCR_LOCAL_INT_ENABLE)
26742cee19aSThierry Reding 		wdt->locked = true;
26842cee19aSThierry Reding 
26942cee19aSThierry Reding 	source = value & WDTCR_TIMER_SOURCE_MASK;
27042cee19aSThierry Reding 
27142cee19aSThierry Reding 	wdt->tmr = tegra186_tmr_create(tegra, source);
27242cee19aSThierry Reding 	if (IS_ERR(wdt->tmr))
27342cee19aSThierry Reding 		return ERR_CAST(wdt->tmr);
27442cee19aSThierry Reding 
27542cee19aSThierry Reding 	wdt->base.info = &tegra186_wdt_info;
27642cee19aSThierry Reding 	wdt->base.ops = &tegra186_wdt_ops;
27742cee19aSThierry Reding 	wdt->base.min_timeout = 1;
27842cee19aSThierry Reding 	wdt->base.max_timeout = 255;
27942cee19aSThierry Reding 	wdt->base.parent = tegra->dev;
28042cee19aSThierry Reding 
28142cee19aSThierry Reding 	err = watchdog_init_timeout(&wdt->base, 5, tegra->dev);
28242cee19aSThierry Reding 	if (err < 0) {
28342cee19aSThierry Reding 		dev_err(tegra->dev, "failed to initialize timeout: %d\n", err);
28442cee19aSThierry Reding 		return ERR_PTR(err);
28542cee19aSThierry Reding 	}
28642cee19aSThierry Reding 
28742cee19aSThierry Reding 	err = devm_watchdog_register_device(tegra->dev, &wdt->base);
28842cee19aSThierry Reding 	if (err < 0) {
28942cee19aSThierry Reding 		dev_err(tegra->dev, "failed to register WDT: %d\n", err);
29042cee19aSThierry Reding 		return ERR_PTR(err);
29142cee19aSThierry Reding 	}
29242cee19aSThierry Reding 
29342cee19aSThierry Reding 	return wdt;
29442cee19aSThierry Reding }
29542cee19aSThierry Reding 
29642cee19aSThierry Reding static u64 tegra186_timer_tsc_read(struct clocksource *cs)
29742cee19aSThierry Reding {
29842cee19aSThierry Reding 	struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
29942cee19aSThierry Reding 						    tsc);
30042cee19aSThierry Reding 	u32 hi, lo, ss;
30142cee19aSThierry Reding 
30242cee19aSThierry Reding 	hi = readl_relaxed(tegra->regs + TKETSC1);
30342cee19aSThierry Reding 
30442cee19aSThierry Reding 	/*
30542cee19aSThierry Reding 	 * The 56-bit value of the TSC is spread across two registers that are
30642cee19aSThierry Reding 	 * not synchronized. In order to read them atomically, ensure that the
30742cee19aSThierry Reding 	 * high 24 bits match before and after reading the low 32 bits.
30842cee19aSThierry Reding 	 */
30942cee19aSThierry Reding 	do {
31042cee19aSThierry Reding 		/* snapshot the high 24 bits */
31142cee19aSThierry Reding 		ss = hi;
31242cee19aSThierry Reding 
31342cee19aSThierry Reding 		lo = readl_relaxed(tegra->regs + TKETSC0);
31442cee19aSThierry Reding 		hi = readl_relaxed(tegra->regs + TKETSC1);
31542cee19aSThierry Reding 	} while (hi != ss);
31642cee19aSThierry Reding 
31742cee19aSThierry Reding 	return (u64)hi << 32 | lo;
31842cee19aSThierry Reding }
31942cee19aSThierry Reding 
32042cee19aSThierry Reding static int tegra186_timer_tsc_init(struct tegra186_timer *tegra)
32142cee19aSThierry Reding {
32242cee19aSThierry Reding 	tegra->tsc.name = "tsc";
32342cee19aSThierry Reding 	tegra->tsc.rating = 300;
32442cee19aSThierry Reding 	tegra->tsc.read = tegra186_timer_tsc_read;
32542cee19aSThierry Reding 	tegra->tsc.mask = CLOCKSOURCE_MASK(56);
32642cee19aSThierry Reding 	tegra->tsc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
32742cee19aSThierry Reding 
32842cee19aSThierry Reding 	return clocksource_register_hz(&tegra->tsc, 31250000);
32942cee19aSThierry Reding }
33042cee19aSThierry Reding 
33142cee19aSThierry Reding static u64 tegra186_timer_osc_read(struct clocksource *cs)
33242cee19aSThierry Reding {
33342cee19aSThierry Reding 	struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
33442cee19aSThierry Reding 						    osc);
33542cee19aSThierry Reding 
33642cee19aSThierry Reding 	return readl_relaxed(tegra->regs + TKEOSC);
33742cee19aSThierry Reding }
33842cee19aSThierry Reding 
33942cee19aSThierry Reding static int tegra186_timer_osc_init(struct tegra186_timer *tegra)
34042cee19aSThierry Reding {
34142cee19aSThierry Reding 	tegra->osc.name = "osc";
34242cee19aSThierry Reding 	tegra->osc.rating = 300;
34342cee19aSThierry Reding 	tegra->osc.read = tegra186_timer_osc_read;
34442cee19aSThierry Reding 	tegra->osc.mask = CLOCKSOURCE_MASK(32);
34542cee19aSThierry Reding 	tegra->osc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
34642cee19aSThierry Reding 
34742cee19aSThierry Reding 	return clocksource_register_hz(&tegra->osc, 38400000);
34842cee19aSThierry Reding }
34942cee19aSThierry Reding 
35042cee19aSThierry Reding static u64 tegra186_timer_usec_read(struct clocksource *cs)
35142cee19aSThierry Reding {
35242cee19aSThierry Reding 	struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
35342cee19aSThierry Reding 						    usec);
35442cee19aSThierry Reding 
35542cee19aSThierry Reding 	return readl_relaxed(tegra->regs + TKEUSEC);
35642cee19aSThierry Reding }
35742cee19aSThierry Reding 
35842cee19aSThierry Reding static int tegra186_timer_usec_init(struct tegra186_timer *tegra)
35942cee19aSThierry Reding {
36042cee19aSThierry Reding 	tegra->usec.name = "usec";
36142cee19aSThierry Reding 	tegra->usec.rating = 300;
36242cee19aSThierry Reding 	tegra->usec.read = tegra186_timer_usec_read;
36342cee19aSThierry Reding 	tegra->usec.mask = CLOCKSOURCE_MASK(32);
36442cee19aSThierry Reding 	tegra->usec.flags = CLOCK_SOURCE_IS_CONTINUOUS;
36542cee19aSThierry Reding 
36642cee19aSThierry Reding 	return clocksource_register_hz(&tegra->usec, USEC_PER_SEC);
36742cee19aSThierry Reding }
36842cee19aSThierry Reding 
36942cee19aSThierry Reding static irqreturn_t tegra186_timer_irq(int irq, void *data)
37042cee19aSThierry Reding {
37142cee19aSThierry Reding 	struct tegra186_timer *tegra = data;
37242cee19aSThierry Reding 
37342cee19aSThierry Reding 	if (watchdog_active(&tegra->wdt->base)) {
37442cee19aSThierry Reding 		tegra186_wdt_disable(tegra->wdt);
37542cee19aSThierry Reding 		tegra186_wdt_enable(tegra->wdt);
37642cee19aSThierry Reding 	}
37742cee19aSThierry Reding 
37842cee19aSThierry Reding 	return IRQ_HANDLED;
37942cee19aSThierry Reding }
38042cee19aSThierry Reding 
38142cee19aSThierry Reding static int tegra186_timer_probe(struct platform_device *pdev)
38242cee19aSThierry Reding {
38342cee19aSThierry Reding 	struct device *dev = &pdev->dev;
38442cee19aSThierry Reding 	struct tegra186_timer *tegra;
38542cee19aSThierry Reding 	unsigned int irq;
38642cee19aSThierry Reding 	int err;
38742cee19aSThierry Reding 
38842cee19aSThierry Reding 	tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL);
38942cee19aSThierry Reding 	if (!tegra)
39042cee19aSThierry Reding 		return -ENOMEM;
39142cee19aSThierry Reding 
39242cee19aSThierry Reding 	tegra->soc = of_device_get_match_data(dev);
39342cee19aSThierry Reding 	dev_set_drvdata(dev, tegra);
39442cee19aSThierry Reding 	tegra->dev = dev;
39542cee19aSThierry Reding 
39642cee19aSThierry Reding 	tegra->regs = devm_platform_ioremap_resource(pdev, 0);
39742cee19aSThierry Reding 	if (IS_ERR(tegra->regs))
39842cee19aSThierry Reding 		return PTR_ERR(tegra->regs);
39942cee19aSThierry Reding 
40042cee19aSThierry Reding 	err = platform_get_irq(pdev, 0);
40142cee19aSThierry Reding 	if (err < 0)
40242cee19aSThierry Reding 		return err;
40342cee19aSThierry Reding 
40442cee19aSThierry Reding 	irq = err;
40542cee19aSThierry Reding 
40642cee19aSThierry Reding 	/* create a watchdog using a preconfigured timer */
40742cee19aSThierry Reding 	tegra->wdt = tegra186_wdt_create(tegra, 0);
40842cee19aSThierry Reding 	if (IS_ERR(tegra->wdt)) {
40942cee19aSThierry Reding 		err = PTR_ERR(tegra->wdt);
41042cee19aSThierry Reding 		dev_err(dev, "failed to create WDT: %d\n", err);
41142cee19aSThierry Reding 		return err;
41242cee19aSThierry Reding 	}
41342cee19aSThierry Reding 
41442cee19aSThierry Reding 	err = tegra186_timer_tsc_init(tegra);
41542cee19aSThierry Reding 	if (err < 0) {
41642cee19aSThierry Reding 		dev_err(dev, "failed to register TSC counter: %d\n", err);
41742cee19aSThierry Reding 		return err;
41842cee19aSThierry Reding 	}
41942cee19aSThierry Reding 
42042cee19aSThierry Reding 	err = tegra186_timer_osc_init(tegra);
42142cee19aSThierry Reding 	if (err < 0) {
42242cee19aSThierry Reding 		dev_err(dev, "failed to register OSC counter: %d\n", err);
42342cee19aSThierry Reding 		goto unregister_tsc;
42442cee19aSThierry Reding 	}
42542cee19aSThierry Reding 
42642cee19aSThierry Reding 	err = tegra186_timer_usec_init(tegra);
42742cee19aSThierry Reding 	if (err < 0) {
42842cee19aSThierry Reding 		dev_err(dev, "failed to register USEC counter: %d\n", err);
42942cee19aSThierry Reding 		goto unregister_osc;
43042cee19aSThierry Reding 	}
43142cee19aSThierry Reding 
43242cee19aSThierry Reding 	err = devm_request_irq(dev, irq, tegra186_timer_irq, 0,
43342cee19aSThierry Reding 			       "tegra186-timer", tegra);
43442cee19aSThierry Reding 	if (err < 0) {
43542cee19aSThierry Reding 		dev_err(dev, "failed to request IRQ#%u: %d\n", irq, err);
43642cee19aSThierry Reding 		goto unregister_usec;
43742cee19aSThierry Reding 	}
43842cee19aSThierry Reding 
43942cee19aSThierry Reding 	return 0;
44042cee19aSThierry Reding 
44142cee19aSThierry Reding unregister_usec:
44242cee19aSThierry Reding 	clocksource_unregister(&tegra->usec);
44342cee19aSThierry Reding unregister_osc:
44442cee19aSThierry Reding 	clocksource_unregister(&tegra->osc);
44542cee19aSThierry Reding unregister_tsc:
44642cee19aSThierry Reding 	clocksource_unregister(&tegra->tsc);
44742cee19aSThierry Reding 	return err;
44842cee19aSThierry Reding }
44942cee19aSThierry Reding 
45042cee19aSThierry Reding static int tegra186_timer_remove(struct platform_device *pdev)
45142cee19aSThierry Reding {
45242cee19aSThierry Reding 	struct tegra186_timer *tegra = platform_get_drvdata(pdev);
45342cee19aSThierry Reding 
45442cee19aSThierry Reding 	clocksource_unregister(&tegra->usec);
45542cee19aSThierry Reding 	clocksource_unregister(&tegra->osc);
45642cee19aSThierry Reding 	clocksource_unregister(&tegra->tsc);
45742cee19aSThierry Reding 
45842cee19aSThierry Reding 	return 0;
45942cee19aSThierry Reding }
46042cee19aSThierry Reding 
46142cee19aSThierry Reding static int __maybe_unused tegra186_timer_suspend(struct device *dev)
46242cee19aSThierry Reding {
46342cee19aSThierry Reding 	struct tegra186_timer *tegra = dev_get_drvdata(dev);
46442cee19aSThierry Reding 
46542cee19aSThierry Reding 	if (watchdog_active(&tegra->wdt->base))
46642cee19aSThierry Reding 		tegra186_wdt_disable(tegra->wdt);
46742cee19aSThierry Reding 
46842cee19aSThierry Reding 	return 0;
46942cee19aSThierry Reding }
47042cee19aSThierry Reding 
47142cee19aSThierry Reding static int __maybe_unused tegra186_timer_resume(struct device *dev)
47242cee19aSThierry Reding {
47342cee19aSThierry Reding 	struct tegra186_timer *tegra = dev_get_drvdata(dev);
47442cee19aSThierry Reding 
47542cee19aSThierry Reding 	if (watchdog_active(&tegra->wdt->base))
47642cee19aSThierry Reding 		tegra186_wdt_enable(tegra->wdt);
47742cee19aSThierry Reding 
47842cee19aSThierry Reding 	return 0;
47942cee19aSThierry Reding }
48042cee19aSThierry Reding 
48142cee19aSThierry Reding static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend,
48242cee19aSThierry Reding 			 tegra186_timer_resume);
48342cee19aSThierry Reding 
48442cee19aSThierry Reding static const struct tegra186_timer_soc tegra186_timer = {
48542cee19aSThierry Reding 	.num_timers = 10,
48642cee19aSThierry Reding 	.num_wdts = 3,
48742cee19aSThierry Reding };
48842cee19aSThierry Reding 
489*07385a60SKartik static const struct tegra186_timer_soc tegra234_timer = {
490*07385a60SKartik 	.num_timers = 16,
491*07385a60SKartik 	.num_wdts = 3,
492*07385a60SKartik };
493*07385a60SKartik 
49442cee19aSThierry Reding static const struct of_device_id tegra186_timer_of_match[] = {
49542cee19aSThierry Reding 	{ .compatible = "nvidia,tegra186-timer", .data = &tegra186_timer },
496*07385a60SKartik 	{ .compatible = "nvidia,tegra234-timer", .data = &tegra234_timer },
49742cee19aSThierry Reding 	{ }
49842cee19aSThierry Reding };
49942cee19aSThierry Reding MODULE_DEVICE_TABLE(of, tegra186_timer_of_match);
50042cee19aSThierry Reding 
50142cee19aSThierry Reding static struct platform_driver tegra186_wdt_driver = {
50242cee19aSThierry Reding 	.driver = {
50342cee19aSThierry Reding 		.name = "tegra186-timer",
50442cee19aSThierry Reding 		.pm = &tegra186_timer_pm_ops,
50542cee19aSThierry Reding 		.of_match_table = tegra186_timer_of_match,
50642cee19aSThierry Reding 	},
50742cee19aSThierry Reding 	.probe = tegra186_timer_probe,
50842cee19aSThierry Reding 	.remove = tegra186_timer_remove,
50942cee19aSThierry Reding };
51042cee19aSThierry Reding module_platform_driver(tegra186_wdt_driver);
51142cee19aSThierry Reding 
51242cee19aSThierry Reding MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
51342cee19aSThierry Reding MODULE_DESCRIPTION("NVIDIA Tegra186 timers driver");
51442cee19aSThierry Reding MODULE_LICENSE("GPL v2");
515