diff options
author | Ludovic Barre <ludovic.barre@st.com> | 2018-06-25 11:43:00 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@linux-watchdog.org> | 2018-08-02 09:57:09 -0400 |
commit | c2cf466cca87e2072bd6616f751f4c385c2ab71e (patch) | |
tree | 5f8427e4c98c5920a7501059b8ce9db2b85c9f0a | |
parent | e454652eb21535055337ae8908e164daed355ee7 (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.c | 116 |
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 | |||
57 | struct stm32_iwdg { | 60 | struct 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 | ||
64 | static inline u32 reg_read(void __iomem *base, u32 reg) | 69 | static 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 | ||
141 | static 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 | |||
136 | static const struct watchdog_info stm32_iwdg_info = { | 179 | static 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 | ||
193 | static 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 | }; | ||
198 | MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); | ||
199 | |||
150 | static int stm32_iwdg_probe(struct platform_device *pdev) | 200 | static 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; |
219 | err: | 255 | err: |
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 | ||
235 | static const struct of_device_id stm32_iwdg_of_match[] = { | ||
236 | { .compatible = "st,stm32-iwdg" }, | ||
237 | { /* end node */ } | ||
238 | }; | ||
239 | MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); | ||
240 | |||
241 | static struct platform_driver stm32_iwdg_driver = { | 273 | static 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 | }; |
249 | module_platform_driver(stm32_iwdg_driver); | 281 | module_platform_driver(stm32_iwdg_driver); |