From 793d448a51b53d81e2dbd58a5865a204de92ad34 Mon Sep 17 00:00:00 2001 From: Pavel Kubelun Date: Fri, 4 Nov 2016 02:12:32 +0300 Subject: ipq806x: backport upstream wdt driver Signed-off-by: Pavel Kubelun --- ...or-standalone-watchdog-not-in-timer-block.patch | 162 +++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch (limited to 'target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch') diff --git a/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch new file mode 100644 index 0000000000..82dd875434 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/010-5-watchdog-qcom-add-option-for-standalone-watchdog-not-in-timer-block.patch @@ -0,0 +1,162 @@ +From f0d9d0f4b44ae5503ea368e7f066b20f12ca1d37 Mon Sep 17 00:00:00 2001 +From: Matthew McClintock +Date: Wed, 29 Jun 2016 10:50:01 -0700 +Subject: watchdog: qcom: add option for standalone watchdog not in timer block + +Commit 0dfd582e026a ("watchdog: qcom: use timer devicetree +binding") moved to use the watchdog as a subset timer +register block. Some devices have the watchdog completely +standalone with slightly different register offsets as +well so let's account for the differences here. + +The existing "kpss-standalone" compatible string doesn't +make it entirely clear exactly what the device is so +rename to "kpss-wdt" to reflect watchdog timer +functionality. Also update ipq4019 DTS with an SoC +specific compatible. + +Signed-off-by: Matthew McClintock +Signed-off-by: Thomas Pedersen +Reviewed-by: Guenter Roeck +Signed-off-by: Guenter Roeck +Signed-off-by: Wim Van Sebroeck +--- + .../devicetree/bindings/watchdog/qcom-wdt.txt | 2 + + arch/arm/boot/dts/qcom-ipq4019.dtsi | 2 +- + drivers/watchdog/qcom-wdt.c | 64 ++++++++++++++++------ + 3 files changed, 51 insertions(+), 17 deletions(-) + +--- a/drivers/watchdog/qcom-wdt.c ++++ b/drivers/watchdog/qcom-wdt.c +@@ -18,19 +18,42 @@ + #include + #include + #include ++#include + +-#define WDT_RST 0x38 +-#define WDT_EN 0x40 +-#define WDT_STS 0x44 +-#define WDT_BITE_TIME 0x5C ++enum wdt_reg { ++ WDT_RST, ++ WDT_EN, ++ WDT_STS, ++ WDT_BITE_TIME, ++}; ++ ++static const u32 reg_offset_data_apcs_tmr[] = { ++ [WDT_RST] = 0x38, ++ [WDT_EN] = 0x40, ++ [WDT_STS] = 0x44, ++ [WDT_BITE_TIME] = 0x5C, ++}; ++ ++static const u32 reg_offset_data_kpss[] = { ++ [WDT_RST] = 0x4, ++ [WDT_EN] = 0x8, ++ [WDT_STS] = 0xC, ++ [WDT_BITE_TIME] = 0x14, ++}; + + struct qcom_wdt { + struct watchdog_device wdd; + struct clk *clk; + unsigned long rate; + void __iomem *base; ++ const u32 *layout; + }; + ++static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg) ++{ ++ return wdt->base + wdt->layout[reg]; ++} ++ + static inline + struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) + { +@@ -41,10 +64,10 @@ static int qcom_wdt_start(struct watchdo + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + +- writel(0, wdt->base + WDT_EN); +- writel(1, wdt->base + WDT_RST); +- writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); +- writel(1, wdt->base + WDT_EN); ++ writel(0, wdt_addr(wdt, WDT_EN)); ++ writel(1, wdt_addr(wdt, WDT_RST)); ++ writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); ++ writel(1, wdt_addr(wdt, WDT_EN)); + return 0; + } + +@@ -52,7 +75,7 @@ static int qcom_wdt_stop(struct watchdog + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + +- writel(0, wdt->base + WDT_EN); ++ writel(0, wdt_addr(wdt, WDT_EN)); + return 0; + } + +@@ -60,7 +83,7 @@ static int qcom_wdt_ping(struct watchdog + { + struct qcom_wdt *wdt = to_qcom_wdt(wdd); + +- writel(1, wdt->base + WDT_RST); ++ writel(1, wdt_addr(wdt, WDT_RST)); + return 0; + } + +@@ -83,10 +106,10 @@ static int qcom_wdt_restart(struct watch + */ + timeout = 128 * wdt->rate / 1000; + +- writel(0, wdt->base + WDT_EN); +- writel(1, wdt->base + WDT_RST); +- writel(timeout, wdt->base + WDT_BITE_TIME); +- writel(1, wdt->base + WDT_EN); ++ writel(0, wdt_addr(wdt, WDT_EN)); ++ writel(1, wdt_addr(wdt, WDT_RST)); ++ writel(timeout, wdt_addr(wdt, WDT_BITE_TIME)); ++ writel(1, wdt_addr(wdt, WDT_EN)); + + /* + * Actually make sure the above sequence hits hardware before sleeping. +@@ -119,9 +142,16 @@ static int qcom_wdt_probe(struct platfor + struct qcom_wdt *wdt; + struct resource *res; + struct device_node *np = pdev->dev.of_node; ++ const u32 *regs; + u32 percpu_offset; + int ret; + ++ regs = of_device_get_match_data(&pdev->dev); ++ if (!regs) { ++ dev_err(&pdev->dev, "Unsupported QCOM WDT module\n"); ++ return -ENODEV; ++ } ++ + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; +@@ -172,6 +202,7 @@ static int qcom_wdt_probe(struct platfor + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = 0x10000000U / wdt->rate; + wdt->wdd.parent = &pdev->dev; ++ wdt->layout = regs; + + if (readl(wdt->base + WDT_STS) & 1) + wdt->wdd.bootstatus = WDIOF_CARDRESET; +@@ -208,8 +239,9 @@ static int qcom_wdt_remove(struct platfo + } + + static const struct of_device_id qcom_wdt_of_table[] = { +- { .compatible = "qcom,kpss-timer" }, +- { .compatible = "qcom,scss-timer" }, ++ { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr }, ++ { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr }, ++ { .compatible = "qcom,kpss-wdt", .data = reg_offset_data_kpss }, + { }, + }; + MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); -- cgit v1.2.3