2 * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
10 #include <linux/clk.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/watchdog.h>
14 #include <loongson1.h>
16 #define DEFAULT_HEARTBEAT 30
18 static bool nowayout
= WATCHDOG_NOWAYOUT
;
19 module_param(nowayout
, bool, 0444);
21 static unsigned int heartbeat
;
22 module_param(heartbeat
, uint
, 0444);
24 struct ls1x_wdt_drvdata
{
27 unsigned long clk_rate
;
28 struct watchdog_device wdt
;
31 static int ls1x_wdt_ping(struct watchdog_device
*wdt_dev
)
33 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
35 writel(0x1, drvdata
->base
+ WDT_SET
);
40 static int ls1x_wdt_set_timeout(struct watchdog_device
*wdt_dev
,
43 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
44 unsigned int max_hw_heartbeat
= wdt_dev
->max_hw_heartbeat_ms
/ 1000;
47 wdt_dev
->timeout
= timeout
;
49 counts
= drvdata
->clk_rate
* min(timeout
, max_hw_heartbeat
);
50 writel(counts
, drvdata
->base
+ WDT_TIMER
);
55 static int ls1x_wdt_start(struct watchdog_device
*wdt_dev
)
57 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
59 writel(0x1, drvdata
->base
+ WDT_EN
);
64 static int ls1x_wdt_stop(struct watchdog_device
*wdt_dev
)
66 struct ls1x_wdt_drvdata
*drvdata
= watchdog_get_drvdata(wdt_dev
);
68 writel(0x0, drvdata
->base
+ WDT_EN
);
73 static const struct watchdog_info ls1x_wdt_info
= {
74 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
75 .identity
= "Loongson1 Watchdog",
78 static const struct watchdog_ops ls1x_wdt_ops
= {
80 .start
= ls1x_wdt_start
,
81 .stop
= ls1x_wdt_stop
,
82 .ping
= ls1x_wdt_ping
,
83 .set_timeout
= ls1x_wdt_set_timeout
,
86 static void ls1x_clk_disable_unprepare(void *data
)
88 clk_disable_unprepare(data
);
91 static int ls1x_wdt_probe(struct platform_device
*pdev
)
93 struct device
*dev
= &pdev
->dev
;
94 struct ls1x_wdt_drvdata
*drvdata
;
95 struct watchdog_device
*ls1x_wdt
;
96 unsigned long clk_rate
;
99 drvdata
= devm_kzalloc(dev
, sizeof(*drvdata
), GFP_KERNEL
);
103 drvdata
->base
= devm_platform_ioremap_resource(pdev
, 0);
104 if (IS_ERR(drvdata
->base
))
105 return PTR_ERR(drvdata
->base
);
107 drvdata
->clk
= devm_clk_get(dev
, pdev
->name
);
108 if (IS_ERR(drvdata
->clk
))
109 return PTR_ERR(drvdata
->clk
);
111 err
= clk_prepare_enable(drvdata
->clk
);
113 dev_err(dev
, "clk enable failed\n");
116 err
= devm_add_action_or_reset(dev
, ls1x_clk_disable_unprepare
,
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
);
140 dev_err(dev
, "failed to register watchdog device\n");
144 platform_set_drvdata(pdev
, drvdata
);
146 dev_info(dev
, "Loongson1 Watchdog driver registered\n");
151 static struct platform_driver ls1x_wdt_driver
= {
152 .probe
= ls1x_wdt_probe
,
158 module_platform_driver(ls1x_wdt_driver
);
160 MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>");
161 MODULE_DESCRIPTION("Loongson1 Watchdog Driver");
162 MODULE_LICENSE("GPL");