aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLudovic Barre <ludovic.barre@st.com>2018-06-25 11:43:00 -0400
committerWim Van Sebroeck <wim@linux-watchdog.org>2018-08-02 09:57:09 -0400
commitc2cf466cca87e2072bd6616f751f4c385c2ab71e (patch)
tree5f8427e4c98c5920a7501059b8ce9db2b85c9f0a
parente454652eb21535055337ae8908e164daed355ee7 (diff)
watchdog: stm32: add pclk feature for stm32mp1
This patch adds compatible data to manage pclk clock by compatible. Adds stm32mp1 support which requires pclk clock. Signed-off-by: Ludovic Barre <ludovic.barre@st.com> Acked-by: Alexandre TORGUE <alexandre.torgue@st.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
-rw-r--r--drivers/watchdog/stm32_iwdg.c116
1 files changed, 74 insertions, 42 deletions
diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c
index c97ad5619cb0..e00e3b3526c6 100644
--- a/drivers/watchdog/stm32_iwdg.c
+++ b/drivers/watchdog/stm32_iwdg.c
@@ -11,12 +11,13 @@
11 11
12#include <linux/clk.h> 12#include <linux/clk.h>
13#include <linux/delay.h> 13#include <linux/delay.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/interrupt.h> 14#include <linux/interrupt.h>
17#include <linux/io.h> 15#include <linux/io.h>
18#include <linux/iopoll.h> 16#include <linux/iopoll.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/of.h> 19#include <linux/of.h>
20#include <linux/of_device.h>
20#include <linux/platform_device.h> 21#include <linux/platform_device.h>
21#include <linux/watchdog.h> 22#include <linux/watchdog.h>
22 23
@@ -54,11 +55,15 @@
54#define TIMEOUT_US 100000 55#define TIMEOUT_US 100000
55#define SLEEP_US 1000 56#define SLEEP_US 1000
56 57
58#define HAS_PCLK true
59
57struct stm32_iwdg { 60struct stm32_iwdg {
58 struct watchdog_device wdd; 61 struct watchdog_device wdd;
59 void __iomem *regs; 62 void __iomem *regs;
60 struct clk *clk; 63 struct clk *clk_lsi;
64 struct clk *clk_pclk;
61 unsigned int rate; 65 unsigned int rate;
66 bool has_pclk;
62}; 67};
63 68
64static inline u32 reg_read(void __iomem *base, u32 reg) 69static inline u32 reg_read(void __iomem *base, u32 reg)
@@ -133,6 +138,44 @@ static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
133 return 0; 138 return 0;
134} 139}
135 140
141static int stm32_iwdg_clk_init(struct platform_device *pdev,
142 struct stm32_iwdg *wdt)
143{
144 u32 ret;
145
146 wdt->clk_lsi = devm_clk_get(&pdev->dev, "lsi");
147 if (IS_ERR(wdt->clk_lsi)) {
148 dev_err(&pdev->dev, "Unable to get lsi clock\n");
149 return PTR_ERR(wdt->clk_lsi);
150 }
151
152 /* optional peripheral clock */
153 if (wdt->has_pclk) {
154 wdt->clk_pclk = devm_clk_get(&pdev->dev, "pclk");
155 if (IS_ERR(wdt->clk_pclk)) {
156 dev_err(&pdev->dev, "Unable to get pclk clock\n");
157 return PTR_ERR(wdt->clk_pclk);
158 }
159
160 ret = clk_prepare_enable(wdt->clk_pclk);
161 if (ret) {
162 dev_err(&pdev->dev, "Unable to prepare pclk clock\n");
163 return ret;
164 }
165 }
166
167 ret = clk_prepare_enable(wdt->clk_lsi);
168 if (ret) {
169 dev_err(&pdev->dev, "Unable to prepare lsi clock\n");
170 clk_disable_unprepare(wdt->clk_pclk);
171 return ret;
172 }
173
174 wdt->rate = clk_get_rate(wdt->clk_lsi);
175
176 return 0;
177}
178
136static const struct watchdog_info stm32_iwdg_info = { 179static const struct watchdog_info stm32_iwdg_info = {
137 .options = WDIOF_SETTIMEOUT | 180 .options = WDIOF_SETTIMEOUT |
138 WDIOF_MAGICCLOSE | 181 WDIOF_MAGICCLOSE |
@@ -147,49 +190,42 @@ static const struct watchdog_ops stm32_iwdg_ops = {
147 .set_timeout = stm32_iwdg_set_timeout, 190 .set_timeout = stm32_iwdg_set_timeout,
148}; 191};
149 192
193static const struct of_device_id stm32_iwdg_of_match[] = {
194 { .compatible = "st,stm32-iwdg", .data = (void *)!HAS_PCLK },
195 { .compatible = "st,stm32mp1-iwdg", .data = (void *)HAS_PCLK },
196 { /* end node */ }
197};
198MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
199
150static int stm32_iwdg_probe(struct platform_device *pdev) 200static int stm32_iwdg_probe(struct platform_device *pdev)
151{ 201{
152 struct watchdog_device *wdd; 202 struct watchdog_device *wdd;
203 const struct of_device_id *match;
153 struct stm32_iwdg *wdt; 204 struct stm32_iwdg *wdt;
154 struct resource *res; 205 struct resource *res;
155 void __iomem *regs;
156 struct clk *clk;
157 int ret; 206 int ret;
158 207
208 match = of_match_device(stm32_iwdg_of_match, &pdev->dev);
209 if (!match)
210 return -ENODEV;
211
212 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
213 if (!wdt)
214 return -ENOMEM;
215
216 wdt->has_pclk = match->data;
217
159 /* This is the timer base. */ 218 /* This is the timer base. */
160 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 219 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
161 regs = devm_ioremap_resource(&pdev->dev, res); 220 wdt->regs = devm_ioremap_resource(&pdev->dev, res);
162 if (IS_ERR(regs)) { 221 if (IS_ERR(wdt->regs)) {
163 dev_err(&pdev->dev, "Could not get resource\n"); 222 dev_err(&pdev->dev, "Could not get resource\n");
164 return PTR_ERR(regs); 223 return PTR_ERR(wdt->regs);
165 } 224 }
166 225
167 clk = devm_clk_get(&pdev->dev, NULL); 226 ret = stm32_iwdg_clk_init(pdev, wdt);
168 if (IS_ERR(clk)) { 227 if (ret)
169 dev_err(&pdev->dev, "Unable to get clock\n");
170 return PTR_ERR(clk);
171 }
172
173 ret = clk_prepare_enable(clk);
174 if (ret) {
175 dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk);
176 return ret; 228 return ret;
177 }
178
179 /*
180 * Allocate our watchdog driver data, which has the
181 * struct watchdog_device nested within it.
182 */
183 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
184 if (!wdt) {
185 ret = -ENOMEM;
186 goto err;
187 }
188
189 /* Initialize struct stm32_iwdg. */
190 wdt->regs = regs;
191 wdt->clk = clk;
192 wdt->rate = clk_get_rate(clk);
193 229
194 /* Initialize struct watchdog_device. */ 230 /* Initialize struct watchdog_device. */
195 wdd = &wdt->wdd; 231 wdd = &wdt->wdd;
@@ -217,7 +253,8 @@ static int stm32_iwdg_probe(struct platform_device *pdev)
217 253
218 return 0; 254 return 0;
219err: 255err:
220 clk_disable_unprepare(clk); 256 clk_disable_unprepare(wdt->clk_lsi);
257 clk_disable_unprepare(wdt->clk_pclk);
221 258
222 return ret; 259 return ret;
223} 260}
@@ -227,23 +264,18 @@ static int stm32_iwdg_remove(struct platform_device *pdev)
227 struct stm32_iwdg *wdt = platform_get_drvdata(pdev); 264 struct stm32_iwdg *wdt = platform_get_drvdata(pdev);
228 265
229 watchdog_unregister_device(&wdt->wdd); 266 watchdog_unregister_device(&wdt->wdd);
230 clk_disable_unprepare(wdt->clk); 267 clk_disable_unprepare(wdt->clk_lsi);
268 clk_disable_unprepare(wdt->clk_pclk);
231 269
232 return 0; 270 return 0;
233} 271}
234 272
235static const struct of_device_id stm32_iwdg_of_match[] = {
236 { .compatible = "st,stm32-iwdg" },
237 { /* end node */ }
238};
239MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
240
241static struct platform_driver stm32_iwdg_driver = { 273static struct platform_driver stm32_iwdg_driver = {
242 .probe = stm32_iwdg_probe, 274 .probe = stm32_iwdg_probe,
243 .remove = stm32_iwdg_remove, 275 .remove = stm32_iwdg_remove,
244 .driver = { 276 .driver = {
245 .name = "iwdg", 277 .name = "iwdg",
246 .of_match_table = stm32_iwdg_of_match, 278 .of_match_table = of_match_ptr(stm32_iwdg_of_match),
247 }, 279 },
248}; 280};
249module_platform_driver(stm32_iwdg_driver); 281module_platform_driver(stm32_iwdg_driver);