Skip to contentSkip to conversation

mpc52xx/wdt: merge WDT code into the GPT

PATCH In review Albrecht Dreß 2 participants 2 messages Nov 10, 2009

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 2/3 mpc52xx/wdt: merge WDT code into the GPT

Merge the WDT code into the GPT interface.

---

Notes:

The maximum timeout for a 5200 GPT @ 33 MHz clock is ~130 seconds. As this exceeds the range of an int, some api's had to be changed to u64.

The WDT api is exported as to keep the WDT driver separated from the GPT driver.

If GPT0 is used as WDT, this prevents the use of any GPT0 GPT function (i.e. they will fail with -EBUSY). IOW, the safety function always has precedence over the GPT function. If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT, this means that GPT0 is locked in WDT mode until the next reboot - this may be a requirement in safety applications.

 arch/powerpc/include/asm/mpc52xx.h        |   18 ++-
 arch/powerpc/platforms/52xx/mpc52xx_gpt.c |  281 ++++++++++++++++++++++++++---
 2 files changed, 270 insertions(+), 29 deletions(-)
arch/powerpc/include/asm/mpc52xx.h +15 -3
index 707ab75..0ece07f 100644
@@ -279,9 +279,21 @@ extern void mpc52xx_restart(char *cmd);
 /* mpc52xx_gpt.c */
 struct mpc52xx_gpt_priv;
 extern struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq);
-extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,
-                            int continuous);
-extern void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt);
+extern int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
+				   int continuous);
+extern int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt);
+extern u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt);
+#if (defined(CONFIG_MPC5200_WDT)) || (defined(CONFIG_MPC5200_WDT_MODULE))
+extern struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt_probe(void);
+extern int mpc52xx_gpt_wdt_start(struct mpc52xx_gpt_priv *gpt_wdt,
+				 int wdt_timeout);
+extern int mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv *gpt_wdt);
+extern int mpc52xx_gpt_wdt_release(struct mpc52xx_gpt_priv *gpt_wdt);
+#if !defined(CONFIG_WATCHDOG_NOWAYOUT)
+extern int mpc52xx_gpt_wdt_stop(struct mpc52xx_gpt_priv *gpt_wdt);
+#endif  /*  CONFIG_WATCHDOG_NOWAYOUT  */
+#endif  /*  CONFIG_MPC5200_WDT  */
+
 
 /* mpc52xx_lpbfifo.c */
 #define MPC52XX_LPBFIFO_FLAG_READ		(0)
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +255 -26

353 lines not on this page · raw message

#1 lore raw
Quoted 4 lines · Albrecht Dreß

Merge the WDT code into the GPT interface.

---

Hi Albrecht,

Thanks for this work. Comments below.

Quoted 6 lines

Notes:

The maximum timeout for a 5200 GPT @ 33 MHz clock is ~130 seconds.  As this exceeds the range of an int, some api's had to be changed to u64.

Quoted 8 lines

The WDT api is exported as to keep the WDT driver separated from the GPT driver.

If GPT0 is used as WDT, this prevents the use of any GPT0 GPT function (i.e. they will fail with -EBUSY).  IOW, the safety function always has precedence over the GPT function.  If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT, this means that GPT0 is locked in WDT mode until the next reboot - this may be a requirement in safety applications.

This description would look great in the header comment block of the .c file.

Also, as I commented in patch 3; I'd rather see all the WDT functionality rolled into this driver. As long as you keep the functional code blocks logically arranged, I think the driver will be easier to maintain and understand that way.

Quoted 33 lines

 arch/powerpc/include/asm/mpc52xx.h        |   18 ++- arch/powerpc/platforms/52xx/mpc52xx_gpt.c |  281 ++++++++++++++++++++++++++---

 2 files changed, 270 insertions(+), 29 deletions(-)
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +7 -1
index 2c3fa13..8274ebb 100644
@@ -60,9 +60,13 @@
  #include <asm/mpc52xx.h>
 
  MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
-MODULE_AUTHOR("Sascha Hauer, Grant Likely");
+MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß");
  MODULE_LICENSE("GPL");
 
+#if (defined(CONFIG_MPC5200_WDT)) || (defined(CONFIG_MPC5200_WDT_MODULE))
+#define HAVE_MPC5200_WDT
+#endif
+
  /**
  * struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver
  * @dev: pointer to device structure
@@ -70,6 +74,8 @@ MODULE_LICENSE("GPL");
  * @lock: spinlock to coordinate between different functions.
  * @of_gc: of_gpio_chip instance structure; used when GPIO is enabled
  * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported
+ * @wdt_mode: only used for gpt 0: 0 gpt-only timer, 1 can be used as a
+ *            wdt, 2 currently used as wdt, cannot be used as gpt
  */
  struct mpc52xx_gpt_priv {

       struct list_head list;          /* List of all GPT devices */

arch/powerpc/platforms/52xx/mpc52xx_gpt.c struct mpc52xx_gpt_priv { · line 89

Quoted 6 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +3 -0
@@ -78,6 +84,9 @@ struct mpc52xx_gpt_priv {
        spinlock_t lock;
        struct irq_host *irqhost;
        u32 ipb_freq;
+#if defined(HAVE_MPC5200_WDT)
+       u8 wdt_mode;
+#endif

I wouldn't bother with the #if/#endif. I'm willing to sacrifice 32bits for the sake of readability of the code.

Quoted 37 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +6 -0
+#define NS_PER_SEC                     1000000000LL
+
+#define MPC52xx_GPT_CAN_WDT            (1 << 0)
+#define MPC52xx_GPT_IS_WDT             (1 << 1)
+
+

 /* ---------------------------------------------------------------------
 * Cascaded interrupt controller hooks
 */

arch/powerpc/platforms/52xx/mpc52xx_gpt.c +4 -17
@@ -375,36 +393,22 @@ struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq)
  }
  EXPORT_SYMBOL(mpc52xx_gpt_from_irq);
 
-/**
- * mpc52xx_gpt_start_timer - Set and enable the GPT timer
- * @gpt: Pointer to gpt private data structure
- * @period: period of timer
- * @continuous: set to 1 to make timer continuous free running
- *
- * An interrupt will be generated every time the timer fires
- */
-int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,
-                            int continuous)
+/* Calculate the timer counter input register MBAR + 0x6n4 from the
+ * period in ns.  The maximum period for 33 MHz IPB clock is ~130s. */
+static int mpc52xx_gpt_calc_counter_input(u64 period, u64 ipb_freq,
+                                         u32 *reg_val)
  {
-       u32 clear, set;
        u64 clocks;
        u32 prescale;
-       unsigned long flags;
-
-       clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS;
-       set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE;
-       if (continuous)
-               set |= MPC52xx_GPT_MODE_CONTINUOUS;

arch/powerpc/platforms/52xx/mpc52xx_gpt.c struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq) · line 408

Quoted 10 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +3 -4
 
        /* Determine the number of clocks in the requested period.  64 bit
         * arithmatic is done here to preserve the precision until the value
-        * is scaled back down into the u32 range.  Period is in 'ns', bus
-        * frequency is in Hz. */
-       clocks = (u64)period * (u64)gpt->ipb_freq;
-       do_div(clocks, 1000000000); /* Scale it down to ns range */
+        * is scaled back down into the u32 range. */
+       clocks = period * ipb_freq;
+       do_div(clocks, NS_PER_SEC);         /* Scale it down to ns range */

Nit: I wouldn't bother with the NS_PER_SEC macro personally. ns per s is such a fundamental property that I'd rather see the actual number.

arch/powerpc/platforms/52xx/mpc52xx_gpt.c struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq) · line 412

Quoted 6 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +2 -2
-       /* This device cannot handle a clock count greater than 32 bits */
-       if (clocks > 0xffffffff)
+       /* the maximum count is 0x10000 pre-scaler * 0xffff count */
+       if (clocks > 0xffff0000)
                return -EINVAL;

This a bug fix? Separate patch please.

Quoted 23 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +18 -0
        /* Calculate the prescaler and count values from the clocks value.
@@ -427,9 +431,47 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,
                return -EINVAL;
        }
 
+       *reg_val = (prescale & 0xffff) << 16 | clocks;
+       return 0;
+}
+
+/**
+ * mpc52xx_gpt_start_timer - Set and enable the GPT timer
+ * @gpt: Pointer to gpt private data structure
+ * @period: period of timer in ns
+ * @continuous: set to 1 to make timer continuous free running
+ *
+ * An interrupt will be generated every time the timer fires
+ */
+int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
+                           int continuous)
+{
+       u32 clear, set;
+       u32 counter_reg;
+       unsigned long flags;

arch/powerpc/platforms/52xx/mpc52xx_gpt.c int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period, · line 461

Quoted 10 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +10 -0
+
+#if defined(HAVE_MPC5200_WDT)
+       /* reject the operation if the timer is used as watchdog (gpt 0 only) */
+       spin_lock_irqsave(&gpt->lock, flags);
+       if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
+               spin_unlock_irqrestore(&gpt->lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&gpt->lock, flags);
+#endif

I think the behaviour needs to be consistent, regardless of HAVE_MPC5200_WDT state. If the IS_WDT flag is set, then this needs to bail, even if WDT support is not enabled.

Also, move this code block down into the spin lock/unlock block lower in the function. it doesn't make much sense to grab the spin lock twice in the same function, and the worst think that can happen if this test fails is that a few extra cycles are burned calculating what the counter_reg value should be.

Quoted 32 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +21 -2
+
+       clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS;
+       set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE;
+       if (continuous)
+               set |= MPC52xx_GPT_MODE_CONTINUOUS;
+
+       if (mpc52xx_gpt_calc_counter_input(period, (u64)gpt->ipb_freq,
+                                          &counter_reg) != 0)
+               return -EINVAL;
+
        /* Set and enable the timer */
        spin_lock_irqsave(&gpt->lock, flags);
-       out_be32(&gpt->regs->count, prescale << 16 | clocks);
+       out_be32(&gpt->regs->count, counter_reg);
        clrsetbits_be32(&gpt->regs->mode, clear, set);
        spin_unlock_irqrestore(&gpt->lock, flags);
 
@@ -437,12 +479,175 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period,
  }
  EXPORT_SYMBOL(mpc52xx_gpt_start_timer);
 
-void mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
+/**
+ * mpc52xx_gpt_timer_period - Return the timer period in nanoseconds
+ * Note: reads the timer config directly from the hardware
+ */
+u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt)
+{
+       u64 period;
+       u32 prescale;
+       unsigned long flags;
+

arch/powerpc/platforms/52xx/mpc52xx_gpt.c int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period, · line 501

Quoted 10 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +10 -0
+       spin_lock_irqsave(&gpt->lock, flags);
+       period = in_be32(&gpt->regs->count);
+       spin_unlock_irqrestore(&gpt->lock, flags);
+
+       prescale = period >> 16;
+       period &= 0xffff;
+       if (prescale == 0)
+               prescale = 0x10000;
+       period = period * (u64) prescale * NS_PER_SEC;
+       do_div(period, (u64)gpt->ipb_freq);

Are the casts necessary? Maybe prescale should just be a u64 value?

Quoted 6 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +5 -0
+       return period;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
+
+int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
  {

arch/powerpc/platforms/52xx/mpc52xx_gpt.c int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, int period, · line 517

Quoted 10 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +10 -0
+       unsigned long flags;
+
+       /* reject the operation if the timer is used as watchdog (gpt 0 only) */
+       spin_lock_irqsave(&gpt->lock, flags);
+#if defined(HAVE_MPC5200_WDT)
+       if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
+               spin_unlock_irqrestore(&gpt->lock, flags);
+               return -EBUSY;
+       }
+#endif

Again, drop the #if/endif wrapper.

Quoted 66 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +59 -0
+/**
+ * mpc52xx_gpt_wdt_release - Release the watchdog so it can be used as gpt
+ * @gpt_wdt: the watchdog gpt
+ *
+ * Note: Stops the watchdog if CONFIG_WATCHDOG_NOWAYOUT is not defined.
+ * the function does not protect itself form being called without a
+ * timer or with a timer which cannot function as wdt.
+ */
+int mpc52xx_gpt_wdt_release(struct mpc52xx_gpt_priv *gpt_wdt)
+{
+#if !defined(CONFIG_WATCHDOG_NOWAYOUT)
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpt_wdt->lock, flags);
+       clrbits32(&gpt_wdt->regs->mode,
+                 MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN);
+       gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT;
+       spin_unlock_irqrestore(&gpt_wdt->lock, flags);
+#endif
+       return 0;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_release);
+
+#if !defined(CONFIG_WATCHDOG_NOWAYOUT)
+/**
+ * mpc52xx_gpt_wdt_stop - Stop the watchdog
+ * @gpt_wdt: the watchdog gpt
+ *
+ * Note: the function does not protect itself form being called without a
+ * timer or with a timer which cannot function as wdt.
+ */
+int mpc52xx_gpt_wdt_stop(struct mpc52xx_gpt_priv *gpt_wdt)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpt_wdt->lock, flags);
+       clrbits32(&gpt_wdt->regs->mode,
+                 MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN);
+       gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT;
+       spin_unlock_irqrestore(&gpt_wdt->lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_stop);
+#endif  /*  CONFIG_WATCHDOG_NOWAYOUT  */
+#endif  /*  HAVE_MPC5200_WDT  */
+
  /* ---------------------------------------------------------------------
  * of_platform bus binding code
  */
@@ -473,6 +678,30 @@ static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev,
        list_add(&gpt->list, &mpc52xx_gpt_list);
        mutex_unlock(&mpc52xx_gpt_list_mutex);
 
+#if defined(HAVE_MPC5200_WDT)
+       /* check if this device could be a watchdog */
+       if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) ||
+           of_get_property(ofdev->node, "has-wdt", NULL)) {
+               const u32 *on_boot_wdt;
+
+               gpt->wdt_mode = MPC52xx_GPT_CAN_WDT;
+
+               /* check if the device shall be used as on-boot watchdog */
+               on_boot_wdt = of_get_property(ofdev->node, "wdt,on-boot", NULL);
+               if (on_boot_wdt) {
+                       gpt->wdt_mode |= MPC52xx_GPT_IS_WDT;
+                       if (*on_boot_wdt > 0 &&

arch/powerpc/platforms/52xx/mpc52xx_gpt.c static int __devinit mpc52xx_gpt_probe(struct of_device *ofdev, · line 703

Quoted 10 lines
arch/powerpc/platforms/52xx/mpc52xx_gpt.c +10 -0
+                           mpc52xx_gpt_wdt_start(gpt, *on_boot_wdt) == 0)
+                               dev_info(gpt->dev,
+                                        "running as wdt, timeout %us\n",
+                                        *on_boot_wdt);
+                       else
+                               dev_info(gpt->dev, "reserved as wdt\n");
+               } else
+                       dev_info(gpt->dev, "can function as wdt\n");
+       }
+#endif

Ditto on the #if/endif

Cheers,
g.

Signature · 2 lines
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.