[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/drivers/watchdog/mtk_wdt.c b/src/kernel/linux/v4.14/drivers/watchdog/mtk_wdt.c
new file mode 100644
index 0000000..5eabdf4
--- /dev/null
+++ b/src/kernel/linux/v4.14/drivers/watchdog/mtk_wdt.c
@@ -0,0 +1,492 @@
+/*
+ * Mediatek Watchdog Driver
+ *
+ * Copyright (C) 2014 Matthias Brugger
+ *
+ * Matthias Brugger <matthias.bgg@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * Based on sunxi_wdt.c
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/delay.h>
+#include <asm/system_misc.h>
+#include <linux/of_address.h>
+#include <drm.h>
+
+#define WDT_MAX_TIMEOUT		31
+#define WDT_MIN_TIMEOUT		1
+#define WDT_LENGTH_TIMEOUT(n)	((n) << 5)
+
+#define WDT_LENGTH		0x04
+#define WDT_LENGTH_KEY		0x8
+
+#define WDT_RST			0x08
+#define WDT_RST_RELOAD		0x1971
+
+#define WDT_MODE		0x00
+#define WDT_MODE_EN		(1 << 0)
+#define WDT_MODE_EXT_POL_LOW	(0 << 1)
+#define WDT_MODE_EXT_POL_HIGH	(1 << 1)
+#define WDT_MODE_EXRST_EN	(1 << 2)
+#define WDT_MODE_IRQ_EN		(1 << 3)
+#define WDT_MODE_AUTO_START	(1 << 4)
+#define WDT_MODE_DUAL_EN	(1 << 6)
+#define WDT_MODE_KEY		0x22000000
+
+#define WDT_SWRST		0x14
+#define WDT_SWRST_KEY		0x1209
+
+#define WDT_NONRST_REG2	0x24
+#define WDT_BOOTMODE_MSK	0x1F
+
+#define WDT_REQ_MODE		0x30
+#define WDT_REQ_MODE_DEBUG_EN	(1 << 19)
+#define WDT_REQ_MODE_SPM_THERMAL	(1 << 0)
+#define WDT_REQ_MODE_SPM_SCPSYS	(1 << 1)
+#define WDT_REQ_MODE_EINT	(1 << 2)
+#define WDT_REQ_MODE_SYSRST	(1 << 3)
+#define WDT_REQ_MODE_THERMAL	(1 << 18)
+#define WDT_REQ_MODE_KEY	0x33000000
+
+#define WDT_REQ_IRQ_EN	0x34
+#define WDT_REQ_IRQ_DEBUG_EN	(1 << 19)
+#define WDT_REQ_IRQ_SPM_THERMAL_EN	(1 << 0)
+#define WDT_REQ_IRQ_SPM_SCPSYS_EN	(1 << 1)
+#define WDT_REQ_IRQ_EINT_EN	(1 << 2)
+#define WDT_REQ_IRQ_SYSRST_EN	(1 << 3)
+#define WDT_REQ_IRQ_THERMAL_EN	(1 << 18)
+#define WDT_REQ_IRQ_KEY		0x44000000
+
+#define WDT_EXT_REQ_CON		0x38
+#define WDT_EXT_REQ_CON_EN	(1 << 0)
+#define WDT_EXT_REQ_CON_SHIFT	4
+#define WDT_EXT_REQ_CON_MASK	0xF
+#define WDT_EXT_REQ_CON_SEL(x)	(((x) & WDT_EXT_REQ_CON_MASK) << \
+					WDT_EXT_REQ_CON_SHIFT)
+
+#define WDT_SYSDBG_DEG_EN1	0x88
+#define WDT_SYSDBG_DEG_EN1_KEY	(0x1B2A)
+
+#define WDT_SYSDBG_DEG_EN2	0x8c
+#define WDT_SYSDBG_DEG_EN2_KEY	(0x4F59)
+
+#define DRV_NAME		"mtk-wdt"
+#define DRV_VERSION		"1.0"
+
+enum ext_wdt_mode {
+	WDT_IRQ_ONLY_MODE,
+	WDT_HW_REBOOT_ONLY_MODE,
+	WDT_DUAL_MODE,
+};
+
+enum wk_req_en {
+	WD_REQ_DIS,
+	WD_REQ_EN,
+};
+
+enum wk_req_mode {
+	WD_REQ_IRQ_MODE,
+	WD_REQ_RST_MODE,
+};
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+static unsigned int timeout = WDT_MAX_TIMEOUT;
+
+struct mtk_wdt_dev {
+	struct watchdog_device wdt_dev;
+	void __iomem *wdt_base;
+	u32	eint;
+};
+
+void __iomem *toprgu_base;
+
+static int mtk_wdt_restart(struct watchdog_device *wdt_dev,
+			   unsigned long action, void *cmd)
+{
+	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
+	void __iomem *wdt_base;
+	u32 reg;
+
+	wdt_base = mtk_wdt->wdt_base;
+
+	iowrite32(ioread32(wdt_base + WDT_NONRST_REG2) & ~WDT_BOOTMODE_MSK,
+		  wdt_base + WDT_NONRST_REG2);
+
+	if (cmd && (strcmp(cmd, "rpmbpk") == 0))
+		iowrite32(ioread32(wdt_base + WDT_NONRST_REG2) |
+			   (1U << 0), wdt_base + WDT_NONRST_REG2);
+	else if (cmd && (strcmp(cmd, "recovery") == 0))
+		iowrite32(ioread32(wdt_base + WDT_NONRST_REG2) |
+			   (1U << 1), wdt_base + WDT_NONRST_REG2);
+	else if (cmd && (strcmp(cmd, "bootloader") == 0))
+		iowrite32(ioread32(wdt_base + WDT_NONRST_REG2) |
+			   (1U << 2), wdt_base + WDT_NONRST_REG2);
+	else if (cmd && !strcmp(cmd, "meta")) {
+		pr_info("arch_reset, reboot meta mode\n");
+		iowrite32(ioread32(wdt_base + WDT_NONRST_REG2) |
+			   (1U << 3), wdt_base + WDT_NONRST_REG2);
+	} else if (cmd && !strcmp(cmd, "ddr-reserve")) {
+		pr_info("arch_reset, reboot ddr-reserve mode\n");
+		iowrite32(ioread32(wdt_base + WDT_NONRST_REG2) |
+			   (1U << 4), wdt_base + WDT_NONRST_REG2);
+	}
+
+	reg = ioread32(wdt_base + WDT_MODE);
+	reg |= WDT_MODE_KEY | WDT_MODE_EXRST_EN;
+	reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
+
+	iowrite32(reg, wdt_base + WDT_MODE);
+
+	if (!(ioread32(wdt_base + WDT_NONRST_REG2) & (1U << 4)))
+		mtk_drm_dram_reserved(0);
+
+	if (!arm_pm_restart) {
+		while (1) {
+			writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
+			mdelay(5);
+		}
+	}
+
+	return NOTIFY_DONE;
+}
+
+static void mtk_wdt_request_en_set(struct mtk_wdt_dev *mtk_wdt,
+					int mark_bit, enum wk_req_en en)
+{
+	void __iomem *wdt_base = mtk_wdt->wdt_base;
+	u32 reg;
+
+	reg = readl(wdt_base + WDT_REQ_MODE);
+	reg |= WDT_REQ_MODE_KEY;
+
+	if (mark_bit == WDT_REQ_MODE_SPM_SCPSYS) {
+		if (en == WD_REQ_EN)
+			reg |= (WDT_REQ_MODE_SPM_SCPSYS);
+		if (en == WD_REQ_DIS)
+			reg &=  ~(WDT_REQ_MODE_SPM_SCPSYS);
+	} else if (mark_bit == WDT_REQ_MODE_EINT) {
+		if (en == WD_REQ_EN) {
+			writel(WDT_EXT_REQ_CON_SEL(mtk_wdt->eint) |
+				WDT_EXT_REQ_CON_EN, wdt_base + WDT_EXT_REQ_CON);
+			reg |= (WDT_REQ_MODE_EINT);
+		}
+		if (en == WD_REQ_DIS)
+			reg &= ~(WDT_REQ_MODE_EINT);
+	} else if (mark_bit == WDT_REQ_MODE_SYSRST) {
+		if (en == WD_REQ_EN) {
+			writel(WDT_SYSDBG_DEG_EN1_KEY,
+				WDT_SYSDBG_DEG_EN1);
+			writel(WDT_SYSDBG_DEG_EN2_KEY,
+				WDT_SYSDBG_DEG_EN2);
+			reg |= (WDT_REQ_MODE_SYSRST);
+		}
+		if (en == WD_REQ_DIS) {
+			writel(0, WDT_SYSDBG_DEG_EN1);
+			writel(0, WDT_SYSDBG_DEG_EN2);
+			reg &= ~(WDT_REQ_MODE_SYSRST);
+		}
+	} else if (mark_bit == WDT_REQ_MODE_THERMAL) {
+		if (en == WD_REQ_EN)
+			reg |= (WDT_REQ_MODE_THERMAL);
+		if (en == WD_REQ_DIS)
+			reg &=  ~(WDT_REQ_MODE_THERMAL);
+	}
+
+	writel(reg, wdt_base + WDT_REQ_MODE);
+}
+
+static void mtk_wdt_request_mode_set(struct mtk_wdt_dev *mtk_wdt,
+					 int mark_bit, enum wk_req_mode mode)
+{
+	void __iomem *wdt_base = mtk_wdt->wdt_base;
+	u32 reg;
+
+	reg = readl(wdt_base + WDT_REQ_IRQ_EN);
+	reg |= WDT_REQ_IRQ_KEY;
+
+	if (mark_bit == WDT_REQ_MODE_SPM_SCPSYS) {
+		if (mode == WD_REQ_IRQ_MODE)
+			reg |= (WDT_REQ_IRQ_SPM_SCPSYS_EN);
+		if (mode == WD_REQ_RST_MODE)
+			reg &= ~(WDT_REQ_IRQ_SPM_SCPSYS_EN);
+	} else if (mark_bit == WDT_REQ_MODE_EINT) {
+		if (mode == WD_REQ_IRQ_MODE)
+			reg |= (WDT_REQ_IRQ_EINT_EN);
+		if (mode == WD_REQ_RST_MODE)
+			reg &= ~(WDT_REQ_IRQ_EINT_EN);
+	} else if (mark_bit == WDT_REQ_MODE_SYSRST) {
+		if (mode == WD_REQ_IRQ_MODE)
+			reg |= (WDT_REQ_IRQ_SYSRST_EN);
+		if (mode == WD_REQ_RST_MODE)
+			reg &= ~(WDT_REQ_IRQ_SYSRST_EN);
+	} else if (mark_bit == WDT_REQ_MODE_THERMAL) {
+		if (mode == WD_REQ_IRQ_MODE)
+			reg |= (WDT_REQ_IRQ_THERMAL_EN);
+		if (mode == WD_REQ_RST_MODE)
+			reg &= ~(WDT_REQ_IRQ_THERMAL_EN);
+	}
+
+	writel(reg, wdt_base + WDT_REQ_IRQ_EN);
+}
+
+static void mtk_wdt_request_default(struct mtk_wdt_dev *mtk_wdt)
+{
+
+	mtk_wdt_request_en_set(mtk_wdt, WDT_REQ_MODE_THERMAL, WD_REQ_EN);
+	mtk_wdt_request_mode_set(mtk_wdt, WDT_REQ_MODE_THERMAL,
+				    WD_REQ_RST_MODE);
+
+	mtk_wdt_request_en_set(mtk_wdt, WDT_REQ_MODE_SPM_SCPSYS, WD_REQ_EN);
+	mtk_wdt_request_mode_set(mtk_wdt, WDT_REQ_MODE_SPM_SCPSYS,
+				    WD_REQ_RST_MODE);
+}
+
+static int mtk_wdt_ping(struct watchdog_device *wdt_dev)
+{
+	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
+	void __iomem *wdt_base = mtk_wdt->wdt_base;
+
+	iowrite32(WDT_RST_RELOAD, wdt_base + WDT_RST);
+
+	return 0;
+}
+
+static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev,
+				unsigned int timeout)
+{
+	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
+	void __iomem *wdt_base = mtk_wdt->wdt_base;
+	u32 reg;
+
+	wdt_dev->timeout = timeout;
+
+	/*
+	 * One bit is the value of 512 ticks
+	 * The clock has 32 KHz
+	 */
+	reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY;
+	iowrite32(reg, wdt_base + WDT_LENGTH);
+
+	mtk_wdt_ping(wdt_dev);
+
+	return 0;
+}
+
+static int mtk_wdt_stop(struct watchdog_device *wdt_dev)
+{
+	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
+	void __iomem *wdt_base = mtk_wdt->wdt_base;
+	u32 reg;
+
+	reg = readl(wdt_base + WDT_MODE);
+	reg &= ~WDT_MODE_EN;
+	reg |= WDT_MODE_KEY;
+	iowrite32(reg, wdt_base + WDT_MODE);
+
+	return 0;
+}
+
+static int mtk_wdt_start(struct watchdog_device *wdt_dev)
+{
+	u32 reg;
+	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
+	void __iomem *wdt_base = mtk_wdt->wdt_base;
+	int ret;
+
+	ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
+	if (ret < 0)
+		return ret;
+
+	reg = ioread32(wdt_base + WDT_MODE);
+	reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);
+	reg |= (WDT_MODE_KEY | WDT_MODE_EXRST_EN);
+	iowrite32(reg, wdt_base + WDT_MODE);
+
+	return 0;
+}
+
+static const struct watchdog_info mtk_wdt_info = {
+	.identity	= DRV_NAME,
+	.options	= WDIOF_SETTIMEOUT |
+			  WDIOF_KEEPALIVEPING |
+			  WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops mtk_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= mtk_wdt_start,
+	.stop		= mtk_wdt_stop,
+	.ping		= mtk_wdt_ping,
+	.set_timeout	= mtk_wdt_set_timeout,
+	.restart	= mtk_wdt_restart,
+};
+
+static int mtk_wdt_probe(struct platform_device *pdev)
+{
+	struct mtk_wdt_dev *mtk_wdt;
+	struct resource *res;
+	int err;
+
+	mtk_wdt = devm_kzalloc(&pdev->dev, sizeof(*mtk_wdt), GFP_KERNEL);
+	if (!mtk_wdt)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, mtk_wdt);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mtk_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(mtk_wdt->wdt_base))
+		return PTR_ERR(mtk_wdt->wdt_base);
+
+	toprgu_base = mtk_wdt->wdt_base;
+	arm_pm_restart = NULL;
+	mtk_wdt->wdt_dev.info = &mtk_wdt_info;
+	mtk_wdt->wdt_dev.ops = &mtk_wdt_ops;
+	mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;
+	mtk_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT;
+	mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT;
+	mtk_wdt->wdt_dev.parent = &pdev->dev;
+
+	err = of_property_read_u32(pdev->dev.of_node,
+				      "eint", &mtk_wdt->eint);
+	if (err)
+		mtk_wdt->eint = 0;
+
+	mtk_wdt_request_default(mtk_wdt);
+
+	watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, &pdev->dev);
+	watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout);
+	watchdog_set_restart_priority(&mtk_wdt->wdt_dev, 128);
+
+	watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt);
+
+	if (readl(mtk_wdt->wdt_base + WDT_MODE) & WDT_MODE_EN) {
+		set_bit(WDOG_HW_RUNNING, &mtk_wdt->wdt_dev.status);
+		mtk_wdt_start(&mtk_wdt->wdt_dev);
+	} else
+		mtk_wdt_stop(&mtk_wdt->wdt_dev);
+
+	err = watchdog_register_device(&mtk_wdt->wdt_dev);
+	if (unlikely(err))
+		return err;
+
+	dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n",
+			mtk_wdt->wdt_dev.timeout, nowayout);
+
+	return 0;
+}
+
+static void mtk_wdt_shutdown(struct platform_device *pdev)
+{
+	struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
+
+	if (watchdog_active(&mtk_wdt->wdt_dev))
+		mtk_wdt_stop(&mtk_wdt->wdt_dev);
+}
+
+static int mtk_wdt_remove(struct platform_device *pdev)
+{
+	struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
+
+	watchdog_unregister_device(&mtk_wdt->wdt_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_wdt_suspend(struct device *dev)
+{
+	struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);
+
+	if (watchdog_active(&mtk_wdt->wdt_dev))
+		mtk_wdt_stop(&mtk_wdt->wdt_dev);
+
+	return 0;
+}
+
+static int mtk_wdt_resume(struct device *dev)
+{
+	struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);
+
+	if (watchdog_active(&mtk_wdt->wdt_dev)) {
+		mtk_wdt_start(&mtk_wdt->wdt_dev);
+		mtk_wdt_ping(&mtk_wdt->wdt_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static const struct of_device_id mtk_wdt_dt_ids[] = {
+	{ .compatible = "mediatek,mt6589-wdt" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
+
+void __iomem *mtk_wd_Gettoprgu(void)
+{
+	struct device_node *np_rgu;
+
+	pr_info("get toprgu is %p\n", toprgu_base);
+
+	np_rgu = of_find_compatible_node(NULL, NULL,
+			mtk_wdt_dt_ids[0].compatible);
+
+	if (!toprgu_base)
+		toprgu_base = of_iomap(np_rgu, 0);
+		if (!toprgu_base)
+			pr_debug("RGU iomap failed\n");
+
+	return toprgu_base;
+}
+EXPORT_SYMBOL_GPL(mtk_wd_Gettoprgu);
+
+static const struct dev_pm_ops mtk_wdt_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend,
+				mtk_wdt_resume)
+};
+
+static struct platform_driver mtk_wdt_driver = {
+	.probe		= mtk_wdt_probe,
+	.remove		= mtk_wdt_remove,
+	.shutdown	= mtk_wdt_shutdown,
+	.driver		= {
+		.name		= DRV_NAME,
+		.pm		= &mtk_wdt_pm_ops,
+		.of_match_table	= mtk_wdt_dt_ids,
+	},
+};
+
+module_platform_driver(mtk_wdt_driver);
+
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matthias Brugger <matthias.bgg@gmail.com>");
+MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver");
+MODULE_VERSION(DRV_VERSION);