Commit 48402cee authored by Hans de Goede's avatar Hans de Goede Committed by Rafael J. Wysocki

ACPI / LPSS: Resume BYT/CHT I2C controllers from resume_noirq

On some Cherry Trail systems the GPU ACPI fwnode has power-resources which
point to the PMIC, which is connected over a LPSS I2C controller.

We add a device-link to make sure that the I2C controller is resumed before
the GPU is. But the pci-core changes the power-state of PCI devices from
D3 to D0 at noirq time (to restore the PCI config registers) and before
this commit we were bringing up the I2C controllers from a resume_early
handler which runs later. More specifically the pm-core will first run
all resume_noirq handlers in order and then all resume_early handlers.

So we must not only make sure that the handlers are run in the right order,
but also that the resume of the I2C controller is done at noirq time.

The behavior before this commit, resuming the I2C controller from a
resume_early handler leads to the following errors:

 i2c_designware 808622C1:06: controller timed out
 ACPI Error: AE_ERROR, Returned by Handler for [UserDefinedRegion]
 ACPI Error: Method parse/execution failed \_SB.P18W._ON, AE_ERROR
 video LNXVIDEO:00: Failed to change power state to D0

This commit changes the acpi_lpss.c code to resume the BYT/CHT I2C
controllers at resume_noirq time fixing this.
Tested-by: default avatarJarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 2d71ee0c
...@@ -84,6 +84,7 @@ struct lpss_device_desc { ...@@ -84,6 +84,7 @@ struct lpss_device_desc {
size_t prv_size_override; size_t prv_size_override;
struct property_entry *properties; struct property_entry *properties;
void (*setup)(struct lpss_private_data *pdata); void (*setup)(struct lpss_private_data *pdata);
bool resume_from_noirq;
}; };
static const struct lpss_device_desc lpss_dma_desc = { static const struct lpss_device_desc lpss_dma_desc = {
...@@ -293,12 +294,14 @@ static const struct lpss_device_desc byt_i2c_dev_desc = { ...@@ -293,12 +294,14 @@ static const struct lpss_device_desc byt_i2c_dev_desc = {
.flags = LPSS_CLK | LPSS_SAVE_CTX, .flags = LPSS_CLK | LPSS_SAVE_CTX,
.prv_offset = 0x800, .prv_offset = 0x800,
.setup = byt_i2c_setup, .setup = byt_i2c_setup,
.resume_from_noirq = true,
}; };
static const struct lpss_device_desc bsw_i2c_dev_desc = { static const struct lpss_device_desc bsw_i2c_dev_desc = {
.flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
.prv_offset = 0x800, .prv_offset = 0x800,
.setup = byt_i2c_setup, .setup = byt_i2c_setup,
.resume_from_noirq = true,
}; };
static const struct lpss_device_desc bsw_spi_dev_desc = { static const struct lpss_device_desc bsw_spi_dev_desc = {
...@@ -1039,7 +1042,7 @@ static int acpi_lpss_resume(struct device *dev) ...@@ -1039,7 +1042,7 @@ static int acpi_lpss_resume(struct device *dev)
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late(struct device *dev) static int acpi_lpss_do_suspend_late(struct device *dev)
{ {
int ret; int ret;
...@@ -1050,12 +1053,62 @@ static int acpi_lpss_suspend_late(struct device *dev) ...@@ -1050,12 +1053,62 @@ static int acpi_lpss_suspend_late(struct device *dev)
return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
} }
static int acpi_lpss_resume_early(struct device *dev) static int acpi_lpss_suspend_late(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
if (pdata->dev_desc->resume_from_noirq)
return 0;
return acpi_lpss_do_suspend_late(dev);
}
static int acpi_lpss_suspend_noirq(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
if (pdata->dev_desc->resume_from_noirq) {
ret = acpi_lpss_do_suspend_late(dev);
if (ret)
return ret;
}
return acpi_subsys_suspend_noirq(dev);
}
static int acpi_lpss_do_resume_early(struct device *dev)
{ {
int ret = acpi_lpss_resume(dev); int ret = acpi_lpss_resume(dev);
return ret ? ret : pm_generic_resume_early(dev); return ret ? ret : pm_generic_resume_early(dev);
} }
static int acpi_lpss_resume_early(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
if (pdata->dev_desc->resume_from_noirq)
return 0;
return acpi_lpss_do_resume_early(dev);
}
static int acpi_lpss_resume_noirq(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
ret = acpi_subsys_resume_noirq(dev);
if (ret)
return ret;
if (!dev_pm_may_skip_resume(dev) && pdata->dev_desc->resume_from_noirq)
ret = acpi_lpss_do_resume_early(dev);
return ret;
}
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev) static int acpi_lpss_runtime_suspend(struct device *dev)
...@@ -1085,8 +1138,8 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { ...@@ -1085,8 +1138,8 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
.complete = acpi_subsys_complete, .complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend, .suspend = acpi_subsys_suspend,
.suspend_late = acpi_lpss_suspend_late, .suspend_late = acpi_lpss_suspend_late,
.suspend_noirq = acpi_subsys_suspend_noirq, .suspend_noirq = acpi_lpss_suspend_noirq,
.resume_noirq = acpi_subsys_resume_noirq, .resume_noirq = acpi_lpss_resume_noirq,
.resume_early = acpi_lpss_resume_early, .resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze, .freeze = acpi_subsys_freeze,
.freeze_late = acpi_subsys_freeze_late, .freeze_late = acpi_subsys_freeze_late,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment