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