File Index
Files and directories
Sign in
FILE

loongson1_wdt.c

File preview with syntax highlighting.

linux-6.19.14/drivers/watchdog/loongson1_wdt.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
  4 * Copyright (C) 2025 Binbin Zhou <zhoubinbin@loongson.cn>
  5 */
  6
  7#include <linux/clk.h>
  8#include <linux/io.h>
  9#include <linux/module.h>
 10#include <linux/of.h>
 11#include <linux/platform_device.h>
 12#include <linux/watchdog.h>
 13
 14/* Loongson Watchdog Register Definitions */
 15#define WDT_EN			0x0
 16
 17#define DEFAULT_HEARTBEAT	30
 18
 19static bool nowayout = WATCHDOG_NOWAYOUT;
 20module_param(nowayout, bool, 0);
 21MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 22		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 23
 24static unsigned int heartbeat;
 25module_param(heartbeat, uint, 0);
 26MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default="
 27		 __MODULE_STRING(DEFAULT_HEARTBEAT) ")");
 28
 29struct ls1x_wdt_pdata {
 30	u32 timer_offset;
 31	u32 set_offset;
 32	u32 wdt_en_bit;
 33};
 34
 35static const struct ls1x_wdt_pdata ls1b_wdt_pdata = {
 36	.timer_offset = 0x4,
 37	.set_offset = 0x8,
 38	.wdt_en_bit = BIT(0),
 39};
 40
 41static const struct ls1x_wdt_pdata ls2k0300_wdt_pdata = {
 42	.timer_offset = 0x8,
 43	.set_offset = 0x4,
 44	.wdt_en_bit = BIT(1),
 45};
 46
 47struct ls1x_wdt_drvdata {
 48	void __iomem *base;
 49	struct clk *clk;
 50	unsigned long clk_rate;
 51	struct watchdog_device wdt;
 52	const struct ls1x_wdt_pdata *pdata;
 53};
 54
 55static int ls1x_wdt_ping(struct watchdog_device *wdt_dev)
 56{
 57	struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 58
 59	writel(0x1, drvdata->base + drvdata->pdata->set_offset);
 60
 61	return 0;
 62}
 63
 64static int ls1x_wdt_set_timeout(struct watchdog_device *wdt_dev,
 65				unsigned int timeout)
 66{
 67	struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 68	unsigned int max_hw_heartbeat = wdt_dev->max_hw_heartbeat_ms / 1000;
 69	unsigned int counts;
 70
 71	wdt_dev->timeout = timeout;
 72
 73	counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat);
 74	writel(counts, drvdata->base + drvdata->pdata->timer_offset);
 75
 76	return 0;
 77}
 78
 79static int ls1x_wdt_start(struct watchdog_device *wdt_dev)
 80{
 81	struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 82
 83	writel(drvdata->pdata->wdt_en_bit, drvdata->base + WDT_EN);
 84
 85	return 0;
 86}
 87
 88static int ls1x_wdt_stop(struct watchdog_device *wdt_dev)
 89{
 90	struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
 91	u32 val = readl(drvdata->base + WDT_EN);
 92
 93	val &= ~(drvdata->pdata->wdt_en_bit);
 94	writel(val, drvdata->base + WDT_EN);
 95
 96	return 0;
 97}
 98
 99static int ls1x_wdt_restart(struct watchdog_device *wdt_dev,
100			    unsigned long action, void *data)
101{
102	struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
103
104	writel(drvdata->pdata->wdt_en_bit, drvdata->base + WDT_EN);
105	writel(0x1, drvdata->base + drvdata->pdata->timer_offset);
106	writel(0x1, drvdata->base + drvdata->pdata->set_offset);
107
108	return 0;
109}
110
111static const struct watchdog_info ls1x_wdt_info = {
112	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
113	.identity = "Loongson1 Watchdog",
114};
115
116static const struct watchdog_ops ls1x_wdt_ops = {
117	.owner = THIS_MODULE,
118	.start = ls1x_wdt_start,
119	.stop = ls1x_wdt_stop,
120	.ping = ls1x_wdt_ping,
121	.set_timeout = ls1x_wdt_set_timeout,
122	.restart = ls1x_wdt_restart,
123};
124
125static int ls1x_wdt_probe(struct platform_device *pdev)
126{
127	struct device *dev = &pdev->dev;
128	struct ls1x_wdt_drvdata *drvdata;
129	struct watchdog_device *ls1x_wdt;
130	unsigned long clk_rate;
131
132	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
133	if (!drvdata)
134		return -ENOMEM;
135	platform_set_drvdata(pdev, drvdata);
136
137	drvdata->pdata = of_device_get_match_data(dev);
138
139	drvdata->base = devm_platform_ioremap_resource(pdev, 0);
140	if (IS_ERR(drvdata->base))
141		return PTR_ERR(drvdata->base);
142
143	drvdata->clk = devm_clk_get_enabled(dev, NULL);
144	if (IS_ERR(drvdata->clk))
145		return PTR_ERR(drvdata->clk);
146
147	clk_rate = clk_get_rate(drvdata->clk);
148	if (!clk_rate)
149		return -EINVAL;
150	drvdata->clk_rate = clk_rate;
151
152	ls1x_wdt = &drvdata->wdt;
153	ls1x_wdt->info = &ls1x_wdt_info;
154	ls1x_wdt->ops = &ls1x_wdt_ops;
155	ls1x_wdt->timeout = DEFAULT_HEARTBEAT;
156	ls1x_wdt->min_timeout = 1;
157	ls1x_wdt->max_hw_heartbeat_ms = U32_MAX / clk_rate * 1000;
158	ls1x_wdt->parent = dev;
159
160	watchdog_init_timeout(ls1x_wdt, heartbeat, dev);
161	watchdog_set_nowayout(ls1x_wdt, nowayout);
162	watchdog_set_drvdata(ls1x_wdt, drvdata);
163
164	return devm_watchdog_register_device(dev, &drvdata->wdt);
165}
166
167static int ls1x_wdt_resume(struct device *dev)
168{
169	struct ls1x_wdt_drvdata *data = dev_get_drvdata(dev);
170
171	if (watchdog_active(&data->wdt))
172		ls1x_wdt_start(&data->wdt);
173
174	return 0;
175}
176
177static int ls1x_wdt_suspend(struct device *dev)
178{
179	struct ls1x_wdt_drvdata *data = dev_get_drvdata(dev);
180
181	if (watchdog_active(&data->wdt))
182		ls1x_wdt_stop(&data->wdt);
183
184	return 0;
185}
186
187static DEFINE_SIMPLE_DEV_PM_OPS(ls1x_wdt_pm_ops, ls1x_wdt_suspend, ls1x_wdt_resume);
188
189static const struct of_device_id ls1x_wdt_dt_ids[] = {
190	{ .compatible = "loongson,ls1b-wdt", .data = &ls1b_wdt_pdata },
191	{ .compatible = "loongson,ls1c-wdt", .data = &ls1b_wdt_pdata },
192	{ .compatible = "loongson,ls2k0300-wdt", .data = &ls2k0300_wdt_pdata },
193	{ /* sentinel */ }
194};
195MODULE_DEVICE_TABLE(of, ls1x_wdt_dt_ids);
196
197static struct platform_driver ls1x_wdt_driver = {
198	.probe = ls1x_wdt_probe,
199	.driver = {
200		.name = "ls1x-wdt",
201		.of_match_table = ls1x_wdt_dt_ids,
202		.pm = pm_ptr(&ls1x_wdt_pm_ops),
203	},
204};
205
206module_platform_driver(ls1x_wdt_driver);
207
208MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>");
209MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
210MODULE_DESCRIPTION("Loongson Watchdog Driver");
211MODULE_LICENSE("GPL");
~/ Read-only file preview.