summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXingyu Wu <xingyu.wu@starfivetech.com>2022-11-09 09:34:14 +0300
committerXingyu Wu <xingyu.wu@starfivetech.com>2022-11-10 05:12:21 +0300
commit6c91490b1adcd474cf219b6a075d7d41c5f90433 (patch)
tree7b12cdfdaa43479d76845559a38816fb616a42fb
parent37c389bcb7db4ccc38244ec9199daa6438bb38a5 (diff)
downloadlinux-6c91490b1adcd474cf219b6a075d7d41c5f90433.tar.xz
watchdog: starfive: Get timeleft correctly
Use flag to count timeleft and the flag is base on reading register instead of interrupt handler function. Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
-rw-r--r--[-rwxr-xr-x]drivers/watchdog/starfive-wdt.c58
1 files changed, 31 insertions, 27 deletions
diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 4eb16c435577..d316767fcaf5 100755..100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -127,6 +127,7 @@ struct starfive_wdt_variant {
u32 int_clr;
u32 int_mask;
u32 unlock;
+ u32 enter_irq_status;
struct starfive_wdt_variant_t *variant;
};
@@ -143,8 +144,7 @@ struct starfive_wdt {
void __iomem *base;
spinlock_t lock;
- unsigned int interrupt_flag;
- struct resource *wdt_irq;
+ int irq;
};
#ifdef CONFIG_OF
@@ -181,6 +181,7 @@ static const struct starfive_wdt_variant drv_data_jh7110 = {
.value = JH7110_WDOGVALUE,
.int_clr = JH7110_WDOGINTCLR,
.unlock = JH7110_WDOGLOCK,
+ .enter_irq_status = JH7110_WDOGIMS,
.variant = &jh7110_variant,
};
@@ -354,6 +355,12 @@ static void starfive_wdt_disable_reset(struct starfive_wdt *wdt)
writel(val, wdt->base + wdt->drv_data->control);
}
+static bool starfive_wdt_enter_irq_status(struct starfive_wdt *wdt)
+{
+ /* interrupt status whether has entered from the counter */
+ return !!readl(wdt->base + wdt->drv_data->enter_irq_status);
+}
+
static void starfive_wdt_int_clr(struct starfive_wdt *wdt)
{
void __iomem *addr;
@@ -374,6 +381,9 @@ static void starfive_wdt_int_clr(struct starfive_wdt *wdt)
if (!ret)
writel(STARFIVE_WATCHDOG_INTCLR, addr);
+
+ if (starfive_wdt_enter_irq_status(wdt))
+ enable_irq(wdt->irq);
}
static inline void starfive_wdt_set_count(struct starfive_wdt *wdt, u32 val)
@@ -438,21 +448,20 @@ static unsigned int starfive_wdt_get_timeleft(struct watchdog_device *wdd)
{
struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
u32 count;
+ u32 irq_status;
starfive_wdt_unlock(wdt);
- count = starfive_wdt_get_count(wdt) + (1 - wdt->interrupt_flag) * wdt->count;
+ /*
+ * Because set half count value,
+ * timeleft value should add the count value before first timeout.
+ */
+ irq_status = starfive_wdt_enter_irq_status(wdt) ? 1 : 0;
+ count = starfive_wdt_get_count(wdt) + (1 - irq_status) * wdt->count;
starfive_wdt_lock(wdt);
return starfive_wdt_ticks_to_sec(wdt, count);
}
-static void starfive_wdt_irq_flag_clr(struct starfive_wdt *wdt)
-{
- if (wdt->interrupt_flag)
- enable_irq(wdt->wdt_irq->start);
- wdt->interrupt_flag = 0;
-}
-
static int starfive_wdt_keepalive(struct watchdog_device *wdd)
{
struct starfive_wdt *wdt = watchdog_get_drvdata(wdd);
@@ -461,7 +470,6 @@ static int starfive_wdt_keepalive(struct watchdog_device *wdd)
starfive_wdt_unlock(wdt);
starfive_wdt_int_clr(wdt);
- starfive_wdt_irq_flag_clr(wdt);
starfive_wdt_set_relod_count(wdt, wdt->count);
starfive_wdt_lock(wdt);
@@ -479,9 +487,8 @@ static irqreturn_t starfive_wdt_interrupt_handler(int irq, void *data)
struct platform_device *pdev = data;
struct starfive_wdt *wdt = platform_get_drvdata(pdev);
- /* Clear the IRQ status and set flag. */
- disable_irq_nosync(wdt->wdt_irq->start);
- wdt->interrupt_flag = 1;
+ /* Disable the IRQ and avoid re-entry interrupt. */
+ disable_irq_nosync(wdt->irq);
return IRQ_HANDLED;
}
@@ -528,7 +535,7 @@ static int starfive_wdt_start(struct watchdog_device *wdd)
else
starfive_wdt_enable_reset(wdt);
- starfive_wdt_irq_flag_clr(wdt);
+ starfive_wdt_int_clr(wdt);
starfive_wdt_set_count(wdt, wdt->count);
starfive_wdt_int_enable(wdt);
starfive_wdt_enable(wdt);
@@ -554,7 +561,6 @@ static int starfive_wdt_restart(struct watchdog_device *wdd, unsigned long actio
else
starfive_wdt_enable_reset(wdt);
- starfive_wdt_irq_flag_clr(wdt);
/* put initial values into count and data */
starfive_wdt_set_count(wdt, wdt->count);
@@ -582,6 +588,10 @@ static int starfive_wdt_set_timeout(struct watchdog_device *wdd,
if (timeout < 1)
return -EINVAL;
+ /*
+ * This watchdog takes twice timeouts to reset.
+ * In order to reduce time to reset, should set half count value.
+ */
count = timeout * freq / 2;
if (count > STARFIVE_WATCHDOG_MAXCNT) {
@@ -596,7 +606,6 @@ static int starfive_wdt_set_timeout(struct watchdog_device *wdd,
starfive_wdt_unlock(wdt);
starfive_wdt_disable(wdt);
- starfive_wdt_irq_flag_clr(wdt);
starfive_wdt_set_relod_count(wdt, count);
starfive_wdt_enable(wdt);
starfive_wdt_lock(wdt);
@@ -650,7 +659,6 @@ static int starfive_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct starfive_wdt *wdt;
- struct resource *wdt_irq;
int started = 0;
int ret;
@@ -666,14 +674,11 @@ static int starfive_wdt_probe(struct platform_device *pdev)
wdt->drv_data = starfive_get_wdt_drv_data(pdev);
- wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (wdt_irq == NULL) {
- dev_err(dev, "no irq resource specified\n");
- ret = -ENOENT;
- goto err;
+ wdt->irq = platform_get_irq(pdev, 0);
+ if (wdt->irq < 0) {
+ dev_err(dev, "can not find irq.\n");
+ return wdt->irq;
}
- wdt->wdt_irq = wdt_irq;
- starfive_wdt_irq_flag_clr(wdt);
/* get the memory region for the watchdog timer */
wdt->base = devm_platform_ioremap_resource(pdev, 0);
@@ -712,7 +717,7 @@ static int starfive_wdt_probe(struct platform_device *pdev)
STARFIVE_WATCHDOG_DEFAULT_TIME);
}
- ret = devm_request_irq(dev, wdt_irq->start, starfive_wdt_interrupt_handler, 0,
+ ret = devm_request_irq(dev, wdt->irq, starfive_wdt_interrupt_handler, 0,
pdev->name, pdev);
if (ret != 0) {
dev_err(dev, "failed to install irq (%d)\n", ret);
@@ -816,7 +821,6 @@ static int starfive_wdt_resume(struct device *dev)
starfive_wdt_unlock(wdt);
- starfive_wdt_irq_flag_clr(wdt);
/* Restore watchdog state. */
starfive_wdt_set_relod_count(wdt, wdt->reload);