All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
@ 2009-11-11  8:27 ` Albrecht Dreß
  0 siblings, 0 replies; 8+ messages in thread
From: Albrecht Dreß @ 2009-11-11  8:27 UTC (permalink / raw)
  To: grant.likely-s3s/WqlpOiPyB63q8FvJNQ
  Cc: linuxppc-dev-mnsaURCQ41sdnm+yROfE0A,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	wim-IQzOog9fTRqzQB+pC5nmwQ

Hi Grant:

Thanks a lot for your comments!

> On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dreß <albrecht.dress@arcor.de>
> wrote:
> > Merge the WDT code into the GPT interface.
> >
> > Signed-off-by: Albrecht Dreß <albrecht.dress-KvP5wT2u2U0@public.gmane.org>
> > ---
> 
> Hi Albrecht,
> 
> Thanks for this work.  Comments below.
> 
> >
> > 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.
> 
> This description would look great in the header comment block of the
> .c file.

O.k.

> 
> 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.
> 
> >  arch/powerpc/include/asm/mpc52xx.h        |   18 ++-
> >  arch/powerpc/platforms/52xx/mpc52xx_gpt.c |  281
> ++++++++++++++++++++++++++---
> >  2 files changed, 270 insertions(+), 29 deletions(-)
> >
> > diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > index 2c3fa13..8274ebb 100644
> > --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > @@ -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 */
> > @@ -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.

O.k., will remove it.

> 
> > +#define NS_PER_SEC                     1000000000LL
> > +
> > +#define MPC52xx_GPT_CAN_WDT            (1 << 0)
> > +#define MPC52xx_GPT_IS_WDT             (1 << 1)
> > +
> > +
> >  /* ---------------------------------------------------------------------
> >  * Cascaded interrupt controller hooks
> >  */
> > @@ -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;
> >
> >        /* 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.

O.k.

> 
> >
> > -       /* 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.

Ooops, nope.  That's wrong.  Will remove it.  The only *real* bug fix is the fact that the period must be a 64-bit as the timer can serve longer periods than 2.1 seconds (int limit).  I will provide a separate fix for that.

> 
> >        /* 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;
> > +
> > +#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.

O.k.,  will do.  It's a no-op if WDT support isn't enabled (i.e. IS_WDT is never set then), though, see below.

> 
> 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.

Ouch.  Yes, that's a source for races!

> 
> > +
> > +       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;
> > +
> > +       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?

Good point.  Will do that.

> 
> > +       return period;
> > +}
> > +EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
> > +
> > +int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
> >  {
> > +       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.
> 
> > +/**
> > + * 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 &&
> > +                           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

I don't think it can be removed here.  Think about the following:
- user has a dtb with fsl,has-wdt and fsl,wdt-on-boot properties and
- disables 5200 WDT in the kernel config.

Now the system refuses to use GPT0 as GPT, although the WDT functionality has been disabled.  Of course, the dts doesn't fit the kernel config now, but I think we should catch this case.

Actually I tried to implement your requirements from <http://lists.ozlabs.org/pipermail/linuxppc-dev/2009-August/074595.html>:
- fsl,has-wdt indicates that the GPT can serve as WDT;
- any access through the wdt api to a wdt-capable gpt actually reserves it as wdt;
- the new of property forces reserving the wdt before any gpt api function has chance to grab it as gpt.

Or did I get something wrong here?

Thanks,
Albrecht.

Jetzt NEU: Do it youself E-Cards bei Arcor.de!
Stellen Sie Ihr eigenes Unikat zusammen und machen Sie dem Empfänger eine ganz persönliche Freude!
E-Card Marke Eigenbau: HIER KLICKEN: http://www.arcor.de/rd/footer.ecard

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
@ 2009-11-11  8:27 ` Albrecht Dreß
  0 siblings, 0 replies; 8+ messages in thread
From: Albrecht Dreß @ 2009-11-11  8:27 UTC (permalink / raw)
  To: grant.likely; +Cc: linuxppc-dev, devicetree-discuss, wim

Hi Grant:

Thanks a lot for your comments!

> On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dre=DF <albrecht.dress@arcor.d=
e>
> wrote:
> > Merge the WDT code into the GPT interface.
> >
> > Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
> > ---
>=20
> Hi Albrecht,
>=20
> Thanks for this work.  Comments below.
>=20
> >
> > Notes:
> >
> > The maximum timeout for a 5200 GPT @ 33 MHz clock is ~130 seconds. =A0A=
s
> 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 GP=
T
> > driver.
> >
> > If GPT0 is used as WDT, this prevents the use of any GPT0 GPT function
> (i.e.
> > they will fail with -EBUSY). =A0IOW, the safety function always has
> precedence
> > over the GPT function. =A0If 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.
>=20
> This description would look great in the header comment block of the
> .c file.

O.k.

>=20
> 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.
>=20
> > =A0arch/powerpc/include/asm/mpc52xx.h =A0 =A0 =A0 =A0| =A0 18 ++-
> > =A0arch/powerpc/platforms/52xx/mpc52xx_gpt.c | =A0281
> ++++++++++++++++++++++++++---
> > =A02 files changed, 270 insertions(+), 29 deletions(-)
> >
> > diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > index 2c3fa13..8274ebb 100644
> > --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> > @@ -60,9 +60,13 @@
> > =A0#include <asm/mpc52xx.h>
> >
> > =A0MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
> > -MODULE_AUTHOR("Sascha Hauer, Grant Likely");
> > +MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dre=DF");
> > =A0MODULE_LICENSE("GPL");
> >
> > +#if (defined(CONFIG_MPC5200_WDT)) ||
> (defined(CONFIG_MPC5200_WDT_MODULE))
> > +#define HAVE_MPC5200_WDT
> > +#endif
> > +
> > =A0/**
> > =A0* struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver
> > =A0* @dev: pointer to device structure
> > @@ -70,6 +74,8 @@ MODULE_LICENSE("GPL");
> > =A0* @lock: spinlock to coordinate between different functions.
> > =A0* @of_gc: of_gpio_chip instance structure; used when GPIO is enabled
> > =A0* @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
> > + * =A0 =A0 =A0 =A0 =A0 =A0wdt, 2 currently used as wdt, cannot be used=
 as gpt
> > =A0*/
> > =A0struct mpc52xx_gpt_priv {
> > =A0 =A0 =A0 =A0struct list_head list; =A0 =A0 =A0 =A0 =A0/* List of all=
 GPT devices */
> > @@ -78,6 +84,9 @@ struct mpc52xx_gpt_priv {
> > =A0 =A0 =A0 =A0spinlock_t lock;
> > =A0 =A0 =A0 =A0struct irq_host *irqhost;
> > =A0 =A0 =A0 =A0u32 ipb_freq;
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 u8 wdt_mode;
> > +#endif
>=20
> I wouldn't bother with the #if/#endif.  I'm willing to sacrifice
> 32bits for the sake of readability of the code.

O.k., will remove it.

>=20
> > +#define NS_PER_SEC =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 1000000000L=
L
> > +
> > +#define MPC52xx_GPT_CAN_WDT =A0 =A0 =A0 =A0 =A0 =A0(1 << 0)
> > +#define MPC52xx_GPT_IS_WDT =A0 =A0 =A0 =A0 =A0 =A0 (1 << 1)
> > +
> > +
> > =A0/* -----------------------------------------------------------------=
----
> > =A0* Cascaded interrupt controller hooks
> > =A0*/
> > @@ -375,36 +393,22 @@ struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int
> irq)
> > =A0}
> > =A0EXPORT_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,
> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int continuous=
)
> > +/* Calculate the timer counter input register MBAR + 0x6n4 from the
> > + * period in ns. =A0The maximum period for 33 MHz IPB clock is ~130s. =
*/
> > +static int mpc52xx_gpt_calc_counter_input(u64 period, u64 ipb_freq,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 u32 *reg_val)
> > =A0{
> > - =A0 =A0 =A0 u32 clear, set;
> > =A0 =A0 =A0 =A0u64 clocks;
> > =A0 =A0 =A0 =A0u32 prescale;
> > - =A0 =A0 =A0 unsigned long flags;
> > -
> > - =A0 =A0 =A0 clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CON=
TINUOUS;
> > - =A0 =A0 =A0 set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNT=
ER_ENABLE;
> > - =A0 =A0 =A0 if (continuous)
> > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
> >
> > =A0 =A0 =A0 =A0/* Determine the number of clocks in the requested perio=
d. =A064 bit
> > =A0 =A0 =A0 =A0 * arithmatic is done here to preserve the precision unt=
il the
> value
> > - =A0 =A0 =A0 =A0* is scaled back down into the u32 range. =A0Period is=
 in 'ns',
> bus
> > - =A0 =A0 =A0 =A0* frequency is in Hz. */
> > - =A0 =A0 =A0 clocks =3D (u64)period * (u64)gpt->ipb_freq;
> > - =A0 =A0 =A0 do_div(clocks, 1000000000); /* Scale it down to ns range =
*/
> > + =A0 =A0 =A0 =A0* is scaled back down into the u32 range. */
> > + =A0 =A0 =A0 clocks =3D period * ipb_freq;
> > + =A0 =A0 =A0 do_div(clocks, NS_PER_SEC); =A0 =A0 =A0 =A0 /* Scale it d=
own to ns range
> */
>=20
> 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.

O.k.

>=20
> >
> > - =A0 =A0 =A0 /* This device cannot handle a clock count greater than 3=
2 bits
> */
> > - =A0 =A0 =A0 if (clocks > 0xffffffff)
> > + =A0 =A0 =A0 /* the maximum count is 0x10000 pre-scaler * 0xffff count=
 */
> > + =A0 =A0 =A0 if (clocks > 0xffff0000)
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
>=20
> This a bug fix?  Separate patch please.

Ooops, nope.  That's wrong.  Will remove it.  The only *real* bug fix is th=
e fact that the period must be a 64-bit as the timer can serve longer perio=
ds than 2.1 seconds (int limit).  I will provide a separate fix for that.

>=20
> > =A0 =A0 =A0 =A0/* Calculate the prescaler and count values from the clo=
cks value.
> > @@ -427,9 +431,47 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_pri=
v
> *gpt, int period,
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
> > =A0 =A0 =A0 =A0}
> >
> > + =A0 =A0 =A0 *reg_val =3D (prescale & 0xffff) << 16 | clocks;
> > + =A0 =A0 =A0 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,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int continuous)
> > +{
> > + =A0 =A0 =A0 u32 clear, set;
> > + =A0 =A0 =A0 u32 counter_reg;
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 /* reject the operation if the timer is used as watchdog =
(gpt 0
> only) */
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> > + =A0 =A0 =A0 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags)=
;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> > + =A0 =A0 =A0 }
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> > +#endif
>=20
> 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.

O.k.,  will do.  It's a no-op if WDT support isn't enabled (i.e. IS_WDT is =
never set then), though, see below.

>=20
> 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.

Ouch.  Yes, that's a source for races!

>=20
> > +
> > + =A0 =A0 =A0 clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CON=
TINUOUS;
> > + =A0 =A0 =A0 set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNT=
ER_ENABLE;
> > + =A0 =A0 =A0 if (continuous)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
> > +
> > + =A0 =A0 =A0 if (mpc52xx_gpt_calc_counter_input(period, (u64)gpt->ipb_=
freq,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0&counter_reg) !=3D 0)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> > +
> > =A0 =A0 =A0 =A0/* Set and enable the timer */
> > =A0 =A0 =A0 =A0spin_lock_irqsave(&gpt->lock, flags);
> > - =A0 =A0 =A0 out_be32(&gpt->regs->count, prescale << 16 | clocks);
> > + =A0 =A0 =A0 out_be32(&gpt->regs->count, counter_reg);
> > =A0 =A0 =A0 =A0clrsetbits_be32(&gpt->regs->mode, clear, set);
> > =A0 =A0 =A0 =A0spin_unlock_irqrestore(&gpt->lock, flags);
> >
> > @@ -437,12 +479,175 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_p=
riv
> *gpt, int period,
> > =A0}
> > =A0EXPORT_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)
> > +{
> > + =A0 =A0 =A0 u64 period;
> > + =A0 =A0 =A0 u32 prescale;
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> > + =A0 =A0 =A0 period =3D in_be32(&gpt->regs->count);
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> > +
> > + =A0 =A0 =A0 prescale =3D period >> 16;
> > + =A0 =A0 =A0 period &=3D 0xffff;
> > + =A0 =A0 =A0 if (prescale =3D=3D 0)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prescale =3D 0x10000;
> > + =A0 =A0 =A0 period =3D period * (u64) prescale * NS_PER_SEC;
> > + =A0 =A0 =A0 do_div(period, (u64)gpt->ipb_freq);
>=20
> Are the casts necessary?  Maybe prescale should just be a u64 value?

Good point.  Will do that.

>=20
> > + =A0 =A0 =A0 return period;
> > +}
> > +EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
> > +
> > +int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
> > =A0{
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 /* reject the operation if the timer is used as watchdog =
(gpt 0
> only) */
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags)=
;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> > + =A0 =A0 =A0 }
> > +#endif
>=20
> Again, drop the #if/endif wrapper.
>=20
> > +/**
> > + * 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)
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt_wdt->lock, flags);
> > + =A0 =A0 =A0 clrbits32(&gpt_wdt->regs->mode,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC52xx_GPT_MODE_COUNTER_ENABLE |
> MPC52xx_GPT_MODE_WDT_EN);
> > + =A0 =A0 =A0 gpt_wdt->wdt_mode &=3D ~MPC52xx_GPT_IS_WDT;
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
> > +#endif
> > + =A0 =A0 =A0 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 withou=
t
> a
> > + * timer or with a timer which cannot function as wdt.
> > + */
> > +int mpc52xx_gpt_wdt_stop(struct mpc52xx_gpt_priv *gpt_wdt)
> > +{
> > + =A0 =A0 =A0 unsigned long flags;
> > +
> > + =A0 =A0 =A0 spin_lock_irqsave(&gpt_wdt->lock, flags);
> > + =A0 =A0 =A0 clrbits32(&gpt_wdt->regs->mode,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC52xx_GPT_MODE_COUNTER_ENABLE |
> MPC52xx_GPT_MODE_WDT_EN);
> > + =A0 =A0 =A0 gpt_wdt->wdt_mode &=3D ~MPC52xx_GPT_IS_WDT;
> > + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
> > + =A0 =A0 =A0 return 0;
> > +}
> > +EXPORT_SYMBOL(mpc52xx_gpt_wdt_stop);
> > +#endif =A0/* =A0CONFIG_WATCHDOG_NOWAYOUT =A0*/
> > +#endif =A0/* =A0HAVE_MPC5200_WDT =A0*/
> > +
> > =A0/* -----------------------------------------------------------------=
----
> > =A0* of_platform bus binding code
> > =A0*/
> > @@ -473,6 +678,30 @@ static int __devinit mpc52xx_gpt_probe(struct
> of_device *ofdev,
> > =A0 =A0 =A0 =A0list_add(&gpt->list, &mpc52xx_gpt_list);
> > =A0 =A0 =A0 =A0mutex_unlock(&mpc52xx_gpt_list_mutex);
> >
> > +#if defined(HAVE_MPC5200_WDT)
> > + =A0 =A0 =A0 /* check if this device could be a watchdog */
> > + =A0 =A0 =A0 if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) ||
> > + =A0 =A0 =A0 =A0 =A0 of_get_property(ofdev->node, "has-wdt", NULL)) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 const u32 *on_boot_wdt;
> > +
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode =3D MPC52xx_GPT_CAN_WDT;
> > +
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* check if the device shall be used as o=
n-boot watchdog
> */
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 on_boot_wdt =3D of_get_property(ofdev->no=
de, "wdt,on-boot",
> NULL);
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (on_boot_wdt) {
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode |=3D MPC52x=
x_GPT_IS_WDT;
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (*on_boot_wdt > 0 &&
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc52xx_gpt_wdt_s=
tart(gpt, *on_boot_wdt) =3D=3D
> 0)
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(=
gpt->dev,
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0"running as wdt, timeout %us\n",
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0*on_boot_wdt);
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(=
gpt->dev, "reserved as wdt\n");
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else
> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(gpt->dev, "can f=
unction as wdt\n");
> > + =A0 =A0 =A0 }
> > +#endif
>=20
> Ditto on the #if/endif

I don't think it can be removed here.  Think about the following:
- user has a dtb with fsl,has-wdt and fsl,wdt-on-boot properties and
- disables 5200 WDT in the kernel config.

Now the system refuses to use GPT0 as GPT, although the WDT functionality h=
as been disabled.  Of course, the dts doesn't fit the kernel config now, bu=
t I think we should catch this case.

Actually I tried to implement your requirements from <http://lists.ozlabs.o=
rg/pipermail/linuxppc-dev/2009-August/074595.html>:
- fsl,has-wdt indicates that the GPT can serve as WDT;
- any access through the wdt api to a wdt-capable gpt actually reserves it =
as wdt;
- the new of property forces reserving the wdt before any gpt api function =
has chance to grab it as gpt.

Or did I get something wrong here?

Thanks,
Albrecht.

Jetzt NEU: Do it youself E-Cards bei Arcor.de!
Stellen Sie Ihr eigenes Unikat zusammen und machen Sie dem Empf=E4nger eine=
 ganz pers=F6nliche Freude!
E-Card Marke Eigenbau: HIER KLICKEN: http://www.arcor.de/rd/footer.ecard

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
  2009-11-11  8:27 ` Albrecht Dreß
@ 2009-11-11 20:11     ` Grant Likely
  -1 siblings, 0 replies; 8+ messages in thread
From: Grant Likely @ 2009-11-11 20:11 UTC (permalink / raw)
  To: Albrecht Dreß
  Cc: linuxppc-dev-mnsaURCQ41sdnm+yROfE0A,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	wim-IQzOog9fTRqzQB+pC5nmwQ

On Wed, Nov 11, 2009 at 1:27 AM, Albrecht Dreß <albrecht.dress-KvP5wT2u2U0@public.gmane.org> wrote:
> Hi Grant:
>
> Thanks a lot for your comments!
>
>> On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dreß <albrecht.dress@arcor.de>
>> > +#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 &&
>> > +                           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
>
> I don't think it can be removed here.  Think about the following:
> - user has a dtb with fsl,has-wdt and fsl,wdt-on-boot properties and
> - disables 5200 WDT in the kernel config.
>
> Now the system refuses to use GPT0 as GPT, although the WDT functionality has been disabled.  Of course, the dts doesn't fit the kernel config now, but I think we should catch this case.

I think the behaviour should be consistent regardless of what support
is compiled into the kernel.  If the wdt-on-boot property is set, the
it does make sense for the kernel to never use it as a GPT.  Otherwise
the kernel behaviour becomes subtly different in ways non-obvious to
the user.

> Actually I tried to implement your requirements from <http://lists.ozlabs.org/pipermail/linuxppc-dev/2009-August/074595.html>:
> - fsl,has-wdt indicates that the GPT can serve as WDT;
> - any access through the wdt api to a wdt-capable gpt actually reserves it as wdt;
> - the new of property forces reserving the wdt before any gpt api function has chance to grab it as gpt.
>
> Or did I get something wrong here?

No, you got it right...  Well, I can't even remember what I said
yesterday, but this list of requirements looks sane.  :-)

Cheers,
g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
@ 2009-11-11 20:11     ` Grant Likely
  0 siblings, 0 replies; 8+ messages in thread
From: Grant Likely @ 2009-11-11 20:11 UTC (permalink / raw)
  To: Albrecht Dreß; +Cc: linuxppc-dev, devicetree-discuss, wim

On Wed, Nov 11, 2009 at 1:27 AM, Albrecht Dre=DF <albrecht.dress@arcor.de> =
wrote:
> Hi Grant:
>
> Thanks a lot for your comments!
>
>> On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dre=DF <albrecht.dress@arcor.=
de>
>> > +#if defined(HAVE_MPC5200_WDT)
>> > + =A0 =A0 =A0 /* check if this device could be a watchdog */
>> > + =A0 =A0 =A0 if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) ||
>> > + =A0 =A0 =A0 =A0 =A0 of_get_property(ofdev->node, "has-wdt", NULL)) {
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 const u32 *on_boot_wdt;
>> > +
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode =3D MPC52xx_GPT_CAN_WDT;
>> > +
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* check if the device shall be used as =
on-boot watchdog
>> */
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 on_boot_wdt =3D of_get_property(ofdev->n=
ode, "wdt,on-boot",
>> NULL);
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (on_boot_wdt) {
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode |=3D MPC52=
xx_GPT_IS_WDT;
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (*on_boot_wdt > 0 &&
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc52xx_gpt_wdt_=
start(gpt, *on_boot_wdt) =3D=3D
>> 0)
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info=
(gpt->dev,
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0"running as wdt, timeout %us\n",
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0*on_boot_wdt);
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info=
(gpt->dev, "reserved as wdt\n");
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else
>> > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(gpt->dev, "can =
function as wdt\n");
>> > + =A0 =A0 =A0 }
>> > +#endif
>>
>> Ditto on the #if/endif
>
> I don't think it can be removed here. =A0Think about the following:
> - user has a dtb with fsl,has-wdt and fsl,wdt-on-boot properties and
> - disables 5200 WDT in the kernel config.
>
> Now the system refuses to use GPT0 as GPT, although the WDT functionality=
 has been disabled. =A0Of course, the dts doesn't fit the kernel config now=
, but I think we should catch this case.

I think the behaviour should be consistent regardless of what support
is compiled into the kernel.  If the wdt-on-boot property is set, the
it does make sense for the kernel to never use it as a GPT.  Otherwise
the kernel behaviour becomes subtly different in ways non-obvious to
the user.

> Actually I tried to implement your requirements from <http://lists.ozlabs=
.org/pipermail/linuxppc-dev/2009-August/074595.html>:
> - fsl,has-wdt indicates that the GPT can serve as WDT;
> - any access through the wdt api to a wdt-capable gpt actually reserves i=
t as wdt;
> - the new of property forces reserving the wdt before any gpt api functio=
n has chance to grab it as gpt.
>
> Or did I get something wrong here?

No, you got it right...  Well, I can't even remember what I said
yesterday, but this list of requirements looks sane.  :-)

Cheers,
g.

--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
  2009-11-10 19:41 ` Albrecht Dreß
@ 2009-11-10 20:59   ` Grant Likely
  -1 siblings, 0 replies; 8+ messages in thread
From: Grant Likely @ 2009-11-10 20:59 UTC (permalink / raw)
  To: Albrecht Dreß
  Cc: Linux PPC Development, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	Wim Van Sebroeck

On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dreß <albrecht.dress-KvP5wT2u2U0@public.gmane.org> wrote:
> Merge the WDT code into the GPT interface.
>
> Signed-off-by: Albrecht Dreß <albrecht.dress-KvP5wT2u2U0@public.gmane.org>
> ---

Hi Albrecht,

Thanks for this work.  Comments below.

>
> 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.

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.

>  arch/powerpc/include/asm/mpc52xx.h        |   18 ++-
>  arch/powerpc/platforms/52xx/mpc52xx_gpt.c |  281 ++++++++++++++++++++++++++---
>  2 files changed, 270 insertions(+), 29 deletions(-)
>
> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> index 2c3fa13..8274ebb 100644
> --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> @@ -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 */
> @@ -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.

> +#define NS_PER_SEC                     1000000000LL
> +
> +#define MPC52xx_GPT_CAN_WDT            (1 << 0)
> +#define MPC52xx_GPT_IS_WDT             (1 << 1)
> +
> +
>  /* ---------------------------------------------------------------------
>  * Cascaded interrupt controller hooks
>  */
> @@ -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;
>
>        /* 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.

>
> -       /* 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.

>        /* 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;
> +
> +#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.

> +
> +       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;
> +
> +       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?

> +       return period;
> +}
> +EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
> +
> +int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
>  {
> +       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.

> +/**
> + * 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 &&
> +                           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.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
@ 2009-11-10 20:59   ` Grant Likely
  0 siblings, 0 replies; 8+ messages in thread
From: Grant Likely @ 2009-11-10 20:59 UTC (permalink / raw)
  To: Albrecht Dreß
  Cc: Linux PPC Development, devicetree-discuss, Wim Van Sebroeck

On Tue, Nov 10, 2009 at 12:41 PM, Albrecht Dre=DF <albrecht.dress@arcor.de>=
 wrote:
> Merge the WDT code into the GPT interface.
>
> Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
> ---

Hi Albrecht,

Thanks for this work.  Comments below.

>
> Notes:
>
> The maximum timeout for a 5200 GPT @ 33 MHz clock is ~130 seconds. =A0As =
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). =A0IOW, the safety function always has prece=
dence
> over the GPT function. =A0If the kernel has been compiled with
> CONFIG_WATCHDOG_NOWAYOUT, this means that GPT0 is locked in WDT mode unti=
l
> 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.

> =A0arch/powerpc/include/asm/mpc52xx.h =A0 =A0 =A0 =A0| =A0 18 ++-
> =A0arch/powerpc/platforms/52xx/mpc52xx_gpt.c | =A0281 +++++++++++++++++++=
+++++++---
> =A02 files changed, 270 insertions(+), 29 deletions(-)
>
> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/pla=
tforms/52xx/mpc52xx_gpt.c
> index 2c3fa13..8274ebb 100644
> --- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
> @@ -60,9 +60,13 @@
> =A0#include <asm/mpc52xx.h>
>
> =A0MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
> -MODULE_AUTHOR("Sascha Hauer, Grant Likely");
> +MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dre=DF");
> =A0MODULE_LICENSE("GPL");
>
> +#if (defined(CONFIG_MPC5200_WDT)) || (defined(CONFIG_MPC5200_WDT_MODULE)=
)
> +#define HAVE_MPC5200_WDT
> +#endif
> +
> =A0/**
> =A0* struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver
> =A0* @dev: pointer to device structure
> @@ -70,6 +74,8 @@ MODULE_LICENSE("GPL");
> =A0* @lock: spinlock to coordinate between different functions.
> =A0* @of_gc: of_gpio_chip instance structure; used when GPIO is enabled
> =A0* @irqhost: Pointer to irq_host instance; used when IRQ mode is suppor=
ted
> + * @wdt_mode: only used for gpt 0: 0 gpt-only timer, 1 can be used as a
> + * =A0 =A0 =A0 =A0 =A0 =A0wdt, 2 currently used as wdt, cannot be used a=
s gpt
> =A0*/
> =A0struct mpc52xx_gpt_priv {
> =A0 =A0 =A0 =A0struct list_head list; =A0 =A0 =A0 =A0 =A0/* List of all G=
PT devices */
> @@ -78,6 +84,9 @@ struct mpc52xx_gpt_priv {
> =A0 =A0 =A0 =A0spinlock_t lock;
> =A0 =A0 =A0 =A0struct irq_host *irqhost;
> =A0 =A0 =A0 =A0u32 ipb_freq;
> +#if defined(HAVE_MPC5200_WDT)
> + =A0 =A0 =A0 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.

> +#define NS_PER_SEC =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 1000000000LL
> +
> +#define MPC52xx_GPT_CAN_WDT =A0 =A0 =A0 =A0 =A0 =A0(1 << 0)
> +#define MPC52xx_GPT_IS_WDT =A0 =A0 =A0 =A0 =A0 =A0 (1 << 1)
> +
> +
> =A0/* -------------------------------------------------------------------=
--
> =A0* Cascaded interrupt controller hooks
> =A0*/
> @@ -375,36 +393,22 @@ struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int i=
rq)
> =A0}
> =A0EXPORT_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,
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int continuous)
> +/* Calculate the timer counter input register MBAR + 0x6n4 from the
> + * period in ns. =A0The maximum period for 33 MHz IPB clock is ~130s. */
> +static int mpc52xx_gpt_calc_counter_input(u64 period, u64 ipb_freq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 u32 *reg_val)
> =A0{
> - =A0 =A0 =A0 u32 clear, set;
> =A0 =A0 =A0 =A0u64 clocks;
> =A0 =A0 =A0 =A0u32 prescale;
> - =A0 =A0 =A0 unsigned long flags;
> -
> - =A0 =A0 =A0 clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTI=
NUOUS;
> - =A0 =A0 =A0 set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER=
_ENABLE;
> - =A0 =A0 =A0 if (continuous)
> - =A0 =A0 =A0 =A0 =A0 =A0 =A0 set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
>
> =A0 =A0 =A0 =A0/* Determine the number of clocks in the requested period.=
 =A064 bit
> =A0 =A0 =A0 =A0 * arithmatic is done here to preserve the precision until=
 the value
> - =A0 =A0 =A0 =A0* is scaled back down into the u32 range. =A0Period is i=
n 'ns', bus
> - =A0 =A0 =A0 =A0* frequency is in Hz. */
> - =A0 =A0 =A0 clocks =3D (u64)period * (u64)gpt->ipb_freq;
> - =A0 =A0 =A0 do_div(clocks, 1000000000); /* Scale it down to ns range */
> + =A0 =A0 =A0 =A0* is scaled back down into the u32 range. */
> + =A0 =A0 =A0 clocks =3D period * ipb_freq;
> + =A0 =A0 =A0 do_div(clocks, NS_PER_SEC); =A0 =A0 =A0 =A0 /* Scale it dow=
n 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.

>
> - =A0 =A0 =A0 /* This device cannot handle a clock count greater than 32 =
bits */
> - =A0 =A0 =A0 if (clocks > 0xffffffff)
> + =A0 =A0 =A0 /* the maximum count is 0x10000 pre-scaler * 0xffff count *=
/
> + =A0 =A0 =A0 if (clocks > 0xffff0000)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;

This a bug fix?  Separate patch please.

> =A0 =A0 =A0 =A0/* Calculate the prescaler and count values from the clock=
s value.
> @@ -427,9 +431,47 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv =
*gpt, int period,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return -EINVAL;
> =A0 =A0 =A0 =A0}
>
> + =A0 =A0 =A0 *reg_val =3D (prescale & 0xffff) << 16 | clocks;
> + =A0 =A0 =A0 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,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int continuous)
> +{
> + =A0 =A0 =A0 u32 clear, set;
> + =A0 =A0 =A0 u32 counter_reg;
> + =A0 =A0 =A0 unsigned long flags;
> +
> +#if defined(HAVE_MPC5200_WDT)
> + =A0 =A0 =A0 /* reject the operation if the timer is used as watchdog (g=
pt 0 only) */
> + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> + =A0 =A0 =A0 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> + =A0 =A0 =A0 }
> + =A0 =A0 =A0 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.

> +
> + =A0 =A0 =A0 clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTI=
NUOUS;
> + =A0 =A0 =A0 set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER=
_ENABLE;
> + =A0 =A0 =A0 if (continuous)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
> +
> + =A0 =A0 =A0 if (mpc52xx_gpt_calc_counter_input(period, (u64)gpt->ipb_fr=
eq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0 =A0&counter_reg) !=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
> +
> =A0 =A0 =A0 =A0/* Set and enable the timer */
> =A0 =A0 =A0 =A0spin_lock_irqsave(&gpt->lock, flags);
> - =A0 =A0 =A0 out_be32(&gpt->regs->count, prescale << 16 | clocks);
> + =A0 =A0 =A0 out_be32(&gpt->regs->count, counter_reg);
> =A0 =A0 =A0 =A0clrsetbits_be32(&gpt->regs->mode, clear, set);
> =A0 =A0 =A0 =A0spin_unlock_irqrestore(&gpt->lock, flags);
>
> @@ -437,12 +479,175 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_pri=
v *gpt, int period,
> =A0}
> =A0EXPORT_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)
> +{
> + =A0 =A0 =A0 u64 period;
> + =A0 =A0 =A0 u32 prescale;
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> + =A0 =A0 =A0 period =3D in_be32(&gpt->regs->count);
> + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> +
> + =A0 =A0 =A0 prescale =3D period >> 16;
> + =A0 =A0 =A0 period &=3D 0xffff;
> + =A0 =A0 =A0 if (prescale =3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 prescale =3D 0x10000;
> + =A0 =A0 =A0 period =3D period * (u64) prescale * NS_PER_SEC;
> + =A0 =A0 =A0 do_div(period, (u64)gpt->ipb_freq);

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

> + =A0 =A0 =A0 return period;
> +}
> +EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
> +
> +int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
> =A0{
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 /* reject the operation if the timer is used as watchdog (g=
pt 0 only) */
> + =A0 =A0 =A0 spin_lock_irqsave(&gpt->lock, flags);
> +#if defined(HAVE_MPC5200_WDT)
> + =A0 =A0 =A0 if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 spin_unlock_irqrestore(&gpt->lock, flags);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EBUSY;
> + =A0 =A0 =A0 }
> +#endif

Again, drop the #if/endif wrapper.

> +/**
> + * mpc52xx_gpt_wdt_release - Release the watchdog so it can be used as g=
pt
> + * @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)
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&gpt_wdt->lock, flags);
> + =A0 =A0 =A0 clrbits32(&gpt_wdt->regs->mode,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52=
xx_GPT_MODE_WDT_EN);
> + =A0 =A0 =A0 gpt_wdt->wdt_mode &=3D ~MPC52xx_GPT_IS_WDT;
> + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
> +#endif
> + =A0 =A0 =A0 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)
> +{
> + =A0 =A0 =A0 unsigned long flags;
> +
> + =A0 =A0 =A0 spin_lock_irqsave(&gpt_wdt->lock, flags);
> + =A0 =A0 =A0 clrbits32(&gpt_wdt->regs->mode,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52=
xx_GPT_MODE_WDT_EN);
> + =A0 =A0 =A0 gpt_wdt->wdt_mode &=3D ~MPC52xx_GPT_IS_WDT;
> + =A0 =A0 =A0 spin_unlock_irqrestore(&gpt_wdt->lock, flags);
> + =A0 =A0 =A0 return 0;
> +}
> +EXPORT_SYMBOL(mpc52xx_gpt_wdt_stop);
> +#endif =A0/* =A0CONFIG_WATCHDOG_NOWAYOUT =A0*/
> +#endif =A0/* =A0HAVE_MPC5200_WDT =A0*/
> +
> =A0/* -------------------------------------------------------------------=
--
> =A0* of_platform bus binding code
> =A0*/
> @@ -473,6 +678,30 @@ static int __devinit mpc52xx_gpt_probe(struct of_dev=
ice *ofdev,
> =A0 =A0 =A0 =A0list_add(&gpt->list, &mpc52xx_gpt_list);
> =A0 =A0 =A0 =A0mutex_unlock(&mpc52xx_gpt_list_mutex);
>
> +#if defined(HAVE_MPC5200_WDT)
> + =A0 =A0 =A0 /* check if this device could be a watchdog */
> + =A0 =A0 =A0 if (of_get_property(ofdev->node, "fsl,has-wdt", NULL) ||
> + =A0 =A0 =A0 =A0 =A0 of_get_property(ofdev->node, "has-wdt", NULL)) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 const u32 *on_boot_wdt;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode =3D MPC52xx_GPT_CAN_WDT;
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* check if the device shall be used as on-=
boot watchdog */
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 on_boot_wdt =3D of_get_property(ofdev->node=
, "wdt,on-boot", NULL);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (on_boot_wdt) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gpt->wdt_mode |=3D MPC52xx_=
GPT_IS_WDT;
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (*on_boot_wdt > 0 &&
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 mpc52xx_gpt_wdt_sta=
rt(gpt, *on_boot_wdt) =3D=3D 0)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(gp=
t->dev,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0"running as wdt, timeout %us\n",
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=
 =A0 =A0*on_boot_wdt);
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(gp=
t->dev, "reserved as wdt\n");
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 } else
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_info(gpt->dev, "can fun=
ction as wdt\n");
> + =A0 =A0 =A0 }
> +#endif

Ditto on the #if/endif

Cheers,
g.

--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
@ 2009-11-10 19:41 ` Albrecht Dreß
  0 siblings, 0 replies; 8+ messages in thread
From: Albrecht Dreß @ 2009-11-10 19:41 UTC (permalink / raw)
  To: Linux PPC Development, Grant Likely, devicetree-discuss,
	Wim Van Sebroeck

Merge the WDT code into the GPT interface.

Signed-off-by: Albrecht Dreß <albrecht.dress@arcor.de>
---

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

diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/mpc52xx.h
index 707ab75..0ece07f 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -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)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
index 2c3fa13..8274ebb 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
@@ -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 */
@@ -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
 
 #if defined(CONFIG_GPIOLIB)
 	struct of_gpio_chip of_gc;
@@ -101,14 +110,23 @@ DEFINE_MUTEX(mpc52xx_gpt_list_mutex);
 #define MPC52xx_GPT_MODE_CONTINUOUS	(0x0400)
 #define MPC52xx_GPT_MODE_OPEN_DRAIN	(0x0200)
 #define MPC52xx_GPT_MODE_IRQ_EN		(0x0100)
+#define MPC52xx_GPT_MODE_WDT_EN		(0x8000)
 
 #define MPC52xx_GPT_MODE_ICT_MASK	(0x030000)
 #define MPC52xx_GPT_MODE_ICT_RISING	(0x010000)
 #define MPC52xx_GPT_MODE_ICT_FALLING	(0x020000)
 #define MPC52xx_GPT_MODE_ICT_TOGGLE	(0x030000)
 
+#define MPC52xx_GPT_MODE_WDT_PING	(0xa5)
+
 #define MPC52xx_GPT_STATUS_IRQMASK	(0x000f)
 
+#define NS_PER_SEC			1000000000LL
+
+#define MPC52xx_GPT_CAN_WDT		(1 << 0)
+#define MPC52xx_GPT_IS_WDT		(1 << 1)
+
+
 /* ---------------------------------------------------------------------
  * Cascaded interrupt controller hooks
  */
@@ -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;
 
 	/* 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 */
 
-	/* 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;
 
 	/* 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;
+
+#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
+
+	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;
+
+	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);
+	return period;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
+
+int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
 {
+	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
+
 	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE);
+	spin_unlock_irqrestore(&gpt->lock, flags);
+	return 0;
 }
 EXPORT_SYMBOL(mpc52xx_gpt_stop_timer);
 
+#if defined(HAVE_MPC5200_WDT)
+/**
+ * mpc52xx_gpt_wdt_probe - Find the wdt devide in the gpt list
+ */
+struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt_probe(void)
+{
+	struct list_head *pos;
+	struct mpc52xx_gpt_priv *this_gpt = NULL;
+
+	/* find the wdt device in our list */
+	mutex_lock(&mpc52xx_gpt_list_mutex);
+	list_for_each(pos, &mpc52xx_gpt_list) {
+		this_gpt = container_of(pos, struct mpc52xx_gpt_priv, list);
+		if ((this_gpt->wdt_mode & MPC52xx_GPT_CAN_WDT)) {
+			mutex_unlock(&mpc52xx_gpt_list_mutex);
+			return this_gpt;
+		}
+	}
+	mutex_unlock(&mpc52xx_gpt_list_mutex);
+	return NULL;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_probe);
+
+/**
+ * mpc52xx_gpt_wdt_start - Start the passed timer as watchdog
+ * @gpt_wdt: the watchdog gpt
+ * @wdt_timeout: watchdog timeout in seconds
+ *
+ * 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_start(struct mpc52xx_gpt_priv *gpt_wdt, int wdt_timeout)
+{
+	u32 clear, set;
+	u32 counter_reg;
+	unsigned long flags;
+
+	/* calculate register settings */
+	if (mpc52xx_gpt_calc_counter_input((u64) wdt_timeout * NS_PER_SEC,
+					   (u64)gpt_wdt->ipb_freq,
+					   &counter_reg) != 0) {
+		dev_info(gpt_wdt->dev, "bad timeout value %d\n", wdt_timeout);
+		return -EINVAL;
+	}
+
+	clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS |
+		MPC52xx_GPT_MODE_IRQ_EN;
+	set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE |
+		MPC52xx_GPT_MODE_WDT_EN;
+
+	/* set the time-out and launch as wdt */
+	spin_lock_irqsave(&gpt_wdt->lock, flags);
+	out_be32(&gpt_wdt->regs->count, counter_reg);
+	clrsetbits_be32(&gpt_wdt->regs->mode, clear, set);
+	gpt_wdt->wdt_mode |= MPC52xx_GPT_IS_WDT;
+	spin_unlock_irqrestore(&gpt_wdt->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_start);
+
+/**
+ * mpc52xx_gpt_wdt_ping - Toggle the keep-alive signal of 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_ping(struct mpc52xx_gpt_priv *gpt_wdt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpt_wdt->lock, flags);
+	out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING);
+	spin_unlock_irqrestore(&gpt_wdt->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_ping);
+
+/**
+ * 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 &&
+			    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
+
 	return 0;
 }
 

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT
@ 2009-11-10 19:41 ` Albrecht Dreß
  0 siblings, 0 replies; 8+ messages in thread
From: Albrecht Dreß @ 2009-11-10 19:41 UTC (permalink / raw)
  To: Linux PPC Development, Grant Likely, devicetree-discuss,
	Wim Van Sebroeck

Merge the WDT code into the GPT interface.

Signed-off-by: Albrecht Dre=DF <albrecht.dress@arcor.de>
---

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 precedenc=
e
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(-)

diff --git a/arch/powerpc/include/asm/mpc52xx.h b/arch/powerpc/include/asm/=
mpc52xx.h
index 707ab75..0ece07f 100644
--- a/arch/powerpc/include/asm/mpc52xx.h
+++ b/arch/powerpc/include/asm/mpc52xx.h
@@ -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 perio=
d,
-                            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 perio=
d,
+				   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  */
+
=20
 /* mpc52xx_lpbfifo.c */
 #define MPC52XX_LPBFIFO_FLAG_READ		(0)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platf=
orms/52xx/mpc52xx_gpt.c
index 2c3fa13..8274ebb 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c
@@ -60,9 +60,13 @@
 #include <asm/mpc52xx.h>
=20
 MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
-MODULE_AUTHOR("Sascha Hauer, Grant Likely");
+MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dre=DF");
 MODULE_LICENSE("GPL");
=20
+#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 */
@@ -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
=20
 #if defined(CONFIG_GPIOLIB)
 	struct of_gpio_chip of_gc;
@@ -101,14 +110,23 @@ DEFINE_MUTEX(mpc52xx_gpt_list_mutex);
 #define MPC52xx_GPT_MODE_CONTINUOUS	(0x0400)
 #define MPC52xx_GPT_MODE_OPEN_DRAIN	(0x0200)
 #define MPC52xx_GPT_MODE_IRQ_EN		(0x0100)
+#define MPC52xx_GPT_MODE_WDT_EN		(0x8000)
=20
 #define MPC52xx_GPT_MODE_ICT_MASK	(0x030000)
 #define MPC52xx_GPT_MODE_ICT_RISING	(0x010000)
 #define MPC52xx_GPT_MODE_ICT_FALLING	(0x020000)
 #define MPC52xx_GPT_MODE_ICT_TOGGLE	(0x030000)
=20
+#define MPC52xx_GPT_MODE_WDT_PING	(0xa5)
+
 #define MPC52xx_GPT_STATUS_IRQMASK	(0x000f)
=20
+#define NS_PER_SEC			1000000000LL
+
+#define MPC52xx_GPT_CAN_WDT		(1 << 0)
+#define MPC52xx_GPT_IS_WDT		(1 << 1)
+
+
 /* ---------------------------------------------------------------------
  * Cascaded interrupt controller hooks
  */
@@ -375,36 +393,22 @@ struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq=
)
 }
 EXPORT_SYMBOL(mpc52xx_gpt_from_irq);
=20
-/**
- * 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 =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS;
-	set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE;
-	if (continuous)
-		set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
=20
 	/* 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 =3D (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 =3D period * ipb_freq;
+	do_div(clocks, NS_PER_SEC);         /* Scale it down to ns range */
=20
-	/* 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;
=20
 	/* Calculate the prescaler and count values from the clocks value.
@@ -427,9 +431,47 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *g=
pt, int period,
 		return -EINVAL;
 	}
=20
+	*reg_val =3D (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;
+
+#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
+
+	clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS;
+	set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE;
+	if (continuous)
+		set |=3D MPC52xx_GPT_MODE_CONTINUOUS;
+
+	if (mpc52xx_gpt_calc_counter_input(period, (u64)gpt->ipb_freq,
+					   &counter_reg) !=3D 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);
=20
@@ -437,12 +479,175 @@ int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv =
*gpt, int period,
 }
 EXPORT_SYMBOL(mpc52xx_gpt_start_timer);
=20
-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;
+
+	spin_lock_irqsave(&gpt->lock, flags);
+	period =3D in_be32(&gpt->regs->count);
+	spin_unlock_irqrestore(&gpt->lock, flags);
+
+	prescale =3D period >> 16;
+	period &=3D 0xffff;
+	if (prescale =3D=3D 0)
+		prescale =3D 0x10000;
+	period =3D period * (u64) prescale * NS_PER_SEC;
+	do_div(period, (u64)gpt->ipb_freq);
+	return period;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
+
+int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
 {
+	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
+
 	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE);
+	spin_unlock_irqrestore(&gpt->lock, flags);
+	return 0;
 }
 EXPORT_SYMBOL(mpc52xx_gpt_stop_timer);
=20
+#if defined(HAVE_MPC5200_WDT)
+/**
+ * mpc52xx_gpt_wdt_probe - Find the wdt devide in the gpt list
+ */
+struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt_probe(void)
+{
+	struct list_head *pos;
+	struct mpc52xx_gpt_priv *this_gpt =3D NULL;
+
+	/* find the wdt device in our list */
+	mutex_lock(&mpc52xx_gpt_list_mutex);
+	list_for_each(pos, &mpc52xx_gpt_list) {
+		this_gpt =3D container_of(pos, struct mpc52xx_gpt_priv, list);
+		if ((this_gpt->wdt_mode & MPC52xx_GPT_CAN_WDT)) {
+			mutex_unlock(&mpc52xx_gpt_list_mutex);
+			return this_gpt;
+		}
+	}
+	mutex_unlock(&mpc52xx_gpt_list_mutex);
+	return NULL;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_probe);
+
+/**
+ * mpc52xx_gpt_wdt_start - Start the passed timer as watchdog
+ * @gpt_wdt: the watchdog gpt
+ * @wdt_timeout: watchdog timeout in seconds
+ *
+ * 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_start(struct mpc52xx_gpt_priv *gpt_wdt, int wdt_timeou=
t)
+{
+	u32 clear, set;
+	u32 counter_reg;
+	unsigned long flags;
+
+	/* calculate register settings */
+	if (mpc52xx_gpt_calc_counter_input((u64) wdt_timeout * NS_PER_SEC,
+					   (u64)gpt_wdt->ipb_freq,
+					   &counter_reg) !=3D 0) {
+		dev_info(gpt_wdt->dev, "bad timeout value %d\n", wdt_timeout);
+		return -EINVAL;
+	}
+
+	clear =3D MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS |
+		MPC52xx_GPT_MODE_IRQ_EN;
+	set =3D MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE |
+		MPC52xx_GPT_MODE_WDT_EN;
+
+	/* set the time-out and launch as wdt */
+	spin_lock_irqsave(&gpt_wdt->lock, flags);
+	out_be32(&gpt_wdt->regs->count, counter_reg);
+	clrsetbits_be32(&gpt_wdt->regs->mode, clear, set);
+	gpt_wdt->wdt_mode |=3D MPC52xx_GPT_IS_WDT;
+	spin_unlock_irqrestore(&gpt_wdt->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_start);
+
+/**
+ * mpc52xx_gpt_wdt_ping - Toggle the keep-alive signal of 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_ping(struct mpc52xx_gpt_priv *gpt_wdt)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gpt_wdt->lock, flags);
+	out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING);
+	spin_unlock_irqrestore(&gpt_wdt->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpc52xx_gpt_wdt_ping);
+
+/**
+ * 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 &=3D ~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 &=3D ~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_devic=
e *ofdev,
 	list_add(&gpt->list, &mpc52xx_gpt_list);
 	mutex_unlock(&mpc52xx_gpt_list_mutex);
=20
+#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 =3D MPC52xx_GPT_CAN_WDT;
+
+		/* check if the device shall be used as on-boot watchdog */
+		on_boot_wdt =3D of_get_property(ofdev->node, "wdt,on-boot", NULL);
+		if (on_boot_wdt) {
+			gpt->wdt_mode |=3D MPC52xx_GPT_IS_WDT;
+			if (*on_boot_wdt > 0 &&
+			    mpc52xx_gpt_wdt_start(gpt, *on_boot_wdt) =3D=3D 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
+
 	return 0;
 }
=20

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-11-11 20:11 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-11  8:27 [PATCH 2/3] mpc52xx/wdt: merge WDT code into the GPT Albrecht Dreß
2009-11-11  8:27 ` Albrecht Dreß
     [not found] ` <1980629.1257928058732.JavaMail.ngmail-hrWuGRo80rRiK3oW1guXusM6rOWSkUom@public.gmane.org>
2009-11-11 20:11   ` Grant Likely
2009-11-11 20:11     ` Grant Likely
  -- strict thread matches above, loose matches on Subject: below --
2009-11-10 19:41 Albrecht Dreß
2009-11-10 19:41 ` Albrecht Dreß
2009-11-10 20:59 ` Grant Likely
2009-11-10 20:59   ` Grant Likely

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.