brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · c35f85c Raw
523 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Mediatek Watchdog Driver4 *5 * Copyright (C) 2014 Matthias Brugger6 *7 * Matthias Brugger <matthias.bgg@gmail.com>8 *9 * Based on sunxi_wdt.c10 */11 12#include <dt-bindings/reset/mt2712-resets.h>13#include <dt-bindings/reset/mediatek,mt6795-resets.h>14#include <dt-bindings/reset/mt7986-resets.h>15#include <dt-bindings/reset/mt8183-resets.h>16#include <dt-bindings/reset/mt8186-resets.h>17#include <dt-bindings/reset/mt8188-resets.h>18#include <dt-bindings/reset/mt8192-resets.h>19#include <dt-bindings/reset/mt8195-resets.h>20#include <linux/delay.h>21#include <linux/err.h>22#include <linux/init.h>23#include <linux/io.h>24#include <linux/kernel.h>25#include <linux/module.h>26#include <linux/moduleparam.h>27#include <linux/of.h>28#include <linux/platform_device.h>29#include <linux/reset-controller.h>30#include <linux/types.h>31#include <linux/watchdog.h>32#include <linux/interrupt.h>33 34#define WDT_MAX_TIMEOUT		3135#define WDT_MIN_TIMEOUT		236#define WDT_LENGTH_TIMEOUT(n)	((n) << 5)37 38#define WDT_LENGTH		0x0439#define WDT_LENGTH_KEY		0x840 41#define WDT_RST			0x0842#define WDT_RST_RELOAD		0x197143 44#define WDT_MODE		0x0045#define WDT_MODE_EN		(1 << 0)46#define WDT_MODE_EXT_POL_LOW	(0 << 1)47#define WDT_MODE_EXT_POL_HIGH	(1 << 1)48#define WDT_MODE_EXRST_EN	(1 << 2)49#define WDT_MODE_IRQ_EN		(1 << 3)50#define WDT_MODE_AUTO_START	(1 << 4)51#define WDT_MODE_DUAL_EN	(1 << 6)52#define WDT_MODE_CNT_SEL	(1 << 8)53#define WDT_MODE_KEY		0x2200000054 55#define WDT_SWRST		0x1456#define WDT_SWRST_KEY		0x120957 58#define WDT_SWSYSRST		0x18U59#define WDT_SWSYS_RST_KEY	0x8800000060 61#define WDT_SWSYSRST_EN		0xfc62 63#define DRV_NAME		"mtk-wdt"64#define DRV_VERSION		"1.0"65 66#define MT7988_TOPRGU_SW_RST_NUM	2467 68static bool nowayout = WATCHDOG_NOWAYOUT;69static unsigned int timeout;70 71struct mtk_wdt_dev {72	struct watchdog_device wdt_dev;73	void __iomem *wdt_base;74	spinlock_t lock; /* protects WDT_SWSYSRST reg */75	struct reset_controller_dev rcdev;76	bool disable_wdt_extrst;77	bool reset_by_toprgu;78	bool has_swsysrst_en;79};80 81struct mtk_wdt_data {82	int toprgu_sw_rst_num;83	bool has_swsysrst_en;84};85 86static const struct mtk_wdt_data mt2712_data = {87	.toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM,88};89 90static const struct mtk_wdt_data mt6795_data = {91	.toprgu_sw_rst_num = MT6795_TOPRGU_SW_RST_NUM,92};93 94static const struct mtk_wdt_data mt7986_data = {95	.toprgu_sw_rst_num = MT7986_TOPRGU_SW_RST_NUM,96};97 98static const struct mtk_wdt_data mt7988_data = {99	.toprgu_sw_rst_num = MT7988_TOPRGU_SW_RST_NUM,100	.has_swsysrst_en = true,101};102 103static const struct mtk_wdt_data mt8183_data = {104	.toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM,105};106 107static const struct mtk_wdt_data mt8186_data = {108	.toprgu_sw_rst_num = MT8186_TOPRGU_SW_RST_NUM,109};110 111static const struct mtk_wdt_data mt8188_data = {112	.toprgu_sw_rst_num = MT8188_TOPRGU_SW_RST_NUM,113};114 115static const struct mtk_wdt_data mt8192_data = {116	.toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM,117};118 119static const struct mtk_wdt_data mt8195_data = {120	.toprgu_sw_rst_num = MT8195_TOPRGU_SW_RST_NUM,121};122 123/**124 * toprgu_reset_sw_en_unlocked() - enable/disable software control for reset bit125 * @data: Pointer to instance of driver data.126 * @id: Bit number identifying the reset to be enabled or disabled.127 * @enable: If true, enable software control for that bit, disable otherwise.128 *129 * Context: The caller must hold lock of struct mtk_wdt_dev.130 */131static void toprgu_reset_sw_en_unlocked(struct mtk_wdt_dev *data,132					unsigned long id, bool enable)133{134	u32 tmp;135 136	tmp = readl(data->wdt_base + WDT_SWSYSRST_EN);137	if (enable)138		tmp |= BIT(id);139	else140		tmp &= ~BIT(id);141 142	writel(tmp, data->wdt_base + WDT_SWSYSRST_EN);143}144 145static int toprgu_reset_update(struct reset_controller_dev *rcdev,146			       unsigned long id, bool assert)147{148	unsigned int tmp;149	unsigned long flags;150	struct mtk_wdt_dev *data =151		 container_of(rcdev, struct mtk_wdt_dev, rcdev);152 153	spin_lock_irqsave(&data->lock, flags);154 155	if (assert && data->has_swsysrst_en)156		toprgu_reset_sw_en_unlocked(data, id, true);157 158	tmp = readl(data->wdt_base + WDT_SWSYSRST);159	if (assert)160		tmp |= BIT(id);161	else162		tmp &= ~BIT(id);163	tmp |= WDT_SWSYS_RST_KEY;164	writel(tmp, data->wdt_base + WDT_SWSYSRST);165 166	if (!assert && data->has_swsysrst_en)167		toprgu_reset_sw_en_unlocked(data, id, false);168 169	spin_unlock_irqrestore(&data->lock, flags);170 171	return 0;172}173 174static int toprgu_reset_assert(struct reset_controller_dev *rcdev,175			       unsigned long id)176{177	return toprgu_reset_update(rcdev, id, true);178}179 180static int toprgu_reset_deassert(struct reset_controller_dev *rcdev,181				 unsigned long id)182{183	return toprgu_reset_update(rcdev, id, false);184}185 186static int toprgu_reset(struct reset_controller_dev *rcdev,187			unsigned long id)188{189	int ret;190 191	ret = toprgu_reset_assert(rcdev, id);192	if (ret)193		return ret;194 195	return toprgu_reset_deassert(rcdev, id);196}197 198static const struct reset_control_ops toprgu_reset_ops = {199	.assert = toprgu_reset_assert,200	.deassert = toprgu_reset_deassert,201	.reset = toprgu_reset,202};203 204static int toprgu_register_reset_controller(struct platform_device *pdev,205					    int rst_num)206{207	int ret;208	struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);209 210	spin_lock_init(&mtk_wdt->lock);211 212	mtk_wdt->rcdev.owner = THIS_MODULE;213	mtk_wdt->rcdev.nr_resets = rst_num;214	mtk_wdt->rcdev.ops = &toprgu_reset_ops;215	mtk_wdt->rcdev.of_node = pdev->dev.of_node;216	ret = devm_reset_controller_register(&pdev->dev, &mtk_wdt->rcdev);217	if (ret != 0)218		dev_err(&pdev->dev,219			"couldn't register wdt reset controller: %d\n", ret);220	return ret;221}222 223static int mtk_wdt_restart(struct watchdog_device *wdt_dev,224			   unsigned long action, void *data)225{226	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);227	void __iomem *wdt_base;228 229	wdt_base = mtk_wdt->wdt_base;230 231	while (1) {232		writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);233		mdelay(5);234	}235 236	return 0;237}238 239static int mtk_wdt_ping(struct watchdog_device *wdt_dev)240{241	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);242	void __iomem *wdt_base = mtk_wdt->wdt_base;243 244	iowrite32(WDT_RST_RELOAD, wdt_base + WDT_RST);245 246	return 0;247}248 249static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev,250				unsigned int timeout)251{252	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);253	void __iomem *wdt_base = mtk_wdt->wdt_base;254	u32 reg;255 256	wdt_dev->timeout = timeout;257	/*258	 * In dual mode, irq will be triggered at timeout / 2259	 * the real timeout occurs at timeout260	 */261	if (wdt_dev->pretimeout)262		wdt_dev->pretimeout = timeout / 2;263 264	/*265	 * One bit is the value of 512 ticks266	 * The clock has 32 KHz267	 */268	reg = WDT_LENGTH_TIMEOUT((timeout - wdt_dev->pretimeout) << 6)269			| WDT_LENGTH_KEY;270	iowrite32(reg, wdt_base + WDT_LENGTH);271 272	mtk_wdt_ping(wdt_dev);273 274	return 0;275}276 277static void mtk_wdt_init(struct watchdog_device *wdt_dev)278{279	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);280	void __iomem *wdt_base;281 282	wdt_base = mtk_wdt->wdt_base;283 284	if (readl(wdt_base + WDT_MODE) & WDT_MODE_EN) {285		set_bit(WDOG_HW_RUNNING, &wdt_dev->status);286		mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);287	}288}289 290static int mtk_wdt_stop(struct watchdog_device *wdt_dev)291{292	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);293	void __iomem *wdt_base = mtk_wdt->wdt_base;294	u32 reg;295 296	reg = readl(wdt_base + WDT_MODE);297	reg &= ~WDT_MODE_EN;298	reg |= WDT_MODE_KEY;299	iowrite32(reg, wdt_base + WDT_MODE);300 301	return 0;302}303 304static int mtk_wdt_start(struct watchdog_device *wdt_dev)305{306	u32 reg;307	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);308	void __iomem *wdt_base = mtk_wdt->wdt_base;309	int ret;310 311	ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);312	if (ret < 0)313		return ret;314 315	reg = ioread32(wdt_base + WDT_MODE);316	if (wdt_dev->pretimeout)317		reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);318	else319		reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);320	if (mtk_wdt->disable_wdt_extrst)321		reg &= ~WDT_MODE_EXRST_EN;322	if (mtk_wdt->reset_by_toprgu)323		reg |= WDT_MODE_CNT_SEL;324	reg |= (WDT_MODE_EN | WDT_MODE_KEY);325	iowrite32(reg, wdt_base + WDT_MODE);326 327	return 0;328}329 330static int mtk_wdt_set_pretimeout(struct watchdog_device *wdd,331				  unsigned int timeout)332{333	struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdd);334	void __iomem *wdt_base = mtk_wdt->wdt_base;335	u32 reg = ioread32(wdt_base + WDT_MODE);336 337	if (timeout && !wdd->pretimeout) {338		wdd->pretimeout = wdd->timeout / 2;339		reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);340	} else if (!timeout && wdd->pretimeout) {341		wdd->pretimeout = 0;342		reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN);343	} else {344		return 0;345	}346 347	reg |= WDT_MODE_KEY;348	iowrite32(reg, wdt_base + WDT_MODE);349 350	return mtk_wdt_set_timeout(wdd, wdd->timeout);351}352 353static irqreturn_t mtk_wdt_isr(int irq, void *arg)354{355	struct watchdog_device *wdd = arg;356 357	watchdog_notify_pretimeout(wdd);358 359	return IRQ_HANDLED;360}361 362static const struct watchdog_info mtk_wdt_info = {363	.identity	= DRV_NAME,364	.options	= WDIOF_SETTIMEOUT |365			  WDIOF_KEEPALIVEPING |366			  WDIOF_MAGICCLOSE,367};368 369static const struct watchdog_info mtk_wdt_pt_info = {370	.identity	= DRV_NAME,371	.options	= WDIOF_SETTIMEOUT |372			  WDIOF_PRETIMEOUT |373			  WDIOF_KEEPALIVEPING |374			  WDIOF_MAGICCLOSE,375};376 377static const struct watchdog_ops mtk_wdt_ops = {378	.owner		= THIS_MODULE,379	.start		= mtk_wdt_start,380	.stop		= mtk_wdt_stop,381	.ping		= mtk_wdt_ping,382	.set_timeout	= mtk_wdt_set_timeout,383	.set_pretimeout	= mtk_wdt_set_pretimeout,384	.restart	= mtk_wdt_restart,385};386 387static int mtk_wdt_probe(struct platform_device *pdev)388{389	struct device *dev = &pdev->dev;390	struct mtk_wdt_dev *mtk_wdt;391	const struct mtk_wdt_data *wdt_data;392	int err, irq;393 394	mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL);395	if (!mtk_wdt)396		return -ENOMEM;397 398	platform_set_drvdata(pdev, mtk_wdt);399 400	mtk_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);401	if (IS_ERR(mtk_wdt->wdt_base))402		return PTR_ERR(mtk_wdt->wdt_base);403 404	irq = platform_get_irq_optional(pdev, 0);405	if (irq > 0) {406		err = devm_request_irq(&pdev->dev, irq, mtk_wdt_isr, 0, "wdt_bark",407				       &mtk_wdt->wdt_dev);408		if (err)409			return err;410 411		mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info;412		mtk_wdt->wdt_dev.pretimeout = WDT_MAX_TIMEOUT / 2;413	} else {414		if (irq == -EPROBE_DEFER)415			return -EPROBE_DEFER;416 417		mtk_wdt->wdt_dev.info = &mtk_wdt_info;418	}419 420	mtk_wdt->wdt_dev.ops = &mtk_wdt_ops;421	mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT;422	mtk_wdt->wdt_dev.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT * 1000;423	mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT;424	mtk_wdt->wdt_dev.parent = dev;425 426	watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, dev);427	watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout);428	watchdog_set_restart_priority(&mtk_wdt->wdt_dev, 128);429 430	watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt);431 432	mtk_wdt_init(&mtk_wdt->wdt_dev);433 434	watchdog_stop_on_reboot(&mtk_wdt->wdt_dev);435	err = devm_watchdog_register_device(dev, &mtk_wdt->wdt_dev);436	if (unlikely(err))437		return err;438 439	dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n",440		 mtk_wdt->wdt_dev.timeout, nowayout);441 442	wdt_data = of_device_get_match_data(dev);443	if (wdt_data) {444		err = toprgu_register_reset_controller(pdev,445						       wdt_data->toprgu_sw_rst_num);446		if (err)447			return err;448 449		mtk_wdt->has_swsysrst_en = wdt_data->has_swsysrst_en;450	}451 452	mtk_wdt->disable_wdt_extrst =453		of_property_read_bool(dev->of_node, "mediatek,disable-extrst");454 455	mtk_wdt->reset_by_toprgu =456		of_property_read_bool(dev->of_node, "mediatek,reset-by-toprgu");457 458	return 0;459}460 461static int mtk_wdt_suspend(struct device *dev)462{463	struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);464 465	if (watchdog_active(&mtk_wdt->wdt_dev))466		mtk_wdt_stop(&mtk_wdt->wdt_dev);467 468	return 0;469}470 471static int mtk_wdt_resume(struct device *dev)472{473	struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);474 475	if (watchdog_active(&mtk_wdt->wdt_dev)) {476		mtk_wdt_start(&mtk_wdt->wdt_dev);477		mtk_wdt_ping(&mtk_wdt->wdt_dev);478	}479 480	return 0;481}482 483static const struct of_device_id mtk_wdt_dt_ids[] = {484	{ .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data },485	{ .compatible = "mediatek,mt6589-wdt" },486	{ .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data },487	{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },488	{ .compatible = "mediatek,mt7988-wdt", .data = &mt7988_data },489	{ .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },490	{ .compatible = "mediatek,mt8186-wdt", .data = &mt8186_data },491	{ .compatible = "mediatek,mt8188-wdt", .data = &mt8188_data },492	{ .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data },493	{ .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data },494	{ /* sentinel */ }495};496MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);497 498static DEFINE_SIMPLE_DEV_PM_OPS(mtk_wdt_pm_ops,499				mtk_wdt_suspend, mtk_wdt_resume);500 501static struct platform_driver mtk_wdt_driver = {502	.probe		= mtk_wdt_probe,503	.driver		= {504		.name		= DRV_NAME,505		.pm		= pm_sleep_ptr(&mtk_wdt_pm_ops),506		.of_match_table	= mtk_wdt_dt_ids,507	},508};509 510module_platform_driver(mtk_wdt_driver);511 512module_param(timeout, uint, 0);513MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");514 515module_param(nowayout, bool, 0);516MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="517			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");518 519MODULE_LICENSE("GPL");520MODULE_AUTHOR("Matthias Brugger <matthias.bgg@gmail.com>");521MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver");522MODULE_VERSION(DRV_VERSION);523