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); |
