diff mbox

ACPI / LPSS: Add device link for CHT SD card dependency on I2C

Message ID 1512390731-11171-1-git-send-email-adrian.hunter@intel.com (mailing list archive)
State Changes Requested, archived
Headers show

Commit Message

Adrian Hunter Dec. 4, 2017, 12:32 p.m. UTC
Some Cherry Trail boards have a dependency between the SDHCI host
controller used for SD cards and an external PMIC accessed via I2C. Add a
device link between the SDHCI host controller (consumer) and the I2C
adapter (supplier).

This patch depends on a fix to devices links, namely commit 0ff26c662d5f
("driver core: Fix device link deferred probe"). And also either,
commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
runtime PM".

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
 drivers/acpi/acpi_lpss.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

Comments

Hans de Goede Dec. 4, 2017, 1:48 p.m. UTC | #1
Hi,

Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
add something like this to the the probe function:

	struct acpi_device = ACPI_COMPANION(device);

	if (acpi_device->dep_unmet)
		return -EPROBE_DEFER;

No idea if this will work, but if it does work, using the deps described
in the ACPI tables seems like a better solution then hardcoding this.

Regards,

Hans



On 04-12-17 13:32, Adrian Hunter wrote:
> Some Cherry Trail boards have a dependency between the SDHCI host
> controller used for SD cards and an external PMIC accessed via I2C. Add a
> device link between the SDHCI host controller (consumer) and the I2C
> adapter (supplier).
> 
> This patch depends on a fix to devices links, namely commit 0ff26c662d5f
> ("driver core: Fix device link deferred probe"). And also either,
> commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
> probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
> runtime PM".
> 
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
>   drivers/acpi/acpi_lpss.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 139 insertions(+)
> 
> diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
> index 7f2b02cc8ea1..9d82f2f6c327 100644
> --- a/drivers/acpi/acpi_lpss.c
> +++ b/drivers/acpi/acpi_lpss.c
> @@ -290,6 +290,11 @@ static void bsw_pwm_setup(struct lpss_private_data *pdata)
>   	{}
>   };
>   
> +static const struct x86_cpu_id cht_cpu[] = {
> +	ICPU(INTEL_FAM6_ATOM_AIRMONT),	/* Braswell, Cherry Trail */
> +	{}
> +};
> +
>   #else
>   
>   #define LPSS_ADDR(desc) (0UL)
> @@ -427,6 +432,139 @@ static int register_device_clock(struct acpi_device *adev,
>   	return 0;
>   }
>   
> +struct lpss_device_links {
> +	const char *supplier_hid;
> +	const char *supplier_uid;
> +	const char *consumer_hid;
> +	const char *consumer_uid;
> +	const struct x86_cpu_id *cpus;
> +	u32 flags;
> +};
> +
> +static const struct lpss_device_links lpss_device_links[] = {
> +	{"808622C1", "7", "80860F14", "3", cht_cpu, DL_FLAG_PM_RUNTIME},
> +};
> +
> +static bool hid_uid_match(const char *hid1, const char *uid1,
> +			  const char *hid2, const char *uid2)
> +{
> +	return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2);
> +}
> +
> +static bool acpi_lpss_is_supplier(struct acpi_device *adev,
> +				  const struct lpss_device_links *link)
> +{
> +	return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
> +			     link->supplier_hid, link->supplier_uid);
> +}
> +
> +static bool acpi_lpss_is_consumer(struct acpi_device *adev,
> +				  const struct lpss_device_links *link)
> +{
> +	return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
> +			     link->consumer_hid, link->consumer_uid);
> +}
> +
> +struct hid_uid {
> +	const char *hid;
> +	const char *uid;
> +};
> +
> +static int match_hid_uid(struct device *dev, void *data)
> +{
> +	struct acpi_device *adev = ACPI_COMPANION(dev);
> +	struct hid_uid *id = data;
> +
> +	if (!adev)
> +		return 0;
> +
> +	return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
> +			     id->hid, id->uid);
> +}
> +
> +static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
> +{
> +	struct hid_uid data = {
> +		.hid = hid,
> +		.uid = uid,
> +	};
> +
> +	return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
> +}
> +
> +static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
> +{
> +	struct acpi_handle_list dep_devices;
> +	acpi_status status;
> +	int i;
> +
> +	if (!acpi_has_method(adev->handle, "_DEP"))
> +		return false;
> +
> +	status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
> +					 &dep_devices);
> +	if (ACPI_FAILURE(status)) {
> +		dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
> +		return false;
> +	}
> +
> +	for (i = 0; i < dep_devices.count; i++) {
> +		if (dep_devices.handles[i] == handle)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +static void acpi_lpss_link_consumer(struct device *dev1,
> +				    const struct lpss_device_links *link)
> +{
> +	struct device *dev2;
> +
> +	dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
> +	if (!dev2)
> +		return;
> +
> +	if (acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
> +		device_link_add(dev2, dev1, link->flags);
> +
> +	put_device(dev2);
> +}
> +
> +static void acpi_lpss_link_supplier(struct device *dev1,
> +				    const struct lpss_device_links *link)
> +{
> +	struct device *dev2;
> +
> +	dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
> +	if (!dev2)
> +		return;
> +
> +	if (acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
> +		device_link_add(dev1, dev2, link->flags);
> +
> +	put_device(dev2);
> +}
> +
> +static void acpi_lpss_create_device_links(struct acpi_device *adev,
> +					  struct platform_device *pdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
> +		const struct lpss_device_links *link = &lpss_device_links[i];
> +
> +		if (link->cpus && !x86_match_cpu(link->cpus))
> +			continue;
> +
> +		if (acpi_lpss_is_supplier(adev, link))
> +			acpi_lpss_link_consumer(&pdev->dev, link);
> +
> +		if (acpi_lpss_is_consumer(adev, link))
> +			acpi_lpss_link_supplier(&pdev->dev, link);
> +	}
> +}
> +
>   static int acpi_lpss_create_device(struct acpi_device *adev,
>   				   const struct acpi_device_id *id)
>   {
> @@ -500,6 +638,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
>   	adev->driver_data = pdata;
>   	pdev = acpi_create_platform_device(adev, dev_desc->properties);
>   	if (!IS_ERR_OR_NULL(pdev)) {
> +		acpi_lpss_create_device_links(adev, pdev);
>   		return 1;
>   	}
>   
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Carlo Caione Dec. 4, 2017, 1:56 p.m. UTC | #2
On Mon, Dec 4, 2017 at 12:32 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
> Some Cherry Trail boards have a dependency between the SDHCI host
> controller used for SD cards and an external PMIC accessed via I2C. Add a
> device link between the SDHCI host controller (consumer) and the I2C
> adapter (supplier).
>
> This patch depends on a fix to devices links, namely commit 0ff26c662d5f
> ("driver core: Fix device link deferred probe"). And also either,
> commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
> probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
> runtime PM".
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>

Hey Adrian,
thank you for working on this.

I tried this patch on top of linus HEAD (0ff26c662d5f and 126dbc6b49c8
are already applied) but I'm still experiencing some difficulties with
some SD cards on the cherry-trail laptop I'm working with. You can
find the DSDT in [0].

With an ultra high speed SDR104 SDHC card I get:

  mmc2: Tuning timeout, falling back to fixed sampling clock
  mmc2: new ultra high speed SDR104 SDHC card at address 59b4
  mmcblk2: mmc2:59b4 USD00 15.0 GiB
  mmc2: Timeout waiting for hardware interrupt.
  mmc2: sdhci: ============ SDHCI REGISTER DUMP ===========
  mmc2: sdhci: Sys addr:  0x00000008 | Version:  0x00001002
  mmc2: sdhci: Blk size:  0x00007200 | Blk cnt:  0x00000008
  mmc2: sdhci: Argument:  0x00000000 | Trn mode: 0x0000003b
  mmc2: sdhci: Present:   0x01ff0001 | Host ctl: 0x00000017
  mmc2: sdhci: Power:     0x0000000f | Blk gap:  0x00000080
  mmc2: sdhci: Wake-up:   0x00000000 | Clock:    0x00000007
  mmc2: sdhci: Timeout:   0x0000000a | Int stat: 0x00000000
  mmc2: sdhci: Int enab:  0x02ff008b | Sig enab: 0x02ff008b
  mmc2: sdhci: AC12 err:  0x00000000 | Slot int: 0x00000000
  mmc2: sdhci: Caps:      0x0568c8b2 | Caps_1:   0x00000807
  mmc2: sdhci: Cmd:       0x0000123a | Max curr: 0x00000000
  mmc2: sdhci: Resp[0]:   0x00000000 | Resp[1]:  0x0077dd7f
  mmc2: sdhci: Resp[2]:   0x325b5900 | Resp[3]:  0x00000000
  mmc2: sdhci: Host ctl2: 0x0000800b
  mmc2: sdhci: ADMA Err:  0x00000000 | ADMA Ptr: 0x33773200
  mmc2: sdhci: ============================================
  mmcblk2: error -110 sending status command, retrying
  mmcblk2: error -110 sending status command, retrying
  mmcblk2: error -110 sending status command, aborting
  mmc2: Tuning timeout, falling back to fixed sampling clock

For an high speed SDHC card I simply get:

  mmc2: error -110 whilst initialising SD card

Some other cards just work fine, i.e. ultra high speed DDR50.

At this point I'm not sure if the problem I'm seeing is actually
related to the issue you are fixing with this commit (and if now,
sorry to have hijacked this thread).
Any idea on that?

Thank you,

[0] https://gist.github.com/carlocaione/82bff95ababb67dd33f52a86e94ce3ff
Adrian Hunter Dec. 4, 2017, 2:30 p.m. UTC | #3
On 04/12/17 15:48, Hans de Goede wrote:
> Hi,
> 
> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.

It is using _DEP, see acpi_lpss_dep()

> add something like this to the the probe function:
> 
>     struct acpi_device = ACPI_COMPANION(device);
> 
>     if (acpi_device->dep_unmet)
>         return -EPROBE_DEFER;
> 
> No idea if this will work, but if it does work, using the deps described
> in the ACPI tables seems like a better solution then hardcoding this.

That would not work because there are other devices listed in the _DEP
method so dep_unmet is always true.  So we are left checking _DEP but only
for specific device dependencies.

> 
> Regards,
> 
> Hans
> 
> 
> 
> On 04-12-17 13:32, Adrian Hunter wrote:
>> Some Cherry Trail boards have a dependency between the SDHCI host
>> controller used for SD cards and an external PMIC accessed via I2C. Add a
>> device link between the SDHCI host controller (consumer) and the I2C
>> adapter (supplier).
>>
>> This patch depends on a fix to devices links, namely commit 0ff26c662d5f
>> ("driver core: Fix device link deferred probe"). And also either,
>> commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
>> probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
>> runtime PM".
>>
>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>> ---
>>   drivers/acpi/acpi_lpss.c | 139
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 139 insertions(+)
>>
>> diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
>> index 7f2b02cc8ea1..9d82f2f6c327 100644
>> --- a/drivers/acpi/acpi_lpss.c
>> +++ b/drivers/acpi/acpi_lpss.c
>> @@ -290,6 +290,11 @@ static void bsw_pwm_setup(struct lpss_private_data
>> *pdata)
>>       {}
>>   };
>>   +static const struct x86_cpu_id cht_cpu[] = {
>> +    ICPU(INTEL_FAM6_ATOM_AIRMONT),    /* Braswell, Cherry Trail */
>> +    {}
>> +};
>> +
>>   #else
>>     #define LPSS_ADDR(desc) (0UL)
>> @@ -427,6 +432,139 @@ static int register_device_clock(struct acpi_device
>> *adev,
>>       return 0;
>>   }
>>   +struct lpss_device_links {
>> +    const char *supplier_hid;
>> +    const char *supplier_uid;
>> +    const char *consumer_hid;
>> +    const char *consumer_uid;
>> +    const struct x86_cpu_id *cpus;
>> +    u32 flags;
>> +};
>> +
>> +static const struct lpss_device_links lpss_device_links[] = {
>> +    {"808622C1", "7", "80860F14", "3", cht_cpu, DL_FLAG_PM_RUNTIME},
>> +};
>> +
>> +static bool hid_uid_match(const char *hid1, const char *uid1,
>> +              const char *hid2, const char *uid2)
>> +{
>> +    return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2);
>> +}
>> +
>> +static bool acpi_lpss_is_supplier(struct acpi_device *adev,
>> +                  const struct lpss_device_links *link)
>> +{
>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>> +                 link->supplier_hid, link->supplier_uid);
>> +}
>> +
>> +static bool acpi_lpss_is_consumer(struct acpi_device *adev,
>> +                  const struct lpss_device_links *link)
>> +{
>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>> +                 link->consumer_hid, link->consumer_uid);
>> +}
>> +
>> +struct hid_uid {
>> +    const char *hid;
>> +    const char *uid;
>> +};
>> +
>> +static int match_hid_uid(struct device *dev, void *data)
>> +{
>> +    struct acpi_device *adev = ACPI_COMPANION(dev);
>> +    struct hid_uid *id = data;
>> +
>> +    if (!adev)
>> +        return 0;
>> +
>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>> +                 id->hid, id->uid);
>> +}
>> +
>> +static struct device *acpi_lpss_find_device(const char *hid, const char
>> *uid)
>> +{
>> +    struct hid_uid data = {
>> +        .hid = hid,
>> +        .uid = uid,
>> +    };
>> +
>> +    return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
>> +}
>> +
>> +static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
>> +{
>> +    struct acpi_handle_list dep_devices;
>> +    acpi_status status;
>> +    int i;
>> +
>> +    if (!acpi_has_method(adev->handle, "_DEP"))
>> +        return false;
>> +
>> +    status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
>> +                     &dep_devices);
>> +    if (ACPI_FAILURE(status)) {
>> +        dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
>> +        return false;
>> +    }
>> +
>> +    for (i = 0; i < dep_devices.count; i++) {
>> +        if (dep_devices.handles[i] == handle)
>> +            return true;
>> +    }
>> +
>> +    return false;
>> +}
>> +
>> +static void acpi_lpss_link_consumer(struct device *dev1,
>> +                    const struct lpss_device_links *link)
>> +{
>> +    struct device *dev2;
>> +
>> +    dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
>> +    if (!dev2)
>> +        return;
>> +
>> +    if (acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
>> +        device_link_add(dev2, dev1, link->flags);
>> +
>> +    put_device(dev2);
>> +}
>> +
>> +static void acpi_lpss_link_supplier(struct device *dev1,
>> +                    const struct lpss_device_links *link)
>> +{
>> +    struct device *dev2;
>> +
>> +    dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
>> +    if (!dev2)
>> +        return;
>> +
>> +    if (acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
>> +        device_link_add(dev1, dev2, link->flags);
>> +
>> +    put_device(dev2);
>> +}
>> +
>> +static void acpi_lpss_create_device_links(struct acpi_device *adev,
>> +                      struct platform_device *pdev)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
>> +        const struct lpss_device_links *link = &lpss_device_links[i];
>> +
>> +        if (link->cpus && !x86_match_cpu(link->cpus))
>> +            continue;
>> +
>> +        if (acpi_lpss_is_supplier(adev, link))
>> +            acpi_lpss_link_consumer(&pdev->dev, link);
>> +
>> +        if (acpi_lpss_is_consumer(adev, link))
>> +            acpi_lpss_link_supplier(&pdev->dev, link);
>> +    }
>> +}
>> +
>>   static int acpi_lpss_create_device(struct acpi_device *adev,
>>                      const struct acpi_device_id *id)
>>   {
>> @@ -500,6 +638,7 @@ static int acpi_lpss_create_device(struct acpi_device
>> *adev,
>>       adev->driver_data = pdata;
>>       pdev = acpi_create_platform_device(adev, dev_desc->properties);
>>       if (!IS_ERR_OR_NULL(pdev)) {
>> +        acpi_lpss_create_device_links(adev, pdev);
>>           return 1;
>>       }
>>  
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hans de Goede Dec. 4, 2017, 2:33 p.m. UTC | #4
Hi,

On 04-12-17 15:30, Adrian Hunter wrote:
> On 04/12/17 15:48, Hans de Goede wrote:
>> Hi,
>>
>> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
> 
> It is using _DEP, see acpi_lpss_dep()
> 
>> add something like this to the the probe function:
>>
>>      struct acpi_device = ACPI_COMPANION(device);
>>
>>      if (acpi_device->dep_unmet)
>>          return -EPROBE_DEFER;
>>
>> No idea if this will work, but if it does work, using the deps described
>> in the ACPI tables seems like a better solution then hardcoding this.
> 
> That would not work because there are other devices listed in the _DEP
> method so dep_unmet is always true.  So we are left checking _DEP but only
> for specific device dependencies.

Ugh, understood thank you for explaining this. Perhaps it is a good idea
to mention in the commit message why acpi_dev->dep_unmet cannot be used
here?

Regards,

Hans


>> On 04-12-17 13:32, Adrian Hunter wrote:
>>> Some Cherry Trail boards have a dependency between the SDHCI host
>>> controller used for SD cards and an external PMIC accessed via I2C. Add a
>>> device link between the SDHCI host controller (consumer) and the I2C
>>> adapter (supplier).
>>>
>>> This patch depends on a fix to devices links, namely commit 0ff26c662d5f
>>> ("driver core: Fix device link deferred probe"). And also either,
>>> commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
>>> probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
>>> runtime PM".
>>>
>>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>>> ---
>>>    drivers/acpi/acpi_lpss.c | 139
>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>    1 file changed, 139 insertions(+)
>>>
>>> diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
>>> index 7f2b02cc8ea1..9d82f2f6c327 100644
>>> --- a/drivers/acpi/acpi_lpss.c
>>> +++ b/drivers/acpi/acpi_lpss.c
>>> @@ -290,6 +290,11 @@ static void bsw_pwm_setup(struct lpss_private_data
>>> *pdata)
>>>        {}
>>>    };
>>>    +static const struct x86_cpu_id cht_cpu[] = {
>>> +    ICPU(INTEL_FAM6_ATOM_AIRMONT),    /* Braswell, Cherry Trail */
>>> +    {}
>>> +};
>>> +
>>>    #else
>>>      #define LPSS_ADDR(desc) (0UL)
>>> @@ -427,6 +432,139 @@ static int register_device_clock(struct acpi_device
>>> *adev,
>>>        return 0;
>>>    }
>>>    +struct lpss_device_links {
>>> +    const char *supplier_hid;
>>> +    const char *supplier_uid;
>>> +    const char *consumer_hid;
>>> +    const char *consumer_uid;
>>> +    const struct x86_cpu_id *cpus;
>>> +    u32 flags;
>>> +};
>>> +
>>> +static const struct lpss_device_links lpss_device_links[] = {
>>> +    {"808622C1", "7", "80860F14", "3", cht_cpu, DL_FLAG_PM_RUNTIME},
>>> +};
>>> +
>>> +static bool hid_uid_match(const char *hid1, const char *uid1,
>>> +              const char *hid2, const char *uid2)
>>> +{
>>> +    return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2);
>>> +}
>>> +
>>> +static bool acpi_lpss_is_supplier(struct acpi_device *adev,
>>> +                  const struct lpss_device_links *link)
>>> +{
>>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>>> +                 link->supplier_hid, link->supplier_uid);
>>> +}
>>> +
>>> +static bool acpi_lpss_is_consumer(struct acpi_device *adev,
>>> +                  const struct lpss_device_links *link)
>>> +{
>>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>>> +                 link->consumer_hid, link->consumer_uid);
>>> +}
>>> +
>>> +struct hid_uid {
>>> +    const char *hid;
>>> +    const char *uid;
>>> +};
>>> +
>>> +static int match_hid_uid(struct device *dev, void *data)
>>> +{
>>> +    struct acpi_device *adev = ACPI_COMPANION(dev);
>>> +    struct hid_uid *id = data;
>>> +
>>> +    if (!adev)
>>> +        return 0;
>>> +
>>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>>> +                 id->hid, id->uid);
>>> +}
>>> +
>>> +static struct device *acpi_lpss_find_device(const char *hid, const char
>>> *uid)
>>> +{
>>> +    struct hid_uid data = {
>>> +        .hid = hid,
>>> +        .uid = uid,
>>> +    };
>>> +
>>> +    return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
>>> +}
>>> +
>>> +static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
>>> +{
>>> +    struct acpi_handle_list dep_devices;
>>> +    acpi_status status;
>>> +    int i;
>>> +
>>> +    if (!acpi_has_method(adev->handle, "_DEP"))
>>> +        return false;
>>> +
>>> +    status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
>>> +                     &dep_devices);
>>> +    if (ACPI_FAILURE(status)) {
>>> +        dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
>>> +        return false;
>>> +    }
>>> +
>>> +    for (i = 0; i < dep_devices.count; i++) {
>>> +        if (dep_devices.handles[i] == handle)
>>> +            return true;
>>> +    }
>>> +
>>> +    return false;
>>> +}
>>> +
>>> +static void acpi_lpss_link_consumer(struct device *dev1,
>>> +                    const struct lpss_device_links *link)
>>> +{
>>> +    struct device *dev2;
>>> +
>>> +    dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
>>> +    if (!dev2)
>>> +        return;
>>> +
>>> +    if (acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
>>> +        device_link_add(dev2, dev1, link->flags);
>>> +
>>> +    put_device(dev2);
>>> +}
>>> +
>>> +static void acpi_lpss_link_supplier(struct device *dev1,
>>> +                    const struct lpss_device_links *link)
>>> +{
>>> +    struct device *dev2;
>>> +
>>> +    dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
>>> +    if (!dev2)
>>> +        return;
>>> +
>>> +    if (acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
>>> +        device_link_add(dev1, dev2, link->flags);
>>> +
>>> +    put_device(dev2);
>>> +}
>>> +
>>> +static void acpi_lpss_create_device_links(struct acpi_device *adev,
>>> +                      struct platform_device *pdev)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
>>> +        const struct lpss_device_links *link = &lpss_device_links[i];
>>> +
>>> +        if (link->cpus && !x86_match_cpu(link->cpus))
>>> +            continue;
>>> +
>>> +        if (acpi_lpss_is_supplier(adev, link))
>>> +            acpi_lpss_link_consumer(&pdev->dev, link);
>>> +
>>> +        if (acpi_lpss_is_consumer(adev, link))
>>> +            acpi_lpss_link_supplier(&pdev->dev, link);
>>> +    }
>>> +}
>>> +
>>>    static int acpi_lpss_create_device(struct acpi_device *adev,
>>>                       const struct acpi_device_id *id)
>>>    {
>>> @@ -500,6 +638,7 @@ static int acpi_lpss_create_device(struct acpi_device
>>> *adev,
>>>        adev->driver_data = pdata;
>>>        pdev = acpi_create_platform_device(adev, dev_desc->properties);
>>>        if (!IS_ERR_OR_NULL(pdev)) {
>>> +        acpi_lpss_create_device_links(adev, pdev);
>>>            return 1;
>>>        }
>>>   
>>
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Adrian Hunter Dec. 4, 2017, 2:41 p.m. UTC | #5
On 04/12/17 16:33, Hans de Goede wrote:
> Hi,
> 
> On 04-12-17 15:30, Adrian Hunter wrote:
>> On 04/12/17 15:48, Hans de Goede wrote:
>>> Hi,
>>>
>>> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
>>
>> It is using _DEP, see acpi_lpss_dep()
>>
>>> add something like this to the the probe function:
>>>
>>>      struct acpi_device = ACPI_COMPANION(device);
>>>
>>>      if (acpi_device->dep_unmet)
>>>          return -EPROBE_DEFER;
>>>
>>> No idea if this will work, but if it does work, using the deps described
>>> in the ACPI tables seems like a better solution then hardcoding this.
>>
>> That would not work because there are other devices listed in the _DEP
>> method so dep_unmet is always true.  So we are left checking _DEP but only
>> for specific device dependencies.
> 
> Ugh, understood thank you for explaining this. Perhaps it is a good idea
> to mention in the commit message why acpi_dev->dep_unmet cannot be used
> here?

dep_unmet predates device links, but now we have device links, they are
better anyway.

> 
> Regards,
> 
> Hans
> 
> 
>>> On 04-12-17 13:32, Adrian Hunter wrote:
>>>> Some Cherry Trail boards have a dependency between the SDHCI host
>>>> controller used for SD cards and an external PMIC accessed via I2C. Add a
>>>> device link between the SDHCI host controller (consumer) and the I2C
>>>> adapter (supplier).
>>>>
>>>> This patch depends on a fix to devices links, namely commit 0ff26c662d5f
>>>> ("driver core: Fix device link deferred probe"). And also either,
>>>> commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
>>>> probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
>>>> runtime PM".
>>>>
>>>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>>>> ---
>>>>    drivers/acpi/acpi_lpss.c | 139
>>>> +++++++++++++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 139 insertions(+)
>>>>
>>>> diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
>>>> index 7f2b02cc8ea1..9d82f2f6c327 100644
>>>> --- a/drivers/acpi/acpi_lpss.c
>>>> +++ b/drivers/acpi/acpi_lpss.c
>>>> @@ -290,6 +290,11 @@ static void bsw_pwm_setup(struct lpss_private_data
>>>> *pdata)
>>>>        {}
>>>>    };
>>>>    +static const struct x86_cpu_id cht_cpu[] = {
>>>> +    ICPU(INTEL_FAM6_ATOM_AIRMONT),    /* Braswell, Cherry Trail */
>>>> +    {}
>>>> +};
>>>> +
>>>>    #else
>>>>      #define LPSS_ADDR(desc) (0UL)
>>>> @@ -427,6 +432,139 @@ static int register_device_clock(struct acpi_device
>>>> *adev,
>>>>        return 0;
>>>>    }
>>>>    +struct lpss_device_links {
>>>> +    const char *supplier_hid;
>>>> +    const char *supplier_uid;
>>>> +    const char *consumer_hid;
>>>> +    const char *consumer_uid;
>>>> +    const struct x86_cpu_id *cpus;
>>>> +    u32 flags;
>>>> +};
>>>> +
>>>> +static const struct lpss_device_links lpss_device_links[] = {
>>>> +    {"808622C1", "7", "80860F14", "3", cht_cpu, DL_FLAG_PM_RUNTIME},
>>>> +};
>>>> +
>>>> +static bool hid_uid_match(const char *hid1, const char *uid1,
>>>> +              const char *hid2, const char *uid2)
>>>> +{
>>>> +    return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2);
>>>> +}
>>>> +
>>>> +static bool acpi_lpss_is_supplier(struct acpi_device *adev,
>>>> +                  const struct lpss_device_links *link)
>>>> +{
>>>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>>>> +                 link->supplier_hid, link->supplier_uid);
>>>> +}
>>>> +
>>>> +static bool acpi_lpss_is_consumer(struct acpi_device *adev,
>>>> +                  const struct lpss_device_links *link)
>>>> +{
>>>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>>>> +                 link->consumer_hid, link->consumer_uid);
>>>> +}
>>>> +
>>>> +struct hid_uid {
>>>> +    const char *hid;
>>>> +    const char *uid;
>>>> +};
>>>> +
>>>> +static int match_hid_uid(struct device *dev, void *data)
>>>> +{
>>>> +    struct acpi_device *adev = ACPI_COMPANION(dev);
>>>> +    struct hid_uid *id = data;
>>>> +
>>>> +    if (!adev)
>>>> +        return 0;
>>>> +
>>>> +    return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
>>>> +                 id->hid, id->uid);
>>>> +}
>>>> +
>>>> +static struct device *acpi_lpss_find_device(const char *hid, const char
>>>> *uid)
>>>> +{
>>>> +    struct hid_uid data = {
>>>> +        .hid = hid,
>>>> +        .uid = uid,
>>>> +    };
>>>> +
>>>> +    return bus_find_device(&platform_bus_type, NULL, &data,
>>>> match_hid_uid);
>>>> +}
>>>> +
>>>> +static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
>>>> +{
>>>> +    struct acpi_handle_list dep_devices;
>>>> +    acpi_status status;
>>>> +    int i;
>>>> +
>>>> +    if (!acpi_has_method(adev->handle, "_DEP"))
>>>> +        return false;
>>>> +
>>>> +    status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
>>>> +                     &dep_devices);
>>>> +    if (ACPI_FAILURE(status)) {
>>>> +        dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
>>>> +        return false;
>>>> +    }
>>>> +
>>>> +    for (i = 0; i < dep_devices.count; i++) {
>>>> +        if (dep_devices.handles[i] == handle)
>>>> +            return true;
>>>> +    }
>>>> +
>>>> +    return false;
>>>> +}
>>>> +
>>>> +static void acpi_lpss_link_consumer(struct device *dev1,
>>>> +                    const struct lpss_device_links *link)
>>>> +{
>>>> +    struct device *dev2;
>>>> +
>>>> +    dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
>>>> +    if (!dev2)
>>>> +        return;
>>>> +
>>>> +    if (acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
>>>> +        device_link_add(dev2, dev1, link->flags);
>>>> +
>>>> +    put_device(dev2);
>>>> +}
>>>> +
>>>> +static void acpi_lpss_link_supplier(struct device *dev1,
>>>> +                    const struct lpss_device_links *link)
>>>> +{
>>>> +    struct device *dev2;
>>>> +
>>>> +    dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
>>>> +    if (!dev2)
>>>> +        return;
>>>> +
>>>> +    if (acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
>>>> +        device_link_add(dev1, dev2, link->flags);
>>>> +
>>>> +    put_device(dev2);
>>>> +}
>>>> +
>>>> +static void acpi_lpss_create_device_links(struct acpi_device *adev,
>>>> +                      struct platform_device *pdev)
>>>> +{
>>>> +    int i;
>>>> +
>>>> +    for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
>>>> +        const struct lpss_device_links *link = &lpss_device_links[i];
>>>> +
>>>> +        if (link->cpus && !x86_match_cpu(link->cpus))
>>>> +            continue;
>>>> +
>>>> +        if (acpi_lpss_is_supplier(adev, link))
>>>> +            acpi_lpss_link_consumer(&pdev->dev, link);
>>>> +
>>>> +        if (acpi_lpss_is_consumer(adev, link))
>>>> +            acpi_lpss_link_supplier(&pdev->dev, link);
>>>> +    }
>>>> +}
>>>> +
>>>>    static int acpi_lpss_create_device(struct acpi_device *adev,
>>>>                       const struct acpi_device_id *id)
>>>>    {
>>>> @@ -500,6 +638,7 @@ static int acpi_lpss_create_device(struct acpi_device
>>>> *adev,
>>>>        adev->driver_data = pdata;
>>>>        pdev = acpi_create_platform_device(adev, dev_desc->properties);
>>>>        if (!IS_ERR_OR_NULL(pdev)) {
>>>> +        acpi_lpss_create_device_links(adev, pdev);
>>>>            return 1;
>>>>        }
>>>>   
>>>
>>
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael J. Wysocki Dec. 4, 2017, 2:56 p.m. UTC | #6
On Monday, December 4, 2017 3:33:29 PM CET Hans de Goede wrote:
> Hi,
> 
> On 04-12-17 15:30, Adrian Hunter wrote:
> > On 04/12/17 15:48, Hans de Goede wrote:
> >> Hi,
> >>
> >> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
> > 
> > It is using _DEP, see acpi_lpss_dep()
> > 
> >> add something like this to the the probe function:
> >>
> >>      struct acpi_device = ACPI_COMPANION(device);
> >>
> >>      if (acpi_device->dep_unmet)
> >>          return -EPROBE_DEFER;
> >>
> >> No idea if this will work, but if it does work, using the deps described
> >> in the ACPI tables seems like a better solution then hardcoding this.
> > 
> > That would not work because there are other devices listed in the _DEP
> > method so dep_unmet is always true.  So we are left checking _DEP but only
> > for specific device dependencies.
> 
> Ugh, understood thank you for explaining this. Perhaps it is a good idea
> to mention in the commit message why acpi_dev->dep_unmet cannot be used
> here?

Not just in the commit message, but I'd suggest adding a comment to that effect
next to the definition of lpss_device_links[].

Thanks,
Rafael

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael J. Wysocki Dec. 4, 2017, 3 p.m. UTC | #7
On Monday, December 4, 2017 3:41:45 PM CET Adrian Hunter wrote:
> On 04/12/17 16:33, Hans de Goede wrote:
> > Hi,
> > 
> > On 04-12-17 15:30, Adrian Hunter wrote:
> >> On 04/12/17 15:48, Hans de Goede wrote:
> >>> Hi,
> >>>
> >>> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
> >>
> >> It is using _DEP, see acpi_lpss_dep()
> >>
> >>> add something like this to the the probe function:
> >>>
> >>>      struct acpi_device = ACPI_COMPANION(device);
> >>>
> >>>      if (acpi_device->dep_unmet)
> >>>          return -EPROBE_DEFER;
> >>>
> >>> No idea if this will work, but if it does work, using the deps described
> >>> in the ACPI tables seems like a better solution then hardcoding this.
> >>
> >> That would not work because there are other devices listed in the _DEP
> >> method so dep_unmet is always true.  So we are left checking _DEP but only
> >> for specific device dependencies.
> > 
> > Ugh, understood thank you for explaining this. Perhaps it is a good idea
> > to mention in the commit message why acpi_dev->dep_unmet cannot be used
> > here?
> 
> dep_unmet predates device links, but now we have device links, they are
> better anyway.

Right (they cover PM too, for example), but it would be good to note why
it is necessary to hardcode the links information in the code.

Thanks,
Rafael

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Adrian Hunter Dec. 4, 2017, 3:52 p.m. UTC | #8
On 04/12/17 15:56, Carlo Caione wrote:
> On Mon, Dec 4, 2017 at 12:32 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
>> Some Cherry Trail boards have a dependency between the SDHCI host
>> controller used for SD cards and an external PMIC accessed via I2C. Add a
>> device link between the SDHCI host controller (consumer) and the I2C
>> adapter (supplier).
>>
>> This patch depends on a fix to devices links, namely commit 0ff26c662d5f
>> ("driver core: Fix device link deferred probe"). And also either,
>> commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
>> probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
>> runtime PM".
>>
>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> 
> Hey Adrian,
> thank you for working on this.
> 
> I tried this patch on top of linus HEAD (0ff26c662d5f and 126dbc6b49c8
> are already applied) but I'm still experiencing some difficulties with
> some SD cards on the cherry-trail laptop I'm working with. You can
> find the DSDT in [0].
> 
> With an ultra high speed SDR104 SDHC card I get:

SDR104 works for me but I now see it is not supported on all boards.  I will
send a patch for that i.e. you will end up with no SDR104.

> 
>   mmc2: Tuning timeout, falling back to fixed sampling clock
>   mmc2: new ultra high speed SDR104 SDHC card at address 59b4
>   mmcblk2: mmc2:59b4 USD00 15.0 GiB
>   mmc2: Timeout waiting for hardware interrupt.
>   mmc2: sdhci: ============ SDHCI REGISTER DUMP ===========
>   mmc2: sdhci: Sys addr:  0x00000008 | Version:  0x00001002
>   mmc2: sdhci: Blk size:  0x00007200 | Blk cnt:  0x00000008
>   mmc2: sdhci: Argument:  0x00000000 | Trn mode: 0x0000003b
>   mmc2: sdhci: Present:   0x01ff0001 | Host ctl: 0x00000017
>   mmc2: sdhci: Power:     0x0000000f | Blk gap:  0x00000080
>   mmc2: sdhci: Wake-up:   0x00000000 | Clock:    0x00000007
>   mmc2: sdhci: Timeout:   0x0000000a | Int stat: 0x00000000
>   mmc2: sdhci: Int enab:  0x02ff008b | Sig enab: 0x02ff008b
>   mmc2: sdhci: AC12 err:  0x00000000 | Slot int: 0x00000000
>   mmc2: sdhci: Caps:      0x0568c8b2 | Caps_1:   0x00000807
>   mmc2: sdhci: Cmd:       0x0000123a | Max curr: 0x00000000
>   mmc2: sdhci: Resp[0]:   0x00000000 | Resp[1]:  0x0077dd7f
>   mmc2: sdhci: Resp[2]:   0x325b5900 | Resp[3]:  0x00000000
>   mmc2: sdhci: Host ctl2: 0x0000800b
>   mmc2: sdhci: ADMA Err:  0x00000000 | ADMA Ptr: 0x33773200
>   mmc2: sdhci: ============================================
>   mmcblk2: error -110 sending status command, retrying
>   mmcblk2: error -110 sending status command, retrying
>   mmcblk2: error -110 sending status command, aborting
>   mmc2: Tuning timeout, falling back to fixed sampling clock
> 
> For an high speed SDHC card I simply get:
> 
>   mmc2: error -110 whilst initialising SD card

I will investigate that some more.

> 
> Some other cards just work fine, i.e. ultra high speed DDR50.

This patch should help in the DDR50 case when booting with a card already
inserted.  Did it help?

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Adrian Hunter Dec. 5, 2017, 2:25 p.m. UTC | #9
On 04/12/17 17:00, Rafael J. Wysocki wrote:
> On Monday, December 4, 2017 3:41:45 PM CET Adrian Hunter wrote:
>> On 04/12/17 16:33, Hans de Goede wrote:
>>> Hi,
>>>
>>> On 04-12-17 15:30, Adrian Hunter wrote:
>>>> On 04/12/17 15:48, Hans de Goede wrote:
>>>>> Hi,
>>>>>
>>>>> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
>>>>
>>>> It is using _DEP, see acpi_lpss_dep()
>>>>
>>>>> add something like this to the the probe function:
>>>>>
>>>>>      struct acpi_device = ACPI_COMPANION(device);
>>>>>
>>>>>      if (acpi_device->dep_unmet)
>>>>>          return -EPROBE_DEFER;
>>>>>
>>>>> No idea if this will work, but if it does work, using the deps described
>>>>> in the ACPI tables seems like a better solution then hardcoding this.
>>>>
>>>> That would not work because there are other devices listed in the _DEP
>>>> method so dep_unmet is always true.  So we are left checking _DEP but only
>>>> for specific device dependencies.
>>>
>>> Ugh, understood thank you for explaining this. Perhaps it is a good idea
>>> to mention in the commit message why acpi_dev->dep_unmet cannot be used
>>> here?
>>
>> dep_unmet predates device links, but now we have device links, they are
>> better anyway.
> 
> Right (they cover PM too, for example), but it would be good to note why
> it is necessary to hardcode the links information in the code.

It isn't entirely necessary to hardcode the links information.  For example,
another possibility is to create device links for all LPSS devices with _DEP
methods that point to other LPSS devices i.e. match against
acpi_lpss_device_ids.  The assumptions would be that all LPSS devices have
drivers so it would be safe to create links between them, and that the
nature of the dependency is correctly represented by a device link.

An advantage of that approach would be that it might work for future
dependencies between LPSS devices without having to add entries to a table.
The disadvantage would be the possibility that creating a device link
somehow turns out not to be the right thing to do.
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael J. Wysocki Dec. 5, 2017, 3:05 p.m. UTC | #10
On Tue, Dec 5, 2017 at 3:25 PM, Adrian Hunter <adrian.hunter@intel.com> wrote:
> On 04/12/17 17:00, Rafael J. Wysocki wrote:
>> On Monday, December 4, 2017 3:41:45 PM CET Adrian Hunter wrote:
>>> On 04/12/17 16:33, Hans de Goede wrote:
>>>> Hi,
>>>>
>>>> On 04-12-17 15:30, Adrian Hunter wrote:
>>>>> On 04/12/17 15:48, Hans de Goede wrote:
>>>>>> Hi,
>>>>>>
>>>>>> Wouldn't it be easier to use the ACPI _DEP tracking for this, e.g.
>>>>>
>>>>> It is using _DEP, see acpi_lpss_dep()
>>>>>
>>>>>> add something like this to the the probe function:
>>>>>>
>>>>>>      struct acpi_device = ACPI_COMPANION(device);
>>>>>>
>>>>>>      if (acpi_device->dep_unmet)
>>>>>>          return -EPROBE_DEFER;
>>>>>>
>>>>>> No idea if this will work, but if it does work, using the deps described
>>>>>> in the ACPI tables seems like a better solution then hardcoding this.
>>>>>
>>>>> That would not work because there are other devices listed in the _DEP
>>>>> method so dep_unmet is always true.  So we are left checking _DEP but only
>>>>> for specific device dependencies.
>>>>
>>>> Ugh, understood thank you for explaining this. Perhaps it is a good idea
>>>> to mention in the commit message why acpi_dev->dep_unmet cannot be used
>>>> here?
>>>
>>> dep_unmet predates device links, but now we have device links, they are
>>> better anyway.
>>
>> Right (they cover PM too, for example), but it would be good to note why
>> it is necessary to hardcode the links information in the code.
>
> It isn't entirely necessary to hardcode the links information.  For example,
> another possibility is to create device links for all LPSS devices with _DEP
> methods that point to other LPSS devices i.e. match against
> acpi_lpss_device_ids.  The assumptions would be that all LPSS devices have
> drivers so it would be safe to create links between them, and that the
> nature of the dependency is correctly represented by a device link.
>
> An advantage of that approach would be that it might work for future
> dependencies between LPSS devices without having to add entries to a table.
> The disadvantage would be the possibility that creating a device link
> somehow turns out not to be the right thing to do.

OK

To me, hardcoding is fine for the time being, but I would just add the
above information as a comment to explain the choice made.

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 7f2b02cc8ea1..9d82f2f6c327 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -290,6 +290,11 @@  static void bsw_pwm_setup(struct lpss_private_data *pdata)
 	{}
 };
 
+static const struct x86_cpu_id cht_cpu[] = {
+	ICPU(INTEL_FAM6_ATOM_AIRMONT),	/* Braswell, Cherry Trail */
+	{}
+};
+
 #else
 
 #define LPSS_ADDR(desc) (0UL)
@@ -427,6 +432,139 @@  static int register_device_clock(struct acpi_device *adev,
 	return 0;
 }
 
+struct lpss_device_links {
+	const char *supplier_hid;
+	const char *supplier_uid;
+	const char *consumer_hid;
+	const char *consumer_uid;
+	const struct x86_cpu_id *cpus;
+	u32 flags;
+};
+
+static const struct lpss_device_links lpss_device_links[] = {
+	{"808622C1", "7", "80860F14", "3", cht_cpu, DL_FLAG_PM_RUNTIME},
+};
+
+static bool hid_uid_match(const char *hid1, const char *uid1,
+			  const char *hid2, const char *uid2)
+{
+	return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2);
+}
+
+static bool acpi_lpss_is_supplier(struct acpi_device *adev,
+				  const struct lpss_device_links *link)
+{
+	return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
+			     link->supplier_hid, link->supplier_uid);
+}
+
+static bool acpi_lpss_is_consumer(struct acpi_device *adev,
+				  const struct lpss_device_links *link)
+{
+	return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
+			     link->consumer_hid, link->consumer_uid);
+}
+
+struct hid_uid {
+	const char *hid;
+	const char *uid;
+};
+
+static int match_hid_uid(struct device *dev, void *data)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	struct hid_uid *id = data;
+
+	if (!adev)
+		return 0;
+
+	return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
+			     id->hid, id->uid);
+}
+
+static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
+{
+	struct hid_uid data = {
+		.hid = hid,
+		.uid = uid,
+	};
+
+	return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
+}
+
+static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
+{
+	struct acpi_handle_list dep_devices;
+	acpi_status status;
+	int i;
+
+	if (!acpi_has_method(adev->handle, "_DEP"))
+		return false;
+
+	status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
+					 &dep_devices);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
+		return false;
+	}
+
+	for (i = 0; i < dep_devices.count; i++) {
+		if (dep_devices.handles[i] == handle)
+			return true;
+	}
+
+	return false;
+}
+
+static void acpi_lpss_link_consumer(struct device *dev1,
+				    const struct lpss_device_links *link)
+{
+	struct device *dev2;
+
+	dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
+	if (!dev2)
+		return;
+
+	if (acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
+		device_link_add(dev2, dev1, link->flags);
+
+	put_device(dev2);
+}
+
+static void acpi_lpss_link_supplier(struct device *dev1,
+				    const struct lpss_device_links *link)
+{
+	struct device *dev2;
+
+	dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
+	if (!dev2)
+		return;
+
+	if (acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
+		device_link_add(dev1, dev2, link->flags);
+
+	put_device(dev2);
+}
+
+static void acpi_lpss_create_device_links(struct acpi_device *adev,
+					  struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
+		const struct lpss_device_links *link = &lpss_device_links[i];
+
+		if (link->cpus && !x86_match_cpu(link->cpus))
+			continue;
+
+		if (acpi_lpss_is_supplier(adev, link))
+			acpi_lpss_link_consumer(&pdev->dev, link);
+
+		if (acpi_lpss_is_consumer(adev, link))
+			acpi_lpss_link_supplier(&pdev->dev, link);
+	}
+}
+
 static int acpi_lpss_create_device(struct acpi_device *adev,
 				   const struct acpi_device_id *id)
 {
@@ -500,6 +638,7 @@  static int acpi_lpss_create_device(struct acpi_device *adev,
 	adev->driver_data = pdata;
 	pdev = acpi_create_platform_device(adev, dev_desc->properties);
 	if (!IS_ERR_OR_NULL(pdev)) {
+		acpi_lpss_create_device_links(adev, pdev);
 		return 1;
 	}