On Thu, 2019-08-29 at 05:46 -0700, Guenter Roeck wrote:
> On 8/29/19 1:50 AM, Chris Packham wrote:
> > The orion watchdog can either reset the CPU or generate an interrupt.
> > The interrupt would be useful for debugging as it provides panic()
> > output about the watchdog expiry, however if the interrupt is used the
> > watchdog can't reset the CPU in the event of being stuck in a loop with
> > interrupts disabled or if the CPU is prevented from accessing memory
> > (e.g. an unterminated DMA).
> > 
> > The Armada SoCs have spare timers that aren't currently used by the
> > Linux kernel. We can use timer1 to provide a pre-timeout ahead of the
> > watchdog timer and provide the possibility of gathering debug before the
> > reset triggers.
> > 
> > Signed-off-by: Chris Packham <[email protected]>

<snip>

> > @@ -277,7 +291,7 @@ static int orion_stop(struct watchdog_device *wdt_dev)
> >   static int armada375_stop(struct watchdog_device *wdt_dev)
> >   {
> >     struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
> > -   u32 reg;
> > +   u32 reg, mask;
> >   
> >     /* Disable reset on watchdog */
> >     atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
> > @@ -287,7 +301,10 @@ static int armada375_stop(struct watchdog_device 
> > *wdt_dev)
> >     writel(reg, dev->rstout);
> >   
> >     /* Disable watchdog timer */
> > -   atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
> > +   mask = dev->data->wdt_enable_bit;
> > +   if (wdt_dev->info->options & WDIOF_PRETIMEOUT)
> > +           mask &= ~TIMER1_ENABLE_BIT;
> 
> Sorry, I am lost. Why &= and ~ ?

Blame late night coding.

I saw the lines above with 'reg &= ~dev->data->rstout_enable_bit' and
without the requisite level of caffine in my system my brain said "you
need to clear that bit". Which is exactly what the atomic_io_modify()
below would do if I actually gave it the right mask.

> Guenter
> 
> > +   atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0);
> >   
> >     return 0;
> >   }
> > @@ -349,7 +366,7 @@ static unsigned int orion_wdt_get_timeleft(struct 
> > watchdog_device *wdt_dev)
> >     return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
> >   }
> >   
> > -static const struct watchdog_info orion_wdt_info = {
> > +static struct watchdog_info orion_wdt_info = {
> >     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> >     .identity = "Orion Watchdog",
> >   };
> > @@ -368,6 +385,16 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid)
> >     return IRQ_HANDLED;
> >   }
> >   
> > +static irqreturn_t orion_wdt_pre_irq(int irq, void *devid)
> > +{
> > +   struct orion_watchdog *dev = devid;
> > +
> > +   atomic_io_modify(dev->reg + TIMER_A370_STATUS,
> > +                    TIMER1_STATUS_BIT, 0);
> > +   watchdog_notify_pretimeout(&dev->wdt);
> > +   return IRQ_HANDLED;
> > +}
> > +
> >   /*
> >    * The original devicetree binding for this driver specified only
> >    * one memory resource, so in order to keep DT backwards compatibility
> > @@ -589,6 +616,18 @@ static int orion_wdt_probe(struct platform_device 
> > *pdev)
> >             }
> >     }
> >   

I'll add a comment above the first platform_get_irq() about
intentionally not handling EPROBE_DEFER. And a comment here about the
2nd interrupt being optional.

> > +   irq = platform_get_irq(pdev, 1);
> > +   if (irq > 0) {
> > +           orion_wdt_info.options |= WDIOF_PRETIMEOUT;
> > +           ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq,
> > +                                  0, pdev->name, dev);
> > +           if (ret < 0) {
> > +                   dev_err(&pdev->dev, "failed to request IRQ\n");
> > +                   goto disable_clk;
> > +           }
> > +   }
> > +
> > +
> >     watchdog_set_nowayout(&dev->wdt, nowayout);
> >     ret = watchdog_register_device(&dev->wdt);
> >     if (ret)
> > 
> 
> 

Reply via email to