brintos

brintos / linux-shallow public Read only

0
0
Text · 12.1 KiB · 304537d Raw
511 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.4 */5 6#include <linux/clocksource.h>7#include <linux/module.h>8#include <linux/interrupt.h>9#include <linux/io.h>10#include <linux/of.h>11#include <linux/platform_device.h>12#include <linux/pm.h>13#include <linux/watchdog.h>14 15/* shared registers */16#define TKETSC0 0x00017#define TKETSC1 0x00418#define TKEUSEC 0x00819#define TKEOSC  0x00c20 21#define TKEIE(x) (0x100 + ((x) * 4))22#define  TKEIE_WDT_MASK(x, y) ((y) << (16 + 4 * (x)))23 24/* timer registers */25#define TMRCR 0x00026#define  TMRCR_ENABLE BIT(31)27#define  TMRCR_PERIODIC BIT(30)28#define  TMRCR_PTV(x) ((x) & 0x0fffffff)29 30#define TMRSR 0x00431#define  TMRSR_INTR_CLR BIT(30)32 33#define TMRCSSR 0x00834#define  TMRCSSR_SRC_USEC (0 << 0)35 36/* watchdog registers */37#define WDTCR 0x00038#define  WDTCR_SYSTEM_POR_RESET_ENABLE BIT(16)39#define  WDTCR_SYSTEM_DEBUG_RESET_ENABLE BIT(15)40#define  WDTCR_REMOTE_INT_ENABLE BIT(14)41#define  WDTCR_LOCAL_FIQ_ENABLE BIT(13)42#define  WDTCR_LOCAL_INT_ENABLE BIT(12)43#define  WDTCR_PERIOD_MASK (0xff << 4)44#define  WDTCR_PERIOD(x) (((x) & 0xff) << 4)45#define  WDTCR_TIMER_SOURCE_MASK 0xf46#define  WDTCR_TIMER_SOURCE(x) ((x) & 0xf)47 48#define WDTCMDR 0x00849#define  WDTCMDR_DISABLE_COUNTER BIT(1)50#define  WDTCMDR_START_COUNTER BIT(0)51 52#define WDTUR 0x00c53#define  WDTUR_UNLOCK_PATTERN 0x0000c45a54 55struct tegra186_timer_soc {56	unsigned int num_timers;57	unsigned int num_wdts;58};59 60struct tegra186_tmr {61	struct tegra186_timer *parent;62	void __iomem *regs;63	unsigned int index;64	unsigned int hwirq;65};66 67struct tegra186_wdt {68	struct watchdog_device base;69 70	void __iomem *regs;71	unsigned int index;72	bool locked;73 74	struct tegra186_tmr *tmr;75};76 77static inline struct tegra186_wdt *to_tegra186_wdt(struct watchdog_device *wdd)78{79	return container_of(wdd, struct tegra186_wdt, base);80}81 82struct tegra186_timer {83	const struct tegra186_timer_soc *soc;84	struct device *dev;85	void __iomem *regs;86 87	struct tegra186_wdt *wdt;88	struct clocksource usec;89	struct clocksource tsc;90	struct clocksource osc;91};92 93static void tmr_writel(struct tegra186_tmr *tmr, u32 value, unsigned int offset)94{95	writel_relaxed(value, tmr->regs + offset);96}97 98static void wdt_writel(struct tegra186_wdt *wdt, u32 value, unsigned int offset)99{100	writel_relaxed(value, wdt->regs + offset);101}102 103static u32 wdt_readl(struct tegra186_wdt *wdt, unsigned int offset)104{105	return readl_relaxed(wdt->regs + offset);106}107 108static struct tegra186_tmr *tegra186_tmr_create(struct tegra186_timer *tegra,109						unsigned int index)110{111	unsigned int offset = 0x10000 + index * 0x10000;112	struct tegra186_tmr *tmr;113 114	tmr = devm_kzalloc(tegra->dev, sizeof(*tmr), GFP_KERNEL);115	if (!tmr)116		return ERR_PTR(-ENOMEM);117 118	tmr->parent = tegra;119	tmr->regs = tegra->regs + offset;120	tmr->index = index;121	tmr->hwirq = 0;122 123	return tmr;124}125 126static const struct watchdog_info tegra186_wdt_info = {127	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,128	.identity = "NVIDIA Tegra186 WDT",129};130 131static void tegra186_wdt_disable(struct tegra186_wdt *wdt)132{133	/* unlock and disable the watchdog */134	wdt_writel(wdt, WDTUR_UNLOCK_PATTERN, WDTUR);135	wdt_writel(wdt, WDTCMDR_DISABLE_COUNTER, WDTCMDR);136 137	/* disable timer */138	tmr_writel(wdt->tmr, 0, TMRCR);139}140 141static void tegra186_wdt_enable(struct tegra186_wdt *wdt)142{143	struct tegra186_timer *tegra = wdt->tmr->parent;144	u32 value;145 146	/* unmask hardware IRQ, this may have been lost across powergate */147	value = TKEIE_WDT_MASK(wdt->index, 1);148	writel(value, tegra->regs + TKEIE(wdt->tmr->hwirq));149 150	/* clear interrupt */151	tmr_writel(wdt->tmr, TMRSR_INTR_CLR, TMRSR);152 153	/* select microsecond source */154	tmr_writel(wdt->tmr, TMRCSSR_SRC_USEC, TMRCSSR);155 156	/* configure timer (system reset happens on the fifth expiration) */157	value = TMRCR_PTV(wdt->base.timeout * USEC_PER_SEC / 5) |158		TMRCR_PERIODIC | TMRCR_ENABLE;159	tmr_writel(wdt->tmr, value, TMRCR);160 161	if (!wdt->locked) {162		value = wdt_readl(wdt, WDTCR);163 164		/* select the proper timer source */165		value &= ~WDTCR_TIMER_SOURCE_MASK;166		value |= WDTCR_TIMER_SOURCE(wdt->tmr->index);167 168		/* single timer period since that's already configured */169		value &= ~WDTCR_PERIOD_MASK;170		value |= WDTCR_PERIOD(1);171 172		/* enable local interrupt for WDT petting */173		value |= WDTCR_LOCAL_INT_ENABLE;174 175		/* enable local FIQ and remote interrupt for debug dump */176		if (0)177			value |= WDTCR_REMOTE_INT_ENABLE |178				 WDTCR_LOCAL_FIQ_ENABLE;179 180		/* enable system debug reset (doesn't properly reboot) */181		if (0)182			value |= WDTCR_SYSTEM_DEBUG_RESET_ENABLE;183 184		/* enable system POR reset */185		value |= WDTCR_SYSTEM_POR_RESET_ENABLE;186 187		wdt_writel(wdt, value, WDTCR);188	}189 190	wdt_writel(wdt, WDTCMDR_START_COUNTER, WDTCMDR);191}192 193static int tegra186_wdt_start(struct watchdog_device *wdd)194{195	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);196 197	tegra186_wdt_enable(wdt);198 199	return 0;200}201 202static int tegra186_wdt_stop(struct watchdog_device *wdd)203{204	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);205 206	tegra186_wdt_disable(wdt);207 208	return 0;209}210 211static int tegra186_wdt_ping(struct watchdog_device *wdd)212{213	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);214 215	tegra186_wdt_disable(wdt);216	tegra186_wdt_enable(wdt);217 218	return 0;219}220 221static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,222				    unsigned int timeout)223{224	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);225 226	if (watchdog_active(&wdt->base))227		tegra186_wdt_disable(wdt);228 229	wdt->base.timeout = timeout;230 231	if (watchdog_active(&wdt->base))232		tegra186_wdt_enable(wdt);233 234	return 0;235}236 237static const struct watchdog_ops tegra186_wdt_ops = {238	.owner = THIS_MODULE,239	.start = tegra186_wdt_start,240	.stop = tegra186_wdt_stop,241	.ping = tegra186_wdt_ping,242	.set_timeout = tegra186_wdt_set_timeout,243};244 245static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra,246						unsigned int index)247{248	unsigned int offset = 0x10000, source;249	struct tegra186_wdt *wdt;250	u32 value;251	int err;252 253	offset += tegra->soc->num_timers * 0x10000 + index * 0x10000;254 255	wdt = devm_kzalloc(tegra->dev, sizeof(*wdt), GFP_KERNEL);256	if (!wdt)257		return ERR_PTR(-ENOMEM);258 259	wdt->regs = tegra->regs + offset;260	wdt->index = index;261 262	/* read the watchdog configuration since it might be locked down */263	value = wdt_readl(wdt, WDTCR);264 265	if (value & WDTCR_LOCAL_INT_ENABLE)266		wdt->locked = true;267 268	source = value & WDTCR_TIMER_SOURCE_MASK;269 270	wdt->tmr = tegra186_tmr_create(tegra, source);271	if (IS_ERR(wdt->tmr))272		return ERR_CAST(wdt->tmr);273 274	wdt->base.info = &tegra186_wdt_info;275	wdt->base.ops = &tegra186_wdt_ops;276	wdt->base.min_timeout = 1;277	wdt->base.max_timeout = 255;278	wdt->base.parent = tegra->dev;279 280	err = watchdog_init_timeout(&wdt->base, 5, tegra->dev);281	if (err < 0) {282		dev_err(tegra->dev, "failed to initialize timeout: %d\n", err);283		return ERR_PTR(err);284	}285 286	err = devm_watchdog_register_device(tegra->dev, &wdt->base);287	if (err < 0) {288		dev_err(tegra->dev, "failed to register WDT: %d\n", err);289		return ERR_PTR(err);290	}291 292	return wdt;293}294 295static u64 tegra186_timer_tsc_read(struct clocksource *cs)296{297	struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,298						    tsc);299	u32 hi, lo, ss;300 301	hi = readl_relaxed(tegra->regs + TKETSC1);302 303	/*304	 * The 56-bit value of the TSC is spread across two registers that are305	 * not synchronized. In order to read them atomically, ensure that the306	 * high 24 bits match before and after reading the low 32 bits.307	 */308	do {309		/* snapshot the high 24 bits */310		ss = hi;311 312		lo = readl_relaxed(tegra->regs + TKETSC0);313		hi = readl_relaxed(tegra->regs + TKETSC1);314	} while (hi != ss);315 316	return (u64)hi << 32 | lo;317}318 319static int tegra186_timer_tsc_init(struct tegra186_timer *tegra)320{321	tegra->tsc.name = "tsc";322	tegra->tsc.rating = 300;323	tegra->tsc.read = tegra186_timer_tsc_read;324	tegra->tsc.mask = CLOCKSOURCE_MASK(56);325	tegra->tsc.flags = CLOCK_SOURCE_IS_CONTINUOUS;326 327	return clocksource_register_hz(&tegra->tsc, 31250000);328}329 330static u64 tegra186_timer_osc_read(struct clocksource *cs)331{332	struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,333						    osc);334 335	return readl_relaxed(tegra->regs + TKEOSC);336}337 338static int tegra186_timer_osc_init(struct tegra186_timer *tegra)339{340	tegra->osc.name = "osc";341	tegra->osc.rating = 300;342	tegra->osc.read = tegra186_timer_osc_read;343	tegra->osc.mask = CLOCKSOURCE_MASK(32);344	tegra->osc.flags = CLOCK_SOURCE_IS_CONTINUOUS;345 346	return clocksource_register_hz(&tegra->osc, 38400000);347}348 349static u64 tegra186_timer_usec_read(struct clocksource *cs)350{351	struct tegra186_timer *tegra = container_of(cs, struct tegra186_timer,352						    usec);353 354	return readl_relaxed(tegra->regs + TKEUSEC);355}356 357static int tegra186_timer_usec_init(struct tegra186_timer *tegra)358{359	tegra->usec.name = "usec";360	tegra->usec.rating = 300;361	tegra->usec.read = tegra186_timer_usec_read;362	tegra->usec.mask = CLOCKSOURCE_MASK(32);363	tegra->usec.flags = CLOCK_SOURCE_IS_CONTINUOUS;364 365	return clocksource_register_hz(&tegra->usec, USEC_PER_SEC);366}367 368static irqreturn_t tegra186_timer_irq(int irq, void *data)369{370	struct tegra186_timer *tegra = data;371 372	if (watchdog_active(&tegra->wdt->base)) {373		tegra186_wdt_disable(tegra->wdt);374		tegra186_wdt_enable(tegra->wdt);375	}376 377	return IRQ_HANDLED;378}379 380static int tegra186_timer_probe(struct platform_device *pdev)381{382	struct device *dev = &pdev->dev;383	struct tegra186_timer *tegra;384	unsigned int irq;385	int err;386 387	tegra = devm_kzalloc(dev, sizeof(*tegra), GFP_KERNEL);388	if (!tegra)389		return -ENOMEM;390 391	tegra->soc = of_device_get_match_data(dev);392	dev_set_drvdata(dev, tegra);393	tegra->dev = dev;394 395	tegra->regs = devm_platform_ioremap_resource(pdev, 0);396	if (IS_ERR(tegra->regs))397		return PTR_ERR(tegra->regs);398 399	err = platform_get_irq(pdev, 0);400	if (err < 0)401		return err;402 403	irq = err;404 405	/* create a watchdog using a preconfigured timer */406	tegra->wdt = tegra186_wdt_create(tegra, 0);407	if (IS_ERR(tegra->wdt)) {408		err = PTR_ERR(tegra->wdt);409		dev_err(dev, "failed to create WDT: %d\n", err);410		return err;411	}412 413	err = tegra186_timer_tsc_init(tegra);414	if (err < 0) {415		dev_err(dev, "failed to register TSC counter: %d\n", err);416		return err;417	}418 419	err = tegra186_timer_osc_init(tegra);420	if (err < 0) {421		dev_err(dev, "failed to register OSC counter: %d\n", err);422		goto unregister_tsc;423	}424 425	err = tegra186_timer_usec_init(tegra);426	if (err < 0) {427		dev_err(dev, "failed to register USEC counter: %d\n", err);428		goto unregister_osc;429	}430 431	err = devm_request_irq(dev, irq, tegra186_timer_irq, 0,432			       "tegra186-timer", tegra);433	if (err < 0) {434		dev_err(dev, "failed to request IRQ#%u: %d\n", irq, err);435		goto unregister_usec;436	}437 438	return 0;439 440unregister_usec:441	clocksource_unregister(&tegra->usec);442unregister_osc:443	clocksource_unregister(&tegra->osc);444unregister_tsc:445	clocksource_unregister(&tegra->tsc);446	return err;447}448 449static void tegra186_timer_remove(struct platform_device *pdev)450{451	struct tegra186_timer *tegra = platform_get_drvdata(pdev);452 453	clocksource_unregister(&tegra->usec);454	clocksource_unregister(&tegra->osc);455	clocksource_unregister(&tegra->tsc);456}457 458static int __maybe_unused tegra186_timer_suspend(struct device *dev)459{460	struct tegra186_timer *tegra = dev_get_drvdata(dev);461 462	if (watchdog_active(&tegra->wdt->base))463		tegra186_wdt_disable(tegra->wdt);464 465	return 0;466}467 468static int __maybe_unused tegra186_timer_resume(struct device *dev)469{470	struct tegra186_timer *tegra = dev_get_drvdata(dev);471 472	if (watchdog_active(&tegra->wdt->base))473		tegra186_wdt_enable(tegra->wdt);474 475	return 0;476}477 478static SIMPLE_DEV_PM_OPS(tegra186_timer_pm_ops, tegra186_timer_suspend,479			 tegra186_timer_resume);480 481static const struct tegra186_timer_soc tegra186_timer = {482	.num_timers = 10,483	.num_wdts = 3,484};485 486static const struct tegra186_timer_soc tegra234_timer = {487	.num_timers = 16,488	.num_wdts = 3,489};490 491static const struct of_device_id tegra186_timer_of_match[] = {492	{ .compatible = "nvidia,tegra186-timer", .data = &tegra186_timer },493	{ .compatible = "nvidia,tegra234-timer", .data = &tegra234_timer },494	{ }495};496MODULE_DEVICE_TABLE(of, tegra186_timer_of_match);497 498static struct platform_driver tegra186_wdt_driver = {499	.driver = {500		.name = "tegra186-timer",501		.pm = &tegra186_timer_pm_ops,502		.of_match_table = tegra186_timer_of_match,503	},504	.probe = tegra186_timer_probe,505	.remove_new = tegra186_timer_remove,506};507module_platform_driver(tegra186_wdt_driver);508 509MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");510MODULE_DESCRIPTION("NVIDIA Tegra186 timers driver");511