diff options
author | Krystian Garbaciak <krystian.garbaciak@diasemi.com> | 2014-09-28 13:05:45 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2014-10-20 14:55:24 -0400 |
commit | 5e9c16e3760893b3721f599f180795ca7160afef (patch) | |
tree | ad7c449807db7d6b58ed5bf4f8234079bb2f216c | |
parent | 22b1c841e31510c3124c88a13b8a7ada14e2e2d1 (diff) |
watchdog: Add DA9063 PMIC watchdog driver.
This driver supports the watchdog device inside the DA9063 PMIC.
Signed-off-by: Krystian Garbaciak <krystian.garbaciak@diasemi.com>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Acked-by: Steve Twiss <stwiss.opensource@diasemi.com>
Tested-by: Steve Twiss <stwiss.opensource@diasemi.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | drivers/watchdog/Kconfig | 9 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/da9063_wdt.c | 191 |
3 files changed, 201 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4edae08800ec..a51ccf3461fb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -87,6 +87,15 @@ config DA9055_WATCHDOG | |||
87 | This driver can also be built as a module. If so, the module | 87 | This driver can also be built as a module. If so, the module |
88 | will be called da9055_wdt. | 88 | will be called da9055_wdt. |
89 | 89 | ||
90 | config DA9063_WATCHDOG | ||
91 | tristate "Dialog DA9063 Watchdog" | ||
92 | depends on MFD_DA9063 | ||
93 | select WATCHDOG_CORE | ||
94 | help | ||
95 | Support for the watchdog in the DA9063 PMIC. | ||
96 | |||
97 | This driver can be built as a module. The module name is da9063_wdt. | ||
98 | |||
90 | config GPIO_WATCHDOG | 99 | config GPIO_WATCHDOG |
91 | tristate "Watchdog device controlled through GPIO-line" | 100 | tristate "Watchdog device controlled through GPIO-line" |
92 | depends on OF_GPIO | 101 | depends on OF_GPIO |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f105999da2f3..f8eaaf45e923 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -176,6 +176,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o | |||
176 | # Architecture Independent | 176 | # Architecture Independent |
177 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | 177 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o |
178 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | 178 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o |
179 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | ||
179 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 180 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
180 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 181 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
181 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 182 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c new file mode 100644 index 000000000000..2cd6b2c2dd2a --- /dev/null +++ b/drivers/watchdog/da9063_wdt.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * Watchdog driver for DA9063 PMICs. | ||
3 | * | ||
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | ||
5 | * | ||
6 | * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/watchdog.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/mfd/da9063/registers.h> | ||
22 | #include <linux/mfd/da9063/core.h> | ||
23 | #include <linux/regmap.h> | ||
24 | |||
25 | /* | ||
26 | * Watchdog selector to timeout in seconds. | ||
27 | * 0: WDT disabled; | ||
28 | * others: timeout = 2048 ms * 2^(TWDSCALE-1). | ||
29 | */ | ||
30 | static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; | ||
31 | #define DA9063_TWDSCALE_DISABLE 0 | ||
32 | #define DA9063_TWDSCALE_MIN 1 | ||
33 | #define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) | ||
34 | #define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN] | ||
35 | #define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] | ||
36 | #define DA9063_WDG_TIMEOUT wdt_timeout[3] | ||
37 | |||
38 | struct da9063_watchdog { | ||
39 | struct da9063 *da9063; | ||
40 | struct watchdog_device wdtdev; | ||
41 | }; | ||
42 | |||
43 | static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) | ||
44 | { | ||
45 | unsigned int i; | ||
46 | |||
47 | for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) { | ||
48 | if (wdt_timeout[i] >= secs) | ||
49 | return i; | ||
50 | } | ||
51 | |||
52 | return DA9063_TWDSCALE_MAX; | ||
53 | } | ||
54 | |||
55 | static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval) | ||
56 | { | ||
57 | return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D, | ||
58 | DA9063_TWDSCALE_MASK, regval); | ||
59 | } | ||
60 | |||
61 | static int da9063_wdt_start(struct watchdog_device *wdd) | ||
62 | { | ||
63 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
64 | unsigned int selector; | ||
65 | int ret; | ||
66 | |||
67 | selector = da9063_wdt_timeout_to_sel(wdt->wdtdev.timeout); | ||
68 | ret = _da9063_wdt_set_timeout(wdt->da9063, selector); | ||
69 | if (ret) | ||
70 | dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n", | ||
71 | ret); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int da9063_wdt_stop(struct watchdog_device *wdd) | ||
77 | { | ||
78 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
79 | int ret; | ||
80 | |||
81 | ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D, | ||
82 | DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE); | ||
83 | if (ret) | ||
84 | dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n", | ||
85 | ret); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static int da9063_wdt_ping(struct watchdog_device *wdd) | ||
91 | { | ||
92 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
93 | int ret; | ||
94 | |||
95 | ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, | ||
96 | DA9063_WATCHDOG); | ||
97 | if (ret) | ||
98 | dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n", | ||
99 | ret); | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int da9063_wdt_set_timeout(struct watchdog_device *wdd, | ||
105 | unsigned int timeout) | ||
106 | { | ||
107 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
108 | unsigned int selector; | ||
109 | int ret; | ||
110 | |||
111 | selector = da9063_wdt_timeout_to_sel(timeout); | ||
112 | ret = _da9063_wdt_set_timeout(wdt->da9063, selector); | ||
113 | if (ret) | ||
114 | dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n", | ||
115 | ret); | ||
116 | else | ||
117 | wdd->timeout = wdt_timeout[selector]; | ||
118 | |||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | static const struct watchdog_info da9063_watchdog_info = { | ||
123 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
124 | .identity = "DA9063 Watchdog", | ||
125 | }; | ||
126 | |||
127 | static const struct watchdog_ops da9063_watchdog_ops = { | ||
128 | .owner = THIS_MODULE, | ||
129 | .start = da9063_wdt_start, | ||
130 | .stop = da9063_wdt_stop, | ||
131 | .ping = da9063_wdt_ping, | ||
132 | .set_timeout = da9063_wdt_set_timeout, | ||
133 | }; | ||
134 | |||
135 | static int da9063_wdt_probe(struct platform_device *pdev) | ||
136 | { | ||
137 | int ret; | ||
138 | struct da9063 *da9063; | ||
139 | struct da9063_watchdog *wdt; | ||
140 | |||
141 | if (!pdev->dev.parent) | ||
142 | return -EINVAL; | ||
143 | |||
144 | da9063 = dev_get_drvdata(pdev->dev.parent); | ||
145 | if (!da9063) | ||
146 | return -EINVAL; | ||
147 | |||
148 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
149 | if (!wdt) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | wdt->da9063 = da9063; | ||
153 | |||
154 | wdt->wdtdev.info = &da9063_watchdog_info; | ||
155 | wdt->wdtdev.ops = &da9063_watchdog_ops; | ||
156 | wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; | ||
157 | wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; | ||
158 | wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; | ||
159 | |||
160 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; | ||
161 | |||
162 | watchdog_set_drvdata(&wdt->wdtdev, wdt); | ||
163 | dev_set_drvdata(&pdev->dev, wdt); | ||
164 | |||
165 | ret = watchdog_register_device(&wdt->wdtdev); | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static int da9063_wdt_remove(struct platform_device *pdev) | ||
171 | { | ||
172 | struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); | ||
173 | |||
174 | watchdog_unregister_device(&wdt->wdtdev); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static struct platform_driver da9063_wdt_driver = { | ||
180 | .probe = da9063_wdt_probe, | ||
181 | .remove = da9063_wdt_remove, | ||
182 | .driver = { | ||
183 | .name = DA9063_DRVNAME_WATCHDOG, | ||
184 | }, | ||
185 | }; | ||
186 | module_platform_driver(da9063_wdt_driver); | ||
187 | |||
188 | MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>"); | ||
189 | MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063"); | ||
190 | MODULE_LICENSE("GPL"); | ||
191 | MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG); | ||