[PATCH v2] dt: platform driver: Fill the resources before probe and defer if needed

Jean-Jacques Hiblot jjhiblot at traphandler.com
Thu Feb 27 11:43:15 EST 2014


Hi Grant,

2014-02-21 17:22 GMT+01:00 Jean-Jacques Hiblot <jjhiblot at traphandler.com>:
> Hi Grygorii,
>
> 2014-02-21 16:37 GMT+01:00 Strashko, Grygorii <grygorii.strashko at ti.com>:
>> Hi  Jean-Jacques,
>>
>> Sorry for top posting.
>>
>> As I know, there have been several attempts to solve the same problem already:)
>> [1] https://lkml.org/lkml/2013/9/18/216
>> [2] https://lkml.org/lkml/2013/11/22/520
>> [3] https://lkml.org/lkml/2014/1/8/240
>>
>> There are some questions related to your approach:
>> 1) How to distinguish between cases "IRQ domain not ready" and "wrong IRQ data in DT" or other IRQ parsing errors?
>> Otherwise, Driver's probe will be deffered wrongly and forever,
>> Thierry Reding has tried to solve this in [1].
>
> This approach doesn't really care about the cause of the problem.  I'm
> of the opinion that never-ending deferred probing is not a big issue,
> being not triggered so often after start-up (only when a new device is
> probed). But if we need to make it right, then we would have to change
> a bit the API of irq_create_of_mapping() and irq_of_parse_and_map()
> (or maybe duplicate this one to keep the patch small) to return a real
> error code instead a simple 0. Then would should be able to
> distinguish the different error causes.

What do you think of the 2nd version of the patch? Is it all right to
allways return EPROBE_DEFER or should we try to discriminate the error
cause?

Jean-Jacques

>
>>
>> 2) How will be handled driver reloading situation?
>> The worst case (sparse IRQ enabled):
>> - remove driver A
>> - remove driver B (irq-controller)
>> - load driver B <--- different set of Linux IRQ numbers can be assigned
>> - load driver A <--- oops. IRQ resources table contains invalid data
>>
>
> It's not handled in the current implementation. But if all interrupts
> entries are re-parsed (see my comment for Grant), it should be all
> right.
> Another problem would appear if the DT is dynamically updated and the
> number of resource is changed. In the 1st version of the patch, this
> was handled but it made the function more expensive.
>
> Jean-Jacques
>>
>>
>> Best regards,
>> Grygorii Strashko
>>
>> =============================================
>>
>> The goal of this patch is to allow drivers to be probed even if at the time of
>> the DT parsing some of their ressources are not available yet.
>>
>> In the current situation, the resource of a platform device are filled from the
>> DT at the time the device is created (of_device_alloc()). The drawbackof this
>> is that a device sitting close to the top of the DT (ahb for example) but
>> depending on ressources that are initialized later (IRQ domain dynamically
>> created for example)  will fail to probe because the ressources don't exist
>> at this time.
>>
>> This patch fills the resource structure only before the device is probed and
>> will defer the probe if the resource are not available yet.
>>
>> Signed-off-by: Jean-Jacques Hiblot <jjhiblot at traphandler.com>
>> Reviewed-by: Gregory CLEMENT <gregory.clement at free-electrons.com>
>> ---
>>
>> Hi Grant,
>>
>> I reworked the patch as you proposed. To keep the overhead minimum, nirq and
>> nreg are computed only the first time.
>> In this implementation, only the missing IRQ ressources are re-tried for. It could
>> easily be changed to re-parse all the IRQs though (replace if (!res->flags)
>> with if ((!res->flags) || (res->flags & IORESOURCE_IRQ)).
>>
>> drivers/base/platform.c     |   5 +++
>>  drivers/of/platform.c       | 100 +++++++++++++++++++++++++++++++++-----------
>>  include/linux/of_platform.h |  10 +++++
>>  3 files changed, 90 insertions(+), 25 deletions(-)
>>
>> diff --git a/drivers/base/platform.c b/drivers/base/platform.c
>> index bc78848..cee9b8d 100644
>> --- a/drivers/base/platform.c
>> +++ b/drivers/base/platform.c
>> @@ -481,6 +481,10 @@ static int platform_drv_probe(struct device *_dev)
>>         struct platform_device *dev = to_platform_device(_dev);
>>         int ret;
>>
>> +       ret = of_platform_device_prepare(dev);
>> +       if (ret)
>> +               goto error;
>> +
>>         if (ACPI_HANDLE(_dev))
>>                 acpi_dev_pm_attach(_dev, true);
>>
>> @@ -488,6 +492,7 @@ static int platform_drv_probe(struct device *_dev)
>>         if (ret && ACPI_HANDLE(_dev))
>>                 acpi_dev_pm_detach(_dev, true);
>>
>> +error:
>>         if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
>>                 dev_warn(_dev, "probe deferral not supported\n");
>>                 ret = -ENXIO;
>> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
>> index 404d1da..a4e2602 100644
>> --- a/drivers/of/platform.c
>> +++ b/drivers/of/platform.c
>> @@ -141,36 +141,11 @@ struct platform_device *of_device_alloc(struct device_node *np,
>>                                   struct device *parent)
>>  {
>>         struct platform_device *dev;
>> -       int rc, i, num_reg = 0, num_irq;
>> -       struct resource *res, temp_res;
>>
>>         dev = platform_device_alloc("", -1);
>>         if (!dev)
>>                 return NULL;
>>
>> -       /* count the io and irq resources */
>> -       if (of_can_translate_address(np))
>> -               while (of_address_to_resource(np, num_reg, &temp_res) == 0)
>> -                       num_reg++;
>> -       num_irq = of_irq_count(np);
>> -
>> -       /* Populate the resource table */
>> -       if (num_irq || num_reg) {
>> -               res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
>> -               if (!res) {
>> -                       platform_device_put(dev);
>> -                       return NULL;
>> -               }
>> -
>> -               dev->num_resources = num_reg + num_irq;
>> -               dev->resource = res;
>> -               for (i = 0; i < num_reg; i++, res++) {
>> -                       rc = of_address_to_resource(np, i, res);
>> -                       WARN_ON(rc);
>> -               }
>> -               WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
>> -       }
>> -
>>         dev->dev.of_node = of_node_get(np);
>>  #if defined(CONFIG_MICROBLAZE)
>>         dev->dev.dma_mask = &dev->archdata.dma_mask;
>> @@ -233,6 +208,81 @@ static struct platform_device *of_platform_device_create_pdata(
>>         return dev;
>>  }
>>
>> +static int of_reg_count(struct device_node *np)
>> +{
>> +       int nreg = 0;
>> +       if (of_can_translate_address(np)) {
>> +               struct resource temp_res;
>> +               while (of_address_to_resource(np, nreg, &temp_res) == 0)
>> +                       nreg++;
>> +       }
>> +       return nreg;
>> +}
>> +
>> +int of_platform_device_prepare(struct platform_device *dev)
>> +{
>> +       struct device_node *np;
>> +       int i, irq_index;
>> +       struct resource *res;
>> +
>> +       /*
>> +        * This function applies only devices described in the DT.
>> +        * Other platform devices have their ressources already populated.
>> +        */
>> +       np = dev->dev.of_node;
>> +       if (!np)
>> +               return 0;
>> +
>> +       /* Populate the resource table */
>> +       if (!dev->resource) {
>> +               int rc, nreg = 0, nirq;
>> +               /* count the io and irq resources */
>> +               nreg = of_reg_count(np);
>> +               nirq = of_irq_count(np);
>> +
>> +               if (!nirq && !nreg)
>> +                       return 0;
>> +
>> +               res = kzalloc(sizeof(*res) * (nirq + nreg), GFP_KERNEL);
>> +               if (!res)
>> +                       return -ENOMEM;
>> +
>> +               dev->resource = res;
>> +               dev->num_resources = nreg + nirq;
>> +
>> +               for (i = 0; i < nreg; i++, res++) {
>> +                       rc = of_address_to_resource(np, i, res);
>> +                       if (WARN_ON(rc)) {
>> +                               /* THIS IS BAD; don't try to defer probing */
>> +                               dev->num_resources = 0;
>> +                               dev->resource = NULL;
>> +                               kfree(res);
>> +                               return rc;
>> +                       }
>> +               }
>> +
>> +               if (!rc && of_irq_to_resource_table(np, res, nirq) != nirq) {
>> +                       /* IRQ controller is yet available. defer probing */
>> +                       return -EPROBE_DEFER;
>> +               }
>> +
>> +               return 0;
>> +       }
>> +
>> +       /* See which IRQ resources need to be redone */
>> +       irq_index = 0;
>> +       for (i = 0, res = dev->resource; i < dev->num_resources; i++, res++) {
>> +               if (!res->flags) {
>> +                       if (!of_irq_to_resource(np, irq_index, res))
>> +                               return -EPROBE_DEFER;
>> +                       irq_index++;
>> +               } else if (res->flags & IORESOURCE_IRQ)
>> +                       irq_index++;
>> +       }
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(of_platform_device_prepare);
>> +
>>  /**
>>   * of_platform_device_create - Alloc, initialize and register an of_device
>>   * @np: pointer to node to create device for
>> diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
>> index 05cb4a9..4e487ff 100644
>> --- a/include/linux/of_platform.h
>> +++ b/include/linux/of_platform.h
>> @@ -53,6 +53,16 @@ struct of_dev_auxdata {
>>
>>  extern const struct of_device_id of_default_bus_match_table[];
>>
>> +/* Populate the resource for a platform device */
>> +#ifdef CONFIG_OF
>> +int of_platform_device_prepare(struct platform_device *dev);
>> +#else
>> +static inline int of_platform_device_prepare(
>> +       struct platform_device *dev)
>> +{
>> +       return 0;
>> +}
>> +#endif
>>  /* Platform drivers register/unregister */
>>  extern struct platform_device *of_device_alloc(struct device_node *np,
>>                                          const char *bus_id,
>> --
>> 1.9.0
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list