123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
- */
- #include <linux/clocksource.h>
- #include <linux/module.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/platform_device.h>
- #include <linux/pm.h>
- #include <linux/watchdog.h>
- /* shared registers */
- #define TKETSC0 0x000
- #define TKETSC1 0x004
- #define TKEUSEC 0x008
- #define TKEOSC 0x00c
- #define TKEIE(x) (0x100 + ((x) * 4))
- #define TKEIE_WDT_MASK(x, y) ((y) << (16 + 4 * (x)))
- /* timer registers */
- #define TMRCR 0x000
- #define TMRCR_ENABLE BIT(31)
- #define TMRCR_PERIODIC BIT(30)
- #define TMRCR_PTV(x) ((x) & 0x0fffffff)
- #define TMRSR 0x004
- #define TMRSR_INTR_CLR BIT(30)
- #define TMRCSSR 0x008
- #define TMRCSSR_SRC_USEC (0 << 0)
- /* watchdog registers */
- #define WDTCR 0x000
- #define WDTCR_SYSTEM_POR_RESET_ENABLE BIT(16)
- #define WDTCR_SYSTEM_DEBUG_RESET_ENABLE BIT(15)
- #define WDTCR_REMOTE_INT_ENABLE BIT(14)
- #define WDTCR_LOCAL_FIQ_ENABLE BIT(13)
- #define WDTCR_LOCAL_INT_ENABLE BIT(12)
- #define WDTCR_PERIOD_MASK (0xff << 4)
- #define WDTCR_PERIOD(x) (((x) & 0xff) << 4)
- #define WDTCR_TIMER_SOURCE_MASK 0xf
- #define WDTCR_TIMER_SOURCE(x) ((x) & 0xf)
- #define WDTCMDR 0x008
- #define WDTCMDR_DISABLE_COUNTER BIT(1)
- #define WDTCMDR_START_COUNTER BIT(0)
- #define WDTUR 0x00c
- #define WDTUR_UNLOCK_PATTERN 0x0000c45a
- struct tegra186_timer_soc {
- unsigned int num_timers;
- unsigned int num_wdts;
- };
- struct tegra186_tmr {
- struct tegra186_timer *parent;
- void __iomem *regs;
- unsigned int index;
- unsigned int hwirq;
- };
- struct tegra186_wdt {
- struct watchdog_device base;
- void __iomem *regs;
- unsigned int index;
- bool locked;
- struct tegra186_tmr *tmr;
- };
- static inline struct tegra186_wdt *to_tegra186_wdt(struct watchdog_device *wdd)
- {
- return container_of(wdd, struct tegra186_wdt, base);
- }
- struct tegra186_timer {
- const struct tegra186_timer_soc *soc;
- struct device *dev;
- void __iomem *regs;
- struct tegra186_wdt *wdt;
- struct clocksource usec;
- struct clocksource tsc;
- struct clocksource osc;
- };
- static void tmr_writel(struct tegra186_tmr *tmr, u32 value, unsigned int offset)
- {
- writel_relaxed(value, tmr->regs + offset);
- }
- static void wdt_writel(struct tegra186_wdt *wdt, u32 value, unsigned int offset)
- {
- writel_relaxed(value, wdt->regs + offset);
- }
- static u32 wdt_readl(struct tegra186_wdt *wdt, unsigned int offset)
- {
- return readl_relaxed(wdt->regs + offset);
- }
- static struct tegra186_tmr *tegra186_tmr_create(struct tegra186_timer *tegra,
- unsigned int index)
- {
- unsigned int offset = 0x10000 + index * 0x10000;
- struct tegra186_tmr *tmr;
- tmr = devm_kzalloc(tegra->dev, sizeof(*tmr), GFP_KERNEL);
- if (!tmr)
- return ERR_PTR(-ENOMEM);
- tmr->parent = tegra;
- tmr->regs = tegra->regs + offset;
- tmr->index = index;
- tmr->hwirq = 0;
- return tmr;
- }
- static const struct watchdog_info tegra186_wdt_info = {
- .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
- .identity = "NVIDIA Tegra186 WDT",
- };
- static void tegra186_wdt_disable(struct tegra186_wdt *wdt)
- {
- /* unlock and disable the watchdog */
- wdt_writel(wdt, WDTUR_UNLOCK_PATTERN, WDTUR);
- wdt_writel(wdt, WDTCMDR_DISABLE_COUNTER, WDTCMDR);
- /* disable timer */
- tmr_writel(wdt->tmr, 0, TMRCR);
- }
- static void tegra186_wdt_enable(struct tegra186_wdt *wdt)
- {
- struct tegra186_timer *tegra = wdt->tmr->parent;
- u32 value;
- /* unmask hardware IRQ, this may have been lost across powergate */
- value = TKEIE_WDT_MASK(wdt->index, 1);
- writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq));
- /* clear interrupt */
- tmr_writel(wdt->tmr, TMRSR_INTR_CLR, TMRSR);
- /* select microsecond source */
- tmr_writel(wdt->tmr, TMRCSSR_SRC_USEC, TMRCSSR);
- /* configure timer (system reset happens on the fifth expiration) */
- value = TMRCR_PTV(wdt->base.timeout * USEC_PER_SEC / 5) |
- TMRCR_PERIODIC | TMRCR_ENABLE;
- tmr_writel(wdt->tmr, value, TMRCR);
- if (!wdt->locked) {
- value = wdt_readl(wdt, WDTCR);
- /* select the proper timer source */
- value &= ~WDTCR_TIMER_SOURCE_MASK;
- value |= WDTCR_TIMER_SOURCE(wdt->tmr->index);
- /* single timer period since that's already configured */
- value &= ~WDTCR_PERIOD_MASK;
- value |= WDTCR_PERIOD(1);
- /* enable local interrupt for WDT petting */
- value |= WDTCR_LOCAL_INT_ENABLE;
- /* enable local FIQ and remote interrupt for debug dump */
- if (0)
- value |= WDTCR_REMOTE_INT_ENABLE |
- WDTCR_LOCAL_FIQ_ENABLE;
- /* enable system debug reset (doesn't properly reboot) */
- if (0)
- value |= WDTCR_SYSTEM_DEBUG_RESET_ENABLE;
- /* enable system POR reset */
- value |= WDTCR_SYSTEM_POR_RESET_ENABLE;
- wdt_writel(wdt, value, WDTCR);
- }
- wdt_writel(wdt, WDTCMDR_START_COUNTER, WDTCMDR);
- }
- static int tegra186_wdt_start(struct watchdog_device *wdd)
- {
- struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
- tegra186_wdt_enable(wdt);
- return 0;
- }
- static int tegra186_wdt_stop(struct watchdog_device *wdd)
- {
- struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
- tegra186_wdt_disable(wdt);
- return 0;
- }
- static int tegra186_wdt_ping(struct watchdog_device *wdd)
- {
- struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
- tegra186_wdt_disable(wdt);
- tegra186_wdt_enable(wdt);
- return 0;
- }
- static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,
- unsigned int timeout)
- {
- struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
- if (watchdog_active(&wdt->base))
- tegra186_wdt_disable(wdt);
- wdt->base.timeout = timeout;
- if (watchdog_active(&wdt->base))
- tegra186_wdt_enable(wdt);
- return 0;
- }
- static const struct watchdog_ops tegra186_wdt_ops = {
- .owner = THIS_MODULE,
- .start = tegra186_wdt_start,
- .stop = tegra186_wdt_stop,
- .ping = tegra186_wdt_ping,
- .set_timeout = tegra186_wdt_set_timeout,
- };
- static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra,
- unsigned int index)
- {
- unsigned int offset = 0x10000, source;
- struct tegra186_wdt *wdt;
- u32 value;
- int err;
- offset += tegra->soc->num_timers * 0x10000 + index * 0x10000;
- wdt = devm_kzalloc(tegra->dev, sizeof(*wdt), GFP_KERNEL);
- if (!wdt)
- return ERR_PTR(-ENOMEM);
- wdt->regs = tegra->regs + offset;
- wdt->index = index;
- /* read the watchdog configuration since it might be locked down */
- value = wdt_readl(wdt, WDTCR);
- if (value & WDTCR_LOCAL_INT_ENABLE)
- wdt->locked = true;
- source = value & WDTCR_TIMER_SOURCE_MASK;
- wdt->tmr = tegra186_tmr_create(tegra, source);
- if (IS_ERR(wdt->tmr))
- return ERR_CAST(wdt->tmr);
- wdt->base.info = &tegra186_wdt_info;
- wdt->base.ops = &tegra186_wdt_ops;
- wdt->base.min_timeout = 1;
- wdt->base.max_timeout = 255;
- wdt->base.parent = tegra->dev;
- err = watchdog_init_timeout(&wdt->base, 5, tegra->dev);
- if (err < 0) {
- dev_err(tegra->dev, "failed to initialize timeout: %d\n", err);
- return ERR_PTR(err);
- }
- err = devm_watchdog_register_device(tegra->dev, &wdt->base);
- if (err < 0) {
- dev_err(tegra->dev, "failed to register WDT: %d\n", err);
- return ERR_PTR(err);
- }
- return wdt;
- }
- static u64 tegra186_timer_tsc_read(struct clocksource *cs)
- {
- struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
- tsc);
- u32 hi, lo, ss;
- hi = readl_relaxed(tegra->regs + TKETSC1);
- /*
- * The 56-bit value of the TSC is spread across two registers that are
- * not synchronized. In order to read them atomically, ensure that the
- * high 24 bits match before and after reading the low 32 bits.
- */
- do {
- /* snapshot the high 24 bits */
- ss = hi;
- lo = readl_relaxed(tegra->regs + TKETSC0);
- hi = readl_relaxed(tegra->regs + TKETSC1);
- } while (hi != ss);
- return (u64)hi << 32 | lo;
- }
- static int tegra186_timer_tsc_init(struct tegra186_timer *tegra)
- {
- tegra->tsc.name = "tsc";
- tegra->tsc.rating = 300;
- tegra->tsc.read = tegra186_timer_tsc_read;
- tegra->tsc.mask = CLOCKSOURCE_MASK(56);
- tegra->tsc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
- return clocksource_register_hz(&tegra->tsc, 31250000);
- }
- static u64 tegra186_timer_osc_read(struct clocksource *cs)
- {
- struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
- osc);
- return readl_relaxed(tegra->regs + TKEOSC);
- }
- static int tegra186_timer_osc_init(struct tegra186_timer *tegra)
- {
- tegra->osc.name = "osc";
- tegra->osc.rating = 300;
- tegra->osc.read = tegra186_timer_osc_read;
- tegra->osc.mask = CLOCKSOURCE_MASK(32);
- tegra->osc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
- return clocksource_register_hz(&tegra->osc, 38400000);
- }
- static u64 tegra186_timer_usec_read(struct clocksource *cs)
- {
- struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,
- usec);
- return readl_relaxed(tegra->regs + TKEUSEC);
- }
- static int tegra186_timer_usec_init(struct tegra186_timer *tegra)
- {
- tegra->usec.name = "usec";
- tegra->usec.rating = 300;
- tegra->usec.read = tegra186_timer_usec_read;
- tegra->usec.mask = CLOCKSOURCE_MASK(32);
- tegra->usec.flags = CLOCK_SOURCE_IS_CONTINUOUS;
- return clocksource_register_hz(&tegra->usec, USEC_PER_SEC);
- }
- static irqreturn_t tegra186_timer_irq(int irq, void *data)
- {
- struct tegra186_timer *tegra = data;
- if (watchdog_active(&tegra->wdt->base)) {
- tegra186_wdt_disable(tegra->wdt);
- tegra186_wdt_enable(tegra->wdt);
- }
- return IRQ_HANDLED;
- }
- static int tegra186_timer_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct tegra186_timer *tegra;
- unsigned int irq;
- int err;
- tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL);
- if (!tegra)
- return -ENOMEM;
- tegra->soc = of_device_get_match_data(dev);
- dev_set_drvdata(dev, tegra);
- tegra->dev = dev;
- tegra->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(tegra->regs))
- return PTR_ERR(tegra->regs);
- err = platform_get_irq(pdev, 0);
- if (err < 0)
- return err;
- irq = err;
- /* create a watchdog using a preconfigured timer */
- tegra->wdt = tegra186_wdt_create(tegra, 0);
- if (IS_ERR(tegra->wdt)) {
- err = PTR_ERR(tegra->wdt);
- dev_err(dev, "failed to create WDT: %d\n", err);
- return err;
- }
- err = tegra186_timer_tsc_init(tegra);
- if (err < 0) {
- dev_err(dev, "failed to register TSC counter: %d\n", err);
- return err;
- }
- err = tegra186_timer_osc_init(tegra);
- if (err < 0) {
- dev_err(dev, "failed to register OSC counter: %d\n", err);
- goto unregister_tsc;
- }
- err = tegra186_timer_usec_init(tegra);
- if (err < 0) {
- dev_err(dev, "failed to register USEC counter: %d\n", err);
- goto unregister_osc;
- }
- err = devm_request_irq(dev, irq, tegra186_timer_irq, 0,
- "tegra186-timer", tegra);
- if (err < 0) {
- dev_err(dev, "failed to request IRQ#%u: %d\n", irq, err);
- goto unregister_usec;
- }
- return 0;
- unregister_usec:
- clocksource_unregister(&tegra->usec);
- unregister_osc:
- clocksource_unregister(&tegra->osc);
- unregister_tsc:
- clocksource_unregister(&tegra->tsc);
- return err;
- }
- static int tegra186_timer_remove(struct platform_device *pdev)
- {
- struct tegra186_timer *tegra = platform_get_drvdata(pdev);
- clocksource_unregister(&tegra->usec);
- clocksource_unregister(&tegra->osc);
- clocksource_unregister(&tegra->tsc);
- return 0;
- }
- static int __maybe_unused tegra186_timer_suspend(struct device *dev)
- {
- struct tegra186_timer *tegra = dev_get_drvdata(dev);
- if (watchdog_active(&tegra->wdt->base))
- tegra186_wdt_disable(tegra->wdt);
- return 0;
- }
- static int __maybe_unused tegra186_timer_resume(struct device *dev)
- {
- struct tegra186_timer *tegra = dev_get_drvdata(dev);
- if (watchdog_active(&tegra->wdt->base))
- tegra186_wdt_enable(tegra->wdt);
- return 0;
- }
- static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend,
- tegra186_timer_resume);
- static const struct tegra186_timer_soc tegra186_timer = {
- .num_timers = 10,
- .num_wdts = 3,
- };
- static const struct tegra186_timer_soc tegra234_timer = {
- .num_timers = 16,
- .num_wdts = 3,
- };
- static const struct of_device_id tegra186_timer_of_match[] = {
- { .compatible = "nvidia,tegra186-timer", .data = &tegra186_timer },
- { .compatible = "nvidia,tegra234-timer", .data = &tegra234_timer },
- { }
- };
- MODULE_DEVICE_TABLE(of, tegra186_timer_of_match);
- static struct platform_driver tegra186_wdt_driver = {
- .driver = {
- .name = "tegra186-timer",
- .pm = &tegra186_timer_pm_ops,
- .of_match_table = tegra186_timer_of_match,
- },
- .probe = tegra186_timer_probe,
- .remove = tegra186_timer_remove,
- };
- module_platform_driver(tegra186_wdt_driver);
- MODULE_AUTHOR("Thierry Reding <[email protected]>");
- MODULE_DESCRIPTION("NVIDIA Tegra186 timers driver");
- MODULE_LICENSE("GPL v2");
|