cregit-Linux how code gets into the kernel

Release 4.7 drivers/watchdog/tegra_wdt.c

Directory: drivers/watchdog
/*
 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>

/* minimum and maximum watchdog trigger timeout, in seconds */

#define MIN_WDT_TIMEOUT			1

#define MAX_WDT_TIMEOUT			255

/*
 * Base of the WDT registers, from the timer base address.  There are
 * actually 5 watchdogs that can be configured (by pairing with an available
 * timer), at bases 0x100 + (WDT ID) * 0x20, where WDT ID is 0 through 4.
 * This driver only configures the first watchdog (WDT ID 0).
 */

#define WDT_BASE			0x100

#define WDT_ID				0

/*
 * Register base of the timer that's selected for pairing with the watchdog.
 * This driver arbitrarily uses timer 5, which is currently unused by
 * other drivers (in particular, the Tegra clocksource driver).  If this
 * needs to change, take care that the new timer is not used by the
 * clocksource driver.
 */

#define WDT_TIMER_BASE			0x60

#define WDT_TIMER_ID			5

/* WDT registers */

#define WDT_CFG				0x0

#define WDT_CFG_PERIOD_SHIFT		4

#define WDT_CFG_PERIOD_MASK		0xff

#define WDT_CFG_INT_EN			(1 << 12)

#define WDT_CFG_PMC2CAR_RST_EN		(1 << 15)

#define WDT_STS				0x4

#define WDT_STS_COUNT_SHIFT		4

#define WDT_STS_COUNT_MASK		0xff

#define WDT_STS_EXP_SHIFT		12

#define WDT_STS_EXP_MASK		0x3

#define WDT_CMD				0x8

#define WDT_CMD_START_COUNTER		(1 << 0)

#define WDT_CMD_DISABLE_COUNTER		(1 << 1)

#define WDT_UNLOCK			(0xc)

#define WDT_UNLOCK_PATTERN		(0xc45a << 0)

/* Timer registers */

#define TIMER_PTV			0x0

#define TIMER_EN			(1 << 31)

#define TIMER_PERIODIC			(1 << 30)


struct tegra_wdt {
	
struct watchdog_device	wdd;
	
void __iomem		*wdt_regs;
	
void __iomem		*tmr_regs;
};


#define WDT_HEARTBEAT 120

static int heartbeat = WDT_HEARTBEAT;
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat,
	"Watchdog heartbeats in seconds. (default = "
	__MODULE_STRING(WDT_HEARTBEAT) ")");


static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
	"Watchdog cannot be stopped once started (default="
	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");


static int tegra_wdt_start(struct watchdog_device *wdd) { struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); u32 val; /* * This thing has a fixed 1MHz clock. Normally, we would set the * period to 1 second by writing 1000000ul, but the watchdog system * reset actually occurs on the 4th expiration of this counter, * so we set the period to 1/4 of this amount. */ val = 1000000ul / 4; val |= (TIMER_EN | TIMER_PERIODIC); writel(val, wdt->tmr_regs + TIMER_PTV); /* * Set number of periods and start counter. * * Interrupt handler is not required for user space * WDT accesses, since the caller is responsible to ping the * WDT to reset the counter before expiration, through ioctls. */ val = WDT_TIMER_ID | (wdd->timeout << WDT_CFG_PERIOD_SHIFT) | WDT_CFG_PMC2CAR_RST_EN; writel(val, wdt->wdt_regs + WDT_CFG); writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew90100.00%1100.00%
Total90100.00%1100.00%


static int tegra_wdt_stop(struct watchdog_device *wdd) { struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK); writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD); writel(0, wdt->tmr_regs + TIMER_PTV); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew57100.00%1100.00%
Total57100.00%1100.00%


static int tegra_wdt_ping(struct watchdog_device *wdd) { struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew35100.00%1100.00%
Total35100.00%1100.00%


static int tegra_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { wdd->timeout = timeout; if (watchdog_active(wdd)) { tegra_wdt_stop(wdd); return tegra_wdt_start(wdd); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew44100.00%2100.00%
Total44100.00%2100.00%


static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd) { struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); u32 val; int count; int exp; val = readl(wdt->wdt_regs + WDT_STS); /* Current countdown (from timeout) */ count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK; /* Number of expirations (we are waiting for the 4th expiration) */ exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK; /* * The entire thing is divided by 4 because we are ticking down 4 times * faster due to needing to wait for the 4th expiration. */ return (((3 - exp) * wdd->timeout) + count) / 4; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew84100.00%1100.00%
Total84100.00%1100.00%

static const struct watchdog_info tegra_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .firmware_version = 0, .identity = "Tegra Watchdog", }; static struct watchdog_ops tegra_wdt_ops = { .owner = THIS_MODULE, .start = tegra_wdt_start, .stop = tegra_wdt_stop, .ping = tegra_wdt_ping, .set_timeout = tegra_wdt_set_timeout, .get_timeleft = tegra_wdt_get_timeleft, };
static int tegra_wdt_probe(struct platform_device *pdev) { struct watchdog_device *wdd; struct tegra_wdt *wdt; struct resource *res; void __iomem *regs; int ret; /* This is the timer base. */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(regs)) return PTR_ERR(regs); /* * Allocate our watchdog driver data, which has the * struct watchdog_device nested within it. */ wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) return -ENOMEM; /* Initialize struct tegra_wdt. */ wdt->wdt_regs = regs + WDT_BASE; wdt->tmr_regs = regs + WDT_TIMER_BASE; /* Initialize struct watchdog_device. */ wdd = &wdt->wdd; wdd->timeout = heartbeat; wdd->info = &tegra_wdt_info; wdd->ops = &tegra_wdt_ops; wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT; wdd->parent = &pdev->dev; watchdog_set_drvdata(wdd, wdt); watchdog_set_nowayout(wdd, nowayout); ret = watchdog_register_device(wdd); if (ret) { dev_err(&pdev->dev, "failed to register watchdog device\n"); return ret; } platform_set_drvdata(pdev, wdt); dev_info(&pdev->dev, "initialized (heartbeat = %d sec, nowayout = %d)\n", heartbeat, nowayout); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew22096.07%150.00%
pratyush anandpratyush anand93.93%150.00%
Total229100.00%2100.00%


static int tegra_wdt_remove(struct platform_device *pdev) { struct tegra_wdt *wdt = platform_get_drvdata(pdev); tegra_wdt_stop(&wdt->wdd); watchdog_unregister_device(&wdt->wdd); dev_info(&pdev->dev, "removed wdt\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew50100.00%1100.00%
Total50100.00%1100.00%

#ifdef CONFIG_PM_SLEEP
static int tegra_wdt_runtime_suspend(struct device *dev) { struct tegra_wdt *wdt = dev_get_drvdata(dev); if (watchdog_active(&wdt->wdd)) tegra_wdt_stop(&wdt->wdd); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew42100.00%1100.00%
Total42100.00%1100.00%


static int tegra_wdt_runtime_resume(struct device *dev) { struct tegra_wdt *wdt = dev_get_drvdata(dev); if (watchdog_active(&wdt->wdd)) tegra_wdt_start(&wdt->wdd); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew42100.00%1100.00%
Total42100.00%1100.00%

#endif static const struct of_device_id tegra_wdt_of_match[] = { { .compatible = "nvidia,tegra30-timer", }, { }, }; MODULE_DEVICE_TABLE(of, tegra_wdt_of_match); static const struct dev_pm_ops tegra_wdt_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend, tegra_wdt_runtime_resume) }; static struct platform_driver tegra_wdt_driver = { .probe = tegra_wdt_probe, .remove = tegra_wdt_remove, .driver = { .name = "tegra-wdt", .pm = &tegra_wdt_pm_ops, .of_match_table = tegra_wdt_of_match, }, }; module_platform_driver(tegra_wdt_driver); MODULE_AUTHOR("NVIDIA Corporation"); MODULE_DESCRIPTION("Tegra Watchdog Driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
andrew chewandrew chew103199.13%266.67%
pratyush anandpratyush anand90.87%133.33%
Total1040100.00%3100.00%
Directory: drivers/watchdog
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}