On Monday 26 May 2014 04:14 PM, Archit Taneja wrote:
> Generally, IP blocks/modules within a clock domain each have their own
> CM_x_CLKCTRL register, each having it's own MODULEMODE field to manage the
> module.
> 
> DSS clockdoain, however, has multiple modules in it, but only one register
> named CM_DSS_DSS_CLKCTRL, which contains one MODULEMODE register field.
> 
> Until now, we defined '.prcm.omap4.modulemode' only for the top level DSS
> hwmod("dss_core") and didn't define it for other DSS hwmods(like "dss_dispc",
> "dss_dsi1" and so on). This made the omapdss driver work as the top level DSS
> platform device and the rest had a parent-child relationship. This ensured 
> that
> the parent hwmod("dss_core") is enabled if any of the children hwmods are
> enabled while using omapdss.
> 
> This method, however, doesn't work when each hwmod is enabled individually.
> This happens early in boot in omap_hwmod_setup_all, where each hwmod is 
> enabled,
> and then reset and idled. All the 'children' DSS hwmods fail to enable and
> reset, since they don't enable modulemode.
> 
> The way to make such modules work both during initialization and when used by
> pm runtime API in the driver would be to add refcounting for 
> enabling/disabling
> the modulemode field.
> 
> We add a new flag bit for the flag in omap_hwmod_omap4_prcm, which tells
> omap_hwmod that this hwmod uses a modulemode field shared by other hwmods.
> 
> We create a struct called 'modulemode_shared'. The hwmod data file should 
> define
> a static instance of this struct. Each hwmod that uses this modulemode field
> should hold a pointer to this instance.
> 
> omap_hwmod's soc enable_module and disable_module ops set the MODULEMODE
> reigster bits only when the first module using it is enabled, or the last 
> module
> using it is disabled. We serialize accesses to the struct with a spinlock.
> 
> Signed-off-by: Archit Taneja <arc...@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod.c | 123 
> +++++++++++++++++++++++++++++++++------
>  arch/arm/mach-omap2/omap_hwmod.h |  38 +++++++++---
>  2 files changed, 133 insertions(+), 28 deletions(-)
> 
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c 
> b/arch/arm/mach-omap2/omap_hwmod.c
> index 66c60fe..b42718d 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -973,17 +973,33 @@ static void _disable_optional_clocks(struct omap_hwmod 
> *oh)
>   */
>  static void _omap4_enable_module(struct omap_hwmod *oh)
>  {
> +     unsigned long flags;
> +     struct modulemode_shared *mmode = NULL;
> +     bool enable = true;
> +
>       if (!oh->clkdm || !oh->prcm.omap4.modulemode)
>               return;
>  
>       pr_debug("omap_hwmod: %s: %s: %d\n",
>                oh->name, __func__, oh->prcm.omap4.modulemode);
>  
> -     omap4_cminst_module_enable(oh->prcm.omap4.modulemode,
> -                                oh->clkdm->prcm_partition,
> -                                oh->clkdm->cm_inst,
> -                                oh->clkdm->clkdm_offs,
> -                                oh->prcm.omap4.clkctrl_offs);
> +     if (oh->prcm.omap4.flags & HWMOD_OMAP4_MODULEMODE_SHARED) {
> +             mmode = oh->prcm.omap4.modulemode_ref;
> +
> +             spin_lock_irqsave(&mmode->lock, flags);
> +
> +             enable = mmode->refcnt++ == 0;
> +     }
> +
> +     if (enable)
> +             omap4_cminst_module_enable(oh->prcm.omap4.modulemode,
> +                                        oh->clkdm->prcm_partition,
> +                                        oh->clkdm->cm_inst,
> +                                        oh->clkdm->clkdm_offs,
> +                                        oh->prcm.omap4.clkctrl_offs);
> +
> +     if (mmode)
> +             spin_unlock_irqrestore(&mmode->lock, flags);
>  }
>  
>  /**
> @@ -995,15 +1011,32 @@ static void _omap4_enable_module(struct omap_hwmod *oh)
>   */
>  static void _am33xx_enable_module(struct omap_hwmod *oh)
>  {
> +     unsigned long flags;
> +     struct modulemode_shared *mmode = NULL;
> +     bool enable = true;
> +
>       if (!oh->clkdm || !oh->prcm.omap4.modulemode)
>               return;
>  
>       pr_debug("omap_hwmod: %s: %s: %d\n",
>                oh->name, __func__, oh->prcm.omap4.modulemode);
>  
> -     am33xx_cm_module_enable(oh->prcm.omap4.modulemode, oh->clkdm->cm_inst,
> -                             oh->clkdm->clkdm_offs,
> -                             oh->prcm.omap4.clkctrl_offs);
> +     if (oh->prcm.omap4.flags & HWMOD_OMAP4_MODULEMODE_SHARED) {
> +             mmode = oh->prcm.omap4.modulemode_ref;
> +
> +             spin_lock_irqsave(&mmode->lock, flags);
> +
> +             enable = mmode->refcnt++ == 0;
> +     }
> +
> +     if (enable)
> +             am33xx_cm_module_enable(oh->prcm.omap4.modulemode,
> +                                     oh->clkdm->cm_inst,
> +                                     oh->clkdm->clkdm_offs,
> +                                     oh->prcm.omap4.clkctrl_offs);
> +
> +     if (mmode)
> +             spin_unlock_irqrestore(&mmode->lock, flags);
>  }
>  
>  /**
> @@ -1846,6 +1879,9 @@ static bool _are_any_hardreset_lines_asserted(struct 
> omap_hwmod *oh)
>  static int _omap4_disable_module(struct omap_hwmod *oh)
>  {
>       int v;
> +     unsigned long flags;
> +     struct modulemode_shared *mmode = NULL;
> +     bool disable = true;
>  
>       if (!oh->clkdm || !oh->prcm.omap4.modulemode)
>               return -EINVAL;
> @@ -1859,15 +1895,30 @@ static int _omap4_disable_module(struct omap_hwmod 
> *oh)
>  
>       pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__);
>  
> -     omap4_cminst_module_disable(oh->clkdm->prcm_partition,
> +     if (oh->prcm.omap4.flags & HWMOD_OMAP4_MODULEMODE_SHARED) {
> +             mmode = oh->prcm.omap4.modulemode_ref;
> +
> +             spin_lock_irqsave(&mmode->lock, flags);
> +
> +             WARN_ON(mmode->refcnt == 0);
> +
> +             disable = --mmode->refcnt == 0;
> +     }
> +
> +     if (disable) {
> +             omap4_cminst_module_disable(oh->clkdm->prcm_partition,
>                                   oh->clkdm->cm_inst,
>                                   oh->clkdm->clkdm_offs,
>                                   oh->prcm.omap4.clkctrl_offs);
>  
> -     v = _omap4_wait_target_disable(oh);
> -     if (v)
> -             pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
> -                     oh->name);
> +             v = _omap4_wait_target_disable(oh);
> +             if (v)
> +                     pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
> +                             oh->name);
> +     }
> +
> +     if (mmode)
> +             spin_unlock_irqrestore(&mmode->lock, flags);
>  
>       return 0;
>  }
> @@ -1882,6 +1933,9 @@ static int _omap4_disable_module(struct omap_hwmod *oh)
>  static int _am33xx_disable_module(struct omap_hwmod *oh)
>  {
>       int v;
> +     unsigned long flags;
> +     struct modulemode_shared *mmode = NULL;
> +     bool disable = true;
>  
>       if (!oh->clkdm || !oh->prcm.omap4.modulemode)
>               return -EINVAL;
> @@ -1891,13 +1945,28 @@ static int _am33xx_disable_module(struct omap_hwmod 
> *oh)
>       if (_are_any_hardreset_lines_asserted(oh))
>               return 0;
>  
> -     am33xx_cm_module_disable(oh->clkdm->cm_inst, oh->clkdm->clkdm_offs,
> -                              oh->prcm.omap4.clkctrl_offs);
> +     if (oh->prcm.omap4.flags & HWMOD_OMAP4_MODULEMODE_SHARED) {
> +             mmode = oh->prcm.omap4.modulemode_ref;
>  
> -     v = _am33xx_wait_target_disable(oh);
> -     if (v)
> -             pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
> -                     oh->name);
> +             spin_lock_irqsave(&mmode->lock, flags);
> +
> +             WARN_ON(mmode->refcnt == 0);
> +
> +             disable = --mmode->refcnt == 0;
> +     }
> +
> +     if (disable) {
> +             am33xx_cm_module_disable(oh->clkdm->cm_inst, 
> oh->clkdm->clkdm_offs,
> +                                      oh->prcm.omap4.clkctrl_offs);
> +
> +             v = _am33xx_wait_target_disable(oh);
> +             if (v)
> +                     pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
> +                             oh->name);
> +     }
> +
> +     if (mmode)
> +             spin_unlock_irqrestore(&mmode->lock, flags);
>  
>       return 0;
>  }
> @@ -2751,6 +2820,13 @@ static int __init _register(struct omap_hwmod *oh)
>       if (_lookup(oh->name))
>               return -EEXIST;
>  
> +     if (oh->prcm.omap4.flags & HWMOD_OMAP4_MODULEMODE_SHARED &&
> +                     !oh->prcm.omap4.modulemode_ref) {

You might also want to check for someone populating a modulemode_ref but
failing to populate the flag?

Alternatively, Since you expect a modulemode_ref to be always available for all 
modules which
share modulemode, that in itself can be used to identify such modules without 
the
need of an additional flag?

> +             pr_err("omap_hwmod: %s shares modulemode, but doesn't hold a 
> ref to it\n",
> +                     oh->name);
> +             return -EINVAL;
> +     }
> +
>       list_add_tail(&oh->node, &omap_hwmod_list);
>  
>       INIT_LIST_HEAD(&oh->master_ports);
> @@ -2759,6 +2835,15 @@ static int __init _register(struct omap_hwmod *oh)
>  
>       oh->_state = _HWMOD_STATE_REGISTERED;
>  
> +     if (oh->prcm.omap4.flags & HWMOD_OMAP4_MODULEMODE_SHARED) {
> +             struct modulemode_shared *mmode = oh->prcm.omap4.modulemode_ref;
> +
> +             if (!mmode->registered) {
> +                     spin_lock_init(&mmode->lock);
> +                     mmode->registered = true;

If this is only used to keep track of the spin_lock being initialized, maybe 
it'll be
more readable if you just call it mmode->spin_lock_init = true.

> +             }
> +     }
> +
>       /*
>        * XXX Rather than doing a strcmp(), this should test a flag
>        * set in the hwmod data, inserted by the autogenerator code.
> diff --git a/arch/arm/mach-omap2/omap_hwmod.h 
> b/arch/arm/mach-omap2/omap_hwmod.h
> index 0f97d63..64c2af6 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.h
> +++ b/arch/arm/mach-omap2/omap_hwmod.h
> @@ -432,13 +432,31 @@ struct omap_hwmod_omap2_prcm {
>  };
>  
>  /*
> + * struct for managing modulemode field shared accross IP blocks
> + * @lock: spinlock to searilze enables/disables performed by different
> + *     hwmods sharing the same modulemode field
> + * @refcnt: keep a refcount of the enabled hwmods using this modulemode
> + *     field
> + * @registered: a flag to track whether the struct has been registered
> + */
> +struct modulemode_shared {
> +     spinlock_t      lock;
> +     unsigned        refcnt;
> +     bool            registered;
> +};
> +
> +/*
>   * Possible values for struct omap_hwmod_omap4_prcm.flags
>   *
>   * HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT: Some IP blocks don't have a PRCM
>   *     module-level context loss register associated with them; this
>   *     flag bit should be set in those cases
> + * HWMOD_OMAP4_MODULEMODE_SHARED: Some IP blocks belonging to the same
> + *     clock domain may have a shared MODULEMODE field; this flag bit
> + *     should be set in those cases
>   */
>  #define HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT              (1 << 0)
> +#define HWMOD_OMAP4_MODULEMODE_SHARED                (1 << 1)
>  
>  /**
>   * struct omap_hwmod_omap4_prcm - OMAP4-specific PRCM data
> @@ -450,6 +468,7 @@ struct omap_hwmod_omap2_prcm {
>   * @submodule_wkdep_bit: bit shift of the WKDEP range
>   * @flags: PRCM register capabilities for this IP block
>   * @modulemode: allowable modulemodes
> + * @modulemode_ref: pointer to modulemode struct shared by multiple hwmods
>   * @context_lost_counter: Count of module level context lost
>   *
>   * If @lostcontext_mask is not defined, context loss check code uses
> @@ -458,15 +477,16 @@ struct omap_hwmod_omap2_prcm {
>   * more hwmods.
>   */
>  struct omap_hwmod_omap4_prcm {
> -     u16             clkctrl_offs;
> -     u16             rstctrl_offs;
> -     u16             rstst_offs;
> -     u16             context_offs;
> -     u32             lostcontext_mask;
> -     u8              submodule_wkdep_bit;
> -     u8              modulemode;
> -     u8              flags;
> -     int             context_lost_counter;
> +     u16                             clkctrl_offs;
> +     u16                             rstctrl_offs;
> +     u16                             rstst_offs;
> +     u16                             context_offs;
> +     u32                             lostcontext_mask;
> +     u8                              submodule_wkdep_bit;
> +     u8                              modulemode;
> +     struct modulemode_shared        *modulemode_ref;
> +     u8                              flags;
> +     int                             context_lost_counter;
>  };
>  
>  
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to