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