diff options
-rw-r--r-- | drivers/watchdog/stm32_iwdg.c | 84 |
1 files changed, 47 insertions, 37 deletions
diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index 6f7c2bc19c07..d569a3634d9b 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c | |||
@@ -34,36 +34,44 @@ | |||
34 | #define KR_KEY_EWA 0x5555 /* write access enable */ | 34 | #define KR_KEY_EWA 0x5555 /* write access enable */ |
35 | #define KR_KEY_DWA 0x0000 /* write access disable */ | 35 | #define KR_KEY_DWA 0x0000 /* write access disable */ |
36 | 36 | ||
37 | /* IWDG_PR register bit values */ | 37 | /* IWDG_PR register */ |
38 | #define PR_4 0x00 /* prescaler set to 4 */ | 38 | #define PR_SHIFT 2 |
39 | #define PR_8 0x01 /* prescaler set to 8 */ | 39 | #define PR_MIN BIT(PR_SHIFT) |
40 | #define PR_16 0x02 /* prescaler set to 16 */ | ||
41 | #define PR_32 0x03 /* prescaler set to 32 */ | ||
42 | #define PR_64 0x04 /* prescaler set to 64 */ | ||
43 | #define PR_128 0x05 /* prescaler set to 128 */ | ||
44 | #define PR_256 0x06 /* prescaler set to 256 */ | ||
45 | 40 | ||
46 | /* IWDG_RLR register values */ | 41 | /* IWDG_RLR register values */ |
47 | #define RLR_MIN 0x07C /* min value supported by reload register */ | 42 | #define RLR_MIN 0x2 /* min value recommended */ |
48 | #define RLR_MAX 0xFFF /* max value supported by reload register */ | 43 | #define RLR_MAX GENMASK(11, 0) /* max value of reload register */ |
49 | 44 | ||
50 | /* IWDG_SR register bit mask */ | 45 | /* IWDG_SR register bit mask */ |
51 | #define FLAG_PVU BIT(0) /* Watchdog prescaler value update */ | 46 | #define SR_PVU BIT(0) /* Watchdog prescaler value update */ |
52 | #define FLAG_RVU BIT(1) /* Watchdog counter reload value update */ | 47 | #define SR_RVU BIT(1) /* Watchdog counter reload value update */ |
53 | 48 | ||
54 | /* set timeout to 100000 us */ | 49 | /* set timeout to 100000 us */ |
55 | #define TIMEOUT_US 100000 | 50 | #define TIMEOUT_US 100000 |
56 | #define SLEEP_US 1000 | 51 | #define SLEEP_US 1000 |
57 | 52 | ||
58 | #define HAS_PCLK true | 53 | struct stm32_iwdg_data { |
54 | bool has_pclk; | ||
55 | u32 max_prescaler; | ||
56 | }; | ||
57 | |||
58 | static const struct stm32_iwdg_data stm32_iwdg_data = { | ||
59 | .has_pclk = false, | ||
60 | .max_prescaler = 256, | ||
61 | }; | ||
62 | |||
63 | static const struct stm32_iwdg_data stm32mp1_iwdg_data = { | ||
64 | .has_pclk = true, | ||
65 | .max_prescaler = 1024, | ||
66 | }; | ||
59 | 67 | ||
60 | struct stm32_iwdg { | 68 | struct stm32_iwdg { |
61 | struct watchdog_device wdd; | 69 | struct watchdog_device wdd; |
70 | const struct stm32_iwdg_data *data; | ||
62 | void __iomem *regs; | 71 | void __iomem *regs; |
63 | struct clk *clk_lsi; | 72 | struct clk *clk_lsi; |
64 | struct clk *clk_pclk; | 73 | struct clk *clk_pclk; |
65 | unsigned int rate; | 74 | unsigned int rate; |
66 | bool has_pclk; | ||
67 | }; | 75 | }; |
68 | 76 | ||
69 | static inline u32 reg_read(void __iomem *base, u32 reg) | 77 | static inline u32 reg_read(void __iomem *base, u32 reg) |
@@ -79,31 +87,35 @@ static inline void reg_write(void __iomem *base, u32 reg, u32 val) | |||
79 | static int stm32_iwdg_start(struct watchdog_device *wdd) | 87 | static int stm32_iwdg_start(struct watchdog_device *wdd) |
80 | { | 88 | { |
81 | struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); | 89 | struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); |
82 | u32 val = FLAG_PVU | FLAG_RVU; | 90 | u32 tout, presc, iwdg_rlr, iwdg_pr, iwdg_sr; |
83 | u32 reload; | ||
84 | int ret; | 91 | int ret; |
85 | 92 | ||
86 | dev_dbg(wdd->parent, "%s\n", __func__); | 93 | dev_dbg(wdd->parent, "%s\n", __func__); |
87 | 94 | ||
88 | /* prescaler fixed to 256 */ | 95 | tout = clamp_t(unsigned int, wdd->timeout, |
89 | reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1, | 96 | wdd->min_timeout, wdd->max_hw_heartbeat_ms / 1000); |
90 | RLR_MIN, RLR_MAX); | 97 | |
98 | presc = DIV_ROUND_UP(tout * wdt->rate, RLR_MAX + 1); | ||
99 | |||
100 | /* The prescaler is align on power of 2 and start at 2 ^ PR_SHIFT. */ | ||
101 | presc = roundup_pow_of_two(presc); | ||
102 | iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT; | ||
103 | iwdg_rlr = ((tout * wdt->rate) / presc) - 1; | ||
91 | 104 | ||
92 | /* enable write access */ | 105 | /* enable write access */ |
93 | reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA); | 106 | reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA); |
94 | 107 | ||
95 | /* set prescaler & reload registers */ | 108 | /* set prescaler & reload registers */ |
96 | reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */ | 109 | reg_write(wdt->regs, IWDG_PR, iwdg_pr); |
97 | reg_write(wdt->regs, IWDG_RLR, reload); | 110 | reg_write(wdt->regs, IWDG_RLR, iwdg_rlr); |
98 | reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE); | 111 | reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE); |
99 | 112 | ||
100 | /* wait for the registers to be updated (max 100ms) */ | 113 | /* wait for the registers to be updated (max 100ms) */ |
101 | ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val, | 114 | ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, iwdg_sr, |
102 | !(val & (FLAG_PVU | FLAG_RVU)), | 115 | !(iwdg_sr & (SR_PVU | SR_RVU)), |
103 | SLEEP_US, TIMEOUT_US); | 116 | SLEEP_US, TIMEOUT_US); |
104 | if (ret) { | 117 | if (ret) { |
105 | dev_err(wdd->parent, | 118 | dev_err(wdd->parent, "Fail to set prescaler, reload regs\n"); |
106 | "Fail to set prescaler or reload registers\n"); | ||
107 | return ret; | 119 | return ret; |
108 | } | 120 | } |
109 | 121 | ||
@@ -156,7 +168,7 @@ static int stm32_iwdg_clk_init(struct platform_device *pdev, | |||
156 | } | 168 | } |
157 | 169 | ||
158 | /* optional peripheral clock */ | 170 | /* optional peripheral clock */ |
159 | if (wdt->has_pclk) { | 171 | if (wdt->data->has_pclk) { |
160 | wdt->clk_pclk = devm_clk_get(dev, "pclk"); | 172 | wdt->clk_pclk = devm_clk_get(dev, "pclk"); |
161 | if (IS_ERR(wdt->clk_pclk)) { | 173 | if (IS_ERR(wdt->clk_pclk)) { |
162 | dev_err(dev, "Unable to get pclk clock\n"); | 174 | dev_err(dev, "Unable to get pclk clock\n"); |
@@ -205,8 +217,8 @@ static const struct watchdog_ops stm32_iwdg_ops = { | |||
205 | }; | 217 | }; |
206 | 218 | ||
207 | static const struct of_device_id stm32_iwdg_of_match[] = { | 219 | static const struct of_device_id stm32_iwdg_of_match[] = { |
208 | { .compatible = "st,stm32-iwdg", .data = (void *)!HAS_PCLK }, | 220 | { .compatible = "st,stm32-iwdg", .data = &stm32_iwdg_data }, |
209 | { .compatible = "st,stm32mp1-iwdg", .data = (void *)HAS_PCLK }, | 221 | { .compatible = "st,stm32mp1-iwdg", .data = &stm32mp1_iwdg_data }, |
210 | { /* end node */ } | 222 | { /* end node */ } |
211 | }; | 223 | }; |
212 | MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); | 224 | MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); |
@@ -215,19 +227,16 @@ static int stm32_iwdg_probe(struct platform_device *pdev) | |||
215 | { | 227 | { |
216 | struct device *dev = &pdev->dev; | 228 | struct device *dev = &pdev->dev; |
217 | struct watchdog_device *wdd; | 229 | struct watchdog_device *wdd; |
218 | const struct of_device_id *match; | ||
219 | struct stm32_iwdg *wdt; | 230 | struct stm32_iwdg *wdt; |
220 | int ret; | 231 | int ret; |
221 | 232 | ||
222 | match = of_match_device(stm32_iwdg_of_match, dev); | ||
223 | if (!match) | ||
224 | return -ENODEV; | ||
225 | |||
226 | wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); | 233 | wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); |
227 | if (!wdt) | 234 | if (!wdt) |
228 | return -ENOMEM; | 235 | return -ENOMEM; |
229 | 236 | ||
230 | wdt->has_pclk = match->data; | 237 | wdt->data = of_device_get_match_data(&pdev->dev); |
238 | if (!wdt->data) | ||
239 | return -ENODEV; | ||
231 | 240 | ||
232 | /* This is the timer base. */ | 241 | /* This is the timer base. */ |
233 | wdt->regs = devm_platform_ioremap_resource(pdev, 0); | 242 | wdt->regs = devm_platform_ioremap_resource(pdev, 0); |
@@ -242,11 +251,12 @@ static int stm32_iwdg_probe(struct platform_device *pdev) | |||
242 | 251 | ||
243 | /* Initialize struct watchdog_device. */ | 252 | /* Initialize struct watchdog_device. */ |
244 | wdd = &wdt->wdd; | 253 | wdd = &wdt->wdd; |
254 | wdd->parent = dev; | ||
245 | wdd->info = &stm32_iwdg_info; | 255 | wdd->info = &stm32_iwdg_info; |
246 | wdd->ops = &stm32_iwdg_ops; | 256 | wdd->ops = &stm32_iwdg_ops; |
247 | wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate; | 257 | wdd->min_timeout = DIV_ROUND_UP((RLR_MIN + 1) * PR_MIN, wdt->rate); |
248 | wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate; | 258 | wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * wdt->data->max_prescaler * |
249 | wdd->parent = dev; | 259 | 1000) / wdt->rate; |
250 | 260 | ||
251 | watchdog_set_drvdata(wdd, wdt); | 261 | watchdog_set_drvdata(wdd, wdt); |
252 | watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); | 262 | watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); |