forked from mirrors/linux
		
	The continual trickle of small conversion patches is grating on me, and is really not helping. Just get rid of the 'remove_new' member function, which is just an alias for the plain 'remove', and had a comment to that effect: /* * .remove_new() is a relic from a prototype conversion of .remove(). * New drivers are supposed to implement .remove(). Once all drivers are * converted to not use .remove_new any more, it will be dropped. */ This was just a tree-wide 'sed' script that replaced '.remove_new' with '.remove', with some care taken to turn a subsequent tab into two tabs to make things line up. I did do some minimal manual whitespace adjustment for places that used spaces to line things up. Then I just removed the old (sic) .remove_new member function, and this is the end result. No more unnecessary conversion noise. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			510 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			510 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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/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 void 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);
 | 
						|
}
 | 
						|
 | 
						|
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 <treding@nvidia.com>");
 | 
						|
MODULE_DESCRIPTION("NVIDIA Tegra186 timers driver");
 |