Merge branch 'rework/misc-cleanups' into for-linus
[linux-2.6-microblaze.git] / drivers / watchdog / ts72xx_wdt.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Watchdog driver for Technologic Systems TS-72xx based SBCs
4  * (TS-7200, TS-7250 and TS-7260). These boards have external
5  * glue logic CPLD chip, which includes programmable watchdog
6  * timer.
7  *
8  * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
9  *
10  * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
11  *
12  */
13
14 #include <linux/platform_device.h>
15 #include <linux/module.h>
16 #include <linux/watchdog.h>
17 #include <linux/io.h>
18
19 #define TS72XX_WDT_DEFAULT_TIMEOUT      30
20
21 static int timeout;
22 module_param(timeout, int, 0);
23 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds.");
24
25 static bool nowayout = WATCHDOG_NOWAYOUT;
26 module_param(nowayout, bool, 0);
27 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
28
29 /* priv->control_reg */
30 #define TS72XX_WDT_CTRL_DISABLE         0x00
31 #define TS72XX_WDT_CTRL_250MS           0x01
32 #define TS72XX_WDT_CTRL_500MS           0x02
33 #define TS72XX_WDT_CTRL_1SEC            0x03
34 #define TS72XX_WDT_CTRL_RESERVED        0x04
35 #define TS72XX_WDT_CTRL_2SEC            0x05
36 #define TS72XX_WDT_CTRL_4SEC            0x06
37 #define TS72XX_WDT_CTRL_8SEC            0x07
38
39 /* priv->feed_reg */
40 #define TS72XX_WDT_FEED_VAL             0x05
41
42 struct ts72xx_wdt_priv {
43         void __iomem    *control_reg;
44         void __iomem    *feed_reg;
45         struct watchdog_device wdd;
46         unsigned char regval;
47 };
48
49 static int ts72xx_wdt_start(struct watchdog_device *wdd)
50 {
51         struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
52
53         writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
54         writeb(priv->regval, priv->control_reg);
55
56         return 0;
57 }
58
59 static int ts72xx_wdt_stop(struct watchdog_device *wdd)
60 {
61         struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
62
63         writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
64         writeb(TS72XX_WDT_CTRL_DISABLE, priv->control_reg);
65
66         return 0;
67 }
68
69 static int ts72xx_wdt_ping(struct watchdog_device *wdd)
70 {
71         struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
72
73         writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
74
75         return 0;
76 }
77
78 static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
79 {
80         struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
81
82         if (to == 1) {
83                 priv->regval = TS72XX_WDT_CTRL_1SEC;
84         } else if (to == 2) {
85                 priv->regval = TS72XX_WDT_CTRL_2SEC;
86         } else if (to <= 4) {
87                 priv->regval = TS72XX_WDT_CTRL_4SEC;
88                 to = 4;
89         } else {
90                 priv->regval = TS72XX_WDT_CTRL_8SEC;
91                 if (to <= 8)
92                         to = 8;
93         }
94
95         wdd->timeout = to;
96
97         if (watchdog_active(wdd)) {
98                 ts72xx_wdt_stop(wdd);
99                 ts72xx_wdt_start(wdd);
100         }
101
102         return 0;
103 }
104
105 static const struct watchdog_info ts72xx_wdt_ident = {
106         .options                = WDIOF_KEEPALIVEPING |
107                                   WDIOF_SETTIMEOUT |
108                                   WDIOF_MAGICCLOSE,
109         .firmware_version       = 1,
110         .identity               = "TS-72XX WDT",
111 };
112
113 static const struct watchdog_ops ts72xx_wdt_ops = {
114         .owner          = THIS_MODULE,
115         .start          = ts72xx_wdt_start,
116         .stop           = ts72xx_wdt_stop,
117         .ping           = ts72xx_wdt_ping,
118         .set_timeout    = ts72xx_wdt_settimeout,
119 };
120
121 static int ts72xx_wdt_probe(struct platform_device *pdev)
122 {
123         struct device *dev = &pdev->dev;
124         struct ts72xx_wdt_priv *priv;
125         struct watchdog_device *wdd;
126         int ret;
127
128         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
129         if (!priv)
130                 return -ENOMEM;
131
132         priv->control_reg = devm_platform_ioremap_resource(pdev, 0);
133         if (IS_ERR(priv->control_reg))
134                 return PTR_ERR(priv->control_reg);
135
136         priv->feed_reg = devm_platform_ioremap_resource(pdev, 1);
137         if (IS_ERR(priv->feed_reg))
138                 return PTR_ERR(priv->feed_reg);
139
140         wdd = &priv->wdd;
141         wdd->info = &ts72xx_wdt_ident;
142         wdd->ops = &ts72xx_wdt_ops;
143         wdd->min_timeout = 1;
144         wdd->max_hw_heartbeat_ms = 8000;
145         wdd->parent = dev;
146
147         watchdog_set_nowayout(wdd, nowayout);
148
149         wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
150         watchdog_init_timeout(wdd, timeout, dev);
151
152         watchdog_set_drvdata(wdd, priv);
153
154         ret = devm_watchdog_register_device(dev, wdd);
155         if (ret)
156                 return ret;
157
158         dev_info(dev, "TS-72xx Watchdog driver\n");
159
160         return 0;
161 }
162
163 static struct platform_driver ts72xx_wdt_driver = {
164         .probe          = ts72xx_wdt_probe,
165         .driver         = {
166                 .name   = "ts72xx-wdt",
167         },
168 };
169
170 module_platform_driver(ts72xx_wdt_driver);
171
172 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
173 MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
174 MODULE_LICENSE("GPL");
175 MODULE_ALIAS("platform:ts72xx-wdt");