1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
8 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/watchdog.h>
13 /* Loongson 1 Watchdog Register Definitions */
18 #define DEFAULT_HEARTBEAT 30
20 static bool nowayout
= WATCHDOG_NOWAYOUT
;
21 module_param(nowayout
, bool, 0444);
23 static unsigned int heartbeat
;
24 module_param(heartbeat
, uint
, 0444);
26 struct ls1x_wdt_drvdata
{
29 unsigned long clk_rate
;
30 struct watchdog_device wdt
;
33 static int ls1x_wdt_ping(struct watchdog_device
*wdt_dev
)
35 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
37 writel(0x1, drvdata
->base
+ WDT_SET
);
42 static int ls1x_wdt_set_timeout(struct watchdog_device
*wdt_dev
,
45 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
46 unsigned int max_hw_heartbeat
= wdt_dev
->max_hw_heartbeat_ms
/ 1000;
49 wdt_dev
->timeout
= timeout
;
51 counts
= drvdata
->clk_rate
* min(timeout
, max_hw_heartbeat
);
52 writel(counts
, drvdata
->base
+ WDT_TIMER
);
57 static int ls1x_wdt_start(struct watchdog_device
*wdt_dev
)
59 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
61 writel(0x1, drvdata
->base
+ WDT_EN
);
66 static int ls1x_wdt_stop(struct watchdog_device
*wdt_dev
)
68 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
70 writel(0x0, drvdata
->base
+ WDT_EN
);
75 static int ls1x_wdt_restart(struct watchdog_device
*wdt_dev
,
76 unsigned long action
, void *data
)
78 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
80 writel(0x1, drvdata
->base
+ WDT_EN
);
81 writel(0x1, drvdata
->base
+ WDT_TIMER
);
82 writel(0x1, drvdata
->base
+ WDT_SET
);
87 static const struct watchdog_info ls1x_wdt_info
= {
88 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
89 .identity
= "Loongson1 Watchdog",
92 static const struct watchdog_ops ls1x_wdt_ops
= {
94 .start
= ls1x_wdt_start
,
95 .stop
= ls1x_wdt_stop
,
96 .ping
= ls1x_wdt_ping
,
97 .set_timeout
= ls1x_wdt_set_timeout
,
98 .restart
= ls1x_wdt_restart
,
101 static int ls1x_wdt_probe(struct platform_device
*pdev
)
103 struct device
*dev
= &pdev
->dev
;
104 struct ls1x_wdt_drvdata
*drvdata
;
105 struct watchdog_device
*ls1x_wdt
;
106 unsigned long clk_rate
;
109 drvdata
= devm_kzalloc(dev
, sizeof(*drvdata
), GFP_KERNEL
);
113 drvdata
->base
= devm_platform_ioremap_resource(pdev
, 0);
114 if (IS_ERR(drvdata
->base
))
115 return PTR_ERR(drvdata
->base
);
117 drvdata
->clk
= devm_clk_get_enabled(dev
, NULL
);
118 if (IS_ERR(drvdata
->clk
))
119 return PTR_ERR(drvdata
->clk
);
121 clk_rate
= clk_get_rate(drvdata
->clk
);
124 drvdata
->clk_rate
= clk_rate
;
126 ls1x_wdt
= &drvdata
->wdt
;
127 ls1x_wdt
->info
= &ls1x_wdt_info
;
128 ls1x_wdt
->ops
= &ls1x_wdt_ops
;
129 ls1x_wdt
->timeout
= DEFAULT_HEARTBEAT
;
130 ls1x_wdt
->min_timeout
= 1;
131 ls1x_wdt
->max_hw_heartbeat_ms
= U32_MAX
/ clk_rate
* 1000;
132 ls1x_wdt
->parent
= dev
;
134 watchdog_init_timeout(ls1x_wdt
, heartbeat
, dev
);
135 watchdog_set_nowayout(ls1x_wdt
, nowayout
);
136 watchdog_set_drvdata(ls1x_wdt
, drvdata
);
138 err
= devm_watchdog_register_device(dev
, &drvdata
->wdt
);
142 platform_set_drvdata(pdev
, drvdata
);
144 dev_info(dev
, "Loongson1 Watchdog driver registered\n");
150 static const struct of_device_id ls1x_wdt_dt_ids
[] = {
151 { .compatible
= "loongson,ls1b-wdt", },
152 { .compatible
= "loongson,ls1c-wdt", },
155 MODULE_DEVICE_TABLE(of
, ls1x_wdt_dt_ids
);
158 static struct platform_driver ls1x_wdt_driver
= {
159 .probe
= ls1x_wdt_probe
,
162 .of_match_table
= of_match_ptr(ls1x_wdt_dt_ids
),
166 module_platform_driver(ls1x_wdt_driver
);
168 MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>");
169 MODULE_DESCRIPTION("Loongson1 Watchdog Driver");
170 MODULE_LICENSE("GPL");