ACPI / LPSS: Resume BYT/CHT I2C controllers from resume_noirq
[sfrench/cifs-2.6.git] / drivers / acpi / acpi_lpss.c
index f8fecfec5df9b85be3e0d6c78a54087952140fdc..10adb8cb3a3f27c4c563983a827457554686f7d5 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/mutex.h>
+#include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/clk-lpss.h>
 #include <linux/platform_data/x86/pmc_atom.h>
@@ -83,6 +84,7 @@ struct lpss_device_desc {
        size_t prv_size_override;
        struct property_entry *properties;
        void (*setup)(struct lpss_private_data *pdata);
+       bool resume_from_noirq;
 };
 
 static const struct lpss_device_desc lpss_dma_desc = {
@@ -99,6 +101,9 @@ struct lpss_private_data {
        u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
 };
 
+/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */
+static u32 pmc_atom_d3_mask = 0xfe000ffe;
+
 /* LPSS run time quirks */
 static unsigned int lpss_quirks;
 
@@ -175,6 +180,21 @@ static void byt_pwm_setup(struct lpss_private_data *pdata)
 
 static void byt_i2c_setup(struct lpss_private_data *pdata)
 {
+       const char *uid_str = acpi_device_uid(pdata->adev);
+       acpi_handle handle = pdata->adev->handle;
+       unsigned long long shared_host = 0;
+       acpi_status status;
+       long uid = 0;
+
+       /* Expected to always be true, but better safe then sorry */
+       if (uid_str)
+               uid = simple_strtol(uid_str, NULL, 10);
+
+       /* Detect I2C bus shared with PUNIT and ignore its d3 status */
+       status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
+       if (ACPI_SUCCESS(status) && shared_host && uid)
+               pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1));
+
        lpss_deassert_reset(pdata);
 
        if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
@@ -274,12 +294,14 @@ static const struct lpss_device_desc byt_i2c_dev_desc = {
        .flags = LPSS_CLK | LPSS_SAVE_CTX,
        .prv_offset = 0x800,
        .setup = byt_i2c_setup,
+       .resume_from_noirq = true,
 };
 
 static const struct lpss_device_desc bsw_i2c_dev_desc = {
        .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
        .prv_offset = 0x800,
        .setup = byt_i2c_setup,
+       .resume_from_noirq = true,
 };
 
 static const struct lpss_device_desc bsw_spi_dev_desc = {
@@ -327,9 +349,11 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
        { "INT33FC", },
 
        /* Braswell LPSS devices */
+       { "80862286", LPSS_ADDR(lpss_dma_desc) },
        { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
        { "8086228A", LPSS_ADDR(bsw_uart_dev_desc) },
        { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) },
+       { "808622C0", LPSS_ADDR(lpss_dma_desc) },
        { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) },
 
        /* Broadwell LPSS devices */
@@ -451,26 +475,35 @@ struct lpss_device_links {
  */
 static const struct lpss_device_links lpss_device_links[] = {
        {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME},
+       {"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
+       {"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
 };
 
-static bool hid_uid_match(const char *hid1, const char *uid1,
+static bool hid_uid_match(struct acpi_device *adev,
                          const char *hid2, const char *uid2)
 {
-       return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2);
+       const char *hid1 = acpi_device_hid(adev);
+       const char *uid1 = acpi_device_uid(adev);
+
+       if (strcmp(hid1, hid2))
+               return false;
+
+       if (!uid2)
+               return true;
+
+       return uid1 && !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);
+       return hid_uid_match(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);
+       return hid_uid_match(adev, link->consumer_hid, link->consumer_uid);
 }
 
 struct hid_uid {
@@ -486,18 +519,23 @@ static int match_hid_uid(struct device *dev, void *data)
        if (!adev)
                return 0;
 
-       return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev),
-                            id->hid, id->uid);
+       return hid_uid_match(adev, id->hid, id->uid);
 }
 
 static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
 {
+       struct device *dev;
+
        struct hid_uid data = {
                .hid = hid,
                .uid = uid,
        };
 
-       return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
+       dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
+       if (dev)
+               return dev;
+
+       return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
 }
 
 static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
@@ -879,6 +917,7 @@ static void acpi_lpss_dismiss(struct device *dev)
 #define LPSS_GPIODEF0_DMA_LLP          BIT(13)
 
 static DEFINE_MUTEX(lpss_iosf_mutex);
+static bool lpss_iosf_d3_entered = true;
 
 static void lpss_iosf_enter_d3_state(void)
 {
@@ -891,7 +930,7 @@ static void lpss_iosf_enter_d3_state(void)
         * Here we read the values related to LPSS power island, i.e. LPSS
         * devices, excluding both LPSS DMA controllers, along with SCC domain.
         */
-       u32 func_dis, d3_sts_0, pmc_status, pmc_mask = 0xfe000ffe;
+       u32 func_dis, d3_sts_0, pmc_status;
        int ret;
 
        ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
@@ -909,7 +948,7 @@ static void lpss_iosf_enter_d3_state(void)
         * Shutdown both LPSS DMA controllers if and only if all other devices
         * are already in D3hot.
         */
-       pmc_status = (~(d3_sts_0 | func_dis)) & pmc_mask;
+       pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask;
        if (pmc_status)
                goto exit;
 
@@ -921,6 +960,9 @@ static void lpss_iosf_enter_d3_state(void)
 
        iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
                        LPSS_IOSF_GPIODEF0, value1, mask1);
+
+       lpss_iosf_d3_entered = true;
+
 exit:
        mutex_unlock(&lpss_iosf_mutex);
 }
@@ -935,6 +977,11 @@ static void lpss_iosf_exit_d3_state(void)
 
        mutex_lock(&lpss_iosf_mutex);
 
+       if (!lpss_iosf_d3_entered)
+               goto exit;
+
+       lpss_iosf_d3_entered = false;
+
        iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
                        LPSS_IOSF_GPIODEF0, value1, mask1);
 
@@ -944,13 +991,13 @@ static void lpss_iosf_exit_d3_state(void)
        iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
                        LPSS_IOSF_PMCSR, value2, mask2);
 
+exit:
        mutex_unlock(&lpss_iosf_mutex);
 }
 
-static int acpi_lpss_suspend(struct device *dev, bool runtime)
+static int acpi_lpss_suspend(struct device *dev, bool wakeup)
 {
        struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
-       bool wakeup = runtime || device_may_wakeup(dev);
        int ret;
 
        if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
@@ -963,14 +1010,14 @@ static int acpi_lpss_suspend(struct device *dev, bool runtime)
         * wrong status for devices being about to be powered off. See
         * lpss_iosf_enter_d3_state() for further information.
         */
-       if ((runtime || !pm_suspend_via_firmware()) &&
+       if (acpi_target_system_state() == ACPI_STATE_S0 &&
            lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
                lpss_iosf_enter_d3_state();
 
        return ret;
 }
 
-static int acpi_lpss_resume(struct device *dev, bool runtime)
+static int acpi_lpss_resume(struct device *dev)
 {
        struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
        int ret;
@@ -979,8 +1026,7 @@ static int acpi_lpss_resume(struct device *dev, bool runtime)
         * This call is kept first to be in symmetry with
         * acpi_lpss_runtime_suspend() one.
         */
-       if ((runtime || !pm_resume_via_firmware()) &&
-           lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
+       if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
                lpss_iosf_exit_d3_state();
 
        ret = acpi_dev_resume(dev);
@@ -996,7 +1042,7 @@ static int acpi_lpss_resume(struct device *dev, bool runtime)
 }
 
 #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;
 
@@ -1004,15 +1050,65 @@ static int acpi_lpss_suspend_late(struct device *dev)
                return 0;
 
        ret = pm_generic_suspend_late(dev);
-       return ret ? ret : acpi_lpss_suspend(dev, false);
+       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)
 {
-       int ret = acpi_lpss_resume(dev, false);
+       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);
 
        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 */
 
 static int acpi_lpss_runtime_suspend(struct device *dev)
@@ -1024,7 +1120,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
 
 static int acpi_lpss_runtime_resume(struct device *dev)
 {
-       int ret = acpi_lpss_resume(dev, true);
+       int ret = acpi_lpss_resume(dev);
 
        return ret ? ret : pm_generic_runtime_resume(dev);
 }
@@ -1042,8 +1138,8 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
                .complete = acpi_subsys_complete,
                .suspend = acpi_subsys_suspend,
                .suspend_late = acpi_lpss_suspend_late,
-               .suspend_noirq = acpi_subsys_suspend_noirq,
-               .resume_noirq = acpi_subsys_resume_noirq,
+               .suspend_noirq = acpi_lpss_suspend_noirq,
+               .resume_noirq = acpi_lpss_resume_noirq,
                .resume_early = acpi_lpss_resume_early,
                .freeze = acpi_subsys_freeze,
                .freeze_late = acpi_subsys_freeze_late,