From 7f5ad900a3b2f2defc3e32c4970b32f95d334d80 Mon Sep 17 00:00:00 2001 From: Shreshtha SAHU Date: Mon, 5 Jun 2017 19:41:44 +0530 Subject: [PATCH] watchdog: tegra: select timer based on timer base Remove hardcoding of timer ID and select watchdog timer source based on timer base provided in DT timer resource property Bug 200314562 Change-Id: Ic12b881663b48b5ad260b39b3985ffdd505873bf Signed-off-by: Shreshtha SAHU Reviewed-on: https://git-master/r/1515196 GVS: Gerrit_Virtual_Submit Reviewed-by: Bibek Basu Tested-by: Bibek Basu --- drivers/watchdog/tegra_wdt.c | 90 +++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index 3871cd0cfdc2..283064fcd077 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -23,16 +23,6 @@ #define MIN_WDT_TIMEOUT 1 #define MAX_WDT_TIMEOUT 255 -/* - * 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 @@ -51,6 +41,7 @@ #define WDT_UNLOCK_PATTERN (0xc45a << 0) /* Timer registers */ +#define TIMER_BASE_ADDR 0x60005088 #define TIMER_PTV 0x0 #define TIMER_EN (1 << 31) #define TIMER_PERIODIC (1 << 30) @@ -59,6 +50,7 @@ struct tegra_wdt { struct watchdog_device wdd; void __iomem *wdt_regs; void __iomem *tmr_regs; + u8 timer_id; }; /* @@ -89,6 +81,49 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +/* + * Timer ID is required to be programmed to WDT_CFG register + * As timer address and timer ID mapping is non-linear, + * hence these function are defined to do the conversion. + */ +static resource_size_t tegra_tmr_addr(int timer_id) +{ + int i; + resource_size_t timer_addr; + + if (timer_id == 0) + i = 10; + else if (timer_id < 3) + i = timer_id - 8; + else if (timer_id > 9) + i = timer_id + 1; + else + i = timer_id; + + timer_addr = TIMER_BASE_ADDR - (10 - i) * 8; + + return timer_addr; +} + +static int tegra_tmr_index(struct resource *tmr_res) +{ + int timer_id; + + /* Select Timer ID using Timer base address provided in DT: + * [Base Addr:Timer#] + * [0x00 : 1], [0x08 : 2], [0x50 : 3], [0x58 : 4], [0x60 : 5], + * [0x68 : 6], [0x70 : 7], [0x78 : 8], [0x80 : 9], [0x88 : 0], + * [0x90 : 10], [0x98 : 11], [0xA0 : 12], [0xA8 : 13] + */ + timer_id = ((3 + ((tmr_res->start & 0xff) - 0x50) / 8)) % 10; + if ((tmr_res->start & 0xff) < 0x50) + timer_id -= 4; + if ((tmr_res->start & 0xff) > 0x88) + timer_id += 9; + + return timer_id; +} + static int tegra_wdt_start(struct watchdog_device *wdd) { struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); @@ -111,7 +146,7 @@ static int tegra_wdt_start(struct watchdog_device *wdd) * WDT accesses, since the caller is responsible to ping the * WDT to reset the counter before expiration, through ioctls. */ - val = WDT_TIMER_ID | + val = (wdt->timer_id) | (trigger_period << WDT_CFG_PERIOD_SHIFT) | WDT_CFG_PMC2CAR_RST_EN; writel(val, wdt->wdt_regs + WDT_CFG); @@ -236,6 +271,39 @@ static int tegra_wdt_probe(struct platform_device *pdev) if (!wdt) return -ENOMEM; + /* + * Get Timer index from (in decending priority): + * from "nvidia,timer-index" DT property + * from Timer Source Address in DT + */ + wdt->timer_id = tegra_tmr_index(tmr_res); + + ret = of_property_read_u32(np, "nvidia,timer-index", &pval); + if (!ret) { + /* + * If timer-index is provided then either corresponding + * timer source address or timer base address address + * should be provided. + */ + if (wdt->timer_id && (wdt->timer_id != pval)) { + dev_err(&pdev->dev, "Invalid Timer base address\n"); + return -EINVAL; + } + + /* Skip adjust resource if timer source address is provided */ + if (!wdt->timer_id) { + ret = adjust_resource(tmr_res, + tegra_tmr_addr(pval), + tmr_res->end - tmr_res->start); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to adjust resource:%d\n", ret); + return ret; + } + } + wdt->timer_id = pval; + } + wdt->wdt_regs = devm_ioremap_resource(&pdev->dev, wdt_res); if (IS_ERR(wdt->wdt_regs)) { dev_err(&pdev->dev, "failed ioremap wdt resource\n"); -- 2.39.2