Hi Guenter, (There are some problem with Eric's email, he can not receive this email, so I help to reply his comments following yours. sorry for troubles.) >>>> +#define SPRD_WDT_MAX_TIMEOUT 60 >>> >>> >>> Is that really the maximum supported timeout ? Seems a bit low. >>> Shouldn't it be something like (U32_MAX / SPRD_WDT_CNT_STEP) ? >>> >> >> It supports the max value like as U32_MAX/SPRD_WDT_CNT_STEP, >> but it doesn't unnecessary to support so big value, if the system can not >> response it in 60s, the watchdog could trigger timeout. >> > It is customary to provide the highest possible value here. > No one is forced to set such high values. You are making a choice for > others here. But, sure, if you insist; not worth arguing about. Eric said 60s is better for us, thanks for your patient explanation. > >>>> + >>>> +#define SPRD_WDT_CNT_HIGH_VALUE 16 >>> >>> >>> Maybe name it "SPRD_WDT_CNT_HIGH_SHIFT". It is not really a value, >>> it is a shift. >>> >> >> Yes, you are right, _SHIFT will be better. >> >>>> >>>> +#define SPRD_WDT_LOW_VALUE_MASK GENMASK(15, 0) >>>> +#define SPRD_WDT_CNT_VALUE_MAX GENMASK(31, 0) >>> >>> >>> Does this mask serve a useful purpose ? >>> >> >> OK, I will remove it, thanks! >> >>>> +#define SPRD_WDT_LOAD_TIMEOUT 1000 >>>> + >>>> +struct sprd_wdt { >>>> + void __iomem *base; >>>> + struct watchdog_device wdd; >>>> + struct clk *enable; >>>> + struct clk *rtc_enable; >>>> + unsigned int irq; >>>> +}; >>>> + >>>> +static inline struct sprd_wdt *to_sprd_wdt(struct watchdog_device *wdd) >>>> +{ >>>> + return container_of(wdd, struct sprd_wdt, wdd); >>>> +} >>>> + >>>> +static inline void sprd_wdt_lock(void __iomem *addr) >>>> +{ >>>> + writel_relaxed(0x0, addr + SPRD_WDT_LOCK); >>>> +} >>>> + >>>> +static inline void sprd_wdt_unlock(void __iomem *addr) >>>> +{ >>>> + writel_relaxed(SPRD_WDT_UNLOCK_KEY, addr + SPRD_WDT_LOCK); >>>> +} >>>> + >>>> +static inline bool sprd_wdt_is_running(struct sprd_wdt *wdt) >>>> +{ >>>> + u32 val; >>>> + >>>> + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); >>>> + return val & SPRD_WDT_NEW_VER_EN; >>>> +} >>>> + >>>> +static irqreturn_t sprd_wdt_isr(int irq, void *dev_id) >>>> +{ >>>> + struct sprd_wdt *wdt = (struct sprd_wdt *)dev_id; >>>> + >>>> + sprd_wdt_unlock(wdt->base); >>>> + writel_relaxed(SPRD_WDT_INT_CLEAR_BIT, wdt->base + >>>> SPRD_WDT_INT_CLR); >>>> + sprd_wdt_lock(wdt->base); >>>> + watchdog_notify_pretimeout(&wdt->wdd); >>>> + return IRQ_HANDLED; >>>> +} >>>> + >>>> +static u32 sprd_wdt_get_cnt_value(struct sprd_wdt *wdt) >>>> +{ >>>> + u32 val; >>>> + >>>> + val = readl_relaxed(wdt->base + SPRD_WDT_CNT_HIGH) << >>>> + SPRD_WDT_CNT_HIGH_VALUE; >>>> + val |= readl_relaxed(wdt->base + SPRD_WDT_CNT_LOW) & >>>> + SPRD_WDT_LOW_VALUE_MASK; >>>> + >>>> + return val; >>>> +} >>>> + >>>> +static int sprd_wdt_load_value(struct sprd_wdt *wdt, u32 timeout, >>>> + u32 pretimeout) >>>> +{ >>>> + u32 val, delay_cnt = 0; >>>> + u32 tmr_step = timeout * SPRD_WDT_CNT_STEP; >>>> + u32 prtmr_step = pretimeout * SPRD_WDT_CNT_STEP; >>>> + >>>> + sprd_wdt_unlock(wdt->base); >>>> + writel_relaxed((tmr_step >> SPRD_WDT_CNT_HIGH_VALUE) & >>>> + SPRD_WDT_LOW_VALUE_MASK, wdt->base + >>>> SPRD_WDT_LOAD_HIGH); >>>> + writel_relaxed((tmr_step & SPRD_WDT_LOW_VALUE_MASK), >>>> + wdt->base + SPRD_WDT_LOAD_LOW); >>>> + writel_relaxed((prtmr_step >> SPRD_WDT_CNT_HIGH_VALUE) & >>>> + SPRD_WDT_LOW_VALUE_MASK, >>>> + wdt->base + SPRD_WDT_IRQ_LOAD_HIGH); >>>> + writel_relaxed(prtmr_step & SPRD_WDT_LOW_VALUE_MASK, >>>> + wdt->base + SPRD_WDT_IRQ_LOAD_LOW); >>>> + sprd_wdt_lock(wdt->base); >>>> + >>>> + /* >>>> + * Waiting the load value operation done, >>>> + * it needs two or three RTC clock cycles. >>>> + */ >>>> + do { >>>> + val = readl_relaxed(wdt->base + SPRD_WDT_INT_RAW); >>>> + if (!(val & SPRD_WDT_LD_BUSY_BIT)) >>>> + break; >>>> + >>>> + cpu_relax(); >>>> + } while (delay_cnt++ < SPRD_WDT_LOAD_TIMEOUT); >>>> + >>>> + if (delay_cnt >= SPRD_WDT_LOAD_TIMEOUT) >>>> + return -EBUSY; >>>> + return 0; >>>> +} >>>> + >>>> +static int sprd_wdt_enable(struct sprd_wdt *wdt) >>>> +{ >>>> + u32 val; >>>> + int ret; >>>> + >>>> + ret = clk_prepare_enable(wdt->enable); >>>> + if (ret) >>>> + return ret; >>>> + ret = clk_prepare_enable(wdt->rtc_enable); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + sprd_wdt_unlock(wdt->base); >>>> + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); >>>> + val |= SPRD_WDT_NEW_VER_EN; >>>> + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); >>>> + sprd_wdt_lock(wdt->base); >>>> + return 0; >>>> +} >>>> + >>>> +static void sprd_wdt_disable(struct sprd_wdt *wdt) >>>> +{ >>>> + sprd_wdt_unlock(wdt->base); >>>> + writel_relaxed(0x0, wdt->base + SPRD_WDT_CTRL); >>>> + sprd_wdt_lock(wdt->base); >>>> + >>>> + clk_disable_unprepare(wdt->rtc_enable); >>>> + clk_disable_unprepare(wdt->enable); >>>> +} >>>> + >>>> +static int sprd_wdt_start(struct watchdog_device *wdd) >>>> +{ >>>> + struct sprd_wdt *wdt = to_sprd_wdt(wdd); >>>> + u32 val; >>>> + int ret; >>>> + >>>> + ret = sprd_wdt_load_value(wdt, wdd->timeout, wdd->pretimeout); >>>> + if (ret) >>>> + return ret; >>>> + >>>> + sprd_wdt_unlock(wdt->base); >>>> + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); >>>> + val |= SPRD_WDT_CNT_EN_BIT | SPRD_WDT_INT_EN_BIT | >>>> SPRD_WDT_RST_EN_BIT; >>>> + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); >>>> + sprd_wdt_lock(wdt->base); >>>> + set_bit(WDOG_HW_RUNNING, &wdd->status); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int sprd_wdt_stop(struct watchdog_device *wdd) >>>> +{ >>>> + struct sprd_wdt *wdt = to_sprd_wdt(wdd); >>>> + u32 val; >>>> + >>>> + sprd_wdt_unlock(wdt->base); >>>> + val = readl_relaxed(wdt->base + SPRD_WDT_CTRL); >>>> + val &= ~(SPRD_WDT_CNT_EN_BIT | SPRD_WDT_RST_EN_BIT | >>>> + SPRD_WDT_INT_EN_BIT); >>>> + writel_relaxed(val, wdt->base + SPRD_WDT_CTRL); >>>> + sprd_wdt_lock(wdt->base); >>>> + return 0; >>>> +} >>>> + >>>> +static int sprd_wdt_set_timeout(struct watchdog_device *wdd, >>>> + u32 timeout) >>>> +{ >>>> + struct sprd_wdt *wdt = to_sprd_wdt(wdd); >>>> + >>>> + if (timeout == wdd->timeout) >>>> + return 0; >>>> + >>>> + wdd->timeout = timeout; >>>> + >>>> + return sprd_wdt_load_value(wdt, timeout, wdd->pretimeout); >>>> +} >>>> + >>>> +static int sprd_wdt_set_pretimeout(struct watchdog_device *wdd, >>>> + u32 new_pretimeout) >>>> +{ >>>> + struct sprd_wdt *wdt = to_sprd_wdt(wdd); >>>> + >>>> + if (new_pretimeout == wdd->pretimeout) >>>> + return 0; >>> >>> >>> This is inconsistent. pretimeout == 0 is accepted until it is set >>> to a non-zero value once, then 0 is no longer accepted. pretimeout==0 >>> should reflect "pretimeout disabled". If that is not possible you >>> need to set some non-0 default value in the probe function. >>> >> >> I understand your opinion, I will fix it. >> >>>> + if (new_pretimeout <= wdd->min_timeout) >>>> + return -EINVAL; >>> >>> >>> Why is pretimeout == wdd->min_timeout invalid ? >>> >> >> OK, you are right, it should be pretimeout < min_timeout. >> >>>> + >>>> + wdd->pretimeout = new_pretimeout; >>>> + >>>> + return sprd_wdt_load_value(wdt, wdd->timeout, new_pretimeout); >>>> +} >>>> + >>>> +static u32 sprd_wdt_get_timeleft(struct watchdog_device *wdd) >>>> +{ >>>> + struct sprd_wdt *wdt = to_sprd_wdt(wdd); >>>> + u32 val; >>>> + >>>> + val = sprd_wdt_get_cnt_value(wdt); >>>> + val = val / SPRD_WDT_CNT_STEP; >>>> + >>>> + return val; >>>> +} >>>> + >>>> +static const struct watchdog_ops sprd_wdt_ops = { >>>> + .owner = THIS_MODULE, >>>> + .start = sprd_wdt_start, >>>> + .stop = sprd_wdt_stop, >>>> + .set_timeout = sprd_wdt_set_timeout, >>>> + .set_pretimeout = sprd_wdt_set_pretimeout, >>>> + .get_timeleft = sprd_wdt_get_timeleft, >>> >>> >>> Just wondering - no heartbeat function ? Having to load the timer >>> values for each heartbeat is expensive. >>> >> >> Unfortunately, this watchdog has not RELOAD register. >> >>>> >>>> +}; >>>> + >>>> +static const struct watchdog_info sprd_wdt_info = { >>>> + .options = WDIOF_SETTIMEOUT | >>>> + WDIOF_PRETIMEOUT | >>>> + WDIOF_MAGICCLOSE | >>>> + WDIOF_KEEPALIVEPING, >>>> + .identity = "Spreadtrum Watchdog Timer", >>>> +}; >>>> + >>>> +static int sprd_wdt_probe(struct platform_device *pdev) >>>> +{ >>>> + struct resource *wdt_res; >>>> + struct sprd_wdt *wdt; >>>> + int ret; >>>> + >>>> + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); >>>> + if (!wdt) >>>> + return -ENOMEM; >>>> + >>>> + wdt_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >>>> + if (!wdt_res) { >>>> + dev_err(&pdev->dev, "failed to memory resource\n"); >>>> + return -ENOMEM; >>>> + } >>> >>> >>> Unnecessary error check and message. devm_ioremap_resource() >>> returns an error if res is NULL. >>> >> >> OK, I will fix it. >> >>>> >>>> + >>>> + wdt->base = devm_ioremap_resource(&pdev->dev, wdt_res); >>>> + if (IS_ERR(wdt->base)) >>> >>> >>> Move the error message to here. >>> >>>> + return PTR_ERR(wdt->base); >>>> + >>>> + wdt->enable = devm_clk_get(&pdev->dev, "enable"); >>>> + if (IS_ERR(wdt->enable)) { >>>> + dev_err(&pdev->dev, "can't get the enable clock\n"); >>>> + return PTR_ERR(wdt->enable); >>>> + } >>>> + >>>> + wdt->rtc_enable = devm_clk_get(&pdev->dev, "rtc_enable"); >>>> + if (IS_ERR(wdt->rtc_enable)) { >>>> + dev_err(&pdev->dev, "can't get the rtc enable clock\n"); >>>> + return PTR_ERR(wdt->rtc_enable); >>>> + } >>>> + >>>> + wdt->irq = platform_get_irq(pdev, 0); >>>> + if (wdt->irq < 0) { >>>> + dev_err(&pdev->dev, "failed to get IRQ resource\n"); >>>> + return wdt->irq; >>>> + } >>>> + >>>> + ret = devm_request_irq(&pdev->dev, wdt->irq, sprd_wdt_isr, >>>> + IRQF_NO_SUSPEND, "sprd-wdt", (void >>>> *)wdt); >>>> + if (ret) { >>>> + dev_err(&pdev->dev, "failed to register irq\n"); >>>> + return ret; >>>> + } >>>> + >>>> + wdt->wdd.info = &sprd_wdt_info; >>>> + wdt->wdd.ops = &sprd_wdt_ops; >>>> + wdt->wdd.parent = &pdev->dev; >>>> + wdt->wdd.min_timeout = SPRD_WDT_MIN_TIMROUT; >>>> + wdt->wdd.max_timeout = SPRD_WDT_MAX_TIMEOUT; >>>> + >>> >>> You might want to set wdt->wdd.timeout to a default value. >>> >> >> OK, I will set a default timeout value in case of no timeout-sec in >> device-tree. >> >>>> >>>> + ret = sprd_wdt_enable(wdt); >>>> + if (ret) { >>>> + dev_err(&pdev->dev, "failed to enable wdt\n"); >>>> + return ret; >>>> + } >>>> + >>> >>> >>> This needs a matching sprd_wdt_disable() call in the remove function. >>> Best way to handle this would be to add devm_add_action() here. >>> >> >> If add devm_add_action(), it will be called in the remove function, >> but this is not our expect, if someone remove this module, we want it just >> timeout. >> > > But that leaves the watchdog enabled even if it was stopped (no call to > sprd_wdt_disable()). Eric said It can not be stopped since we have set WATCHDOG_NOWAYOUT, which means it will reboot the system when removing the watchdog. > Relying on NOWAYOUT would be a better option here. Again, you are > making a choice for the user. we have set WATCHDOG_NOWAYOUT. > > >>>> + watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT); >>>> + watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); >>>> + >>>> + ret = watchdog_register_device(&wdt->wdd); >>> >>> >>> Unfortunately this doesn't work. It leaves interrupts enabled >>> after the watchdog device is removed in sprd_wdt_remove(), >>> but the driver will be gone. I would suggest to use >>> devm_watchdog_register_device() and reorder calls to request >>> the interrupt after registering the watchdog device. Then you >>> can drop the remove function entirely. >>> >> >> Yes, you are right, I understand your opinion, and it is very important, >> devm_watchdog_register_device() is better. Thanks. >> >>>> + if (ret) { >>>> + sprd_wdt_disable(wdt); >>>> + dev_err(&pdev->dev, "failed to register watchdog\n"); >>>> + return ret; >>>> + } >>>> + platform_set_drvdata(pdev, wdt); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int sprd_wdt_remove(struct platform_device *pdev) >>>> +{ >>>> + struct sprd_wdt *wdt = platform_get_drvdata(pdev); >>>> + >>>> + watchdog_unregister_device(&wdt->wdd); >>>> + >>>> + if (sprd_wdt_is_running(wdt)) >>>> + dev_crit(&pdev->dev, "Device removed: Expect >>>> reboot!\n"); >>>> + return 0; >>>> +} >>>> + >>>> +static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) >>>> +{ >>>> + struct watchdog_device *wdd = dev_get_drvdata(dev); >>>> + struct sprd_wdt *wdt = dev_get_drvdata(dev); >>>> + >>>> + if (watchdog_active(wdd)) { >>>> + sprd_wdt_stop(&wdt->wdd); >>>> + sprd_wdt_disable(wdt); >>> >>> >>> Are you sure you only want to disable the clocks if the watchdog was >>> active ? >>> That seems to be a bit inconsistent. >>> >> >> This watchdog is in always on power domain, if system suspend, it needs to >> disable it, or it will timeout. >> > That is not what I asked. I asked why it isn't > > if (watchdog_active(wdd)) > sprd_wdt_stop(&wdt->wdd); > > sprd_wdt_disable(wdt); > > instead. Yes, Eric will fix this in next version. > >>>> + } >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) >>>> +{ >>>> + struct watchdog_device *wdd = dev_get_drvdata(dev); >>>> + struct sprd_wdt *wdt = dev_get_drvdata(dev); >>>> + int ret; >>>> + >>>> + if (watchdog_active(wdd)) { >>>> + ret = sprd_wdt_enable(wdt); >>>> + if (ret) >>>> + return ret; >>>> + ret = sprd_wdt_start(&wdt->wdd); >>>> + if (ret) { >>>> + sprd_wdt_disable(wdt); >>>> + return ret; >>>> + } >>> >>> >>> This can leave the system in an inconsistent state. Sometimes the wdt may >>> be >>> disabled, sometimes not. >>> >> >> If watchdog enable failed, it means there is something wrong in this >> system. >> > > > True. > > Guenter -- Baolin.wang Best Regards -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html