diff options
author | Mans Rullgard <mans@mansr.com> | 2015-11-19 17:09:05 -0500 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2015-12-28 17:03:50 -0500 |
commit | dca536c433a20f916451d8318f4aa7158c0d811c (patch) | |
tree | b2b62c24e10a673ef06ed75fdbcb3580e688de31 | |
parent | 5c6bb88a1b8890225d83fd00d14cf10ae00df91f (diff) |
watchdog: add support for Sigma Designs SMP86xx/SMP87xx
This adds support for the Sigma Designs SMP86xx/SMP87xx family built-in
watchdog.
Signed-off-by: Mans Rullgard <mans@mansr.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | drivers/watchdog/Kconfig | 10 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/tangox_wdt.c | 225 |
3 files changed, 236 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index ba2bf0ea2cf2..afb7f91795cb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -142,6 +142,16 @@ config MENF21BMC_WATCHDOG | |||
142 | This driver can also be built as a module. If so the module | 142 | This driver can also be built as a module. If so the module |
143 | will be called menf21bmc_wdt. | 143 | will be called menf21bmc_wdt. |
144 | 144 | ||
145 | config TANGOX_WATCHDOG | ||
146 | tristate "Sigma Designs SMP86xx/SMP87xx watchdog" | ||
147 | select WATCHDOG_CORE | ||
148 | depends on ARCH_TANGOX || COMPILE_TEST | ||
149 | help | ||
150 | Support for the watchdog in Sigma Designs SMP86xx (tango3) | ||
151 | and SMP87xx (tango4) family chips. | ||
152 | |||
153 | This driver can be built as a module. The module name is tangox_wdt. | ||
154 | |||
145 | config WM831X_WATCHDOG | 155 | config WM831X_WATCHDOG |
146 | tristate "WM831x watchdog" | 156 | tristate "WM831x watchdog" |
147 | depends on MFD_WM831X | 157 | depends on MFD_WM831X |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 62b7fdb0a2ab..2d203fc3cfdb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -190,6 +190,7 @@ obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | |||
190 | obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o | 190 | obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o |
191 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | 191 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o |
192 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 192 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
193 | obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o | ||
193 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 194 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
194 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 195 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
195 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o | 196 | obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o |
diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c new file mode 100644 index 000000000000..b9ee6246e7c2 --- /dev/null +++ b/drivers/watchdog/tangox_wdt.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Mans Rullgard <mans@mansr.com> | ||
3 | * SMP86xx/SMP87xx Watchdog driver | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #include <linux/bitops.h> | ||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/notifier.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/reboot.h> | ||
21 | #include <linux/watchdog.h> | ||
22 | |||
23 | #define DEFAULT_TIMEOUT 30 | ||
24 | |||
25 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
26 | module_param(nowayout, bool, 0); | ||
27 | MODULE_PARM_DESC(nowayout, | ||
28 | "Watchdog cannot be stopped once started (default=" | ||
29 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
30 | |||
31 | static unsigned int timeout; | ||
32 | module_param(timeout, int, 0); | ||
33 | MODULE_PARM_DESC(timeout, "Watchdog timeout"); | ||
34 | |||
35 | /* | ||
36 | * Counter counts down from programmed value. Reset asserts when | ||
37 | * the counter reaches 1. | ||
38 | */ | ||
39 | #define WD_COUNTER 0 | ||
40 | |||
41 | #define WD_CONFIG 4 | ||
42 | #define WD_CONFIG_XTAL_IN BIT(0) | ||
43 | #define WD_CONFIG_DISABLE BIT(31) | ||
44 | |||
45 | struct tangox_wdt_device { | ||
46 | struct watchdog_device wdt; | ||
47 | void __iomem *base; | ||
48 | unsigned long clk_rate; | ||
49 | struct clk *clk; | ||
50 | struct notifier_block restart; | ||
51 | }; | ||
52 | |||
53 | static int tangox_wdt_set_timeout(struct watchdog_device *wdt, | ||
54 | unsigned int new_timeout) | ||
55 | { | ||
56 | wdt->timeout = new_timeout; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static int tangox_wdt_start(struct watchdog_device *wdt) | ||
62 | { | ||
63 | struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); | ||
64 | u32 ticks; | ||
65 | |||
66 | ticks = 1 + wdt->timeout * dev->clk_rate; | ||
67 | writel(ticks, dev->base + WD_COUNTER); | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int tangox_wdt_stop(struct watchdog_device *wdt) | ||
73 | { | ||
74 | struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); | ||
75 | |||
76 | writel(0, dev->base + WD_COUNTER); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static unsigned int tangox_wdt_get_timeleft(struct watchdog_device *wdt) | ||
82 | { | ||
83 | struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); | ||
84 | u32 count; | ||
85 | |||
86 | count = readl(dev->base + WD_COUNTER); | ||
87 | |||
88 | if (!count) | ||
89 | return 0; | ||
90 | |||
91 | return (count - 1) / dev->clk_rate; | ||
92 | } | ||
93 | |||
94 | static const struct watchdog_info tangox_wdt_info = { | ||
95 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
96 | .identity = "tangox watchdog", | ||
97 | }; | ||
98 | |||
99 | static const struct watchdog_ops tangox_wdt_ops = { | ||
100 | .start = tangox_wdt_start, | ||
101 | .stop = tangox_wdt_stop, | ||
102 | .set_timeout = tangox_wdt_set_timeout, | ||
103 | .get_timeleft = tangox_wdt_get_timeleft, | ||
104 | }; | ||
105 | |||
106 | static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action, | ||
107 | void *data) | ||
108 | { | ||
109 | struct tangox_wdt_device *dev = | ||
110 | container_of(nb, struct tangox_wdt_device, restart); | ||
111 | |||
112 | writel(1, dev->base + WD_COUNTER); | ||
113 | |||
114 | return NOTIFY_DONE; | ||
115 | } | ||
116 | |||
117 | static int tangox_wdt_probe(struct platform_device *pdev) | ||
118 | { | ||
119 | struct tangox_wdt_device *dev; | ||
120 | struct resource *res; | ||
121 | u32 config; | ||
122 | int err; | ||
123 | |||
124 | dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); | ||
125 | if (!dev) | ||
126 | return -ENOMEM; | ||
127 | |||
128 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
129 | dev->base = devm_ioremap_resource(&pdev->dev, res); | ||
130 | if (IS_ERR(dev->base)) | ||
131 | return PTR_ERR(dev->base); | ||
132 | |||
133 | dev->clk = devm_clk_get(&pdev->dev, NULL); | ||
134 | if (IS_ERR(dev->clk)) | ||
135 | return PTR_ERR(dev->clk); | ||
136 | |||
137 | err = clk_prepare_enable(dev->clk); | ||
138 | if (err) | ||
139 | return err; | ||
140 | |||
141 | dev->clk_rate = clk_get_rate(dev->clk); | ||
142 | |||
143 | dev->wdt.parent = &pdev->dev; | ||
144 | dev->wdt.info = &tangox_wdt_info; | ||
145 | dev->wdt.ops = &tangox_wdt_ops; | ||
146 | dev->wdt.timeout = DEFAULT_TIMEOUT; | ||
147 | dev->wdt.min_timeout = 1; | ||
148 | dev->wdt.max_timeout = (U32_MAX - 1) / dev->clk_rate; | ||
149 | |||
150 | watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev); | ||
151 | watchdog_set_nowayout(&dev->wdt, nowayout); | ||
152 | watchdog_set_drvdata(&dev->wdt, dev); | ||
153 | |||
154 | /* | ||
155 | * Deactivate counter if disable bit is set to avoid | ||
156 | * accidental reset. | ||
157 | */ | ||
158 | config = readl(dev->base + WD_CONFIG); | ||
159 | if (config & WD_CONFIG_DISABLE) | ||
160 | writel(0, dev->base + WD_COUNTER); | ||
161 | |||
162 | writel(WD_CONFIG_XTAL_IN, dev->base + WD_CONFIG); | ||
163 | |||
164 | /* | ||
165 | * Mark as active and restart with configured timeout if | ||
166 | * already running. | ||
167 | */ | ||
168 | if (readl(dev->base + WD_COUNTER)) { | ||
169 | set_bit(WDOG_ACTIVE, &dev->wdt.status); | ||
170 | tangox_wdt_start(&dev->wdt); | ||
171 | } | ||
172 | |||
173 | err = watchdog_register_device(&dev->wdt); | ||
174 | if (err) { | ||
175 | clk_disable_unprepare(dev->clk); | ||
176 | return err; | ||
177 | } | ||
178 | |||
179 | platform_set_drvdata(pdev, dev); | ||
180 | |||
181 | dev->restart.notifier_call = tangox_wdt_restart; | ||
182 | dev->restart.priority = 128; | ||
183 | err = register_restart_handler(&dev->restart); | ||
184 | if (err) | ||
185 | dev_warn(&pdev->dev, "failed to register restart handler\n"); | ||
186 | |||
187 | dev_info(dev->wdt.dev, "SMP86xx/SMP87xx watchdog registered\n"); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int tangox_wdt_remove(struct platform_device *pdev) | ||
193 | { | ||
194 | struct tangox_wdt_device *dev = platform_get_drvdata(pdev); | ||
195 | |||
196 | tangox_wdt_stop(&dev->wdt); | ||
197 | clk_disable_unprepare(dev->clk); | ||
198 | |||
199 | unregister_restart_handler(&dev->restart); | ||
200 | watchdog_unregister_device(&dev->wdt); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static const struct of_device_id tangox_wdt_dt_ids[] = { | ||
206 | { .compatible = "sigma,smp8642-wdt" }, | ||
207 | { .compatible = "sigma,smp8759-wdt" }, | ||
208 | { } | ||
209 | }; | ||
210 | MODULE_DEVICE_TABLE(of, tangox_wdt_dt_ids); | ||
211 | |||
212 | static struct platform_driver tangox_wdt_driver = { | ||
213 | .probe = tangox_wdt_probe, | ||
214 | .remove = tangox_wdt_remove, | ||
215 | .driver = { | ||
216 | .name = "tangox-wdt", | ||
217 | .of_match_table = tangox_wdt_dt_ids, | ||
218 | }, | ||
219 | }; | ||
220 | |||
221 | module_platform_driver(tangox_wdt_driver); | ||
222 | |||
223 | MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>"); | ||
224 | MODULE_DESCRIPTION("SMP86xx/SMP87xx Watchdog driver"); | ||
225 | MODULE_LICENSE("GPL"); | ||