Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH] driver core: Remove device link creation limitation
From: Rafael J. Wysocki @ 2019-07-11 11:39 UTC (permalink / raw)
  To: Saravana Kannan
  Cc: Rafael J. Wysocki, Greg Kroah-Hartman, LKML, Linux PM,
	Lukas Wunner, Jon Hunter, Ulf Hansson, Marek Szyprowski
In-Reply-To: <CAGETcx-FgohUahM+aLOBUQGgJKyPW6ptFAqRdcPJG2FkE6CN5w@mail.gmail.com>

On Thu, Jul 11, 2019 at 12:06 AM Saravana Kannan <saravanak@google.com> wrote:
>
> On Wed, Jul 10, 2019 at 3:19 AM Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
> >
> > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> >
> > If device_link_add() is called for a consumer/supplier pair with an
> > existing device link between them and the existing link's type is
> > not in agreement with the flags passed to that function by its
> > caller, NULL will be returned.  That is seriously inconvenient,
> > because it forces the callers of device_link_add() to worry about
> > what others may or may not do even if that is not relevant to them
> > for any other reasons.
> >
> > It turns out, however, that this limitation can be made go away
> > relatively easily.
> >
> > The underlying observation is that if DL_FLAG_STATELESS has been
> > passed to device_link_add() in flags for the given consumer/supplier
> > pair at least once, calling either device_link_del() or
> > device_link_remove() to release the link returned by it should work,
> > but there are no other requirements associated with that flag.  In
> > turn, if at least one of the callers of device_link_add() for the
> > given consumer/supplier pair has not passed DL_FLAG_STATELESS to it
> > in flags, the driver core should track the status of the link and act
> > on it as appropriate (ie. the link should be treated as "managed").
> > This means that DL_FLAG_STATELESS needs to be set for managed device
> > links and it should be valid to call device_link_del() or
> > device_link_remove() to drop references to them in certain
> > sutiations.
> >
> > To allow that to happen, introduce a new (internal) device link flag
> > called DL_FLAG_MANAGED and make device_link_add() set it automatically
> > whenever DL_FLAG_STATELESS is not passed to it.  Also make it take
> > additional references to existing device links that were previously
> > stateless (that is, with DL_FLAG_STATELESS set and DL_FLAG_MANAGED
> > unset) and will need to be managed going forward and initialize
> > their status (which has been DL_STATE_NONE so far).
> >
> > Accordingly, when a managed device link is dropped automatically
> > by the driver core, make it clear DL_FLAG_MANAGED, reset the link's
> > status back to DL_STATE_NONE and drop the reference to it associated
> > with DL_FLAG_MANAGED instead of just deleting it right away (to
> > allow it to stay around in case it still needs to be released
> > explicitly by someone).
> >
> > With that, since setting DL_FLAG_STATELESS doesn't mean that the
> > device link in question is not managed any more, replace all of the
> > status-tracking checks against DL_FLAG_STATELESS with analogous
> > checks against DL_FLAG_MANAGED and update the documentation to
> > reflect these changes.
> >
> > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > ---
> >  Documentation/driver-api/device_link.rst |    4
> >  drivers/base/core.c                      |  169 +++++++++++++++++--------------
> >  drivers/base/power/runtime.c             |    4
> >  include/linux/device.h                   |    4
> >  4 files changed, 102 insertions(+), 79 deletions(-)
> >
> > Index: linux-pm/drivers/base/core.c
> > ===================================================================
> > --- linux-pm.orig/drivers/base/core.c
> > +++ linux-pm/drivers/base/core.c
> > @@ -124,6 +124,50 @@ static int device_is_dependent(struct de
> >         return ret;
> >  }
> >
> > +static void device_link_init_status(struct device_link *link,
> > +                                   struct device *consumer,
> > +                                   struct device *supplier)
> > +{
> > +       switch (supplier->links.status) {
> > +       case DL_DEV_PROBING:
> > +               switch (consumer->links.status) {
> > +               case DL_DEV_PROBING:
> > +                       /*
> > +                        * A consumer driver can create a link to a supplier
> > +                        * that has not completed its probing yet as long as it
> > +                        * knows that the supplier is already functional (for
> > +                        * example, it has just acquired some resources from the
> > +                        * supplier).
> > +                        */
> > +                       link->status = DL_STATE_CONSUMER_PROBE;
> > +                       break;
> > +               default:
> > +                       link->status = DL_STATE_DORMANT;
> > +                       break;
> > +               }
> > +               break;
> > +       case DL_DEV_DRIVER_BOUND:
> > +               switch (consumer->links.status) {
> > +               case DL_DEV_PROBING:
> > +                       link->status = DL_STATE_CONSUMER_PROBE;
> > +                       break;
> > +               case DL_DEV_DRIVER_BOUND:
> > +                       link->status = DL_STATE_ACTIVE;
> > +                       break;
> > +               default:
> > +                       link->status = DL_STATE_AVAILABLE;
> > +                       break;
> > +               }
> > +               break;
> > +       case DL_DEV_UNBINDING:
> > +               link->status = DL_STATE_SUPPLIER_UNBIND;
> > +               break;
> > +       default:
> > +               link->status = DL_STATE_DORMANT;
> > +               break;
> > +       }
> > +}
> > +
> >  static int device_reorder_to_tail(struct device *dev, void *not_used)
> >  {
> >         struct device_link *link;
> > @@ -179,9 +223,9 @@ void device_pm_move_to_tail(struct devic
> >   * of the link.  If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be
> >   * ignored.
> >   *
> > - * If DL_FLAG_STATELESS is set in @flags, the link is not going to be managed by
> > - * the driver core and, in particular, the caller of this function is expected
> > - * to drop the reference to the link acquired by it directly.
> > + * If DL_FLAG_STATELESS is set in @flags, the caller of this function is
> > + * expected to release the link returned by it directly with the help of either
> > + * device_link_del() or device_link_remove().
> >   *
> >   * If that flag is not set, however, the caller of this function is handing the
> >   * management of the link over to the driver core entirely and its return value
> > @@ -201,9 +245,16 @@ void device_pm_move_to_tail(struct devic
> >   * be used to request the driver core to automaticall probe for a consmer
> >   * driver after successfully binding a driver to the supplier device.
> >   *
> > - * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER
> > - * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and
> > - * will cause NULL to be returned upfront.
> > + * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER,
> > + * DL_FLAG_AUTOREMOVE_SUPPLIER, or DL_FLAG_AUTOPROBE_CONSUMER set in @flags at
> > + * the same time is invalid and will cause NULL to be returned upfront.
> > + * However, if a device link between the given @consumer and @supplier pair
> > + * exists already when this function is called for them, the existing link will
> > + * be returned regardless of its current type and status (the link's flags may
> > + * be modified then).  The caller of this function is then expected to treat
> > + * the link as though it has just been created, so (in particular) if
> > + * DL_FLAG_STATELESS was passed in @flags, the link needs to be released
> > + * explicitly when not needed any more (as stated above).
> >   *
> >   * A side effect of the link creation is re-ordering of dpm_list and the
> >   * devices_kset list by moving the consumer device and all devices depending
> > @@ -223,7 +274,8 @@ struct device_link *device_link_add(stru
> >             (flags & DL_FLAG_STATELESS &&
> >              flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
> >                       DL_FLAG_AUTOREMOVE_SUPPLIER |
> > -                     DL_FLAG_AUTOPROBE_CONSUMER)) ||
> > +                     DL_FLAG_AUTOPROBE_CONSUMER |
> > +                     DL_FLAG_MANAGED)) ||
> >             (flags & DL_FLAG_AUTOPROBE_CONSUMER &&
> >              flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
> >                       DL_FLAG_AUTOREMOVE_SUPPLIER)))
>
> If DL_FLAG_MANAGED is meant to be an internal flag (as in caller
> shouldn't use it), maybe it'll be better to just check for it in flags
> and reject it? Because looks like you are setting it anyway if
> STATELESS isn't set.

Well, to be honest, I was kind of divided here, because passing
MANAGED if STATELESS is not passed is technically not a bug and
returning NULL in that case seemed a bit awkward to me.

That said, the function doesn't reject flags with unused bits set
anyway and arguably it should, so I'll add that check and that'll
cover MANAGED too.

>
> > @@ -236,6 +288,9 @@ struct device_link *device_link_add(stru
> >                 }
> >         }
> >
> > +       if (!(flags & DL_FLAG_STATELESS))
> > +               flags |= DL_FLAG_MANAGED;
> > +
> >         device_links_write_lock();
> >         device_pm_lock();
> >
> > @@ -262,15 +317,6 @@ struct device_link *device_link_add(stru
> >                 if (link->consumer != consumer)
> >                         continue;
> >
> > -               /*
> > -                * Don't return a stateless link if the caller wants a stateful
> > -                * one and vice versa.
> > -                */
> > -               if (WARN_ON((flags & DL_FLAG_STATELESS) != (link->flags & DL_FLAG_STATELESS))) {
> > -                       link = NULL;
> > -                       goto out;
> > -               }
> > -
> >                 if (flags & DL_FLAG_PM_RUNTIME) {
> >                         if (!(link->flags & DL_FLAG_PM_RUNTIME)) {
> >                                 pm_runtime_new_link(consumer);
> > @@ -281,6 +327,7 @@ struct device_link *device_link_add(stru
> >                 }
> >
> >                 if (flags & DL_FLAG_STATELESS) {
> > +                       link->flags |= DL_FLAG_STATELESS;
> >                         kref_get(&link->kref);
> >                         goto out;
> >                 }
> > @@ -299,6 +346,11 @@ struct device_link *device_link_add(stru
> >                         link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER |
> >                                          DL_FLAG_AUTOREMOVE_SUPPLIER);
> >                 }
> > +               if (!(link->flags & DL_FLAG_MANAGED)) {
> > +                       kref_get(&link->kref);
> > +                       link->flags |= DL_FLAG_MANAGED;
> > +                       device_link_init_status(link, consumer, supplier);
> > +               }
> >                 goto out;
> >         }
> >
> > @@ -325,48 +377,10 @@ struct device_link *device_link_add(stru
> >         kref_init(&link->kref);
> >
> >         /* Determine the initial link state. */
> > -       if (flags & DL_FLAG_STATELESS) {
> > +       if (flags & DL_FLAG_STATELESS)
> >                 link->status = DL_STATE_NONE;
> > -       } else {
> > -               switch (supplier->links.status) {
> > -               case DL_DEV_PROBING:
> > -                       switch (consumer->links.status) {
> > -                       case DL_DEV_PROBING:
> > -                               /*
> > -                                * A consumer driver can create a link to a
> > -                                * supplier that has not completed its probing
> > -                                * yet as long as it knows that the supplier is
> > -                                * already functional (for example, it has just
> > -                                * acquired some resources from the supplier).
> > -                                */
> > -                               link->status = DL_STATE_CONSUMER_PROBE;
> > -                               break;
> > -                       default:
> > -                               link->status = DL_STATE_DORMANT;
> > -                               break;
> > -                       }
> > -                       break;
> > -               case DL_DEV_DRIVER_BOUND:
> > -                       switch (consumer->links.status) {
> > -                       case DL_DEV_PROBING:
> > -                               link->status = DL_STATE_CONSUMER_PROBE;
> > -                               break;
> > -                       case DL_DEV_DRIVER_BOUND:
> > -                               link->status = DL_STATE_ACTIVE;
> > -                               break;
> > -                       default:
> > -                               link->status = DL_STATE_AVAILABLE;
> > -                               break;
> > -                       }
> > -                       break;
> > -               case DL_DEV_UNBINDING:
> > -                       link->status = DL_STATE_SUPPLIER_UNBIND;
> > -                       break;
> > -               default:
> > -                       link->status = DL_STATE_DORMANT;
> > -                       break;
> > -               }
> > -       }
> > +       else
> > +               device_link_init_status(link, consumer, supplier);
> >
> >         /*
> >          * Some callers expect the link creation during consumer driver probe to
> > @@ -528,7 +542,7 @@ static void device_links_missing_supplie
> >   * mark the link as "consumer probe in progress" to make the supplier removal
> >   * wait for us to complete (or bad things may happen).
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
>
> Nitpick. It might be easier to read if you removed the double
> negative. So something like:
> Links without the DL_FLAG_MANAGED flag set are ignored.

Fair enough.

> >  int device_links_check_suppliers(struct device *dev)
> >  {
> > @@ -538,7 +552,7 @@ int device_links_check_suppliers(struct
> >         device_links_write_lock();
> >
> >         list_for_each_entry(link, &dev->links.suppliers, c_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 if (link->status != DL_STATE_AVAILABLE) {
> > @@ -563,7 +577,7 @@ int device_links_check_suppliers(struct
> >   *
> >   * Also change the status of @dev's links to suppliers to "active".
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
> >  void device_links_driver_bound(struct device *dev)
> >  {
> > @@ -572,7 +586,7 @@ void device_links_driver_bound(struct de
> >         device_links_write_lock();
> >
> >         list_for_each_entry(link, &dev->links.consumers, s_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 /*
> > @@ -593,7 +607,7 @@ void device_links_driver_bound(struct de
> >         }
> >
> >         list_for_each_entry(link, &dev->links.suppliers, c_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
> > @@ -605,6 +619,13 @@ void device_links_driver_bound(struct de
> >         device_links_write_unlock();
> >  }
> >
> > +static void device_link_drop_managed(struct device_link *link)
> > +{
> > +       link->flags &= ~DL_FLAG_MANAGED;
> > +       WRITE_ONCE(link->status, DL_STATE_NONE);
> > +       kref_put(&link->kref, __device_link_del);
> > +}
> > +
> >  /**
> >   * __device_links_no_driver - Update links of a device without a driver.
> >   * @dev: Device without a drvier.
> > @@ -615,18 +636,18 @@ void device_links_driver_bound(struct de
> >   * unless they already are in the "supplier unbind in progress" state in which
> >   * case they need not be updated.
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
> >  static void __device_links_no_driver(struct device *dev)
> >  {
> >         struct device_link *link, *ln;
> >
> >         list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER)
> > -                       __device_link_del(&link->kref);
> > +                       device_link_drop_managed(link);
> >                 else if (link->status == DL_STATE_CONSUMER_PROBE ||
> >                          link->status == DL_STATE_ACTIVE)
> >                         WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
> > @@ -643,7 +664,7 @@ static void __device_links_no_driver(str
> >   * %__device_links_no_driver() to update links to suppliers for it as
> >   * appropriate.
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
> >  void device_links_no_driver(struct device *dev)
> >  {
> > @@ -652,7 +673,7 @@ void device_links_no_driver(struct devic
> >         device_links_write_lock();
> >
> >         list_for_each_entry(link, &dev->links.consumers, s_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 /*
> > @@ -680,7 +701,7 @@ void device_links_no_driver(struct devic
> >   * invoke %__device_links_no_driver() to update links to suppliers for it as
> >   * appropriate.
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
> >  void device_links_driver_cleanup(struct device *dev)
> >  {
> > @@ -689,7 +710,7 @@ void device_links_driver_cleanup(struct
> >         device_links_write_lock();
> >
> >         list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER);
> > @@ -702,7 +723,7 @@ void device_links_driver_cleanup(struct
> >                  */
> >                 if (link->status == DL_STATE_SUPPLIER_UNBIND &&
> >                     link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
> > -                       __device_link_del(&link->kref);
> > +                       device_link_drop_managed(link);
> >
> >                 WRITE_ONCE(link->status, DL_STATE_DORMANT);
> >         }
> > @@ -724,7 +745,7 @@ void device_links_driver_cleanup(struct
> >   *
> >   * Return 'false' if there are no probing or active consumers.
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
> >  bool device_links_busy(struct device *dev)
> >  {
> > @@ -734,7 +755,7 @@ bool device_links_busy(struct device *de
> >         device_links_write_lock();
> >
> >         list_for_each_entry(link, &dev->links.consumers, s_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 if (link->status == DL_STATE_CONSUMER_PROBE
> > @@ -764,7 +785,7 @@ bool device_links_busy(struct device *de
> >   * driver to unbind and start over (the consumer will not re-probe as we have
> >   * changed the state of the link already).
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   */
> >  void device_links_unbind_consumers(struct device *dev)
> >  {
> > @@ -776,7 +797,7 @@ void device_links_unbind_consumers(struc
> >         list_for_each_entry(link, &dev->links.consumers, s_node) {
> >                 enum device_link_state status;
> >
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 status = link->status;
> > Index: linux-pm/drivers/base/power/runtime.c
> > ===================================================================
> > --- linux-pm.orig/drivers/base/power/runtime.c
> > +++ linux-pm/drivers/base/power/runtime.c
> > @@ -1624,7 +1624,7 @@ void pm_runtime_remove(struct device *de
> >   * runtime PM references to the device, drop the usage counter of the device
> >   * (as many times as needed).
> >   *
> > - * Links with the DL_FLAG_STATELESS flag set are ignored.
> > + * Links with the DL_FLAG_MANAGED flag unset are ignored.
> >   *
> >   * Since the device is guaranteed to be runtime-active at the point this is
> >   * called, nothing else needs to be done here.
> > @@ -1641,7 +1641,7 @@ void pm_runtime_clean_up_links(struct de
> >         idx = device_links_read_lock();
> >
> >         list_for_each_entry_rcu(link, &dev->links.consumers, s_node) {
> > -               if (link->flags & DL_FLAG_STATELESS)
> > +               if (!(link->flags & DL_FLAG_MANAGED))
> >                         continue;
> >
> >                 while (refcount_dec_not_one(&link->rpm_active))
> > Index: linux-pm/include/linux/device.h
> > ===================================================================
> > --- linux-pm.orig/include/linux/device.h
> > +++ linux-pm/include/linux/device.h
> > @@ -829,12 +829,13 @@ enum device_link_state {
> >  /*
> >   * Device link flags.
> >   *
> > - * STATELESS: The core won't track the presence of supplier/consumer drivers.
> > + * STATELESS: The core will not remove this link automatically.
> >   * AUTOREMOVE_CONSUMER: Remove the link automatically on consumer driver unbind.
> >   * PM_RUNTIME: If set, the runtime PM framework will use this link.
> >   * RPM_ACTIVE: Run pm_runtime_get_sync() on the supplier during link creation.
> >   * AUTOREMOVE_SUPPLIER: Remove the link automatically on supplier driver unbind.
> >   * AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
> > + * MANAGED: The core tracks presence of supplier/consumer drivers (internal).
> >   */
> >  #define DL_FLAG_STATELESS              BIT(0)
> >  #define DL_FLAG_AUTOREMOVE_CONSUMER    BIT(1)
> > @@ -842,6 +843,7 @@ enum device_link_state {
> >  #define DL_FLAG_RPM_ACTIVE             BIT(3)
> >  #define DL_FLAG_AUTOREMOVE_SUPPLIER    BIT(4)
> >  #define DL_FLAG_AUTOPROBE_CONSUMER     BIT(5)
> > +#define DL_FLAG_MANAGED                        BIT(6)
> >
> >  /**
> >   * struct device_link - Device link representation.
> > Index: linux-pm/Documentation/driver-api/device_link.rst
> > ===================================================================
> > --- linux-pm.orig/Documentation/driver-api/device_link.rst
> > +++ linux-pm/Documentation/driver-api/device_link.rst
> > @@ -78,8 +78,8 @@ typically deleted in its ``->remove`` ca
> >  driver is compiled as a module, the device link is added on module load and
> >  orderly deleted on unload.  The same restrictions that apply to device link
> >  addition (e.g. exclusion of a parallel suspend/resume transition) apply equally
> > -to deletion.  Device links with ``DL_FLAG_STATELESS`` unset (i.e. managed
> > -device links) are deleted automatically by the driver core.
> > +to deletion.  Device links managed by the driver core are deleted automatically
> > +by it.
> >
> >  Several flags may be specified on device link addition, two of which
> >  have already been mentioned above:  ``DL_FLAG_STATELESS`` to express that no
> >
>
> Other than those 2 minor comments, this looks good to me.
>
> Reviewed-by: Saravana Kannan <saravanak@google.com>

Thanks!

^ permalink raw reply

* Re: [PATCH] opp: Return genpd virtual devices from dev_pm_opp_attach_genpd()
From: Rajendra Nayak @ 2019-07-11  9:39 UTC (permalink / raw)
  To: Viresh Kumar, Viresh Kumar, Nishanth Menon, Stephen Boyd,
	Rafael J. Wysocki
  Cc: linux-pm, Vincent Guittot, linux-kernel
In-Reply-To: <027985ce35873cd218298302a1408da06d48458b.1562565567.git.viresh.kumar@linaro.org>


On 7/8/2019 11:30 AM, Viresh Kumar wrote:
> The cpufreq drivers don't need to do runtime PM operations on the
> virtual devices returned by dev_pm_domain_attach_by_name() and so the
> virtual devices weren't shared with the callers of
> dev_pm_opp_attach_genpd() earlier.
> 
> But the IO device drivers would want to do that. This patch updates the
> prototype of dev_pm_opp_attach_genpd() to accept another argument to
> return the pointer to the array of genpd virtual devices.
> 
> Reported-by: Rajendra Nayak <rnayak@codeaurora.org>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
> @Rajendra: Can you please test this one ? I have only compile tested it.

Sorry for the delay, I seem to have completely missed this patch.
I just gave this a try and here are some observations,

I have a case where I have one device with 2 power domains, one of them
is scale-able (supports perf state) and the other one supports only being
turned on and off.

1. In the driver I now need to use dev_pm_domain_attach_by_name/id to attach the
power domain which supports only on/off and then use dev_pm_opp_attach_genpd()
for the one which supports perf states.

2. My OPP table has only 1 required_opps, so the required_opp_count for the OPP table is 1.
Now if my device tree has my scale-able powerdomain at index 1 (it works if its at index 0)
then I end up with this error

[    2.858628] ufshcd-qcom 1d84000.ufshc: Index can't be greater than required-opp-count - 1, rpmh_pd (1 : 1)

so it looks like a lot of the OPP core today just assumes that if a device has multiple power domains,
all of them are scale-able which isn't necessarily true.

> 
>   drivers/opp/core.c     | 5 ++++-
>   include/linux/pm_opp.h | 4 ++--
>   2 files changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/opp/core.c b/drivers/opp/core.c
> index 2958cc7bbb58..07b6f1187b3b 100644
> --- a/drivers/opp/core.c
> +++ b/drivers/opp/core.c
> @@ -1775,6 +1775,7 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
>    * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
>    * @dev: Consumer device for which the genpd is getting attached.
>    * @names: Null terminated array of pointers containing names of genpd to attach.
> + * @virt_devs: Pointer to return the array of virtual devices.
>    *
>    * Multiple generic power domains for a device are supported with the help of
>    * virtual genpd devices, which are created for each consumer device - genpd
> @@ -1789,7 +1790,8 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
>    * This helper needs to be called once with a list of all genpd to attach.
>    * Otherwise the original device structure will be used instead by the OPP core.
>    */
> -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
> +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev,
> +		const char **names, struct device ***virt_devs)
>   {
>   	struct opp_table *opp_table;
>   	struct device *virt_dev;
> @@ -1850,6 +1852,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names
>   		name++;
>   	}
>   
> +	*virt_devs = opp_table->genpd_virt_devs;
>   	mutex_unlock(&opp_table->genpd_virt_dev_lock);
>   
>   	return opp_table;
> diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
> index be570761b77a..7c2fe2952f40 100644
> --- a/include/linux/pm_opp.h
> +++ b/include/linux/pm_opp.h
> @@ -131,7 +131,7 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
>   void dev_pm_opp_put_clkname(struct opp_table *opp_table);
>   struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
>   void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
> -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
> +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs);
>   void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
>   int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
>   int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
> @@ -295,7 +295,7 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
>   
>   static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
>   
> -static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
> +static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs)
>   {
>   	return ERR_PTR(-ENOTSUPP);
>   }
> 

-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply

* Re: Coccinelle: Checking of_node_put() calls with SmPL
From: Markus Elfring @ 2019-07-11  9:33 UTC (permalink / raw)
  To: Julia Lawall, Wen Yang, kernel-janitors
  Cc: Rafael J. Wysocki, Daniel Lezcano, Benjamin Herrenschmidt,
	Cheng Shengyu, Kumar Gala, Michael Ellerman, Paul Mackerras,
	Scott Wood, Xue Zhihong, Yi Wang, linux-kernel, linuxppc-dev,
	linux-pm
In-Reply-To: <alpine.DEB.2.20.1907110845551.3626@hadrien>

> In my experience, when you start looking at these of_node_put things,
> all sorts of strange things appear...

How much will this situation influence the achievement of further improvements
also for your software?

Regards,
Markus

^ permalink raw reply

* Re: [PATCH v2 1/6] ARM: tegra: Remove cpuidle drivers
From: Jon Hunter @ 2019-07-11  9:26 UTC (permalink / raw)
  To: Dmitry Osipenko, Thierry Reding, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-2-digetx@gmail.com>


On 11/07/2019 04:13, Dmitry Osipenko wrote:
> Remove the old drivers to replace them cleanly with a new one later on.
> 
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  arch/arm/mach-tegra/Makefile           |  13 --
>  arch/arm/mach-tegra/cpuidle-tegra114.c |  89 -----------
>  arch/arm/mach-tegra/cpuidle-tegra20.c  | 212 -------------------------
>  arch/arm/mach-tegra/cpuidle-tegra30.c  | 132 ---------------
>  arch/arm/mach-tegra/cpuidle.c          |  50 ------
>  arch/arm/mach-tegra/cpuidle.h          |  21 ---
>  arch/arm/mach-tegra/irq.c              |  18 ---
>  arch/arm/mach-tegra/irq.h              |  11 --
>  arch/arm/mach-tegra/pm.c               |   7 -
>  arch/arm/mach-tegra/pm.h               |   1 -
>  arch/arm/mach-tegra/reset-handler.S    |  11 --
>  arch/arm/mach-tegra/reset.h            |   9 +-
>  arch/arm/mach-tegra/sleep-tegra20.S    | 190 +---------------------
>  arch/arm/mach-tegra/sleep.h            |  12 --
>  arch/arm/mach-tegra/tegra.c            |   3 -
>  drivers/soc/tegra/Kconfig              |   1 -
>  include/soc/tegra/cpuidle.h            |   4 -
>  17 files changed, 5 insertions(+), 779 deletions(-)
>  delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra114.c
>  delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra20.c
>  delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra30.c
>  delete mode 100644 arch/arm/mach-tegra/cpuidle.c
>  delete mode 100644 arch/arm/mach-tegra/cpuidle.h
>  delete mode 100644 arch/arm/mach-tegra/irq.h

By removing all the above, it is really hard to review the diff. Is
there any way you could first consolidate the cpuidle drivers into say
the existing arch/arm/mach-tegra/cpuidle-tegra20.c and then move to
drivers/cpuidle?

Jon

-- 
nvpublic

^ permalink raw reply

* Re: Coccinelle: Checking of_node_put() calls with SmPL
From: Markus Elfring @ 2019-07-11  9:04 UTC (permalink / raw)
  To: Wen Yang, linux-pm, kernel-janitors
  Cc: Julia Lawall, Rafael J. Wysocki, Daniel Lezcano,
	Benjamin Herrenschmidt, Cheng Shengyu, Kumar Gala,
	Michael Ellerman, Paul Mackerras, Scott Wood, Xue Zhihong,
	Yi Wang, linux-kernel, linuxppc-dev
In-Reply-To: <201907111435459627761@zte.com.cn>

> 180                         break;                         --->  Jump out of the loop without releasing it

The device node reference is released behind this for loop.


> 183                 if (!of_device_is_available(state_node)) {
> 184                         of_node_put(state_node);

This function call was added by the commit “cpuidle: dt: Add missing 'of_node_put()'”
on 2017-06-12.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/cpuidle/dt_idle_states.c?id=b2cdd8e1b54849477a32d820acc2e87828a38f3d


> 185                         continue;                    --->  Release the object references within a loop

I became curious on the applicability of an other coding style
(for a software refactoring) at this place.
How do you think about to achieve the same effect by using a goto statement
instead of two statements in such an if branch?


> 208                 of_node_put(state_node);  -->  Release the object references within a loop
> 209         }
> 210
> 211         of_node_put(state_node);       -->    There may be double free here.

This information points a recurring challenge out for safe source code analysis.
How would you like to exclude the detection of false positives finally?


> This code pattern is very interesting

Thanks that you think also in this direction.


> and the coccinelle software should also recognize this pattern.

There are some open issues to consider for available analysis tools.
How will corresponding details be clarified then?

Regards,
Markus

^ permalink raw reply

* Re: Coccinelle: Checking of_node_put() calls with SmPL
From: Julia Lawall @ 2019-07-11  6:46 UTC (permalink / raw)
  To: wen.yang99
  Cc: Markus.Elfring, rjw, daniel.lezcano, linux-pm, kernel-janitors,
	benh, cheng.shengyu, galak, mpe, paulus, oss, xue.zhihong,
	wang.yi59, linux-kernel, linuxppc-dev
In-Reply-To: <201907111435459627761@zte.com.cn>

[-- Attachment #1: Type: text/plain, Size: 1808 bytes --]



On Thu, 11 Jul 2019, wen.yang99@zte.com.cn wrote:

> > > we developed a coccinelle script to detect such problems.
> >
> > Would you find the implementation of the function “dt_init_idle_driver”
> > suspicious according to discussed source code search patterns?
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/cpuidle/dt_idle_states.c?id=e9a83bd2322035ed9d7dcf35753d3f984d76c6a5#n208
> > https://elixir.bootlin.com/linux/v5.2/source/drivers/cpuidle/dt_idle_states.c#L208
> >
> >
> > > This script is still being improved.
> >
> > Will corresponding software development challenges become more interesting?
>
> Hello Markus,
> This is the simplified code pattern for it:
>
> 172         for (i = 0; ; i++) {
> 173                 state_node = of_parse_phandle(...);     ---> Obtain here
> ...
> 177                 match_id = of_match_node(matches, state_node);
> 178                 if (!match_id) {
> 179                         err = -ENODEV;
> 180                         break;                         --->  Jump out of the loop without releasing it
> 181                 }
> 182
> 183                 if (!of_device_is_available(state_node)) {
> 184                         of_node_put(state_node);
> 185                         continue;                    --->  Release the object references within a loop
> 186                 }
> ...
> 208                 of_node_put(state_node);  -->  Release the object references within a loop
> 209         }
> 210
> 211         of_node_put(state_node);       -->    There may be double free here.
>
> This code pattern is very interesting and the coccinelle software should also recognize this pattern.

In my experience, when you start looking at these of_node_put things, all
sorts of strange things appear...

julia

^ permalink raw reply

* Re: [PATCH 1/3] opp: core: add regulators enable and disable
From: Viresh Kumar @ 2019-07-11  6:22 UTC (permalink / raw)
  To: k.konieczny
  Cc: Bartlomiej Zolnierkiewicz, Marek Szyprowski, Chanwoo Choi,
	Krzysztof Kozlowski, Kukjin Kim, Kyungmin Park, Mark Rutland,
	MyungJoo Ham, Nishanth Menon, Rob Herring, Stephen Boyd,
	Viresh Kumar, devicetree, linux-arm-kernel, linux-kernel,
	linux-pm, linux-samsung-soc
In-Reply-To: <20190708141140.24379-2-k.konieczny@partner.samsung.com>

On 08-07-19, 16:11, k.konieczny@partner.samsung.com wrote:
> From: Kamil Konieczny <k.konieczny@partner.samsung.com>
> 
> Add enable regulators to dev_pm_opp_set_regulators() and disable
> regulators to dev_pm_opp_put_regulators(). This prepares for
> converting exynos-bus devfreq driver to use dev_pm_opp_set_rate().
> 
> Signed-off-by: Kamil Konieczny <k.konieczny@partner.samsung.com>
> ---
>  drivers/opp/core.c | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/drivers/opp/core.c b/drivers/opp/core.c
> index 0e7703fe733f..947cac452854 100644
> --- a/drivers/opp/core.c
> +++ b/drivers/opp/core.c
> @@ -1580,8 +1580,19 @@ struct opp_table *dev_pm_opp_set_regulators(struct device *dev,
>  	if (ret)
>  		goto free_regulators;
>  
> +	for (i = 0; i < opp_table->regulator_count; i++) {
> +		ret = regulator_enable(opp_table->regulators[i]);
> +		if (ret < 0)
> +			goto disable;
> +	}

What about doing this in the same loop of regulator_get_optional() ?

> +
>  	return opp_table;
>  
> +disable:
> +	while (i != 0)
> +		regulator_disable(opp_table->regulators[--i]);
> +
> +	i = opp_table->regulator_count;

You also need to call _free_set_opp_data() here.

>  free_regulators:
>  	while (i != 0)
>  		regulator_put(opp_table->regulators[--i]);
> @@ -1609,6 +1620,8 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
>  
>  	/* Make sure there are no concurrent readers while updating opp_table */
>  	WARN_ON(!list_empty(&opp_table->opp_list));

Preserve the blank line here.

> +	for (i = opp_table->regulator_count - 1; i >= 0; i--)
> +		regulator_disable(opp_table->regulators[i]);
>  
>  	for (i = opp_table->regulator_count - 1; i >= 0; i--)
>  		regulator_put(opp_table->regulators[i]);

Only single loop should be sufficient for this.

> -- 
> 2.22.0

-- 
viresh

^ permalink raw reply

* Re: [PATCHv3 6/6] drivers/cpufreq: Add a CPUFreq driver for AMD processors (Fam17h and later)
From: Viresh Kumar @ 2019-07-11  6:12 UTC (permalink / raw)
  To: Natarajan, Janakarajan
  Cc: linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-pm@vger.kernel.org, devel@acpica.org, Rafael J . Wysocki,
	Len Brown, Robert Moore, Erik Schmauss, Ghannam, Yazen
In-Reply-To: <e48c6b836f996a16472c777612f1e3343c542077.1562781484.git.Janakarajan.Natarajan@amd.com>

On 10-07-19, 18:37, Natarajan, Janakarajan wrote:
> diff --git a/drivers/cpufreq/amd-cpufreq.c b/drivers/cpufreq/amd-cpufreq.c
> +#define pr_fmt(fmt)	"AMD Cpufreq: " fmt
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/cpu.h>
> +#include <linux/vmalloc.h>
> +#include <linux/cpufreq.h>
> +#include <linux/acpi.h>
> +#include <linux/delay.h>

Please keep them in alphabetical order.

> +
> +#include <asm/unaligned.h>
> +
> +#include <acpi/cppc_acpi.h>
> +
> +struct amd_desc {
> +	int cpu_id;
> +	struct cppc_ctrls ctrls;
> +	struct kobject kobj;
> +};
> +
> +struct amd_desc **all_cpu_data;
> +
> +static unsigned int cppc_enable;
> +module_param(cppc_enable, uint, 0644);
> +MODULE_PARM_DESC(cppc_enable,
> +		 "1 - enable AMD CpuFreq, create CPPC sysfs entries.");
> +
> +#define to_amd_desc(a) container_of(a, struct amd_desc, kobj)
> +
> +#define show_func(access_fn, struct_name, member_name)			\
> +	static ssize_t show_##member_name(struct kobject *kobj,		\
> +					  struct kobj_attribute *attr,	\
> +					  char *buf)			\
> +	{								\
> +		struct amd_desc *desc = to_amd_desc(kobj);		\
> +		struct struct_name st_name = {0};			\
> +		int ret;						\
> +									\
> +		ret = access_fn(desc->cpu_id, &st_name);		\
> +		if (ret)						\
> +			return ret;					\
> +									\
> +		return scnprintf(buf, PAGE_SIZE, "%llu\n",		\
> +				 (u64)st_name.member_name);		\
> +	}								\
> +
> +#define store_func(struct_name, member_name, reg_idx)			\
> +	static ssize_t store_##member_name(struct kobject *kobj,	\
> +					   struct kobj_attribute *attr,	\
> +					   const char *buf, size_t count)\
> +	{								\
> +		struct amd_desc *desc = to_amd_desc(kobj);		\
> +		struct struct_name st_name = {0};			\
> +		u32 val;						\
> +		int ret;						\
> +									\
> +		ret = kstrtou32(buf, 0, &val);				\
> +		if (ret)						\
> +			return ret;					\
> +									\
> +		st_name.member_name = val;				\
> +									\
> +		ret = cppc_set_reg(desc->cpu_id, &st_name, reg_idx);	\
> +		if (ret)						\
> +			return ret;					\
> +									\
> +		return count;						\
> +	}								\
> +
> +#define define_one_rw(struct_name, access_fn, member_name, reg_idx)	\
> +	show_func(access_fn, struct_name, member_name)			\
> +	store_func(struct_name, member_name, reg_idx)			\
> +	define_one_global_rw(member_name)
> +
> +define_one_rw(cppc_ctrls, cppc_get_ctrls, enable, ENABLE);
> +define_one_rw(cppc_ctrls, cppc_get_ctrls, max_perf, MAX_PERF);
> +define_one_rw(cppc_ctrls, cppc_get_ctrls, min_perf, MIN_PERF);
> +define_one_rw(cppc_ctrls, cppc_get_ctrls, desired_perf, DESIRED_PERF);
> +define_one_rw(cppc_ctrls, cppc_get_ctrls, auto_sel_enable, AUTO_SEL_ENABLE);
> +
> +static struct attribute *amd_cpufreq_attributes[] = {
> +	&enable.attr,
> +	&max_perf.attr,
> +	&min_perf.attr,
> +	&desired_perf.attr,
> +	&auto_sel_enable.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group amd_cpufreq_attr_group = {
> +	.attrs = amd_cpufreq_attributes,
> +};
> +
> +static struct kobj_type amd_cpufreq_type = {
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_attrs = amd_cpufreq_attributes,
> +};
> +
> +static int amd_cpufreq_cpu_init(struct cpufreq_policy *policy)
> +{
> +	return 0;
> +}
> +
> +static int amd_cpufreq_cpu_exit(struct cpufreq_policy *policy)
> +{
> +	return 0;
> +}
> +
> +static int amd_cpufreq_cpu_verify(struct cpufreq_policy *policy)
> +{
> +	return 0;
> +}
> +
> +static int amd_cpufreq_cpu_target_index(struct cpufreq_policy *policy,
> +					unsigned int index)
> +{
> +	return 0;
> +}

All empty helpers ? There is nothing you need to do ?

> +
> +static struct cpufreq_driver amd_cpufreq_driver = {
> +	.name = "amd_cpufreq",
> +	.init = amd_cpufreq_cpu_init,
> +	.exit = amd_cpufreq_cpu_exit,
> +	.verify = amd_cpufreq_cpu_verify,
> +	.target_index = amd_cpufreq_cpu_target_index,
> +};
> +
> +static void amd_cpufreq_sysfs_delete_params(void)
> +{
> +	int i;
> +
> +	for_each_possible_cpu(i) {
> +		if (all_cpu_data[i]) {
> +			kobject_del(&all_cpu_data[i]->kobj);

Shouldn't you use kobject_put() instead of this ?

> +			kfree(all_cpu_data[i]);
> +		}
> +	}
> +
> +	kfree(all_cpu_data);
> +}
> +
> +static int __init amd_cpufreq_sysfs_expose_params(void)
> +{
> +	struct device *cpu_dev;
> +	int i, ret;
> +
> +	all_cpu_data = kcalloc(num_possible_cpus(), sizeof(void *),
> +			       GFP_KERNEL);
> +
> +	if (!all_cpu_data)
> +		return -ENOMEM;
> +
> +	for_each_possible_cpu(i) {
> +		all_cpu_data[i] = kzalloc(sizeof(struct amd_desc), GFP_KERNEL);
> +		if (!all_cpu_data[i]) {
> +			ret = -ENOMEM;
> +			goto free;
> +		}
> +
> +		all_cpu_data[i]->cpu_id = i;
> +		cpu_dev = get_cpu_device(i);
> +		ret = kobject_init_and_add(&all_cpu_data[i]->kobj, &amd_cpufreq_type,
> +					   &cpu_dev->kobj, "amd_cpufreq");
> +		if (ret)
> +			goto free;
> +	}
> +
> +	return 0;
> +free:
> +	amd_cpufreq_sysfs_delete_params();
> +	return ret;
> +}

Instead of doing this once for all CPUs, it would be better to do it
every time the ->init() callback of the driver gets called. If you
have one cpufreq policy for each CPU (i.e. no CPUs share clock lines),
then the init() callback will get called once for each CPU.

> +
> +static int __init amd_cpufreq_init(void)
> +{
> +	int ret = 0;
> +
> +	/*
> +	 * Use only if:
> +	 * - AMD,
> +	 * - Family 17h (or) newer and,
> +	 * - Explicitly enabled
> +	 */
> +	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD ||
> +	    boot_cpu_data.x86 < 0x17 || !cppc_enable)
> +		return -ENODEV;
> +
> +	ret = cpufreq_register_driver(&amd_cpufreq_driver);
> +	if (ret) {
> +		pr_info("Failed to register driver\n");
> +		goto out;
> +	}
> +
> +	ret = amd_cpufreq_sysfs_expose_params();
> +	if (ret) {
> +		pr_info("Could not create sysfs entries\n");
> +		cpufreq_unregister_driver(&amd_cpufreq_driver);
> +		goto out;
> +	}
> +
> +	pr_info("Using amd-cpufreq driver\n");
> +	return ret;
> +
> +out:
> +	return ret;
> +}
> +
> +static void __exit amd_cpufreq_exit(void)
> +{
> +	amd_cpufreq_sysfs_delete_params();
> +	cpufreq_unregister_driver(&amd_cpufreq_driver);
> +}
> +
> +static const struct acpi_device_id amd_acpi_ids[] __used = {
> +	{ACPI_PROCESSOR_DEVICE_HID, },
> +	{}
> +};
> +
> +device_initcall(amd_cpufreq_init);
> +module_exit(amd_cpufreq_exit);
> +MODULE_DEVICE_TABLE(acpi, amd_acpi_ids);

All three should be placed directly below the struct/function they
represent without any blank lines in between. As suggested in
kernel documentation.

> +
> +MODULE_AUTHOR("Janakarajan Natarajan");
> +MODULE_DESCRIPTION("AMD CPUFreq driver based on ACPI CPPC v6.1 spec");
> +MODULE_LICENSE("GPL");

Should this be "GPL v2" ?

> -- 
> 2.17.1

-- 
viresh

^ permalink raw reply

* Re: [PATCH V34 10/29] hibernate: Disable when the kernel is locked down
From: joeyli @ 2019-07-11  4:11 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Pavel Machek, Matthew Garrett, jmorris, linux-security-module,
	linux-kernel, linux-api, Josh Boyer, David Howells,
	Matthew Garrett, rjw, linux-pm
In-Reply-To: <nycvar.YFH.7.76.1906241520500.27227@cbobk.fhfr.pm>

Hi experts,

On Mon, Jun 24, 2019 at 03:21:23PM +0200, Jiri Kosina wrote:
> On Sat, 22 Jun 2019, Pavel Machek wrote:
> 
> > > There is currently no way to verify the resume image when returning
> > > from hibernate.  This might compromise the signed modules trust model,
> > > so until we can work with signed hibernate images we disable it when the
> > > kernel is locked down.
> > 
> > I keep getting these...
> > 
> > IIRC suse has patches to verify the images.
> 
> Yeah, Joey Lee is taking care of those. CCing.
>

The last time that I sent for hibernation encryption and authentication is
here:
    https://lkml.org/lkml/2019/1/3/281

It needs some big changes after review:
 - Simplify the design: remove keyring dependency and trampoline.
 - Encrypted whole snapshot image instead of only data pages.
 - Using TPM:
        - Direct use TPM API in hibernation instead of keyring
        - Localities (suggested by James Bottomley)

I am still finding enough time to implement those changes, especial TPM
parts.

Thanks
Joey Lee

^ permalink raw reply

* [PATCH v2 0/6] Consolidate and improve NVIDIA Tegra CPUIDLE driver(s)
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel

Hello,

I was spending quite some time recently trying to hunt down CPU-suspend
bug on Tegra30 SoC and in the end it was nailed. During that time I
realized that the CPU Idle drivers could get some polish and gain new
features, thus that's what this series does:

  1. Unifies Tegra20/30/114 drivers into a single driver and moves it out
     into common drivers/cpuidle/ directory.

  2. Enables CPU cluster power-down idling state on Tegra30.

In the end there is a quite nice clean up of the Tegra CPUIDLE driver(s)
and of the Tegra's arch code in general. Please review, thanks!

Changelog:

v2: - Added patches to enable the new cpuidle driver in the defconfigs:

        ARM: multi_v7_defconfig: Enable Tegra cpuidle driver
        ARM: tegra: Enable Tegra cpuidle driver in tegra_defconfig

    - Dropped patches that removed CPUIDLE_FLAG_TIMER_STOP from the idling
      states because that flag actually doesn't have any negative effects,
      but still is correct for the case of a local CPU timer on older Tegra
      SoCs:

        cpuidle: tegra: Remove CPUIDLE_FLAG_TIMER_STOP from Tegra114/124 idle-state
        cpuidle: tegra: Remove CPUIDLE_FLAG_TIMER_STOP from all states

    - The "Add unified driver for NVIDIA Tegra SoCs" patch got more polish.
      Tegra30 and Terga114 states are now squashed into a single common C7
      state (following Parker TRM terminology, see 17.2.2.2 Power Management
      States), more comments added, etc minor changes.

Dmitry Osipenko (6):
  ARM: tegra: Remove cpuidle drivers
  ARM: tegra: Expose functions required for cpuidle driver
  cpuidle: Add unified driver for NVIDIA Tegra SoCs
  cpuidle: tegra: Support CPU cluster power-down on Tegra30
  ARM: multi_v7_defconfig: Enable Tegra cpuidle driver
  ARM: tegra: Enable Tegra cpuidle driver in tegra_defconfig

 arch/arm/configs/multi_v7_defconfig    |   1 +
 arch/arm/configs/tegra_defconfig       |   1 +
 arch/arm/mach-tegra/Makefile           |  15 +-
 arch/arm/mach-tegra/cpuidle-tegra114.c |  89 --------
 arch/arm/mach-tegra/cpuidle-tegra20.c  | 212 -----------------
 arch/arm/mach-tegra/cpuidle-tegra30.c  | 132 -----------
 arch/arm/mach-tegra/cpuidle.c          |  50 ----
 arch/arm/mach-tegra/cpuidle.h          |  21 --
 arch/arm/mach-tegra/irq.c              |  18 --
 arch/arm/mach-tegra/irq.h              |  11 -
 arch/arm/mach-tegra/platsmp.c          |   2 -
 arch/arm/mach-tegra/pm.c               |  27 +--
 arch/arm/mach-tegra/pm.h               |   4 -
 arch/arm/mach-tegra/reset-handler.S    |  11 -
 arch/arm/mach-tegra/reset.h            |   9 +-
 arch/arm/mach-tegra/sleep-tegra20.S    | 190 +---------------
 arch/arm/mach-tegra/sleep.h            |  13 --
 arch/arm/mach-tegra/tegra.c            |   7 +-
 drivers/cpuidle/Kconfig.arm            |   8 +
 drivers/cpuidle/Makefile               |   1 +
 drivers/cpuidle/cpuidle-tegra.c        | 303 +++++++++++++++++++++++++
 drivers/soc/tegra/Kconfig              |   1 -
 include/linux/clk/tegra.h              |  13 ++
 include/soc/tegra/cpuidle.h            |   2 +-
 include/soc/tegra/pm.h                 |  28 +++
 25 files changed, 375 insertions(+), 794 deletions(-)
 delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra114.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra20.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra30.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle.h
 delete mode 100644 arch/arm/mach-tegra/irq.h
 create mode 100644 drivers/cpuidle/cpuidle-tegra.c

-- 
2.22.0


^ permalink raw reply

* [PATCH v2 1/6] ARM: tegra: Remove cpuidle drivers
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-1-digetx@gmail.com>

Remove the old drivers to replace them cleanly with a new one later on.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/mach-tegra/Makefile           |  13 --
 arch/arm/mach-tegra/cpuidle-tegra114.c |  89 -----------
 arch/arm/mach-tegra/cpuidle-tegra20.c  | 212 -------------------------
 arch/arm/mach-tegra/cpuidle-tegra30.c  | 132 ---------------
 arch/arm/mach-tegra/cpuidle.c          |  50 ------
 arch/arm/mach-tegra/cpuidle.h          |  21 ---
 arch/arm/mach-tegra/irq.c              |  18 ---
 arch/arm/mach-tegra/irq.h              |  11 --
 arch/arm/mach-tegra/pm.c               |   7 -
 arch/arm/mach-tegra/pm.h               |   1 -
 arch/arm/mach-tegra/reset-handler.S    |  11 --
 arch/arm/mach-tegra/reset.h            |   9 +-
 arch/arm/mach-tegra/sleep-tegra20.S    | 190 +---------------------
 arch/arm/mach-tegra/sleep.h            |  12 --
 arch/arm/mach-tegra/tegra.c            |   3 -
 drivers/soc/tegra/Kconfig              |   1 -
 include/soc/tegra/cpuidle.h            |   4 -
 17 files changed, 5 insertions(+), 779 deletions(-)
 delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra114.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra20.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra30.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle.c
 delete mode 100644 arch/arm/mach-tegra/cpuidle.h
 delete mode 100644 arch/arm/mach-tegra/irq.h

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 6c1dff2eccc2..5d93a0b36866 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -8,29 +8,16 @@ obj-y					+= reset.o
 obj-y					+= reset-handler.o
 obj-y					+= sleep.o
 obj-y					+= tegra.o
-obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= sleep-tegra20.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pm-tegra20.o
-ifeq ($(CONFIG_CPU_IDLE),y)
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= cpuidle-tegra20.o
-endif
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= pm-tegra30.o
-ifeq ($(CONFIG_CPU_IDLE),y)
-obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= cpuidle-tegra30.o
-endif
 obj-$(CONFIG_SMP)			+= platsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
 
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= pm-tegra30.o
-ifeq ($(CONFIG_CPU_IDLE),y)
-obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= cpuidle-tegra114.o
-endif
 obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= pm-tegra30.o
-ifeq ($(CONFIG_CPU_IDLE),y)
-obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= cpuidle-tegra114.o
-endif
 
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= board-paz00.o
diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c
deleted file mode 100644
index 5118f777fd66..000000000000
--- a/arch/arm/mach-tegra/cpuidle-tegra114.c
+++ /dev/null
@@ -1,89 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
- */
-
-#include <asm/firmware.h>
-#include <linux/tick.h>
-#include <linux/cpuidle.h>
-#include <linux/cpu_pm.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <linux/firmware/trusted_foundations.h>
-
-#include <asm/cpuidle.h>
-#include <asm/smp_plat.h>
-#include <asm/suspend.h>
-#include <asm/psci.h>
-
-#include "cpuidle.h"
-#include "pm.h"
-#include "sleep.h"
-
-#ifdef CONFIG_PM_SLEEP
-#define TEGRA114_MAX_STATES 2
-#else
-#define TEGRA114_MAX_STATES 1
-#endif
-
-#ifdef CONFIG_PM_SLEEP
-static int tegra114_idle_power_down(struct cpuidle_device *dev,
-				    struct cpuidle_driver *drv,
-				    int index)
-{
-	local_fiq_disable();
-
-	tegra_set_cpu_in_lp2();
-	cpu_pm_enter();
-
-	call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2);
-
-	/* Do suspend by ourselves if the firmware does not implement it */
-	if (call_firmware_op(do_idle, 0) == -ENOSYS)
-		cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
-
-	cpu_pm_exit();
-	tegra_clear_cpu_in_lp2();
-
-	local_fiq_enable();
-
-	return index;
-}
-
-static void tegra114_idle_enter_s2idle(struct cpuidle_device *dev,
-				       struct cpuidle_driver *drv,
-				       int index)
-{
-       tegra114_idle_power_down(dev, drv, index);
-}
-#endif
-
-static struct cpuidle_driver tegra_idle_driver = {
-	.name = "tegra_idle",
-	.owner = THIS_MODULE,
-	.state_count = TEGRA114_MAX_STATES,
-	.states = {
-		[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
-#ifdef CONFIG_PM_SLEEP
-		[1] = {
-			.enter			= tegra114_idle_power_down,
-			.enter_s2idle		= tegra114_idle_enter_s2idle,
-			.exit_latency		= 500,
-			.target_residency	= 1000,
-			.flags			= CPUIDLE_FLAG_TIMER_STOP,
-			.power_usage		= 0,
-			.name			= "powered-down",
-			.desc			= "CPU power gated",
-		},
-#endif
-	},
-};
-
-int __init tegra114_cpuidle_init(void)
-{
-	if (!psci_smp_available())
-		return cpuidle_register(&tegra_idle_driver, NULL);
-
-	return 0;
-}
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
deleted file mode 100644
index 2447427cb4a8..000000000000
--- a/arch/arm/mach-tegra/cpuidle-tegra20.c
+++ /dev/null
@@ -1,212 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * CPU idle driver for Tegra CPUs
- *
- * Copyright (c) 2010-2012, NVIDIA Corporation.
- * Copyright (c) 2011 Google, Inc.
- * Author: Colin Cross <ccross@android.com>
- *         Gary King <gking@nvidia.com>
- *
- * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
- */
-
-#include <linux/clk/tegra.h>
-#include <linux/tick.h>
-#include <linux/cpuidle.h>
-#include <linux/cpu_pm.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <soc/tegra/flowctrl.h>
-
-#include <asm/cpuidle.h>
-#include <asm/smp_plat.h>
-#include <asm/suspend.h>
-
-#include "cpuidle.h"
-#include "iomap.h"
-#include "irq.h"
-#include "pm.h"
-#include "reset.h"
-#include "sleep.h"
-
-#ifdef CONFIG_PM_SLEEP
-static bool abort_flag;
-static atomic_t abort_barrier;
-static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
-				    struct cpuidle_driver *drv,
-				    int index);
-#define TEGRA20_MAX_STATES 2
-#else
-#define TEGRA20_MAX_STATES 1
-#endif
-
-static struct cpuidle_driver tegra_idle_driver = {
-	.name = "tegra_idle",
-	.owner = THIS_MODULE,
-	.states = {
-		ARM_CPUIDLE_WFI_STATE_PWR(600),
-#ifdef CONFIG_PM_SLEEP
-		{
-			.enter            = tegra20_idle_lp2_coupled,
-			.exit_latency     = 5000,
-			.target_residency = 10000,
-			.power_usage      = 0,
-			.flags            = CPUIDLE_FLAG_COUPLED |
-			                    CPUIDLE_FLAG_TIMER_STOP,
-			.name             = "powered-down",
-			.desc             = "CPU power gated",
-		},
-#endif
-	},
-	.state_count = TEGRA20_MAX_STATES,
-	.safe_state_index = 0,
-};
-
-#ifdef CONFIG_PM_SLEEP
-#ifdef CONFIG_SMP
-static int tegra20_reset_sleeping_cpu_1(void)
-{
-	int ret = 0;
-
-	tegra_pen_lock();
-
-	if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE)
-		tegra20_cpu_shutdown(1);
-	else
-		ret = -EINVAL;
-
-	tegra_pen_unlock();
-
-	return ret;
-}
-
-static void tegra20_wake_cpu1_from_reset(void)
-{
-	tegra_pen_lock();
-
-	tegra20_cpu_clear_resettable();
-
-	/* enable cpu clock on cpu */
-	tegra_enable_cpu_clock(1);
-
-	/* take the CPU out of reset */
-	tegra_cpu_out_of_reset(1);
-
-	/* unhalt the cpu */
-	flowctrl_write_cpu_halt(1, 0);
-
-	tegra_pen_unlock();
-}
-
-static int tegra20_reset_cpu_1(void)
-{
-	if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
-		return 0;
-
-	tegra20_wake_cpu1_from_reset();
-	return -EBUSY;
-}
-#else
-static inline void tegra20_wake_cpu1_from_reset(void)
-{
-}
-
-static inline int tegra20_reset_cpu_1(void)
-{
-	return 0;
-}
-#endif
-
-static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
-					   struct cpuidle_driver *drv,
-					   int index)
-{
-	while (tegra20_cpu_is_resettable_soon())
-		cpu_relax();
-
-	if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
-		return false;
-
-	tegra_idle_lp2_last();
-
-	if (cpu_online(1))
-		tegra20_wake_cpu1_from_reset();
-
-	return true;
-}
-
-#ifdef CONFIG_SMP
-static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
-					 struct cpuidle_driver *drv,
-					 int index)
-{
-	cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
-
-	tegra20_cpu_clear_resettable();
-
-	return true;
-}
-#else
-static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
-						struct cpuidle_driver *drv,
-						int index)
-{
-	return true;
-}
-#endif
-
-static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
-				    struct cpuidle_driver *drv,
-				    int index)
-{
-	bool entered_lp2 = false;
-
-	if (tegra_pending_sgi())
-		WRITE_ONCE(abort_flag, true);
-
-	cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
-
-	if (abort_flag) {
-		cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
-		abort_flag = false;	/* clean flag for next coming */
-		return -EINTR;
-	}
-
-	local_fiq_disable();
-
-	tegra_set_cpu_in_lp2();
-	cpu_pm_enter();
-
-	if (dev->cpu == 0)
-		entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index);
-	else
-		entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
-
-	cpu_pm_exit();
-	tegra_clear_cpu_in_lp2();
-
-	local_fiq_enable();
-
-	smp_rmb();
-
-	return entered_lp2 ? index : 0;
-}
-#endif
-
-/*
- * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether
- * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around
- * this, simply disable LP2 if the PCI driver and DT node are both enabled.
- */
-void tegra20_cpuidle_pcie_irqs_in_use(void)
-{
-	pr_info_once(
-		"Disabling cpuidle LP2 state, since PCIe IRQs are in use\n");
-	tegra_idle_driver.states[1].disabled = true;
-}
-
-int __init tegra20_cpuidle_init(void)
-{
-	return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
-}
diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c
deleted file mode 100644
index c6128526877d..000000000000
--- a/arch/arm/mach-tegra/cpuidle-tegra30.c
+++ /dev/null
@@ -1,132 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * CPU idle driver for Tegra CPUs
- *
- * Copyright (c) 2010-2012, NVIDIA Corporation.
- * Copyright (c) 2011 Google, Inc.
- * Author: Colin Cross <ccross@android.com>
- *         Gary King <gking@nvidia.com>
- *
- * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
- */
-
-#include <linux/clk/tegra.h>
-#include <linux/tick.h>
-#include <linux/cpuidle.h>
-#include <linux/cpu_pm.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <asm/cpuidle.h>
-#include <asm/smp_plat.h>
-#include <asm/suspend.h>
-
-#include "cpuidle.h"
-#include "pm.h"
-#include "sleep.h"
-
-#ifdef CONFIG_PM_SLEEP
-static int tegra30_idle_lp2(struct cpuidle_device *dev,
-			    struct cpuidle_driver *drv,
-			    int index);
-#endif
-
-static struct cpuidle_driver tegra_idle_driver = {
-	.name = "tegra_idle",
-	.owner = THIS_MODULE,
-#ifdef CONFIG_PM_SLEEP
-	.state_count = 2,
-#else
-	.state_count = 1,
-#endif
-	.states = {
-		[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
-#ifdef CONFIG_PM_SLEEP
-		[1] = {
-			.enter			= tegra30_idle_lp2,
-			.exit_latency		= 2000,
-			.target_residency	= 2200,
-			.power_usage		= 0,
-			.flags			= CPUIDLE_FLAG_TIMER_STOP,
-			.name			= "powered-down",
-			.desc			= "CPU power gated",
-		},
-#endif
-	},
-};
-
-#ifdef CONFIG_PM_SLEEP
-static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
-					   struct cpuidle_driver *drv,
-					   int index)
-{
-	/* All CPUs entering LP2 is not working.
-	 * Don't let CPU0 enter LP2 when any secondary CPU is online.
-	 */
-	if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) {
-		cpu_do_idle();
-		return false;
-	}
-
-	tegra_idle_lp2_last();
-
-	return true;
-}
-
-#ifdef CONFIG_SMP
-static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
-					struct cpuidle_driver *drv,
-					int index)
-{
-	smp_wmb();
-
-	cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
-
-	return true;
-}
-#else
-static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
-					       struct cpuidle_driver *drv,
-					       int index)
-{
-	return true;
-}
-#endif
-
-static int tegra30_idle_lp2(struct cpuidle_device *dev,
-			    struct cpuidle_driver *drv,
-			    int index)
-{
-	bool entered_lp2 = false;
-	bool last_cpu;
-
-	local_fiq_disable();
-
-	last_cpu = tegra_set_cpu_in_lp2();
-	cpu_pm_enter();
-
-	if (dev->cpu == 0) {
-		if (last_cpu)
-			entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv,
-								     index);
-		else
-			cpu_do_idle();
-	} else {
-		entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
-	}
-
-	cpu_pm_exit();
-	tegra_clear_cpu_in_lp2();
-
-	local_fiq_enable();
-
-	smp_rmb();
-
-	return (entered_lp2) ? index : 0;
-}
-#endif
-
-int __init tegra30_cpuidle_init(void)
-{
-	return cpuidle_register(&tegra_idle_driver, NULL);
-}
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
deleted file mode 100644
index d565c44cfc93..000000000000
--- a/arch/arm/mach-tegra/cpuidle.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * arch/arm/mach-tegra/cpuidle.c
- *
- * CPU idle driver for Tegra CPUs
- *
- * Copyright (c) 2010-2012, NVIDIA Corporation.
- * Copyright (c) 2011 Google, Inc.
- * Author: Colin Cross <ccross@android.com>
- *         Gary King <gking@nvidia.com>
- *
- * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-
-#include <soc/tegra/fuse.h>
-
-#include "cpuidle.h"
-
-void __init tegra_cpuidle_init(void)
-{
-	switch (tegra_get_chip_id()) {
-	case TEGRA20:
-		if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
-			tegra20_cpuidle_init();
-		break;
-	case TEGRA30:
-		if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC))
-			tegra30_cpuidle_init();
-		break;
-	case TEGRA114:
-	case TEGRA124:
-		if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) ||
-		    IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC))
-			tegra114_cpuidle_init();
-		break;
-	}
-}
-
-void tegra_cpuidle_pcie_irqs_in_use(void)
-{
-	switch (tegra_get_chip_id()) {
-	case TEGRA20:
-		if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
-			tegra20_cpuidle_pcie_irqs_in_use();
-		break;
-	}
-}
diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h
deleted file mode 100644
index 4e1f459f5bd8..000000000000
--- a/arch/arm/mach-tegra/cpuidle.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
- */
-
-#ifndef __MACH_TEGRA_CPUIDLE_H
-#define __MACH_TEGRA_CPUIDLE_H
-
-#ifdef CONFIG_CPU_IDLE
-int tegra20_cpuidle_init(void);
-void tegra20_cpuidle_pcie_irqs_in_use(void);
-int tegra30_cpuidle_init(void);
-int tegra114_cpuidle_init(void);
-void tegra_cpuidle_init(void);
-void tegra_cpuidle_pcie_irqs_in_use(void);
-#else
-static inline void tegra_cpuidle_init(void) {}
-static inline void tegra_cpuidle_pcie_irqs_in_use(void) {}
-#endif
-
-#endif
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index ace7a390b5fe..22fca820f5c0 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -20,28 +20,10 @@
 
 #include "board.h"
 #include "iomap.h"
-#include "irq.h"
-
-#define SGI_MASK 0xFFFF
 
 #ifdef CONFIG_PM_SLEEP
 static void __iomem *tegra_gic_cpu_base;
-#endif
-
-bool tegra_pending_sgi(void)
-{
-	u32 pending_set;
-	void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
-
-	pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET);
 
-	if (pending_set & SGI_MASK)
-		return true;
-
-	return false;
-}
-
-#ifdef CONFIG_PM_SLEEP
 static int tegra_gic_notifier(struct notifier_block *self,
 			      unsigned long cmd, void *v)
 {
diff --git a/arch/arm/mach-tegra/irq.h b/arch/arm/mach-tegra/irq.h
deleted file mode 100644
index 7a94cf121448..000000000000
--- a/arch/arm/mach-tegra/irq.h
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
- */
-
-#ifndef __TEGRA_IRQ_H
-#define __TEGRA_IRQ_H
-
-bool tegra_pending_sgi(void);
-
-#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 3cab81b82866..6aaacb5757e1 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -137,18 +137,11 @@ bool tegra_set_cpu_in_lp2(void)
 
 	if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
 		last_cpu = true;
-	else if (tegra_get_chip_id() == TEGRA20 && phy_cpu_id == 1)
-		tegra20_cpu_set_resettable_soon();
 
 	spin_unlock(&tegra_lp2_lock);
 	return last_cpu;
 }
 
-int tegra_cpu_do_idle(void)
-{
-	return cpu_do_idle();
-}
-
 static int tegra_sleep_cpu(unsigned long v2p)
 {
 	/*
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 569151b3edc0..1e51a9b636eb 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -25,7 +25,6 @@ void tegra30_sleep_core_init(void);
 
 void tegra_clear_cpu_in_lp2(void);
 bool tegra_set_cpu_in_lp2(void);
-int tegra_cpu_do_idle(void);
 void tegra_idle_lp2_last(void);
 extern void (*tegra_tear_down_cpu)(void);
 
diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S
index 67b763fea005..df44828a34d3 100644
--- a/arch/arm/mach-tegra/reset-handler.S
+++ b/arch/arm/mach-tegra/reset-handler.S
@@ -183,17 +183,6 @@ after_errata:
 	bleq	__die				@ CPU not present (to OS)
 #endif
 
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
-	/* Are we on Tegra20? */
-	cmp	r6, #TEGRA20
-	bne	1f
-	/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
-	mov	r0, #CPU_NOT_RESETTABLE
-	cmp	r10, #0
-	strbne	r0, [r12, #RESET_DATA(RESETTABLE_STATUS)]
-1:
-#endif
-
 	/* Waking up from LP1? */
 	ldr	r8, [r12, #RESET_DATA(MASK_LP1)]
 	tst	r8, r11				@ if in_lp1
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
index a4cfc08159f6..51265592cb1a 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -16,9 +16,8 @@
 #define TEGRA_RESET_STARTUP_SECONDARY	3
 #define TEGRA_RESET_STARTUP_LP2		4
 #define TEGRA_RESET_STARTUP_LP1		5
-#define TEGRA_RESET_RESETTABLE_STATUS	6
-#define TEGRA_RESET_TF_PRESENT		7
-#define TEGRA_RESET_DATA_SIZE		8
+#define TEGRA_RESET_TF_PRESENT		6
+#define TEGRA_RESET_DATA_SIZE		7
 
 #define RESET_DATA(x)	((TEGRA_RESET_##x)*4)
 
@@ -42,10 +41,6 @@ void __tegra_cpu_reset_handler_end(void);
 	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
 	((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
 	 (u32)__tegra_cpu_reset_handler_start)))
-#define tegra20_cpu1_resettable_status \
-	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
-	((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_RESETTABLE_STATUS] - \
-	 (u32)__tegra_cpu_reset_handler_start)))
 #endif
 
 #define tegra_cpu_reset_handler_offset \
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S
index 9a89f30d53ca..6fb14f76235f 100644
--- a/arch/arm/mach-tegra/sleep-tegra20.S
+++ b/arch/arm/mach-tegra/sleep-tegra20.S
@@ -43,9 +43,6 @@
 #define APB_MISC_XM2CFGCPADCTRL2	0x8e4
 #define APB_MISC_XM2CFGDPADCTRL2	0x8e8
 
-#define __tegra20_cpu1_resettable_status_offset \
-	(__tegra_cpu_reset_handler_data_offset + RESET_DATA(RESETTABLE_STATUS))
-
 .macro pll_enable, rd, r_car_base, pll_base
 	ldr	\rd, [\r_car_base, #\pll_base]
 	tst	\rd, #(1 << 30)
@@ -64,21 +61,6 @@
 /*
  * tegra20_hotplug_shutdown(void)
  *
- * puts the current cpu in reset
- * should never return
- */
-ENTRY(tegra20_hotplug_shutdown)
-	/* Put this CPU down */
-	cpu_id	r0
-	bl	tegra20_cpu_shutdown
-	ret	lr			@ should never get here
-ENDPROC(tegra20_hotplug_shutdown)
-
-/*
- * tegra20_cpu_shutdown(int cpu)
- *
- * r0 is cpu to reset
- *
  * puts the specified CPU in wait-for-event mode on the flow controller
  * and puts the CPU in reset
  * can be called on the current cpu or another cpu
@@ -87,13 +69,10 @@ ENDPROC(tegra20_hotplug_shutdown)
  *
  * corrupts r0-r3, r12
  */
-ENTRY(tegra20_cpu_shutdown)
+ENTRY(tegra20_hotplug_shutdown)
+	cpu_id	r0
 	cmp	r0, #0
 	reteq	lr			@ must not be called for CPU 0
-	mov32	r1, TEGRA_IRAM_RESET_BASE_VIRT
-	ldr	r2, =__tegra20_cpu1_resettable_status_offset
-	mov	r12, #CPU_RESETTABLE
-	strb	r12, [r1, r2]
 
 	cpu_to_halt_reg r1, r0
 	ldr	r3, =TEGRA_FLOW_CTRL_VIRT
@@ -112,111 +91,10 @@ ENTRY(tegra20_cpu_shutdown)
 	cmp	r3, r0
 	beq	.
 	ret	lr
-ENDPROC(tegra20_cpu_shutdown)
+ENDPROC(tegra20_hotplug_shutdown)
 #endif
 
 #ifdef CONFIG_PM_SLEEP
-/*
- * tegra_pen_lock
- *
- * spinlock implementation with no atomic test-and-set and no coherence
- * using Peterson's algorithm on strongly-ordered registers
- * used to synchronize a cpu waking up from wfi with entering lp2 on idle
- *
- * The reference link of Peterson's algorithm:
- * http://en.wikipedia.org/wiki/Peterson's_algorithm
- *
- * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
- * on cpu 0:
- * r2 = flag[0] (in SCRATCH38)
- * r3 = flag[1] (in SCRATCH39)
- * on cpu1:
- * r2 = flag[1] (in SCRATCH39)
- * r3 = flag[0] (in SCRATCH38)
- *
- * must be called with MMU on
- * corrupts r0-r3, r12
- */
-ENTRY(tegra_pen_lock)
-	mov32	r3, TEGRA_PMC_VIRT
-	cpu_id	r0
-	add	r1, r3, #PMC_SCRATCH37
-	cmp	r0, #0
-	addeq	r2, r3, #PMC_SCRATCH38
-	addeq	r3, r3, #PMC_SCRATCH39
-	addne	r2, r3, #PMC_SCRATCH39
-	addne	r3, r3, #PMC_SCRATCH38
-
-	mov	r12, #1
-	str	r12, [r2]		@ flag[cpu] = 1
-	dsb
-	str	r12, [r1]		@ !turn = cpu
-1:	dsb
-	ldr	r12, [r3]
-	cmp	r12, #1			@ flag[!cpu] == 1?
-	ldreq	r12, [r1]
-	cmpeq	r12, r0			@ !turn == cpu?
-	beq	1b			@ while !turn == cpu && flag[!cpu] == 1
-
-	ret	lr			@ locked
-ENDPROC(tegra_pen_lock)
-
-ENTRY(tegra_pen_unlock)
-	dsb
-	mov32	r3, TEGRA_PMC_VIRT
-	cpu_id	r0
-	cmp	r0, #0
-	addeq	r2, r3, #PMC_SCRATCH38
-	addne	r2, r3, #PMC_SCRATCH39
-	mov	r12, #0
-	str	r12, [r2]
-	ret     lr
-ENDPROC(tegra_pen_unlock)
-
-/*
- * tegra20_cpu_clear_resettable(void)
- *
- * Called to clear the "resettable soon" flag in IRAM variable when
- * it is expected that the secondary CPU will be idle soon.
- */
-ENTRY(tegra20_cpu_clear_resettable)
-	mov32	r1, TEGRA_IRAM_RESET_BASE_VIRT
-	ldr	r2, =__tegra20_cpu1_resettable_status_offset
-	mov	r12, #CPU_NOT_RESETTABLE
-	strb	r12, [r1, r2]
-	ret	lr
-ENDPROC(tegra20_cpu_clear_resettable)
-
-/*
- * tegra20_cpu_set_resettable_soon(void)
- *
- * Called to set the "resettable soon" flag in IRAM variable when
- * it is expected that the secondary CPU will be idle soon.
- */
-ENTRY(tegra20_cpu_set_resettable_soon)
-	mov32	r1, TEGRA_IRAM_RESET_BASE_VIRT
-	ldr	r2, =__tegra20_cpu1_resettable_status_offset
-	mov	r12, #CPU_RESETTABLE_SOON
-	strb	r12, [r1, r2]
-	ret	lr
-ENDPROC(tegra20_cpu_set_resettable_soon)
-
-/*
- * tegra20_cpu_is_resettable_soon(void)
- *
- * Returns true if the "resettable soon" flag in IRAM variable has been
- * set because it is expected that the secondary CPU will be idle soon.
- */
-ENTRY(tegra20_cpu_is_resettable_soon)
-	mov32	r1, TEGRA_IRAM_RESET_BASE_VIRT
-	ldr	r2, =__tegra20_cpu1_resettable_status_offset
-	ldrb	r12, [r1, r2]
-	cmp	r12, #CPU_RESETTABLE_SOON
-	moveq	r0, #1
-	movne	r0, #0
-	ret	lr
-ENDPROC(tegra20_cpu_is_resettable_soon)
-
 /*
  * tegra20_sleep_core_finish(unsigned long v2p)
  *
@@ -242,68 +120,6 @@ ENTRY(tegra20_sleep_core_finish)
 	ret	r3
 ENDPROC(tegra20_sleep_core_finish)
 
-/*
- * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
- *
- * Enters WFI on secondary CPU by exiting coherency.
- */
-ENTRY(tegra20_sleep_cpu_secondary_finish)
-	stmfd	sp!, {r4-r11, lr}
-
-	mrc	p15, 0, r11, c1, c0, 1  @ save actlr before exiting coherency
-
-	/* Flush and disable the L1 data cache */
-	mov	r0, #TEGRA_FLUSH_CACHE_LOUIS
-	bl	tegra_disable_clean_inv_dcache
-
-	mov32	r0, TEGRA_IRAM_RESET_BASE_VIRT
-	ldr	r4, =__tegra20_cpu1_resettable_status_offset
-	mov	r3, #CPU_RESETTABLE
-	strb	r3, [r0, r4]
-
-	bl	tegra_cpu_do_idle
-
-	/*
-	 * cpu may be reset while in wfi, which will return through
-	 * tegra_resume to cpu_resume
-	 * or interrupt may wake wfi, which will return here
-	 * cpu state is unchanged - MMU is on, cache is on, coherency
-	 * is off, and the data cache is off
-	 *
-	 * r11 contains the original actlr
-	 */
-
-	bl	tegra_pen_lock
-
-	mov32	r0, TEGRA_IRAM_RESET_BASE_VIRT
-	ldr	r4, =__tegra20_cpu1_resettable_status_offset
-	mov	r3, #CPU_NOT_RESETTABLE
-	strb	r3, [r0, r4]
-
-	bl	tegra_pen_unlock
-
-	/* Re-enable the data cache */
-	mrc	p15, 0, r10, c1, c0, 0
-	orr	r10, r10, #CR_C
-	mcr	p15, 0, r10, c1, c0, 0
-	isb
-
-	mcr	p15, 0, r11, c1, c0, 1	@ reenable coherency
-
-	/* Invalidate the TLBs & BTAC */
-	mov	r1, #0
-	mcr	p15, 0, r1, c8, c3, 0	@ invalidate shared TLBs
-	mcr	p15, 0, r1, c7, c1, 6	@ invalidate shared BTAC
-	dsb
-	isb
-
-	/* the cpu was running with coherency disabled,
-	 * caches may be out of date */
-	bl	v7_flush_kern_cache_louis
-
-	ldmfd	sp!, {r4 - r11, pc}
-ENDPROC(tegra20_sleep_cpu_secondary_finish)
-
 /*
  * tegra20_tear_down_cpu
  *
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index 78ef32a907c8..d219872b7546 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -114,8 +114,6 @@
 .endm
 
 #else
-void tegra_pen_lock(void);
-void tegra_pen_unlock(void);
 void tegra_resume(void);
 int tegra_sleep_cpu_finish(unsigned long);
 void tegra_disable_clean_inv_dcache(u32 flag);
@@ -125,16 +123,6 @@ void tegra20_hotplug_shutdown(void);
 void tegra30_hotplug_shutdown(void);
 #endif
 
-void tegra20_cpu_shutdown(int cpu);
-int tegra20_cpu_is_resettable_soon(void);
-void tegra20_cpu_clear_resettable(void);
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
-void tegra20_cpu_set_resettable_soon(void);
-#else
-static inline void tegra20_cpu_set_resettable_soon(void) {}
-#endif
-
-int tegra20_sleep_cpu_secondary_finish(unsigned long);
 void tegra20_tear_down_cpu(void);
 int tegra30_sleep_cpu_secondary_finish(unsigned long);
 void tegra30_tear_down_cpu(void);
diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c
index e512e606eabd..d9237769a37c 100644
--- a/arch/arm/mach-tegra/tegra.c
+++ b/arch/arm/mach-tegra/tegra.c
@@ -40,9 +40,7 @@
 
 #include "board.h"
 #include "common.h"
-#include "cpuidle.h"
 #include "iomap.h"
-#include "irq.h"
 #include "pm.h"
 #include "reset.h"
 #include "sleep.h"
@@ -86,7 +84,6 @@ static void __init tegra_dt_init(void)
 static void __init tegra_dt_init_late(void)
 {
 	tegra_init_suspend();
-	tegra_cpuidle_init();
 
 	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
 	    of_machine_is_compatible("compal,paz00"))
diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index c8ef05d6b8c7..d98c69efb7e0 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -6,7 +6,6 @@ if ARM
 
 config ARCH_TEGRA_2x_SOC
 	bool "Enable support for Tegra20 family"
-	select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
 	select ARM_ERRATA_720789
 	select ARM_ERRATA_754327 if SMP
 	select ARM_ERRATA_764369 if SMP
diff --git a/include/soc/tegra/cpuidle.h b/include/soc/tegra/cpuidle.h
index 029ba1f4b2cc..f758808342b6 100644
--- a/include/soc/tegra/cpuidle.h
+++ b/include/soc/tegra/cpuidle.h
@@ -6,12 +6,8 @@
 #ifndef __SOC_TEGRA_CPUIDLE_H__
 #define __SOC_TEGRA_CPUIDLE_H__
 
-#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_TEGRA) && defined(CONFIG_CPU_IDLE)
-void tegra_cpuidle_pcie_irqs_in_use(void);
-#else
 static inline void tegra_cpuidle_pcie_irqs_in_use(void)
 {
 }
-#endif
 
 #endif /* __SOC_TEGRA_CPUIDLE_H__ */
-- 
2.22.0


^ permalink raw reply related

* [PATCH v2 2/6] ARM: tegra: Expose functions required for cpuidle driver
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-1-digetx@gmail.com>

The upcoming unified CPUIDLE driver will be added to the drivers/cpuidle/
directory and it will require all these Tegra PM-core functions.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/mach-tegra/Makefile  |  2 +-
 arch/arm/mach-tegra/platsmp.c |  2 --
 arch/arm/mach-tegra/pm.c      | 16 +++++++---------
 arch/arm/mach-tegra/pm.h      |  3 ---
 arch/arm/mach-tegra/sleep.h   |  1 -
 include/linux/clk/tegra.h     | 13 +++++++++++++
 include/soc/tegra/pm.h        | 28 ++++++++++++++++++++++++++++
 7 files changed, 49 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 5d93a0b36866..27bd5d9865e3 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -13,7 +13,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pm-tegra20.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= pm-tegra30.o
 obj-$(CONFIG_SMP)			+= platsmp.o
-obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
+obj-y					+= hotplug.o
 
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= sleep-tegra30.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= pm-tegra30.o
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index e6911a14c096..c8a63719a143 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -183,8 +183,6 @@ const struct smp_operations tegra_smp_ops __initconst = {
 	.smp_prepare_cpus	= tegra_smp_prepare_cpus,
 	.smp_secondary_init	= tegra_secondary_init,
 	.smp_boot_secondary	= tegra_boot_secondary,
-#ifdef CONFIG_HOTPLUG_CPU
 	.cpu_kill		= tegra_cpu_kill,
 	.cpu_die		= tegra_cpu_die,
-#endif
 };
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 6aaacb5757e1..f9c9bce9e15d 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -123,11 +123,9 @@ void tegra_clear_cpu_in_lp2(void)
 	spin_unlock(&tegra_lp2_lock);
 }
 
-bool tegra_set_cpu_in_lp2(void)
+void tegra_set_cpu_in_lp2(void)
 {
 	int phy_cpu_id = cpu_logical_map(smp_processor_id());
-	bool last_cpu = false;
-	cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask;
 	u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
 
 	spin_lock(&tegra_lp2_lock);
@@ -135,11 +133,7 @@ bool tegra_set_cpu_in_lp2(void)
 	BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id)));
 	*cpu_in_lp2 |= BIT(phy_cpu_id);
 
-	if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
-		last_cpu = true;
-
 	spin_unlock(&tegra_lp2_lock);
-	return last_cpu;
 }
 
 static int tegra_sleep_cpu(unsigned long v2p)
@@ -195,14 +189,16 @@ static void tegra_pm_set(enum tegra_suspend_mode mode)
 	tegra_pmc_enter_suspend_mode(mode);
 }
 
-void tegra_idle_lp2_last(void)
+int tegra_idle_lp2_last(void)
 {
+	int err;
+
 	tegra_pm_set(TEGRA_SUSPEND_LP2);
 
 	cpu_cluster_pm_enter();
 	suspend_cpu_complex();
 
-	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+	err = cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
 
 	/*
 	 * Resume L2 cache if it wasn't re-enabled early during resume,
@@ -214,6 +210,8 @@ void tegra_idle_lp2_last(void)
 
 	restore_cpu_complex();
 	cpu_cluster_pm_exit();
+
+	return err;
 }
 
 enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 1e51a9b636eb..81525f5f4a44 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -23,9 +23,6 @@ void tegra20_sleep_core_init(void);
 void tegra30_lp1_iram_hook(void);
 void tegra30_sleep_core_init(void);
 
-void tegra_clear_cpu_in_lp2(void);
-bool tegra_set_cpu_in_lp2(void);
-void tegra_idle_lp2_last(void);
 extern void (*tegra_tear_down_cpu)(void);
 
 #ifdef CONFIG_PM_SLEEP
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index d219872b7546..0d9956e9a8ea 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -124,7 +124,6 @@ void tegra30_hotplug_shutdown(void);
 #endif
 
 void tegra20_tear_down_cpu(void);
-int tegra30_sleep_cpu_secondary_finish(unsigned long);
 void tegra30_tear_down_cpu(void);
 
 #endif
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index b8aef62cc3f5..cf0f2cb5e109 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -108,6 +108,19 @@ static inline void tegra_cpu_clock_resume(void)
 
 	tegra_cpu_car_ops->resume();
 }
+#else
+static inline bool tegra_cpu_rail_off_ready(void)
+{
+	return false;
+}
+
+static inline void tegra_cpu_clock_suspend(void)
+{
+}
+
+static inline void tegra_cpu_clock_resume(void)
+{
+}
 #endif
 
 extern void tegra210_xusb_pll_hw_control_enable(void);
diff --git a/include/soc/tegra/pm.h b/include/soc/tegra/pm.h
index 951fcd738d55..fa18c2df5028 100644
--- a/include/soc/tegra/pm.h
+++ b/include/soc/tegra/pm.h
@@ -20,6 +20,12 @@ tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode);
 
 /* low-level resume entry point */
 void tegra_resume(void);
+
+int tegra30_sleep_cpu_secondary_finish(unsigned long arg);
+void tegra_clear_cpu_in_lp2(void);
+void tegra_set_cpu_in_lp2(void);
+int tegra_idle_lp2_last(void);
+void tegra_cpu_die(unsigned int cpu);
 #else
 static inline enum tegra_suspend_mode
 tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode)
@@ -30,6 +36,28 @@ tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode)
 static inline void tegra_resume(void)
 {
 }
+
+static inline int tegra30_sleep_cpu_secondary_finish(unsigned long arg)
+{
+	return -1;
+}
+
+static inline void tegra_clear_cpu_in_lp2(void)
+{
+}
+
+static inline void tegra_set_cpu_in_lp2(void)
+{
+}
+
+static inline int tegra_idle_lp2_last(void)
+{
+	return -1;
+}
+
+static inline void tegra_cpu_die(unsigned int cpu)
+{
+}
 #endif /* CONFIG_PM_SLEEP */
 
 #endif /* __SOC_TEGRA_PM_H__ */
-- 
2.22.0


^ permalink raw reply related

* [PATCH v2 3/6] cpuidle: Add unified driver for NVIDIA Tegra SoCs
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-1-digetx@gmail.com>

The new driver is based on the old CPU Idle drivers that are removed now
from arm/arch/mach-tegra/ directory. Those removed drivers were reworked
and squashed into a single unified driver that covers multiple hardware
generations, starting from Tegra20 and ending with Tegra124.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/mach-tegra/tegra.c     |   4 +
 drivers/cpuidle/Kconfig.arm     |   8 +
 drivers/cpuidle/Makefile        |   1 +
 drivers/cpuidle/cpuidle-tegra.c | 304 ++++++++++++++++++++++++++++++++
 include/soc/tegra/cpuidle.h     |   4 +
 5 files changed, 321 insertions(+)
 create mode 100644 drivers/cpuidle/cpuidle-tegra.c

diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c
index d9237769a37c..f1ce2857a251 100644
--- a/arch/arm/mach-tegra/tegra.c
+++ b/arch/arm/mach-tegra/tegra.c
@@ -36,6 +36,7 @@
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
 #include <asm/mach-types.h>
+#include <asm/psci.h>
 #include <asm/setup.h>
 
 #include "board.h"
@@ -92,6 +93,9 @@ static void __init tegra_dt_init_late(void)
 	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) &&
 	    of_machine_is_compatible("nvidia,tegra20"))
 		platform_device_register_simple("tegra20-cpufreq", -1, NULL, 0);
+
+	if (IS_ENABLED(CONFIG_ARM_TEGRA_CPUIDLE) && !psci_smp_available())
+		platform_device_register_simple("tegra-cpuidle", -1, NULL, 0);
 }
 
 static const char * const tegra_dt_board_compat[] = {
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 48cb3d4bb7d1..d90861361f1d 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -76,3 +76,11 @@ config ARM_MVEBU_V7_CPUIDLE
 	depends on ARCH_MVEBU && !ARM64
 	help
 	  Select this to enable cpuidle on Armada 370, 38x and XP processors.
+
+config ARM_TEGRA_CPUIDLE
+	bool "CPU Idle Driver for NVIDIA Tegra SoCs"
+	depends on ARCH_TEGRA && !ARM64
+	select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
+	select ARM_CPU_SUSPEND
+	help
+	  Select this to enable cpuidle for NVIDIA Tegra20/30/114/124 SoCs.
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 9d7176cee3d3..470d17fa8746 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ARM_U8500_CPUIDLE)         += cpuidle-ux500.o
 obj-$(CONFIG_ARM_AT91_CPUIDLE)          += cpuidle-at91.o
 obj-$(CONFIG_ARM_EXYNOS_CPUIDLE)        += cpuidle-exynos.o
 obj-$(CONFIG_ARM_CPUIDLE)		+= cpuidle-arm.o
+obj-$(CONFIG_ARM_TEGRA_CPUIDLE)		+= cpuidle-tegra.o
 
 ###############################################################################
 # MIPS drivers
diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c
new file mode 100644
index 000000000000..54974cd2255f
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-tegra.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CPU idle driver for Tegra CPUs
+ *
+ * Copyright (c) 2010-2013, NVIDIA Corporation.
+ * Copyright (c) 2011 Google, Inc.
+ * Author: Colin Cross <ccross@android.com>
+ *         Gary King <gking@nvidia.com>
+ *
+ * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
+ *
+ * Tegra20/124 driver unification by Dmitry Osipenko <digetx@gmail.com>
+ */
+
+#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/errno.h>
+#include <linux/ktime.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <linux/clk/tegra.h>
+#include <linux/firmware/trusted_foundations.h>
+
+#include <soc/tegra/cpuidle.h>
+#include <soc/tegra/flowctrl.h>
+#include <soc/tegra/fuse.h>
+#include <soc/tegra/pm.h>
+
+#include <asm/cpuidle.h>
+#include <asm/firmware.h>
+#include <asm/smp_plat.h>
+#include <asm/suspend.h>
+
+#define TEGRA_C1		0
+#define TEGRA_C7		1
+#define TEGRA_CC6		2
+
+static atomic_t tegra_idle_barrier;
+
+static inline bool tegra_cpuidle_using_firmware(void)
+{
+	return firmware_ops->prepare_idle && firmware_ops->do_idle;
+}
+
+static int tegra_shutdown_secondary_cpu(unsigned long cpu)
+{
+	tegra_cpu_die(cpu);
+
+	return -EINVAL;
+}
+
+static int tegra_await_secondary_cpus_shutdown(void)
+{
+	ktime_t timeout = ktime_add_ms(ktime_get(), 1);
+
+	/*
+	 * The boot CPU shall await for the secondaries shutdown in order
+	 * to power-off CPU's cluster safely.
+	 */
+	do {
+		if (tegra_cpu_rail_off_ready())
+			return 0;
+
+	} while (ktime_compare(ktime_get(), timeout) < 0);
+
+	return -ETIMEDOUT;
+}
+
+static void tegra_wake_up_secondary_cpus(void)
+{
+	unsigned int cpu, lcpu;
+
+	for_each_cpu(lcpu, cpu_online_mask) {
+		cpu = cpu_logical_map(lcpu);
+
+		if (cpu > 0) {
+			tegra_enable_cpu_clock(cpu);
+			tegra_cpu_out_of_reset(cpu);
+			flowctrl_write_cpu_halt(cpu, 0);
+		}
+	}
+}
+
+static int tegra_cpuidle_cc6_enter(void)
+{
+	int err;
+
+	err = tegra_await_secondary_cpus_shutdown();
+	if (err)
+		return err;
+
+	err = tegra_idle_lp2_last();
+
+	tegra_wake_up_secondary_cpus();
+
+	return err;
+}
+
+static int tegra_cpuidle_c7_enter(void)
+{
+	int err;
+
+	if (tegra_cpuidle_using_firmware()) {
+		err = call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2);
+		if (err)
+			return err;
+
+		return call_firmware_op(do_idle, 0);
+	}
+
+	return cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
+}
+
+static int tegra_cpuidle_enter(struct cpuidle_device *dev,
+			       int index, unsigned int cpu)
+{
+	int err;
+
+	local_fiq_disable();
+	tegra_set_cpu_in_lp2();
+	cpu_pm_enter();
+
+	switch (index) {
+	case TEGRA_C7:
+		err = tegra_cpuidle_c7_enter();
+		break;
+	case TEGRA_CC6:
+		cpuidle_coupled_parallel_barrier(dev, &tegra_idle_barrier);
+
+		if (cpu > 0)
+			err = cpu_suspend(cpu, tegra_shutdown_secondary_cpu);
+		else
+			err = tegra_cpuidle_cc6_enter();
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	cpu_pm_exit();
+	tegra_clear_cpu_in_lp2();
+	local_fiq_enable();
+
+	smp_rmb();
+
+	return err;
+}
+
+static int tegra_adjust_state_index(struct cpuidle_device *dev,
+				    struct cpuidle_driver *drv,
+				    int index, unsigned int cpu)
+{
+	/*
+	 * On Tegra30 CPU0 can't be power-gated while secondary CPUs
+	 * are active because it gates the whole CPU cluster.
+	 */
+	if (cpu != 0 || index != TEGRA_C7 || tegra_get_chip_id() != TEGRA30)
+		return index;
+
+	if (!IS_ENABLED(CONFIG_PM_SLEEP) || num_online_cpus() > 1)
+		index = TEGRA_C1;
+	else
+		index = TEGRA_CC6;
+
+	if (drv->states[index].disabled || dev->states_usage[index].disable)
+		index = -1;
+
+	return index;
+}
+
+static int tegra_cpu_idle(struct cpuidle_device *dev,
+			  struct cpuidle_driver *drv,
+			  int index)
+{
+	unsigned int cpu = cpu_logical_map(dev->cpu);
+	int err;
+
+	index = tegra_adjust_state_index(dev, drv, index, cpu);
+	if (index < 0)
+		return index;
+
+	if (index == TEGRA_C1)
+		err = arm_cpuidle_simple_enter(dev, drv, index);
+	else
+		err = tegra_cpuidle_enter(dev, index, cpu);
+
+	return WARN_ON_ONCE(err) ? -1 : index;
+}
+
+static void tegra114_enter_s2idle(struct cpuidle_device *dev,
+				  struct cpuidle_driver *drv,
+				  int index)
+{
+	tegra_cpu_idle(dev, drv, index);
+}
+
+static struct cpuidle_driver tegra_idle_driver = {
+	.name = "tegra_idle",
+	.states = {
+		[TEGRA_C1] = ARM_CPUIDLE_WFI_STATE_PWR(600),
+		[TEGRA_C7] = {
+			.enter			= tegra_cpu_idle,
+			.exit_latency		= 2000,
+			.target_residency	= 2200,
+			.power_usage		= 100,
+			.flags			= CPUIDLE_FLAG_TIMER_STOP,
+			.name			= "C7",
+			.desc			= "CPU core powered off",
+		},
+		[TEGRA_CC6] = {
+			.enter			= tegra_cpu_idle,
+			.exit_latency		= 5000,
+			.target_residency	= 10000,
+			.power_usage		= 0,
+			.flags			= CPUIDLE_FLAG_TIMER_STOP |
+						  CPUIDLE_FLAG_COUPLED,
+			.name			= "CC6",
+			.desc			= "CPU cluster powered off",
+		},
+	},
+	.state_count = 3,
+	.safe_state_index = TEGRA_C1,
+};
+
+/*
+ * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether
+ * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around
+ * this, simply disable LP2 if the PCI driver and DT node are both enabled.
+ */
+void tegra_cpuidle_pcie_irqs_in_use(void)
+{
+	if (tegra_get_chip_id() != TEGRA20 ||
+	    tegra_idle_driver.states[TEGRA_CC6].disabled)
+		return;
+
+	pr_info("Disabling cpuidle LP2 state, since PCIe IRQs are in use\n");
+	tegra_idle_driver.states[TEGRA_CC6].disabled = true;
+}
+
+static void tegra_cpuidle_adjust_tegra114_c7_state(void)
+{
+	struct cpuidle_state *s = &tegra_idle_driver.states[TEGRA_C7];
+
+	s->enter_s2idle = tegra114_enter_s2idle;
+	s->target_residency = 1000;
+	s->exit_latency = 500;
+}
+
+static int tegra_cpuidle_probe(struct platform_device *pdev)
+{
+	/*
+	 * Tegra30+ SoCs are big.LITTLE architectures, but the Little
+	 * core is not supported at the moment.
+	 */
+	const struct cpumask *coupled_cpus = cpu_possible_mask;
+
+	/*
+	 * Required suspend-resume functionality, which is provided by the
+	 * Tegra-arch core and PMC driver, is unavailable if PM-sleep option
+	 * is disabled.
+	 */
+	if (!IS_ENABLED(CONFIG_PM_SLEEP)) {
+		if (!tegra_cpuidle_using_firmware())
+			tegra_idle_driver.states[TEGRA_C7].disabled = true;
+
+		tegra_idle_driver.states[TEGRA_CC6].disabled = true;
+	}
+
+	/*
+	 * Generic WFI state (also known as C1 or LP3) is common for all
+	 * NVIDIA Tegra SoCs.
+	 */
+	switch (tegra_get_chip_id()) {
+	case TEGRA20:
+		/* CPU cores can't be power-gated separately at all */
+		tegra_idle_driver.states[TEGRA_C7].disabled = true;
+		break;
+	case TEGRA30:
+		tegra_idle_driver.states[TEGRA_CC6].disabled = true;
+		break;
+	case TEGRA114:
+	case TEGRA124:
+		tegra_cpuidle_adjust_tegra114_c7_state();
+
+		/* coupled CC6 (LP2) state isn't implemented yet */
+		tegra_idle_driver.states[TEGRA_CC6].disabled = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return cpuidle_register(&tegra_idle_driver, coupled_cpus);
+}
+
+static struct platform_driver tegra_cpuidle_driver = {
+	.probe = tegra_cpuidle_probe,
+	.driver = {
+		.name = "tegra-cpuidle",
+		.suppress_bind_attrs = true,
+	},
+};
+builtin_platform_driver(tegra_cpuidle_driver);
diff --git a/include/soc/tegra/cpuidle.h b/include/soc/tegra/cpuidle.h
index f758808342b6..5665975015d8 100644
--- a/include/soc/tegra/cpuidle.h
+++ b/include/soc/tegra/cpuidle.h
@@ -6,8 +6,12 @@
 #ifndef __SOC_TEGRA_CPUIDLE_H__
 #define __SOC_TEGRA_CPUIDLE_H__
 
+#ifdef CONFIG_ARM_TEGRA_CPUIDLE
+void tegra_cpuidle_pcie_irqs_in_use(void);
+#else
 static inline void tegra_cpuidle_pcie_irqs_in_use(void)
 {
 }
+#endif
 
 #endif /* __SOC_TEGRA_CPUIDLE_H__ */
-- 
2.22.0


^ permalink raw reply related

* [PATCH v2 4/6] cpuidle: tegra: Support CPU cluster power-down on Tegra30
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-1-digetx@gmail.com>

The new CPU Idle driver has all necessary features in order to allow
the deepest idling state on Tegra30 SoC where the whole CPU cluster is
power-gated using the coupled idle state.

Note that outer_disable() now need to be invoked directly since it
produces erroneous warning message about secondary CPUs being online
because it doesn't know that secondaries are turned off at that point
by the cpuidle driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/mach-tegra/pm.c        | 4 ++--
 drivers/cpuidle/cpuidle-tegra.c | 1 -
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index f9c9bce9e15d..cc941b0c97e8 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -146,8 +146,8 @@ static int tegra_sleep_cpu(unsigned long v2p)
 	 * if any of secondary CPU's is online and this is the LP2-idle
 	 * code-path only for Tegra20/30.
 	 */
-	if (trusted_foundations_registered())
-		outer_disable();
+	if (trusted_foundations_registered() && outer_cache.disable)
+		outer_cache.disable();
 
 	/*
 	 * Note that besides of setting up CPU reset vector this firmware
diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c
index 54974cd2255f..1412c4b0f6ee 100644
--- a/drivers/cpuidle/cpuidle-tegra.c
+++ b/drivers/cpuidle/cpuidle-tegra.c
@@ -278,7 +278,6 @@ static int tegra_cpuidle_probe(struct platform_device *pdev)
 		tegra_idle_driver.states[TEGRA_C7].disabled = true;
 		break;
 	case TEGRA30:
-		tegra_idle_driver.states[TEGRA_CC6].disabled = true;
 		break;
 	case TEGRA114:
 	case TEGRA124:
-- 
2.22.0


^ permalink raw reply related

* [PATCH v2 6/6] ARM: tegra: Enable Tegra cpuidle driver in tegra_defconfig
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-1-digetx@gmail.com>

The Tegra CPU Idle driver was moved out into driver/cpuidle/ directory and
it is now a proper platform driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/configs/tegra_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index 8f5c6a5b444c..9a2f11a780a8 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -25,6 +25,7 @@ CONFIG_CPU_FREQ=y
 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
 CONFIG_CPUFREQ_DT=y
 CONFIG_CPU_IDLE=y
+CONFIG_ARM_TEGRA_CPUIDLE=y
 CONFIG_VFP=y
 CONFIG_NEON=y
 CONFIG_TRUSTED_FOUNDATIONS=y
-- 
2.22.0


^ permalink raw reply related

* [PATCH v2 5/6] ARM: multi_v7_defconfig: Enable Tegra cpuidle driver
From: Dmitry Osipenko @ 2019-07-11  3:13 UTC (permalink / raw)
  To: Thierry Reding, Jonathan Hunter, Peter De Schrijver,
	Rafael J. Wysocki, Daniel Lezcano
  Cc: linux-pm, linux-tegra, linux-arm-kernel, linux-kernel
In-Reply-To: <20190711031312.10038-1-digetx@gmail.com>

The Tegra CPU Idle driver was moved out into driver/cpuidle/ directory and
it is now a proper platform driver.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 6a40bc2ef271..2c0fc7968c42 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -107,6 +107,7 @@ CONFIG_CPU_IDLE=y
 CONFIG_ARM_CPUIDLE=y
 CONFIG_ARM_ZYNQ_CPUIDLE=y
 CONFIG_ARM_EXYNOS_CPUIDLE=y
+CONFIG_ARM_TEGRA_CPUIDLE=y
 CONFIG_KERNEL_MODE_NEON=y
 CONFIG_RASPBERRYPI_FIRMWARE=y
 CONFIG_TRUSTED_FOUNDATIONS=y
-- 
2.22.0


^ permalink raw reply related

* Re: [PATCH v2] PM / devfreq: Fix kernel oops on governor module load
From: Chanwoo Choi @ 2019-07-11  2:49 UTC (permalink / raw)
  To: Ezequiel Garcia, cwchoi00
  Cc: Kyungmin Park, MyungJoo Ham, kernel, Linux PM list,
	Enric Balletbo i Serra
In-Reply-To: <85fb2cb764961845a32b8af51e8c6ccebf60c703.camel@collabora.com>

Hi,

On 19. 7. 11. 오전 3:30, Ezequiel Garcia wrote:
> Hello Chanwoo,
> 
> On Sat, 2019-06-22 at 19:46 +0900, Chanwoo Choi wrote:
>> Hi,
>>
>> 2019년 6월 22일 (토) 오전 6:42, Ezequiel Garcia <ezequiel@collabora.com>님이 작성:
>>> A bit unexpectedly (but still documented), request_module may
>>> return a positive value, in case of a modprobe error.
>>> This is currently causing issues in the devfreq framework.
>>>
>>> When a request_module exits with a positive value, we currently
>>> return that via ERR_PTR. However, because the value is positive,
>>> it's not a ERR_VALUE proper, and is therefore treated as a
>>> valid struct devfreq_governor pointer, leading to a kernel oops.
>>>
>>> Fix this by returning -EINVAL if request_module returns a positive
>>> value.
>>>
>>> Fixes: b53b0128052ff ("PM / devfreq: Fix static checker warning in try_then_request_governor")
>>> Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
>>> ---
>>> Changes from v1:
>>> * Rework the fix as suggested by Enric and Chanwoo,
>>>   handling the return vaue.
>>> ---
>>>  drivers/devfreq/devfreq.c | 2 +-
>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>>> index 6b6991f0e873..258f70c1e48f 100644
>>> --- a/drivers/devfreq/devfreq.c
>>> +++ b/drivers/devfreq/devfreq.c
>>> @@ -257,7 +257,7 @@ static struct devfreq_governor *try_then_request_governor(const char *name)
>>>                 /* Restore previous state before return */
>>>                 mutex_lock(&devfreq_list_lock);
>>>                 if (err)
>>> -                       return ERR_PTR(err);
>>> +                       return (err < 0) ? ERR_PTR(err) : ERR_PTR(-EINVAL);
>>>
>>>                 governor = find_devfreq_governor(name);
>>>         }
>>
>> Thanks you for fix-up.
>> Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
>>
>> But, you are missing the stable mailing list. In order to apply this
>> fix-up patch,\
>> you have to send it to stable mailing list. Please send it.
>>
>>
> 
> If I understand correctly, you or any of the devfreq maintainer
> can simply add a Cc: stable@vger.kernel.org tag.

Originally, the author have to send the stable mailing list
with the required style. You can do it.

> 
> This is documented as Option 1 in stable-kernel-rules.rst.
> 
> The Acked-by and Reviewed-by tags need to be collected anyway :-)

This patch[1] was picked to devfreq.git.
[1] https://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git/commit/?h=for-next&id=4f065f69ebc2bdf1fbe224816a3c471babd370dd

> 
> Thanks!
> Eze
> 
> 
> 


-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

^ permalink raw reply

* Re: cpufreq-related deadlock warning on recent linux-next
From: Viresh Kumar @ 2019-07-11  2:28 UTC (permalink / raw)
  To: Heiner Kallweit; +Cc: Rafael J. Wysocki, Linux PM
In-Reply-To: <6addb70f-6b21-4f09-e1a0-21476b55df14@gmail.com>

On 10-07-19, 22:53, Heiner Kallweit wrote:
> I just got the following when manually suspending the system with
> "systemctl suspend" and waking it up with the power button.
> 
> 
> [  380.203172] Restarting tasks ... done.
> 
> [  380.211714] ============================================
> [  380.211719] WARNING: possible recursive locking detected
> [  380.211726] 5.2.0-rc7-next-20190704+ #2 Not tainted
> [  380.211731] --------------------------------------------
> [  380.211737] systemd-sleep/2367 is trying to acquire lock:
> [  380.211745] 0000000043cf69ce (&policy->rwsem){+.+.}, at: refresh_frequency_limits+0x36/0x90
> [  380.211761]
>                but task is already holding lock:
> [  380.211767] 0000000043cf69ce (&policy->rwsem){+.+.}, at: cpufreq_cpu_acquire+0x25/0x50
> [  380.211777]
>                other info that might help us debug this:
> [  380.211783]  Possible unsafe locking scenario:
> 
> [  380.211789]        CPU0
> [  380.211792]        ----
> [  380.211795]   lock(&policy->rwsem);
> [  380.211800]   lock(&policy->rwsem);
> [  380.211805]
>                 *** DEADLOCK ***
> 
> [  380.211811]  May be due to missing lock nesting notation
> 
> [  380.211818] 8 locks held by systemd-sleep/2367:
> [  380.211823]  #0: 000000000e253e21 (sb_writers#5){.+.+}, at: vfs_write+0x16b/0x1d0
> [  380.211835]  #1: 00000000d0140159 (&of->mutex){+.+.}, at: kernfs_fop_write+0xfd/0x1c0
> [  380.211846]  #2: 00000000383c283a (kn->count#155){.+.+}, at: kernfs_fop_write+0x105/0x1c0
> [  380.211857]  #3: 000000007e6f342b (system_transition_mutex){+.+.}, at: pm_suspend.cold+0xd0/0x36a
> [  380.211869]  #4: 000000002ee59360 ((pm_chain_head).rwsem){++++}, at: __blocking_notifier_call_chain+0x46/0x80
> [  380.211883]  #5: 000000003972eb2e (&tz->lock){+.+.}, at: step_wise_throttle+0x3f/0x90
> [  380.211893]  #6: 0000000007747f02 (&cdev->lock){+.+.}, at: thermal_cdev_update+0x1e/0x16c
> [  380.211904]  #7: 0000000043cf69ce (&policy->rwsem){+.+.}, at: cpufreq_cpu_acquire+0x25/0x50

This is already fixed in linux-next few days back. Can you try the
latest stuff again ?

-- 
viresh

^ permalink raw reply

* Re: [PATCH] thermal: Add some error messages
From: Joe Perches @ 2019-07-11  0:14 UTC (permalink / raw)
  To: Amit Kucheria, linux-kernel, Zhang Rui, Eduardo Valentin,
	Daniel Lezcano
  Cc: linux-pm
In-Reply-To: <0d3d6111f8a8bd199f1a5a5cd8c4e83e4f0690be.1562757659.git.amit.kucheria@linaro.org>

On Wed, 2019-07-10 at 17:16 +0530, Amit Kucheria wrote:
> When registering a thermal zone device, we currently return -EINVAL in
> four cases. This makes it a little hard to debug the real cause of the
> failure.
> 
> Print some error messages to make it easier for developer to figure out
> what happened.
[]
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
[]
> @@ -1238,17 +1238,26 @@ thermal_zone_device_register(const char *type, int trips, int mask,
>  	int count;
>  	struct thermal_governor *governor;
>  
> -	if (!type || strlen(type) == 0)
> +	if (!type || strlen(type) == 0) {
> +		pr_err("Error: No thermal zone type defined");

Please use terminating newlines like:

		pr_err("Error: No thermal zone type defined\n");

[]
> +		pr_err("Error: Thermal zone name (%s) too long, should be under %d chars",

etc...



^ permalink raw reply

* Re: [PATCH v4 00/11] add thermal driver for h6
From: Vasily Khoruzhick @ 2019-07-10 23:09 UTC (permalink / raw)
  To: Yangtao Li
  Cc: rui.zhang, Eduardo Valentin, Daniel Lezcano, Rob Herring,
	Mark Rutland, Maxime Ripard, Chen-Yu Tsai, David S. Miller,
	Greg Kroah-Hartman, Mauro Carvalho Chehab, Linus Walleij,
	Nicolas Ferre, paulmck, devicetree, linux-kernel, arm-linux,
	Linux PM
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>

On Sun, Jun 23, 2019 at 9:42 AM Yangtao Li <tiny.windzz@gmail.com> wrote:
>
> This patchset add support for H3 and H6 thermal sensor.
>
> BTY, do a cleanup in thermal makfile.
>
> Yangtao Li (11):
>   thermal: sun8i: add thermal driver for h6
>   dt-bindings: thermal: add binding document for h6 thermal controller
>   thermal: fix indentation in makefile
>   thermal: sun8i: get ths sensor number from device compatible
>   thermal: sun8i: rework for sun8i_ths_get_temp()
>   thermal: sun8i: get ths init func from device compatible
>   thermal: sun8i: rework for ths irq handler func
>   thermal: sun8i: support ahb clocks
>   thermal: sun8i: rework for ths calibrate func
>   dt-bindings: thermal: add binding document for h3 thermal controller
>   thermal: sun8i: add thermal driver for h3

It would be nice to add dts changes to this series. It's unlikely that
you'll get any "Tested-by" otherwise.


>  .../bindings/thermal/sun8i-thermal.yaml       |  94 +++
>  MAINTAINERS                                   |   7 +
>  drivers/thermal/Kconfig                       |  14 +
>  drivers/thermal/Makefile                      |   9 +-
>  drivers/thermal/sun8i_thermal.c               | 534 ++++++++++++++++++
>  5 files changed, 654 insertions(+), 4 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
>  create mode 100644 drivers/thermal/sun8i_thermal.c
>
> ---
> v4:
> -add h3 support
> -fix yaml file
> ---
> 2.17.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH] driver core: Remove device link creation limitation
From: Saravana Kannan @ 2019-07-10 22:05 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Greg Kroah-Hartman, LKML, Linux PM, Lukas Wunner, Jon Hunter,
	Ulf Hansson, Marek Szyprowski
In-Reply-To: <3121545.4lOhFoIcdQ@kreacher>

On Wed, Jul 10, 2019 at 3:19 AM Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
>
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> If device_link_add() is called for a consumer/supplier pair with an
> existing device link between them and the existing link's type is
> not in agreement with the flags passed to that function by its
> caller, NULL will be returned.  That is seriously inconvenient,
> because it forces the callers of device_link_add() to worry about
> what others may or may not do even if that is not relevant to them
> for any other reasons.
>
> It turns out, however, that this limitation can be made go away
> relatively easily.
>
> The underlying observation is that if DL_FLAG_STATELESS has been
> passed to device_link_add() in flags for the given consumer/supplier
> pair at least once, calling either device_link_del() or
> device_link_remove() to release the link returned by it should work,
> but there are no other requirements associated with that flag.  In
> turn, if at least one of the callers of device_link_add() for the
> given consumer/supplier pair has not passed DL_FLAG_STATELESS to it
> in flags, the driver core should track the status of the link and act
> on it as appropriate (ie. the link should be treated as "managed").
> This means that DL_FLAG_STATELESS needs to be set for managed device
> links and it should be valid to call device_link_del() or
> device_link_remove() to drop references to them in certain
> sutiations.
>
> To allow that to happen, introduce a new (internal) device link flag
> called DL_FLAG_MANAGED and make device_link_add() set it automatically
> whenever DL_FLAG_STATELESS is not passed to it.  Also make it take
> additional references to existing device links that were previously
> stateless (that is, with DL_FLAG_STATELESS set and DL_FLAG_MANAGED
> unset) and will need to be managed going forward and initialize
> their status (which has been DL_STATE_NONE so far).
>
> Accordingly, when a managed device link is dropped automatically
> by the driver core, make it clear DL_FLAG_MANAGED, reset the link's
> status back to DL_STATE_NONE and drop the reference to it associated
> with DL_FLAG_MANAGED instead of just deleting it right away (to
> allow it to stay around in case it still needs to be released
> explicitly by someone).
>
> With that, since setting DL_FLAG_STATELESS doesn't mean that the
> device link in question is not managed any more, replace all of the
> status-tracking checks against DL_FLAG_STATELESS with analogous
> checks against DL_FLAG_MANAGED and update the documentation to
> reflect these changes.
>
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
>  Documentation/driver-api/device_link.rst |    4
>  drivers/base/core.c                      |  169 +++++++++++++++++--------------
>  drivers/base/power/runtime.c             |    4
>  include/linux/device.h                   |    4
>  4 files changed, 102 insertions(+), 79 deletions(-)
>
> Index: linux-pm/drivers/base/core.c
> ===================================================================
> --- linux-pm.orig/drivers/base/core.c
> +++ linux-pm/drivers/base/core.c
> @@ -124,6 +124,50 @@ static int device_is_dependent(struct de
>         return ret;
>  }
>
> +static void device_link_init_status(struct device_link *link,
> +                                   struct device *consumer,
> +                                   struct device *supplier)
> +{
> +       switch (supplier->links.status) {
> +       case DL_DEV_PROBING:
> +               switch (consumer->links.status) {
> +               case DL_DEV_PROBING:
> +                       /*
> +                        * A consumer driver can create a link to a supplier
> +                        * that has not completed its probing yet as long as it
> +                        * knows that the supplier is already functional (for
> +                        * example, it has just acquired some resources from the
> +                        * supplier).
> +                        */
> +                       link->status = DL_STATE_CONSUMER_PROBE;
> +                       break;
> +               default:
> +                       link->status = DL_STATE_DORMANT;
> +                       break;
> +               }
> +               break;
> +       case DL_DEV_DRIVER_BOUND:
> +               switch (consumer->links.status) {
> +               case DL_DEV_PROBING:
> +                       link->status = DL_STATE_CONSUMER_PROBE;
> +                       break;
> +               case DL_DEV_DRIVER_BOUND:
> +                       link->status = DL_STATE_ACTIVE;
> +                       break;
> +               default:
> +                       link->status = DL_STATE_AVAILABLE;
> +                       break;
> +               }
> +               break;
> +       case DL_DEV_UNBINDING:
> +               link->status = DL_STATE_SUPPLIER_UNBIND;
> +               break;
> +       default:
> +               link->status = DL_STATE_DORMANT;
> +               break;
> +       }
> +}
> +
>  static int device_reorder_to_tail(struct device *dev, void *not_used)
>  {
>         struct device_link *link;
> @@ -179,9 +223,9 @@ void device_pm_move_to_tail(struct devic
>   * of the link.  If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be
>   * ignored.
>   *
> - * If DL_FLAG_STATELESS is set in @flags, the link is not going to be managed by
> - * the driver core and, in particular, the caller of this function is expected
> - * to drop the reference to the link acquired by it directly.
> + * If DL_FLAG_STATELESS is set in @flags, the caller of this function is
> + * expected to release the link returned by it directly with the help of either
> + * device_link_del() or device_link_remove().
>   *
>   * If that flag is not set, however, the caller of this function is handing the
>   * management of the link over to the driver core entirely and its return value
> @@ -201,9 +245,16 @@ void device_pm_move_to_tail(struct devic
>   * be used to request the driver core to automaticall probe for a consmer
>   * driver after successfully binding a driver to the supplier device.
>   *
> - * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER
> - * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and
> - * will cause NULL to be returned upfront.
> + * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER,
> + * DL_FLAG_AUTOREMOVE_SUPPLIER, or DL_FLAG_AUTOPROBE_CONSUMER set in @flags at
> + * the same time is invalid and will cause NULL to be returned upfront.
> + * However, if a device link between the given @consumer and @supplier pair
> + * exists already when this function is called for them, the existing link will
> + * be returned regardless of its current type and status (the link's flags may
> + * be modified then).  The caller of this function is then expected to treat
> + * the link as though it has just been created, so (in particular) if
> + * DL_FLAG_STATELESS was passed in @flags, the link needs to be released
> + * explicitly when not needed any more (as stated above).
>   *
>   * A side effect of the link creation is re-ordering of dpm_list and the
>   * devices_kset list by moving the consumer device and all devices depending
> @@ -223,7 +274,8 @@ struct device_link *device_link_add(stru
>             (flags & DL_FLAG_STATELESS &&
>              flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
>                       DL_FLAG_AUTOREMOVE_SUPPLIER |
> -                     DL_FLAG_AUTOPROBE_CONSUMER)) ||
> +                     DL_FLAG_AUTOPROBE_CONSUMER |
> +                     DL_FLAG_MANAGED)) ||
>             (flags & DL_FLAG_AUTOPROBE_CONSUMER &&
>              flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
>                       DL_FLAG_AUTOREMOVE_SUPPLIER)))

If DL_FLAG_MANAGED is meant to be an internal flag (as in caller
shouldn't use it), maybe it'll be better to just check for it in flags
and reject it? Because looks like you are setting it anyway if
STATELESS isn't set.

> @@ -236,6 +288,9 @@ struct device_link *device_link_add(stru
>                 }
>         }
>
> +       if (!(flags & DL_FLAG_STATELESS))
> +               flags |= DL_FLAG_MANAGED;
> +
>         device_links_write_lock();
>         device_pm_lock();
>
> @@ -262,15 +317,6 @@ struct device_link *device_link_add(stru
>                 if (link->consumer != consumer)
>                         continue;
>
> -               /*
> -                * Don't return a stateless link if the caller wants a stateful
> -                * one and vice versa.
> -                */
> -               if (WARN_ON((flags & DL_FLAG_STATELESS) != (link->flags & DL_FLAG_STATELESS))) {
> -                       link = NULL;
> -                       goto out;
> -               }
> -
>                 if (flags & DL_FLAG_PM_RUNTIME) {
>                         if (!(link->flags & DL_FLAG_PM_RUNTIME)) {
>                                 pm_runtime_new_link(consumer);
> @@ -281,6 +327,7 @@ struct device_link *device_link_add(stru
>                 }
>
>                 if (flags & DL_FLAG_STATELESS) {
> +                       link->flags |= DL_FLAG_STATELESS;
>                         kref_get(&link->kref);
>                         goto out;
>                 }
> @@ -299,6 +346,11 @@ struct device_link *device_link_add(stru
>                         link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER |
>                                          DL_FLAG_AUTOREMOVE_SUPPLIER);
>                 }
> +               if (!(link->flags & DL_FLAG_MANAGED)) {
> +                       kref_get(&link->kref);
> +                       link->flags |= DL_FLAG_MANAGED;
> +                       device_link_init_status(link, consumer, supplier);
> +               }
>                 goto out;
>         }
>
> @@ -325,48 +377,10 @@ struct device_link *device_link_add(stru
>         kref_init(&link->kref);
>
>         /* Determine the initial link state. */
> -       if (flags & DL_FLAG_STATELESS) {
> +       if (flags & DL_FLAG_STATELESS)
>                 link->status = DL_STATE_NONE;
> -       } else {
> -               switch (supplier->links.status) {
> -               case DL_DEV_PROBING:
> -                       switch (consumer->links.status) {
> -                       case DL_DEV_PROBING:
> -                               /*
> -                                * A consumer driver can create a link to a
> -                                * supplier that has not completed its probing
> -                                * yet as long as it knows that the supplier is
> -                                * already functional (for example, it has just
> -                                * acquired some resources from the supplier).
> -                                */
> -                               link->status = DL_STATE_CONSUMER_PROBE;
> -                               break;
> -                       default:
> -                               link->status = DL_STATE_DORMANT;
> -                               break;
> -                       }
> -                       break;
> -               case DL_DEV_DRIVER_BOUND:
> -                       switch (consumer->links.status) {
> -                       case DL_DEV_PROBING:
> -                               link->status = DL_STATE_CONSUMER_PROBE;
> -                               break;
> -                       case DL_DEV_DRIVER_BOUND:
> -                               link->status = DL_STATE_ACTIVE;
> -                               break;
> -                       default:
> -                               link->status = DL_STATE_AVAILABLE;
> -                               break;
> -                       }
> -                       break;
> -               case DL_DEV_UNBINDING:
> -                       link->status = DL_STATE_SUPPLIER_UNBIND;
> -                       break;
> -               default:
> -                       link->status = DL_STATE_DORMANT;
> -                       break;
> -               }
> -       }
> +       else
> +               device_link_init_status(link, consumer, supplier);
>
>         /*
>          * Some callers expect the link creation during consumer driver probe to
> @@ -528,7 +542,7 @@ static void device_links_missing_supplie
>   * mark the link as "consumer probe in progress" to make the supplier removal
>   * wait for us to complete (or bad things may happen).
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */

Nitpick. It might be easier to read if you removed the double
negative. So something like:
Links without the DL_FLAG_MANAGED flag set are ignored.


>  int device_links_check_suppliers(struct device *dev)
>  {
> @@ -538,7 +552,7 @@ int device_links_check_suppliers(struct
>         device_links_write_lock();
>
>         list_for_each_entry(link, &dev->links.suppliers, c_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 if (link->status != DL_STATE_AVAILABLE) {
> @@ -563,7 +577,7 @@ int device_links_check_suppliers(struct
>   *
>   * Also change the status of @dev's links to suppliers to "active".
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */
>  void device_links_driver_bound(struct device *dev)
>  {
> @@ -572,7 +586,7 @@ void device_links_driver_bound(struct de
>         device_links_write_lock();
>
>         list_for_each_entry(link, &dev->links.consumers, s_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 /*
> @@ -593,7 +607,7 @@ void device_links_driver_bound(struct de
>         }
>
>         list_for_each_entry(link, &dev->links.suppliers, c_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
> @@ -605,6 +619,13 @@ void device_links_driver_bound(struct de
>         device_links_write_unlock();
>  }
>
> +static void device_link_drop_managed(struct device_link *link)
> +{
> +       link->flags &= ~DL_FLAG_MANAGED;
> +       WRITE_ONCE(link->status, DL_STATE_NONE);
> +       kref_put(&link->kref, __device_link_del);
> +}
> +
>  /**
>   * __device_links_no_driver - Update links of a device without a driver.
>   * @dev: Device without a drvier.
> @@ -615,18 +636,18 @@ void device_links_driver_bound(struct de
>   * unless they already are in the "supplier unbind in progress" state in which
>   * case they need not be updated.
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */
>  static void __device_links_no_driver(struct device *dev)
>  {
>         struct device_link *link, *ln;
>
>         list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER)
> -                       __device_link_del(&link->kref);
> +                       device_link_drop_managed(link);
>                 else if (link->status == DL_STATE_CONSUMER_PROBE ||
>                          link->status == DL_STATE_ACTIVE)
>                         WRITE_ONCE(link->status, DL_STATE_AVAILABLE);
> @@ -643,7 +664,7 @@ static void __device_links_no_driver(str
>   * %__device_links_no_driver() to update links to suppliers for it as
>   * appropriate.
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */
>  void device_links_no_driver(struct device *dev)
>  {
> @@ -652,7 +673,7 @@ void device_links_no_driver(struct devic
>         device_links_write_lock();
>
>         list_for_each_entry(link, &dev->links.consumers, s_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 /*
> @@ -680,7 +701,7 @@ void device_links_no_driver(struct devic
>   * invoke %__device_links_no_driver() to update links to suppliers for it as
>   * appropriate.
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */
>  void device_links_driver_cleanup(struct device *dev)
>  {
> @@ -689,7 +710,7 @@ void device_links_driver_cleanup(struct
>         device_links_write_lock();
>
>         list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER);
> @@ -702,7 +723,7 @@ void device_links_driver_cleanup(struct
>                  */
>                 if (link->status == DL_STATE_SUPPLIER_UNBIND &&
>                     link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
> -                       __device_link_del(&link->kref);
> +                       device_link_drop_managed(link);
>
>                 WRITE_ONCE(link->status, DL_STATE_DORMANT);
>         }
> @@ -724,7 +745,7 @@ void device_links_driver_cleanup(struct
>   *
>   * Return 'false' if there are no probing or active consumers.
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */
>  bool device_links_busy(struct device *dev)
>  {
> @@ -734,7 +755,7 @@ bool device_links_busy(struct device *de
>         device_links_write_lock();
>
>         list_for_each_entry(link, &dev->links.consumers, s_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 if (link->status == DL_STATE_CONSUMER_PROBE
> @@ -764,7 +785,7 @@ bool device_links_busy(struct device *de
>   * driver to unbind and start over (the consumer will not re-probe as we have
>   * changed the state of the link already).
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   */
>  void device_links_unbind_consumers(struct device *dev)
>  {
> @@ -776,7 +797,7 @@ void device_links_unbind_consumers(struc
>         list_for_each_entry(link, &dev->links.consumers, s_node) {
>                 enum device_link_state status;
>
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 status = link->status;
> Index: linux-pm/drivers/base/power/runtime.c
> ===================================================================
> --- linux-pm.orig/drivers/base/power/runtime.c
> +++ linux-pm/drivers/base/power/runtime.c
> @@ -1624,7 +1624,7 @@ void pm_runtime_remove(struct device *de
>   * runtime PM references to the device, drop the usage counter of the device
>   * (as many times as needed).
>   *
> - * Links with the DL_FLAG_STATELESS flag set are ignored.
> + * Links with the DL_FLAG_MANAGED flag unset are ignored.
>   *
>   * Since the device is guaranteed to be runtime-active at the point this is
>   * called, nothing else needs to be done here.
> @@ -1641,7 +1641,7 @@ void pm_runtime_clean_up_links(struct de
>         idx = device_links_read_lock();
>
>         list_for_each_entry_rcu(link, &dev->links.consumers, s_node) {
> -               if (link->flags & DL_FLAG_STATELESS)
> +               if (!(link->flags & DL_FLAG_MANAGED))
>                         continue;
>
>                 while (refcount_dec_not_one(&link->rpm_active))
> Index: linux-pm/include/linux/device.h
> ===================================================================
> --- linux-pm.orig/include/linux/device.h
> +++ linux-pm/include/linux/device.h
> @@ -829,12 +829,13 @@ enum device_link_state {
>  /*
>   * Device link flags.
>   *
> - * STATELESS: The core won't track the presence of supplier/consumer drivers.
> + * STATELESS: The core will not remove this link automatically.
>   * AUTOREMOVE_CONSUMER: Remove the link automatically on consumer driver unbind.
>   * PM_RUNTIME: If set, the runtime PM framework will use this link.
>   * RPM_ACTIVE: Run pm_runtime_get_sync() on the supplier during link creation.
>   * AUTOREMOVE_SUPPLIER: Remove the link automatically on supplier driver unbind.
>   * AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
> + * MANAGED: The core tracks presence of supplier/consumer drivers (internal).
>   */
>  #define DL_FLAG_STATELESS              BIT(0)
>  #define DL_FLAG_AUTOREMOVE_CONSUMER    BIT(1)
> @@ -842,6 +843,7 @@ enum device_link_state {
>  #define DL_FLAG_RPM_ACTIVE             BIT(3)
>  #define DL_FLAG_AUTOREMOVE_SUPPLIER    BIT(4)
>  #define DL_FLAG_AUTOPROBE_CONSUMER     BIT(5)
> +#define DL_FLAG_MANAGED                        BIT(6)
>
>  /**
>   * struct device_link - Device link representation.
> Index: linux-pm/Documentation/driver-api/device_link.rst
> ===================================================================
> --- linux-pm.orig/Documentation/driver-api/device_link.rst
> +++ linux-pm/Documentation/driver-api/device_link.rst
> @@ -78,8 +78,8 @@ typically deleted in its ``->remove`` ca
>  driver is compiled as a module, the device link is added on module load and
>  orderly deleted on unload.  The same restrictions that apply to device link
>  addition (e.g. exclusion of a parallel suspend/resume transition) apply equally
> -to deletion.  Device links with ``DL_FLAG_STATELESS`` unset (i.e. managed
> -device links) are deleted automatically by the driver core.
> +to deletion.  Device links managed by the driver core are deleted automatically
> +by it.
>
>  Several flags may be specified on device link addition, two of which
>  have already been mentioned above:  ``DL_FLAG_STATELESS`` to express that no
>

Other than those 2 minor comments, this looks good to me.

Reviewed-by: Saravana Kannan <saravanak@google.com>

-Saravana

^ permalink raw reply

* cpufreq-related deadlock warning on recent linux-next
From: Heiner Kallweit @ 2019-07-10 20:53 UTC (permalink / raw)
  To: Rafael J. Wysocki, Viresh Kumar; +Cc: Linux PM

I just got the following when manually suspending the system with
"systemctl suspend" and waking it up with the power button.


[  380.203172] Restarting tasks ... done.

[  380.211714] ============================================
[  380.211719] WARNING: possible recursive locking detected
[  380.211726] 5.2.0-rc7-next-20190704+ #2 Not tainted
[  380.211731] --------------------------------------------
[  380.211737] systemd-sleep/2367 is trying to acquire lock:
[  380.211745] 0000000043cf69ce (&policy->rwsem){+.+.}, at: refresh_frequency_limits+0x36/0x90
[  380.211761]
               but task is already holding lock:
[  380.211767] 0000000043cf69ce (&policy->rwsem){+.+.}, at: cpufreq_cpu_acquire+0x25/0x50
[  380.211777]
               other info that might help us debug this:
[  380.211783]  Possible unsafe locking scenario:

[  380.211789]        CPU0
[  380.211792]        ----
[  380.211795]   lock(&policy->rwsem);
[  380.211800]   lock(&policy->rwsem);
[  380.211805]
                *** DEADLOCK ***

[  380.211811]  May be due to missing lock nesting notation

[  380.211818] 8 locks held by systemd-sleep/2367:
[  380.211823]  #0: 000000000e253e21 (sb_writers#5){.+.+}, at: vfs_write+0x16b/0x1d0
[  380.211835]  #1: 00000000d0140159 (&of->mutex){+.+.}, at: kernfs_fop_write+0xfd/0x1c0
[  380.211846]  #2: 00000000383c283a (kn->count#155){.+.+}, at: kernfs_fop_write+0x105/0x1c0
[  380.211857]  #3: 000000007e6f342b (system_transition_mutex){+.+.}, at: pm_suspend.cold+0xd0/0x36a
[  380.211869]  #4: 000000002ee59360 ((pm_chain_head).rwsem){++++}, at: __blocking_notifier_call_chain+0x46/0x80
[  380.211883]  #5: 000000003972eb2e (&tz->lock){+.+.}, at: step_wise_throttle+0x3f/0x90
[  380.211893]  #6: 0000000007747f02 (&cdev->lock){+.+.}, at: thermal_cdev_update+0x1e/0x16c
[  380.211904]  #7: 0000000043cf69ce (&policy->rwsem){+.+.}, at: cpufreq_cpu_acquire+0x25/0x50
[  380.211914]
               stack backtrace:
[  380.211921] CPU: 1 PID: 2367 Comm: systemd-sleep Not tainted 5.2.0-rc7-next-20190704+ #2
[  380.211929] Hardware name: NA ZBOX-CI327NANO-GS-01/ZBOX-CI327NANO-GS-01, BIOS 5.12 04/26/2018
[  380.211937] Call Trace:
[  380.211944]  dump_stack+0x70/0xa0
[  380.211950]  __lock_acquire.cold+0xc0/0x206
[  380.211957]  lock_acquire+0x90/0x190
[  380.211963]  ? refresh_frequency_limits+0x36/0x90
[  380.211970]  down_write+0x38/0x80
[  380.211975]  ? refresh_frequency_limits+0x36/0x90
[  380.211981]  refresh_frequency_limits+0x36/0x90
[  380.211987]  ? cpufreq_cpu_get+0x60/0x70
[  380.211993]  ? cpufreq_get_policy+0x5e/0x70
[  380.212000]  ? __module_text_address+0x10/0x60
[  380.212006]  ? is_module_text_address+0x18/0x29
[  380.212013]  ? kernel_text_address+0x3e/0x90
[  380.212019]  ? __kernel_text_address+0x10/0x40
[  380.212026]  ? unwind_get_return_address+0x1c/0x30
[  380.212034]  ? __thaw_task+0x50/0x50
[  380.212040]  ? arch_stack_walk+0xa2/0xf0
[  380.212046]  ? stack_trace_save+0x55/0x80
[  380.212052]  ? alloc_list_entry+0x15/0x60
[  380.212058]  ? __lock_acquire+0x1067/0x1940
[  380.212064]  ? cpufreq_cpu_acquire+0x25/0x50
[  380.212071]  ? down_write+0x38/0x80
[  380.212076]  ? cpufreq_cpu_acquire+0x25/0x50
[  380.212082]  cpufreq_update_policy+0x51/0x70
[  380.212089]  cpufreq_set_cur_state.part.0+0x82/0xa0
[  380.212096]  processor_set_cur_state+0xa1/0x110
[  380.212102]  thermal_cdev_update+0x86/0x16c
[  380.212108]  step_wise_throttle+0x58/0x90
[  380.212114]  handle_thermal_trip+0x80/0x240
[  380.212120]  thermal_zone_device_update.part.0+0x68/0x1a0
[  380.212127]  thermal_pm_notify+0x10f/0x130
[  380.212133]  notifier_call_chain+0x53/0x80
[  380.212139]  __blocking_notifier_call_chain+0x5f/0x80
[  380.212146]  pm_notifier_call_chain+0x22/0x40
[  380.212151]  pm_suspend.cold+0x341/0x36a
[  380.212157]  state_store+0x7d/0xe0
[  380.212162]  kobj_attr_store+0x33/0x50
[  380.212168]  sysfs_kf_write+0x46/0x60
[  380.212174]  kernfs_fop_write+0x134/0x1c0
[  380.212181]  __vfs_write+0x37/0x70
[  380.212186]  vfs_write+0xdc/0x1d0
[  380.212191]  ksys_write+0x6e/0xf0
[  380.212196]  __x64_sys_write+0x19/0x20
[  380.212202]  do_syscall_64+0x50/0x1a0
[  380.212209]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
[  380.212216] RIP: 0033:0x7f0fd7e801f8
[  380.212223] Code: 89 02 48 c7 c0 ff ff ff ff eb bb 0f 1f 80 00 00 00 00 f3 0f 1e fa 48 8d 05 25 50 0d 00 8b 00 85 c0 75 17 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 60 c3 0f 1f 80 00 00 00 00 48 83 ec 28 48 89
[  380.212238] RSP: 002b:00007ffc3d20f288 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[  380.212247] RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007f0fd7e801f8
[  380.212253] RDX: 0000000000000004 RSI: 00007ffc3d20f370 RDI: 0000000000000004
[  380.212260] RBP: 00007ffc3d20f370 R08: 00007f0fd66a6840 R09: 0000561ed5a28e50
[  380.212267] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000004
[  380.212273] R13: 0000561ed5a27290 R14: 0000000000000004 R15: 00007f0fd7f507c0
[  382.995704] r8169 0000:03:00.0 enp3s0: Link is Up - 1Gbps/Full - flow control rx/tx

^ permalink raw reply

* [PATCHv3 3/6] acpi/cppc: Rework cppc_set_perf() to use cppc_regs index
From: Natarajan, Janakarajan @ 2019-07-10 18:37 UTC (permalink / raw)
  To: linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-pm@vger.kernel.org, devel@acpica.org
  Cc: Rafael J . Wysocki, Len Brown, Viresh Kumar, Robert Moore,
	Erik Schmauss, Ghannam, Yazen, Ghannam, Yazen,
	Natarajan, Janakarajan
In-Reply-To: <cover.1562781484.git.Janakarajan.Natarajan@amd.com>

From: Yazen Ghannam <Yazen.Ghannam@amd.com>

The cppc_set_perf() currently only works for DESIRED_PERF. To make it
generic, pass in the index of the register being accessed.

Also, rename cppc_set_perf() to cppc_set_reg(). This is in preparation
for it to be used for more than just the DESIRED_PERF register.

Signed-off-by: Yazen Ghannam <Yazen.Ghannam@amd.com>
[ carved out into a patch, cleaned up, productized ]
Signed-off-by: Janakarajan Natarajan <Janakarajan.Natarajan@amd.com>
---
 drivers/acpi/cppc_acpi.c       | 36 ++++++++++++++++++++++------------
 drivers/cpufreq/cppc_cpufreq.c |  6 +++---
 include/acpi/cppc_acpi.h       |  2 +-
 3 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 53a9dc9960b6..c13dacea4a8b 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -56,7 +56,7 @@ struct cppc_pcc_data {
 	/*
 	 * Lock to provide controlled access to the PCC channel.
 	 *
-	 * For performance critical usecases(currently cppc_set_perf)
+	 * For performance-critical usecases(currently cppc_set_reg)
 	 *	We need to take read_lock and check if channel belongs to OSPM
 	 * before reading or writing to PCC subspace
 	 *	We need to take write_lock before transferring the channel
@@ -1341,26 +1341,38 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
 EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
 
 /**
- * cppc_set_perf - Set a CPU's performance controls.
- * @cpu: CPU for which to set performance controls.
+ * cppc_set_reg - Set the CPUs control register.
+ * @cpu: CPU for which to set the register.
  * @perf_ctrls: ptr to cppc_perf_ctrls. See cppc_acpi.h
+ * @reg_idx: Index of the register being accessed
  *
  * Return: 0 for success, -ERRNO otherwise.
  */
-int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+int cppc_set_reg(int cpu, struct cppc_perf_ctrls *perf_ctrls,
+		 enum cppc_regs reg_idx)
 {
 	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
-	struct cpc_register_resource *desired_reg;
 	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
 	struct cppc_pcc_data *pcc_ss_data = NULL;
+	struct cpc_register_resource *reg;
 	int ret = 0;
+	u32 value;
 
 	if (!cpc_desc) {
 		pr_debug("No CPC descriptor for CPU:%d\n", cpu);
 		return -ENODEV;
 	}
 
-	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+	switch (reg_idx) {
+	case DESIRED_PERF:
+		value = perf_ctrls->desired_perf;
+		break;
+	default:
+		pr_debug("CPC register index #%d not writeable\n", reg_idx);
+		return -EINVAL;
+	}
+
+	reg = &cpc_desc->cpc_regs[reg_idx];
 
 	/*
 	 * This is Phase-I where we want to write to CPC registers
@@ -1369,7 +1381,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 * Since read_lock can be acquired by multiple CPUs simultaneously we
 	 * achieve that goal here
 	 */
-	if (CPC_IN_PCC(desired_reg)) {
+	if (CPC_IN_PCC(reg)) {
 		if (pcc_ss_id < 0) {
 			pr_debug("Invalid pcc_ss_id\n");
 			return -ENODEV;
@@ -1396,14 +1408,14 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 * Skip writing MIN/MAX until Linux knows how to come up with
 	 * useful values.
 	 */
-	cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
+	cpc_write(cpu, reg, value);
 
-	if (CPC_IN_PCC(desired_reg))
+	if (CPC_IN_PCC(reg))
 		up_read(&pcc_ss_data->pcc_lock);	/* END Phase-I */
 	/*
 	 * This is Phase-II where we transfer the ownership of PCC to Platform
 	 *
-	 * Short Summary: Basically if we think of a group of cppc_set_perf
+	 * Short Summary: Basically if we think of a group of cppc_set_reg
 	 * requests that happened in short overlapping interval. The last CPU to
 	 * come out of Phase-I will enter Phase-II and ring the doorbell.
 	 *
@@ -1446,7 +1458,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	 * case during a CMD_READ and if there are pending writes it delivers
 	 * the write command before servicing the read command
 	 */
-	if (CPC_IN_PCC(desired_reg)) {
+	if (CPC_IN_PCC(reg)) {
 		if (down_write_trylock(&pcc_ss_data->pcc_lock)) {/* BEGIN Phase-II */
 			/* Update only if there are pending write commands */
 			if (pcc_ss_data->pending_pcc_write_cmd)
@@ -1462,7 +1474,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
 	}
 	return ret;
 }
-EXPORT_SYMBOL_GPL(cppc_set_perf);
+EXPORT_SYMBOL_GPL(cppc_set_reg);
 
 /**
  * cppc_get_transition_latency - returns frequency transition latency in ns
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 8d8da763adc5..81e9dff03c92 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -207,7 +207,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
 	freqs.new = target_freq;
 
 	cpufreq_freq_transition_begin(policy, &freqs);
-	ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls);
+	ret = cppc_set_reg(cpu->cpu, &cpu->perf_ctrls, DESIRED_PERF);
 	cpufreq_freq_transition_end(policy, &freqs, ret != 0);
 
 	if (ret)
@@ -231,7 +231,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
 
 	cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
 
-	ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
+	ret = cppc_set_reg(cpu_num, &cpu->perf_ctrls, DESIRED_PERF);
 	if (ret)
 		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
 				cpu->perf_caps.lowest_perf, cpu_num, ret);
@@ -344,7 +344,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
 					cpu->perf_caps.highest_perf);
 	cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
 
-	ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
+	ret = cppc_set_reg(cpu_num, &cpu->perf_ctrls, DESIRED_PERF);
 	if (ret)
 		pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n",
 				cpu->perf_caps.highest_perf, cpu_num, ret);
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index a6a9373ab863..f229e903525d 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -135,7 +135,7 @@ struct cppc_cpudata {
 
 extern int cppc_get_desired_perf(int cpunum, u64 *desired_perf);
 extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
-extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
+extern int cppc_set_reg(int cpu, struct cppc_perf_ctrls *perf_ctrls, enum cppc_regs reg_idx);
 extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
 extern int acpi_get_psd_map(struct cppc_cpudata **);
 extern unsigned int cppc_get_transition_latency(int cpu);
-- 
2.17.1


^ permalink raw reply related

* [PATCHv3 4/6] acpi/cppc: Add support for optional CPPC registers
From: Natarajan, Janakarajan @ 2019-07-10 18:37 UTC (permalink / raw)
  To: linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-pm@vger.kernel.org, devel@acpica.org
  Cc: Rafael J . Wysocki, Len Brown, Viresh Kumar, Robert Moore,
	Erik Schmauss, Ghannam, Yazen, Ghannam, Yazen,
	Natarajan, Janakarajan
In-Reply-To: <cover.1562781484.git.Janakarajan.Natarajan@amd.com>

From: Yazen Ghannam <Yazen.Ghannam@amd.com>

Newer AMD processors support a subset of the optional CPPC registers.
Add support for these optional registers.

Signed-off-by: Yazen Ghannam <Yazen.Ghannam@amd.com>
[ carved out into a patch, cleaned up, productized ]
Signed-off-by: Janakarajan Natarajan <Janakarajan.Natarajan@amd.com>
---
 drivers/acpi/cppc_acpi.c | 88 +++++++++++++++++++++++++++++++++++++---
 include/acpi/cppc_acpi.h |  3 ++
 2 files changed, 86 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index c13dacea4a8b..b24e54263efb 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1367,6 +1367,18 @@ int cppc_set_reg(int cpu, struct cppc_perf_ctrls *perf_ctrls,
 	case DESIRED_PERF:
 		value = perf_ctrls->desired_perf;
 		break;
+	case MAX_PERF:
+		value = perf_ctrls->max_perf;
+		break;
+	case MIN_PERF:
+		value = perf_ctrls->min_perf;
+		break;
+	case ENERGY_PERF:
+		value = perf_ctrls->energy_perf;
+		break;
+	case AUTO_SEL_ENABLE:
+		value = perf_ctrls->auto_sel_enable;
+		break;
 	default:
 		pr_debug("CPC register index #%d not writeable\n", reg_idx);
 		return -EINVAL;
@@ -1404,11 +1416,8 @@ int cppc_set_reg(int cpu, struct cppc_perf_ctrls *perf_ctrls,
 		cpc_desc->write_cmd_status = 0;
 	}
 
-	/*
-	 * Skip writing MIN/MAX until Linux knows how to come up with
-	 * useful values.
-	 */
-	cpc_write(cpu, reg, value);
+	if (CPC_SUPPORTED(reg))
+		cpc_write(cpu, reg, value);
 
 	if (CPC_IN_PCC(reg))
 		up_read(&pcc_ss_data->pcc_lock);	/* END Phase-I */
@@ -1476,6 +1485,75 @@ int cppc_set_reg(int cpu, struct cppc_perf_ctrls *perf_ctrls,
 }
 EXPORT_SYMBOL_GPL(cppc_set_reg);
 
+int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
+{
+	struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+	struct cpc_register_resource *desired_reg, *max_reg, *min_reg;
+	struct cpc_register_resource *energy_reg, *auto_sel_enable_reg;
+	int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
+	u64 desired, max, min, energy, auto_sel_enable;
+	struct cppc_pcc_data *pcc_ss_data = NULL;
+	int ret = 0, regs_in_pcc = 0;
+
+	if (!cpc_desc) {
+		pr_debug("No CPC descriptor for CPU: %d\n", cpu);
+		return -ENODEV;
+	}
+
+	desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+	max_reg = &cpc_desc->cpc_regs[MAX_PERF];
+	min_reg = &cpc_desc->cpc_regs[MIN_PERF];
+	energy_reg = &cpc_desc->cpc_regs[ENERGY_PERF];
+	auto_sel_enable_reg = &cpc_desc->cpc_regs[AUTO_SEL_ENABLE];
+
+	/* Check if any of the perf registers are in PCC */
+	if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(max_reg) ||
+	    CPC_IN_PCC(min_reg) || CPC_IN_PCC(energy_reg) ||
+	    CPC_IN_PCC(auto_sel_enable_reg)) {
+		pcc_ss_data = pcc_data[pcc_ss_id];
+		down_write(&pcc_ss_data->pcc_lock);
+		regs_in_pcc = 1;
+
+		/*Ring doorbell once to update PCC subspace */
+		if (send_pcc_cmd(pcc_ss_id, CMD_READ) < 0) {
+			ret = -EIO;
+			goto out_err;
+		}
+	}
+
+	/* desired_perf is the only mandatory value in perf_ctrls */
+	if (cpc_read(cpu, desired_reg, &desired))
+		ret = -EFAULT;
+
+	if (CPC_SUP_BUFFER_ONLY(max_reg) && cpc_read(cpu, max_reg, &max))
+		ret = -EFAULT;
+
+	if (CPC_SUP_BUFFER_ONLY(min_reg) && cpc_read(cpu, min_reg, &min))
+		ret = -EFAULT;
+
+	if (CPC_SUP_BUFFER_ONLY(energy_reg) &&
+	    cpc_read(cpu, energy_reg, &energy))
+		ret = -EFAULT;
+
+	if (CPC_SUPPORTED(auto_sel_enable_reg) &&
+	    cpc_read(cpu, auto_sel_enable_reg, &auto_sel_enable))
+		ret = -EFAULT;
+
+	if (!ret) {
+		perf_ctrls->desired_perf = desired;
+		perf_ctrls->max_perf = max;
+		perf_ctrls->min_perf = min;
+		perf_ctrls->energy_perf = energy;
+		perf_ctrls->auto_sel_enable = auto_sel_enable;
+	}
+
+out_err:
+	if (regs_in_pcc)
+		up_write(&pcc_ss_data->pcc_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf);
+
 /**
  * cppc_get_transition_latency - returns frequency transition latency in ns
  *
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index f229e903525d..80720b246c51 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -113,6 +113,8 @@ struct cppc_perf_ctrls {
 	u32 max_perf;
 	u32 min_perf;
 	u32 desired_perf;
+	u32 auto_sel_enable;
+	u32 energy_perf;
 };
 
 struct cppc_perf_fb_ctrs {
@@ -136,6 +138,7 @@ struct cppc_cpudata {
 extern int cppc_get_desired_perf(int cpunum, u64 *desired_perf);
 extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
 extern int cppc_set_reg(int cpu, struct cppc_perf_ctrls *perf_ctrls, enum cppc_regs reg_idx);
+extern int cppc_get_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
 extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
 extern int acpi_get_psd_map(struct cppc_cpudata **);
 extern unsigned int cppc_get_transition_latency(int cpu);
-- 
2.17.1


^ permalink raw reply related

* [PATCHv3 2/6] acpi/cppc: Ensure only supported CPPC sysfs entries are created
From: Natarajan, Janakarajan @ 2019-07-10 18:37 UTC (permalink / raw)
  To: linux-acpi@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-pm@vger.kernel.org, devel@acpica.org
  Cc: Rafael J . Wysocki, Len Brown, Viresh Kumar, Robert Moore,
	Erik Schmauss, Ghannam, Yazen, Natarajan, Janakarajan
In-Reply-To: <cover.1562781484.git.Janakarajan.Natarajan@amd.com>

Add attributes only for registers that are supported by the platform.
This prevents unsupported, optional registers from having sysfs entries
created.

Signed-off-by: Janakarajan Natarajan <Janakarajan.Natarajan@amd.com>
---
 drivers/acpi/cppc_acpi.c | 82 +++++++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 14 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index c43de65531ae..53a9dc9960b6 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -183,22 +183,8 @@ static ssize_t show_feedback_ctrs(struct kobject *kobj,
 }
 define_one_cppc_ro(feedback_ctrs);
 
-static struct attribute *cppc_attrs[] = {
-	&feedback_ctrs.attr,
-	&reference_perf.attr,
-	&wraparound_time.attr,
-	&highest_perf.attr,
-	&lowest_perf.attr,
-	&lowest_nonlinear_perf.attr,
-	&nominal_perf.attr,
-	&nominal_freq.attr,
-	&lowest_freq.attr,
-	NULL
-};
-
 static struct kobj_type cppc_ktype = {
 	.sysfs_ops = &kobj_sysfs_ops,
-	.default_attrs = cppc_attrs,
 };
 
 static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit)
@@ -733,6 +719,69 @@ static bool is_buf_only(int reg_idx)
 				 CPC_SUP_BUFFER_ONLY(&cpc->cpc_regs[idx]) : \
 				 CPC_SUPPORTED(&cpc->cpc_regs[idx]))
 
+static int is_mandatory_reg(int reg_idx)
+{
+	switch (reg_idx) {
+	case HIGHEST_PERF:
+	case NOMINAL_PERF:
+	case LOW_NON_LINEAR_PERF:
+	case LOWEST_PERF:
+	case REFERENCE_CTR:
+	case DELIVERED_CTR:
+		return 1;
+	}
+
+	return 0;
+}
+
+#define MANDATORY_REG_CNT	6
+
+static int set_cppc_attrs(struct cpc_desc *cpc, int entries)
+{
+	int i, attr_i = 0, opt_reg_cnt;
+	static struct attribute **cppc_attrs;
+
+	cppc_attrs = kcalloc(entries, sizeof(*cppc_attrs), GFP_KERNEL);
+	if (!cppc_attrs)
+		return -ENOMEM;
+
+	/* Set optional regs */
+	opt_reg_cnt = entries - MANDATORY_REG_CNT;
+	for (i = 0; i < MAX_CPC_REG_ENT && attr_i < opt_reg_cnt; i++) {
+		if (is_mandatory_reg(i) || !REG_SUPPORTED(cpc, i))
+			continue;
+
+		switch (i) {
+		case NOMINAL_FREQ:
+			cppc_attrs[attr_i++] = &nominal_freq.attr;
+			break;
+		case LOWEST_FREQ:
+			cppc_attrs[attr_i++] = &lowest_freq.attr;
+			break;
+		case REFERENCE_PERF:
+			cppc_attrs[attr_i++] = &reference_perf.attr;
+			break;
+		case CTR_WRAP_TIME:
+			cppc_attrs[attr_i++] = &wraparound_time.attr;
+			break;
+		}
+	}
+
+	/* Set mandatory regs */
+	cppc_attrs[attr_i++] = &highest_perf.attr;
+	cppc_attrs[attr_i++] = &nominal_perf.attr;
+	cppc_attrs[attr_i++] = &lowest_nonlinear_perf.attr;
+	cppc_attrs[attr_i++] = &lowest_perf.attr;
+
+	/* Set feedback_ctr sysfs entry */
+	cppc_attrs[attr_i] = &feedback_ctrs.attr;
+
+	/* Set kobj_type member */
+	cppc_ktype.default_attrs = cppc_attrs;
+
+	return 0;
+}
+
 /**
  * acpi_cppc_processor_probe - Search for per CPU _CPC objects.
  * @pr: Ptr to acpi_processor containing this CPU's logical ID.
@@ -887,6 +936,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
 	/* Plug PSD data into this CPU's CPC descriptor. */
 	per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr;
 
+	ret = set_cppc_attrs(cpc_ptr, num_ent - 2);
+	if (ret)
+		goto out_free;
+
 	ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
 			"acpi_cppc");
 	if (ret) {
@@ -948,6 +1001,7 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
 			iounmap(addr);
 	}
 
+	kfree(cppc_ktype.default_attrs);
 	kobject_put(&cpc_ptr->kobj);
 	kfree(cpc_ptr);
 }
-- 
2.17.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox