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 int ls1x_wdt_probe(struct platform_device
*pdev
)
88 struct ls1x_wdt_drvdata
*drvdata
;
89 struct watchdog_device
*ls1x_wdt
;
90 unsigned long clk_rate
;
94 drvdata
= devm_kzalloc(&pdev
->dev
, sizeof(*drvdata
), GFP_KERNEL
);
98 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
99 drvdata
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
100 if (IS_ERR(drvdata
->base
))
101 return PTR_ERR(drvdata
->base
);
103 drvdata
->clk
= devm_clk_get(&pdev
->dev
, pdev
->name
);
104 if (IS_ERR(drvdata
->clk
))
105 return PTR_ERR(drvdata
->clk
);
107 err
= clk_prepare_enable(drvdata
->clk
);
109 dev_err(&pdev
->dev
, "clk enable failed\n");
113 clk_rate
= clk_get_rate(drvdata
->clk
);
118 drvdata
->clk_rate
= clk_rate
;
120 ls1x_wdt
= &drvdata
->wdt
;
121 ls1x_wdt
->info
= &ls1x_wdt_info
;
122 ls1x_wdt
->ops
= &ls1x_wdt_ops
;
123 ls1x_wdt
->timeout
= DEFAULT_HEARTBEAT
;
124 ls1x_wdt
->min_timeout
= 1;
125 ls1x_wdt
->max_hw_heartbeat_ms
= U32_MAX
/ clk_rate
* 1000;
126 ls1x_wdt
->parent
= &pdev
->dev
;
128 watchdog_init_timeout(ls1x_wdt
, heartbeat
, &pdev
->dev
);
129 watchdog_set_nowayout(ls1x_wdt
, nowayout
);
130 watchdog_set_drvdata(ls1x_wdt
, drvdata
);
132 err
= watchdog_register_device(&drvdata
->wdt
);
134 dev_err(&pdev
->dev
, "failed to register watchdog device\n");
138 platform_set_drvdata(pdev
, drvdata
);
140 dev_info(&pdev
->dev
, "Loongson1 Watchdog driver registered\n");
144 clk_disable_unprepare(drvdata
->clk
);
148 static int ls1x_wdt_remove(struct platform_device
*pdev
)
150 struct ls1x_wdt_drvdata
*drvdata
= platform_get_drvdata(pdev
);
152 watchdog_unregister_device(&drvdata
->wdt
);
153 clk_disable_unprepare(drvdata
->clk
);
158 static struct platform_driver ls1x_wdt_driver
= {
159 .probe
= ls1x_wdt_probe
,
160 .remove
= ls1x_wdt_remove
,
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");