|
@@ -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)
|
|
@@ -892,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);
|
|
@@ -910,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;
|
|
|
|
|
@@ -1004,7 +1042,7 @@ static int acpi_lpss_resume(struct device *dev)
|
|
|
}
|
|
|
|
|
|
#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;
|
|
|
|
|
@@ -1015,12 +1053,62 @@ static int acpi_lpss_suspend_late(struct device *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);
|
|
|
|
|
|
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)
|
|
@@ -1050,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,
|