On Wed, Mar 3, 2021 at 12:32 AM Saravana Kannan <sarava...@google.com> wrote:
>
> The addition/probe of amba devices has its own weird deferred probe
> mechanism that needs to be maintained separately. It doesn't
> automatically get any bugs fixes or improvements to the common deferred
> probe mechanism.
>
> It also has an arbitrary 5 second periodic attempt. So, even if the
> resources are available, there can be an arbitrary delay before amba
> devices are probed.
>
> This patch used a proxy/stub device so that amba devices can hook into
> the common deferred probe mechanism. This also means amba devices get
> probed as soon as their resources are available.
>
> Cc: Linus Walleij <linus.wall...@linaro.org>
> Cc: Ulf Hansson <ulf.hans...@linaro.org>
> Cc: John Stultz <john.stu...@linaro.org>
> Cc: Saravana Kannan <sarava...@google.com>
> Cc: Sudeep Holla <sudeep.ho...@arm.com>
> Cc: Nicolas Saenz Julienne <nsaenzjulie...@suse.de>
> Cc: Geert Uytterhoeven <geert+rene...@glider.be>
> Cc: Russell King <li...@armlinux.org.uk>
> Cc: Rob Herring <r...@kernel.org>
> Signed-off-by: Saravana Kannan <sarava...@google.com>
> ---
>
> We talked about this almost a year ago[1] and it has been nagging me all
> this time. So, finally got around to giving it a shot. This actually
> seems to work -- I tested it on a device that was lying around.

Btw, what really is the requirement wrt the uevents? Will this whole
thing work if I figure out a way to do this:

1. Add an amba device without the AMBA_ID and MODALIAS uevent vars and
without periphid set.
2. Once the resources (clocks, etc) are available, set periphid and
add those uevents.
3. Trigger a normal deferred probe attempt.

Will userspace properly load the right driver and will things work if
there is a couple of seconds of (theoretical) delay between (1) and
(2)? If so, that might be pretty easy to do without a stub device too.

-Saravana

>
> Thoughts?
>
> [1] - 
> https://lore.kernel.org/linux-arm-kernel/cagetcx8cn-b6l2y10lkb91s3n06b6+be2z_a0402eyny-8y...@mail.gmail.com/
>
> -Saravana
>
>  drivers/amba/bus.c       | 116 ++++++++++++++++++---------------------
>  include/linux/amba/bus.h |   1 +
>  2 files changed, 53 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
> index 939ca220bf78..393d189b6bca 100644
> --- a/drivers/amba/bus.c
> +++ b/drivers/amba/bus.c
> @@ -24,6 +24,9 @@
>
>  #define to_amba_driver(d)      container_of(d, struct amba_driver, drv)
>
> +static int amba_proxy_probe(struct amba_device *adev,
> +                           const struct amba_id *id);
> +
>  /* called on periphid match and class 0x9 coresight device. */
>  static int
>  amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev)
> @@ -46,6 +49,8 @@ amba_cs_uci_id_match(const struct amba_id *table, struct 
> amba_device *dev)
>  static const struct amba_id *
>  amba_lookup(const struct amba_id *table, struct amba_device *dev)
>  {
> +       if (!table)
> +               return NULL;
>         while (table->mask) {
>                 if (((dev->periphid & table->mask) == table->id) &&
>                         ((dev->cid != CORESIGHT_CID) ||
> @@ -185,6 +190,9 @@ static int amba_probe(struct device *dev)
>         const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
>         int ret;
>
> +       if (!pcdev->periphid)
> +               return pcdrv->probe(pcdev, 0);
> +
>         do {
>                 ret = of_clk_set_defaults(dev->of_node, false);
>                 if (ret < 0)
> @@ -224,6 +232,9 @@ static int amba_remove(struct device *dev)
>         struct amba_device *pcdev = to_amba_device(dev);
>         struct amba_driver *drv = to_amba_driver(dev->driver);
>
> +       if (!pcdev->periphid)
> +               return 0;
> +
>         pm_runtime_get_sync(dev);
>         if (drv->remove)
>                 drv->remove(pcdev);
> @@ -325,9 +336,20 @@ struct bus_type amba_bustype = {
>  };
>  EXPORT_SYMBOL_GPL(amba_bustype);
>
> +static struct amba_driver amba_proxy_drv = {
> +       .drv = {
> +               .name = "amba-proxy",
> +       },
> +       .probe = amba_proxy_probe,
> +};
> +
>  static int __init amba_init(void)
>  {
> -       return bus_register(&amba_bustype);
> +       int ret = bus_register(&amba_bustype);
> +
> +       if (ret)
> +               return ret;
> +       return amba_driver_register(&amba_proxy_drv);
>  }
>
>  postcore_initcall(amba_init);
> @@ -490,58 +512,19 @@ static int amba_device_try_add(struct amba_device *dev, 
> struct resource *parent)
>         goto err_release;
>  }
>
> -/*
> - * Registration of AMBA device require reading its pid and cid registers.
> - * To do this, the device must be turned on (if it is a part of power domain)
> - * and have clocks enabled. However in some cases those resources might not 
> be
> - * yet available. Returning EPROBE_DEFER is not a solution in such case,
> - * because callers don't handle this special error code. Instead such devices
> - * are added to the special list and their registration is retried from
> - * periodic worker, until all resources are available and registration 
> succeeds.
> - */
> -struct deferred_device {
> -       struct amba_device *dev;
> -       struct resource *parent;
> -       struct list_head node;
> -};
> -
> -static LIST_HEAD(deferred_devices);
> -static DEFINE_MUTEX(deferred_devices_lock);
> -
> -static void amba_deferred_retry_func(struct work_struct *dummy);
> -static DECLARE_DELAYED_WORK(deferred_retry_work, amba_deferred_retry_func);
> -
> -#define DEFERRED_DEVICE_TIMEOUT (msecs_to_jiffies(5 * 1000))
> -
> -static int amba_deferred_retry(void)
> +static int amba_proxy_probe(struct amba_device *adev,
> +                           const struct amba_id *id)
>  {
> -       struct deferred_device *ddev, *tmp;
> -
> -       mutex_lock(&deferred_devices_lock);
> -
> -       list_for_each_entry_safe(ddev, tmp, &deferred_devices, node) {
> -               int ret = amba_device_try_add(ddev->dev, ddev->parent);
> -
> -               if (ret == -EPROBE_DEFER)
> -                       continue;
> -
> -               list_del_init(&ddev->node);
> -               kfree(ddev);
> -       }
> -
> -       mutex_unlock(&deferred_devices_lock);
> +       int ret;
>
> -       return 0;
> -}
> -late_initcall(amba_deferred_retry);
> +       if (!adev->other_dev)
> +               return -ENODEV;
>
> -static void amba_deferred_retry_func(struct work_struct *dummy)
> -{
> -       amba_deferred_retry();
> +       ret = amba_device_try_add(adev->other_dev, adev->dev.platform_data);
> +       if (ret != -EPROBE_DEFER)
> +               adev->other_dev = NULL;
>
> -       if (!list_empty(&deferred_devices))
> -               schedule_delayed_work(&deferred_retry_work,
> -                                     DEFERRED_DEVICE_TIMEOUT);
> +       return ret;
>  }
>
>  /**
> @@ -557,25 +540,30 @@ int amba_device_add(struct amba_device *dev, struct 
> resource *parent)
>  {
>         int ret = amba_device_try_add(dev, parent);
>
> +       /*
> +        * Registration of AMBA device require reading its pid and cid
> +        * registers.  To do this, the device must be turned on (if it is a
> +        * part of power domain) and have clocks enabled. However in some 
> cases
> +        * those resources might not be yet available. Returning EPROBE_DEFER
> +        * is not a solution in such case, because callers don't handle this
> +        * special error code. Instead, create a dummy proxy device that'll
> +        * keep deferring probe until all the resources are available and then
> +        * add the real device.
> +        */
>         if (ret == -EPROBE_DEFER) {
> -               struct deferred_device *ddev;
> +               struct amba_device *proxy_dev;
>
> -               ddev = kmalloc(sizeof(*ddev), GFP_KERNEL);
> -               if (!ddev)
> +               proxy_dev = amba_device_alloc(NULL, 0, 0);
> +               if (!proxy_dev)
>                         return -ENOMEM;
>
> -               ddev->dev = dev;
> -               ddev->parent = parent;
> -               ret = 0;
> -
> -               mutex_lock(&deferred_devices_lock);
> -
> -               if (list_empty(&deferred_devices))
> -                       schedule_delayed_work(&deferred_retry_work,
> -                                             DEFERRED_DEVICE_TIMEOUT);
> -               list_add_tail(&ddev->node, &deferred_devices);
> -
> -               mutex_unlock(&deferred_devices_lock);
> +               proxy_dev->other_dev = dev;
> +               proxy_dev->dev.platform_data = parent;
> +               proxy_dev->driver_override = "amba-proxy";
> +               dev_set_name(&proxy_dev->dev, "proxy:%s", 
> dev_name(&dev->dev));
> +               ret = device_add(&proxy_dev->dev);
> +               if (ret)
> +                       put_device(&proxy_dev->dev);
>         }
>         return ret;
>  }
> diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
> index 6cc93ab5b809..432b32cf8fcc 100644
> --- a/include/linux/amba/bus.h
> +++ b/include/linux/amba/bus.h
> @@ -71,6 +71,7 @@ struct amba_device {
>         struct amba_cs_uci_id   uci;
>         unsigned int            irq[AMBA_NR_IRQS];
>         char                    *driver_override;
> +       struct amba_device      *other_dev;
>  };
>
>  struct amba_driver {
> --
> 2.30.1.766.gb4fecdf3b7-goog
>

Reply via email to