xref: /openbmc/linux/drivers/acpi/acpi_lpss.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f58b082aSRafael J. Wysocki /*
3f58b082aSRafael J. Wysocki  * ACPI support for Intel Lynxpoint LPSS.
4f58b082aSRafael J. Wysocki  *
53df2da96SRafael J. Wysocki  * Copyright (C) 2013, Intel Corporation
6f58b082aSRafael J. Wysocki  * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
7f58b082aSRafael J. Wysocki  *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>
8f58b082aSRafael J. Wysocki  */
9f58b082aSRafael J. Wysocki 
10f58b082aSRafael J. Wysocki #include <linux/acpi.h>
11f58b082aSRafael J. Wysocki #include <linux/clkdev.h>
12f58b082aSRafael J. Wysocki #include <linux/clk-provider.h>
136025e2faSHans de Goede #include <linux/dmi.h>
14f58b082aSRafael J. Wysocki #include <linux/err.h>
15f58b082aSRafael J. Wysocki #include <linux/io.h>
16eebb3e8dSAndy Shevchenko #include <linux/mutex.h>
171e30124aSHans de Goede #include <linux/pci.h>
18f58b082aSRafael J. Wysocki #include <linux/platform_device.h>
19a9443a63SAndy Shevchenko #include <linux/platform_data/x86/clk-lpss.h>
2080a7581fSIrina Tirdea #include <linux/platform_data/x86/pmc_atom.h>
21989561deSTomeu Vizoso #include <linux/pm_domain.h>
222e0f8822SRafael J. Wysocki #include <linux/pm_runtime.h>
23bf7696a1SHans de Goede #include <linux/pwm.h>
24620c803fSAndy Shevchenko #include <linux/pxa2xx_ssp.h>
25a09c5913SRafael J. Wysocki #include <linux/suspend.h>
26c78b0830SHeikki Krogerus #include <linux/delay.h>
27f58b082aSRafael J. Wysocki 
28f58b082aSRafael J. Wysocki #include "internal.h"
29f58b082aSRafael J. Wysocki 
30d6ddaaacSRafael J. Wysocki #ifdef CONFIG_X86_INTEL_LPSS
31d6ddaaacSRafael J. Wysocki 
32eebb3e8dSAndy Shevchenko #include <asm/cpu_device_id.h>
334626d840SDave Hansen #include <asm/intel-family.h>
34eebb3e8dSAndy Shevchenko #include <asm/iosf_mbi.h>
35eebb3e8dSAndy Shevchenko 
36d6ddaaacSRafael J. Wysocki #define LPSS_ADDR(desc) ((unsigned long)&desc)
37d6ddaaacSRafael J. Wysocki 
38f58b082aSRafael J. Wysocki #define LPSS_CLK_SIZE	0x04
392e0f8822SRafael J. Wysocki #define LPSS_LTR_SIZE	0x18
402e0f8822SRafael J. Wysocki 
412e0f8822SRafael J. Wysocki /* Offsets relative to LPSS_PRIVATE_OFFSET */
42ed3a872eSHeikki Krogerus #define LPSS_CLK_DIVIDER_DEF_MASK	(BIT(1) | BIT(16))
43765bdd4eSMika Westerberg #define LPSS_RESETS			0x04
44765bdd4eSMika Westerberg #define LPSS_RESETS_RESET_FUNC		BIT(0)
45765bdd4eSMika Westerberg #define LPSS_RESETS_RESET_APB		BIT(1)
462e0f8822SRafael J. Wysocki #define LPSS_GENERAL			0x08
472e0f8822SRafael J. Wysocki #define LPSS_GENERAL_LTR_MODE_SW	BIT(2)
48088f1fd2SHeikki Krogerus #define LPSS_GENERAL_UART_RTS_OVRD	BIT(3)
492e0f8822SRafael J. Wysocki #define LPSS_SW_LTR			0x10
502e0f8822SRafael J. Wysocki #define LPSS_AUTO_LTR			0x14
511a8f8351SRafael J. Wysocki #define LPSS_LTR_SNOOP_REQ		BIT(15)
521a8f8351SRafael J. Wysocki #define LPSS_LTR_SNOOP_MASK		0x0000FFFF
531a8f8351SRafael J. Wysocki #define LPSS_LTR_SNOOP_LAT_1US		0x800
541a8f8351SRafael J. Wysocki #define LPSS_LTR_SNOOP_LAT_32US		0xC00
551a8f8351SRafael J. Wysocki #define LPSS_LTR_SNOOP_LAT_SHIFT	5
561a8f8351SRafael J. Wysocki #define LPSS_LTR_SNOOP_LAT_CUTOFF	3000
571a8f8351SRafael J. Wysocki #define LPSS_LTR_MAX_VAL		0x3FF
5806d86415SHeikki Krogerus #define LPSS_TX_INT			0x20
5906d86415SHeikki Krogerus #define LPSS_TX_INT_MASK		BIT(1)
60f58b082aSRafael J. Wysocki 
61c78b0830SHeikki Krogerus #define LPSS_PRV_REG_COUNT		9
62c78b0830SHeikki Krogerus 
63ff8c1af5SHeikki Krogerus /* LPSS Flags */
64ff8c1af5SHeikki Krogerus #define LPSS_CLK			BIT(0)
65ff8c1af5SHeikki Krogerus #define LPSS_CLK_GATE			BIT(1)
66ff8c1af5SHeikki Krogerus #define LPSS_CLK_DIVIDER		BIT(2)
67ff8c1af5SHeikki Krogerus #define LPSS_LTR			BIT(3)
68ff8c1af5SHeikki Krogerus #define LPSS_SAVE_CTX			BIT(4)
6915aa5e4cSHans de Goede /*
7015aa5e4cSHans de Goede  * For some devices the DSDT AML code for another device turns off the device
7115aa5e4cSHans de Goede  * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff)
7215aa5e4cSHans de Goede  * as ctx register values.
7315aa5e4cSHans de Goede  * Luckily these devices always use the same ctx register values, so we can
7415aa5e4cSHans de Goede  * work around this by saving the ctx registers once on activation.
7515aa5e4cSHans de Goede  */
7615aa5e4cSHans de Goede #define LPSS_SAVE_CTX_ONCE		BIT(5)
7715aa5e4cSHans de Goede #define LPSS_NO_D3_DELAY		BIT(6)
78f6272170SMika Westerberg 
7906d86415SHeikki Krogerus struct lpss_private_data;
80f58b082aSRafael J. Wysocki 
81f58b082aSRafael J. Wysocki struct lpss_device_desc {
82ff8c1af5SHeikki Krogerus 	unsigned int flags;
83fcf0789aSHeikki Krogerus 	const char *clk_con_id;
842e0f8822SRafael J. Wysocki 	unsigned int prv_offset;
85958c4eb2SMika Westerberg 	size_t prv_size_override;
86f167c1a1SAndy Shevchenko 	const struct property_entry *properties;
8706d86415SHeikki Krogerus 	void (*setup)(struct lpss_private_data *pdata);
8848402ceeSHans de Goede 	bool resume_from_noirq;
89f58b082aSRafael J. Wysocki };
90f58b082aSRafael J. Wysocki 
91eebb3e8dSAndy Shevchenko static const struct lpss_device_desc lpss_dma_desc = {
923df2da96SRafael J. Wysocki 	.flags = LPSS_CLK,
93b59cc200SRafael J. Wysocki };
94b59cc200SRafael J. Wysocki 
95f58b082aSRafael J. Wysocki struct lpss_private_data {
96dd242a08SHans de Goede 	struct acpi_device *adev;
97f58b082aSRafael J. Wysocki 	void __iomem *mmio_base;
98f58b082aSRafael J. Wysocki 	resource_size_t mmio_size;
9903f09f73SHeikki Krogerus 	unsigned int fixed_clk_rate;
100f58b082aSRafael J. Wysocki 	struct clk *clk;
101f58b082aSRafael J. Wysocki 	const struct lpss_device_desc *dev_desc;
102c78b0830SHeikki Krogerus 	u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
103f58b082aSRafael J. Wysocki };
104f58b082aSRafael J. Wysocki 
10586b62e5cSHans de Goede /* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */
10686b62e5cSHans de Goede static u32 pmc_atom_d3_mask = 0xfe000ffe;
10786b62e5cSHans de Goede 
108eebb3e8dSAndy Shevchenko /* LPSS run time quirks */
109eebb3e8dSAndy Shevchenko static unsigned int lpss_quirks;
110eebb3e8dSAndy Shevchenko 
111eebb3e8dSAndy Shevchenko /*
112eebb3e8dSAndy Shevchenko  * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device.
113eebb3e8dSAndy Shevchenko  *
114fa9e93b1SAndy Shevchenko  * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover
115eebb3e8dSAndy Shevchenko  * it can be powered off automatically whenever the last LPSS device goes down.
116eebb3e8dSAndy Shevchenko  * In case of no power any access to the DMA controller will hang the system.
117eebb3e8dSAndy Shevchenko  * The behaviour is reproduced on some HP laptops based on Intel BayTrail as
118eebb3e8dSAndy Shevchenko  * well as on ASuS T100TA transformer.
119eebb3e8dSAndy Shevchenko  *
120eebb3e8dSAndy Shevchenko  * This quirk overrides power state of entire LPSS island to keep DMA powered
121eebb3e8dSAndy Shevchenko  * on whenever we have at least one other device in use.
122eebb3e8dSAndy Shevchenko  */
123eebb3e8dSAndy Shevchenko #define LPSS_QUIRK_ALWAYS_POWER_ON	BIT(0)
124eebb3e8dSAndy Shevchenko 
1251f47a77cSHeikki Krogerus /* UART Component Parameter Register */
1261f47a77cSHeikki Krogerus #define LPSS_UART_CPR			0xF4
1271f47a77cSHeikki Krogerus #define LPSS_UART_CPR_AFCE		BIT(4)
1281f47a77cSHeikki Krogerus 
lpss_uart_setup(struct lpss_private_data * pdata)12906d86415SHeikki Krogerus static void lpss_uart_setup(struct lpss_private_data *pdata)
13006d86415SHeikki Krogerus {
131088f1fd2SHeikki Krogerus 	unsigned int offset;
1321f47a77cSHeikki Krogerus 	u32 val;
13306d86415SHeikki Krogerus 
134088f1fd2SHeikki Krogerus 	offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
1351f47a77cSHeikki Krogerus 	val = readl(pdata->mmio_base + offset);
1361f47a77cSHeikki Krogerus 	writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
137088f1fd2SHeikki Krogerus 
1381f47a77cSHeikki Krogerus 	val = readl(pdata->mmio_base + LPSS_UART_CPR);
1391f47a77cSHeikki Krogerus 	if (!(val & LPSS_UART_CPR_AFCE)) {
140088f1fd2SHeikki Krogerus 		offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
1411f47a77cSHeikki Krogerus 		val = readl(pdata->mmio_base + offset);
1421f47a77cSHeikki Krogerus 		val |= LPSS_GENERAL_UART_RTS_OVRD;
1431f47a77cSHeikki Krogerus 		writel(val, pdata->mmio_base + offset);
1441f47a77cSHeikki Krogerus 	}
14506d86415SHeikki Krogerus }
14606d86415SHeikki Krogerus 
lpss_deassert_reset(struct lpss_private_data * pdata)1473095794aSMika Westerberg static void lpss_deassert_reset(struct lpss_private_data *pdata)
148765bdd4eSMika Westerberg {
149765bdd4eSMika Westerberg 	unsigned int offset;
150765bdd4eSMika Westerberg 	u32 val;
151765bdd4eSMika Westerberg 
152765bdd4eSMika Westerberg 	offset = pdata->dev_desc->prv_offset + LPSS_RESETS;
153765bdd4eSMika Westerberg 	val = readl(pdata->mmio_base + offset);
154765bdd4eSMika Westerberg 	val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC;
155765bdd4eSMika Westerberg 	writel(val, pdata->mmio_base + offset);
1563095794aSMika Westerberg }
1573095794aSMika Westerberg 
15804434ab5SHans de Goede /*
15904434ab5SHans de Goede  * BYT PWM used for backlight control by the i915 driver on systems without
16004434ab5SHans de Goede  * the Crystal Cove PMIC.
16104434ab5SHans de Goede  */
16204434ab5SHans de Goede static struct pwm_lookup byt_pwm_lookup[] = {
16304434ab5SHans de Goede 	PWM_LOOKUP_WITH_MODULE("80860F09:00", 0, "0000:00:02.0",
164b2147a3aSHans de Goede 			       "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
16504434ab5SHans de Goede 			       "pwm-lpss-platform"),
16604434ab5SHans de Goede };
16704434ab5SHans de Goede 
byt_pwm_setup(struct lpss_private_data * pdata)16804434ab5SHans de Goede static void byt_pwm_setup(struct lpss_private_data *pdata)
16904434ab5SHans de Goede {
1702a036e48SAndy Shevchenko 	u64 uid;
171dd242a08SHans de Goede 
172dd242a08SHans de Goede 	/* Only call pwm_add_table for the first PWM controller */
1732a036e48SAndy Shevchenko 	if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 1)
174dd242a08SHans de Goede 		return;
175dd242a08SHans de Goede 
17604434ab5SHans de Goede 	pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup));
17704434ab5SHans de Goede }
17804434ab5SHans de Goede 
1793095794aSMika Westerberg #define LPSS_I2C_ENABLE			0x6c
1803095794aSMika Westerberg 
byt_i2c_setup(struct lpss_private_data * pdata)1813095794aSMika Westerberg static void byt_i2c_setup(struct lpss_private_data *pdata)
1823095794aSMika Westerberg {
18386b62e5cSHans de Goede 	acpi_handle handle = pdata->adev->handle;
18486b62e5cSHans de Goede 	unsigned long long shared_host = 0;
18586b62e5cSHans de Goede 	acpi_status status;
1862a036e48SAndy Shevchenko 	u64 uid;
18786b62e5cSHans de Goede 
1882a036e48SAndy Shevchenko 	/* Expected to always be successfull, but better safe then sorry */
1892a036e48SAndy Shevchenko 	if (!acpi_dev_uid_to_integer(pdata->adev, &uid) && uid) {
19086b62e5cSHans de Goede 		/* Detect I2C bus shared with PUNIT and ignore its d3 status */
19186b62e5cSHans de Goede 		status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
1928e3ecc68SLiu Shixin 		if (ACPI_SUCCESS(status) && shared_host)
19386b62e5cSHans de Goede 			pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1));
1948e3ecc68SLiu Shixin 	}
19586b62e5cSHans de Goede 
1963095794aSMika Westerberg 	lpss_deassert_reset(pdata);
19703f09f73SHeikki Krogerus 
19803f09f73SHeikki Krogerus 	if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
19903f09f73SHeikki Krogerus 		pdata->fixed_clk_rate = 133000000;
2003293c7b8SMika Westerberg 
2013293c7b8SMika Westerberg 	writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
202765bdd4eSMika Westerberg }
203765bdd4eSMika Westerberg 
204fa578bf5SHans de Goede /*
205fa578bf5SHans de Goede  * BSW PWM1 is used for backlight control by the i915 driver
206fa578bf5SHans de Goede  * BSW PWM2 is used for backlight control for fixed (etched into the glass)
207fa578bf5SHans de Goede  * touch controls on some models. These touch-controls have specialized
208fa578bf5SHans de Goede  * drivers which know they need the "pwm_soc_lpss_2" con-id.
209fa578bf5SHans de Goede  */
210bf7696a1SHans de Goede static struct pwm_lookup bsw_pwm_lookup[] = {
211bf7696a1SHans de Goede 	PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0",
212b2147a3aSHans de Goede 			       "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL,
213bf7696a1SHans de Goede 			       "pwm-lpss-platform"),
214fa578bf5SHans de Goede 	PWM_LOOKUP_WITH_MODULE("80862289:00", 0, NULL,
215fa578bf5SHans de Goede 			       "pwm_soc_lpss_2", 0, PWM_POLARITY_NORMAL,
216fa578bf5SHans de Goede 			       "pwm-lpss-platform"),
217bf7696a1SHans de Goede };
218bf7696a1SHans de Goede 
bsw_pwm_setup(struct lpss_private_data * pdata)219bf7696a1SHans de Goede static void bsw_pwm_setup(struct lpss_private_data *pdata)
220bf7696a1SHans de Goede {
2212a036e48SAndy Shevchenko 	u64 uid;
222dd242a08SHans de Goede 
223dd242a08SHans de Goede 	/* Only call pwm_add_table for the first PWM controller */
2242a036e48SAndy Shevchenko 	if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 1)
225dd242a08SHans de Goede 		return;
226dd242a08SHans de Goede 
227bf7696a1SHans de Goede 	pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup));
228bf7696a1SHans de Goede }
229bf7696a1SHans de Goede 
230620c803fSAndy Shevchenko static const struct property_entry lpt_spi_properties[] = {
231620c803fSAndy Shevchenko 	PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_LPT_SSP),
232620c803fSAndy Shevchenko 	{ }
233620c803fSAndy Shevchenko };
234620c803fSAndy Shevchenko 
235620c803fSAndy Shevchenko static const struct lpss_device_desc lpt_spi_dev_desc = {
23657b30064SJarkko Nikula 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR
23757b30064SJarkko Nikula 			| LPSS_SAVE_CTX,
2382e0f8822SRafael J. Wysocki 	.prv_offset = 0x800,
239620c803fSAndy Shevchenko 	.properties = lpt_spi_properties,
240ed3a872eSHeikki Krogerus };
241ed3a872eSHeikki Krogerus 
242b2687cd7SMathias Krause static const struct lpss_device_desc lpt_i2c_dev_desc = {
24357b30064SJarkko Nikula 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR | LPSS_SAVE_CTX,
244ed3a872eSHeikki Krogerus 	.prv_offset = 0x800,
2452e0f8822SRafael J. Wysocki };
2462e0f8822SRafael J. Wysocki 
247a5565cf2SHeikki Krogerus static struct property_entry uart_properties[] = {
248a5565cf2SHeikki Krogerus 	PROPERTY_ENTRY_U32("reg-io-width", 4),
249a5565cf2SHeikki Krogerus 	PROPERTY_ENTRY_U32("reg-shift", 2),
250a5565cf2SHeikki Krogerus 	PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
251a5565cf2SHeikki Krogerus 	{ },
252a5565cf2SHeikki Krogerus };
253a5565cf2SHeikki Krogerus 
254b2687cd7SMathias Krause static const struct lpss_device_desc lpt_uart_dev_desc = {
25557b30064SJarkko Nikula 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR
25657b30064SJarkko Nikula 			| LPSS_SAVE_CTX,
257fcf0789aSHeikki Krogerus 	.clk_con_id = "baudclk",
25806d86415SHeikki Krogerus 	.prv_offset = 0x800,
25906d86415SHeikki Krogerus 	.setup = lpss_uart_setup,
260a5565cf2SHeikki Krogerus 	.properties = uart_properties,
2612e0f8822SRafael J. Wysocki };
2622e0f8822SRafael J. Wysocki 
263b2687cd7SMathias Krause static const struct lpss_device_desc lpt_sdio_dev_desc = {
264ff8c1af5SHeikki Krogerus 	.flags = LPSS_LTR,
2652e0f8822SRafael J. Wysocki 	.prv_offset = 0x1000,
266958c4eb2SMika Westerberg 	.prv_size_override = 0x1018,
267e1c74817SChew, Chiau Ee };
268e1c74817SChew, Chiau Ee 
269b2687cd7SMathias Krause static const struct lpss_device_desc byt_pwm_dev_desc = {
2703f56bf3eSHeikki Krogerus 	.flags = LPSS_SAVE_CTX,
271fdcb613dSHans de Goede 	.prv_offset = 0x800,
27204434ab5SHans de Goede 	.setup = byt_pwm_setup,
273e1c74817SChew, Chiau Ee };
274e1c74817SChew, Chiau Ee 
275b00855aeSSrinidhi Kasagar static const struct lpss_device_desc bsw_pwm_dev_desc = {
27615aa5e4cSHans de Goede 	.flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY,
277fdcb613dSHans de Goede 	.prv_offset = 0x800,
278bf7696a1SHans de Goede 	.setup = bsw_pwm_setup,
2795e31ee84SHans de Goede 	.resume_from_noirq = true,
280b00855aeSSrinidhi Kasagar };
281b00855aeSSrinidhi Kasagar 
28203c57b01SHans de Goede static const struct lpss_device_desc bsw_pwm2_dev_desc = {
28303c57b01SHans de Goede 	.flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY,
28403c57b01SHans de Goede 	.prv_offset = 0x800,
28503c57b01SHans de Goede 	.resume_from_noirq = true,
28603c57b01SHans de Goede };
28703c57b01SHans de Goede 
288b2687cd7SMathias Krause static const struct lpss_device_desc byt_uart_dev_desc = {
2893df2da96SRafael J. Wysocki 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
290fcf0789aSHeikki Krogerus 	.clk_con_id = "baudclk",
291f6272170SMika Westerberg 	.prv_offset = 0x800,
29206d86415SHeikki Krogerus 	.setup = lpss_uart_setup,
293a5565cf2SHeikki Krogerus 	.properties = uart_properties,
294f6272170SMika Westerberg };
295f6272170SMika Westerberg 
296b00855aeSSrinidhi Kasagar static const struct lpss_device_desc bsw_uart_dev_desc = {
297b00855aeSSrinidhi Kasagar 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
298b00855aeSSrinidhi Kasagar 			| LPSS_NO_D3_DELAY,
299b00855aeSSrinidhi Kasagar 	.clk_con_id = "baudclk",
300b00855aeSSrinidhi Kasagar 	.prv_offset = 0x800,
301b00855aeSSrinidhi Kasagar 	.setup = lpss_uart_setup,
302a5565cf2SHeikki Krogerus 	.properties = uart_properties,
303b00855aeSSrinidhi Kasagar };
304b00855aeSSrinidhi Kasagar 
305620c803fSAndy Shevchenko static const struct property_entry byt_spi_properties[] = {
306620c803fSAndy Shevchenko 	PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BYT_SSP),
307620c803fSAndy Shevchenko 	{ }
308620c803fSAndy Shevchenko };
309620c803fSAndy Shevchenko 
310b2687cd7SMathias Krause static const struct lpss_device_desc byt_spi_dev_desc = {
3113df2da96SRafael J. Wysocki 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
312f6272170SMika Westerberg 	.prv_offset = 0x400,
313620c803fSAndy Shevchenko 	.properties = byt_spi_properties,
314f6272170SMika Westerberg };
315f6272170SMika Westerberg 
316b2687cd7SMathias Krause static const struct lpss_device_desc byt_sdio_dev_desc = {
3173df2da96SRafael J. Wysocki 	.flags = LPSS_CLK,
318f6272170SMika Westerberg };
319f6272170SMika Westerberg 
320b2687cd7SMathias Krause static const struct lpss_device_desc byt_i2c_dev_desc = {
3213df2da96SRafael J. Wysocki 	.flags = LPSS_CLK | LPSS_SAVE_CTX,
322f6272170SMika Westerberg 	.prv_offset = 0x800,
32303f09f73SHeikki Krogerus 	.setup = byt_i2c_setup,
32448402ceeSHans de Goede 	.resume_from_noirq = true,
3251bfbd8ebSAlan Cox };
3261bfbd8ebSAlan Cox 
327b00855aeSSrinidhi Kasagar static const struct lpss_device_desc bsw_i2c_dev_desc = {
328b00855aeSSrinidhi Kasagar 	.flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY,
329b00855aeSSrinidhi Kasagar 	.prv_offset = 0x800,
330b00855aeSSrinidhi Kasagar 	.setup = byt_i2c_setup,
33148402ceeSHans de Goede 	.resume_from_noirq = true,
332b00855aeSSrinidhi Kasagar };
333b00855aeSSrinidhi Kasagar 
334620c803fSAndy Shevchenko static const struct property_entry bsw_spi_properties[] = {
335620c803fSAndy Shevchenko 	PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP),
336*040c3a00SAndy Shevchenko 	PROPERTY_ENTRY_U32("num-cs", 2),
337620c803fSAndy Shevchenko 	{ }
338620c803fSAndy Shevchenko };
339620c803fSAndy Shevchenko 
340eebb3e8dSAndy Shevchenko static const struct lpss_device_desc bsw_spi_dev_desc = {
341b00855aeSSrinidhi Kasagar 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
342b00855aeSSrinidhi Kasagar 			| LPSS_NO_D3_DELAY,
3433095794aSMika Westerberg 	.prv_offset = 0x400,
3443095794aSMika Westerberg 	.setup = lpss_deassert_reset,
345620c803fSAndy Shevchenko 	.properties = bsw_spi_properties,
3463095794aSMika Westerberg };
3473095794aSMika Westerberg 
348eebb3e8dSAndy Shevchenko static const struct x86_cpu_id lpss_cpu_ids[] = {
349e36cf2f7SThomas Gleixner 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT,	NULL),
350e36cf2f7SThomas Gleixner 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT,	NULL),
351eebb3e8dSAndy Shevchenko 	{}
352eebb3e8dSAndy Shevchenko };
353eebb3e8dSAndy Shevchenko 
354d6ddaaacSRafael J. Wysocki #else
355d6ddaaacSRafael J. Wysocki 
356d6ddaaacSRafael J. Wysocki #define LPSS_ADDR(desc) (0UL)
357d6ddaaacSRafael J. Wysocki 
358d6ddaaacSRafael J. Wysocki #endif /* CONFIG_X86_INTEL_LPSS */
359d6ddaaacSRafael J. Wysocki 
360f58b082aSRafael J. Wysocki static const struct acpi_device_id acpi_lpss_device_ids[] = {
361b59cc200SRafael J. Wysocki 	/* Generic LPSS devices */
362d6ddaaacSRafael J. Wysocki 	{ "INTL9C60", LPSS_ADDR(lpss_dma_desc) },
363b59cc200SRafael J. Wysocki 
364f58b082aSRafael J. Wysocki 	/* Lynxpoint LPSS devices */
365620c803fSAndy Shevchenko 	{ "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) },
366620c803fSAndy Shevchenko 	{ "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) },
367d6ddaaacSRafael J. Wysocki 	{ "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) },
368d6ddaaacSRafael J. Wysocki 	{ "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) },
369d6ddaaacSRafael J. Wysocki 	{ "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) },
370d6ddaaacSRafael J. Wysocki 	{ "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) },
371d6ddaaacSRafael J. Wysocki 	{ "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) },
372f58b082aSRafael J. Wysocki 	{ "INT33C7", },
373f58b082aSRafael J. Wysocki 
374f6272170SMika Westerberg 	/* BayTrail LPSS devices */
375d6ddaaacSRafael J. Wysocki 	{ "80860F09", LPSS_ADDR(byt_pwm_dev_desc) },
376d6ddaaacSRafael J. Wysocki 	{ "80860F0A", LPSS_ADDR(byt_uart_dev_desc) },
377d6ddaaacSRafael J. Wysocki 	{ "80860F0E", LPSS_ADDR(byt_spi_dev_desc) },
378d6ddaaacSRafael J. Wysocki 	{ "80860F14", LPSS_ADDR(byt_sdio_dev_desc) },
379d6ddaaacSRafael J. Wysocki 	{ "80860F41", LPSS_ADDR(byt_i2c_dev_desc) },
380f6272170SMika Westerberg 	{ "INT33B2", },
38120482d32SJin Yao 	{ "INT33FC", },
382f6272170SMika Westerberg 
3831bfbd8ebSAlan Cox 	/* Braswell LPSS devices */
38424071406SHans de Goede 	{ "80862286", LPSS_ADDR(lpss_dma_desc) },
385b00855aeSSrinidhi Kasagar 	{ "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
38603c57b01SHans de Goede 	{ "80862289", LPSS_ADDR(bsw_pwm2_dev_desc) },
387b00855aeSSrinidhi Kasagar 	{ "8086228A", LPSS_ADDR(bsw_uart_dev_desc) },
3883095794aSMika Westerberg 	{ "8086228E", LPSS_ADDR(bsw_spi_dev_desc) },
38924071406SHans de Goede 	{ "808622C0", LPSS_ADDR(lpss_dma_desc) },
390b00855aeSSrinidhi Kasagar 	{ "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) },
3911bfbd8ebSAlan Cox 
392b00855aeSSrinidhi Kasagar 	/* Broadwell LPSS devices */
393620c803fSAndy Shevchenko 	{ "INT3430", LPSS_ADDR(lpt_spi_dev_desc) },
394620c803fSAndy Shevchenko 	{ "INT3431", LPSS_ADDR(lpt_spi_dev_desc) },
395d6ddaaacSRafael J. Wysocki 	{ "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) },
396d6ddaaacSRafael J. Wysocki 	{ "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) },
397d6ddaaacSRafael J. Wysocki 	{ "INT3434", LPSS_ADDR(lpt_uart_dev_desc) },
398d6ddaaacSRafael J. Wysocki 	{ "INT3435", LPSS_ADDR(lpt_uart_dev_desc) },
399d6ddaaacSRafael J. Wysocki 	{ "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
400a4d97536SMika Westerberg 	{ "INT3437", },
401a4d97536SMika Westerberg 
402ff8c1af5SHeikki Krogerus 	/* Wildcat Point LPSS devices */
403620c803fSAndy Shevchenko 	{ "INT3438", LPSS_ADDR(lpt_spi_dev_desc) },
40443218a1bSJie Yang 
405f58b082aSRafael J. Wysocki 	{ }
406f58b082aSRafael J. Wysocki };
407f58b082aSRafael J. Wysocki 
408d6ddaaacSRafael J. Wysocki #ifdef CONFIG_X86_INTEL_LPSS
409d6ddaaacSRafael J. Wysocki 
410f58b082aSRafael J. Wysocki /* LPSS main clock device. */
411f58b082aSRafael J. Wysocki static struct platform_device *lpss_clk_dev;
412f58b082aSRafael J. Wysocki 
lpt_register_clock_device(void)413f58b082aSRafael J. Wysocki static inline void lpt_register_clock_device(void)
414f58b082aSRafael J. Wysocki {
415cf0a9565SAndy Shevchenko 	lpss_clk_dev = platform_device_register_simple("clk-lpss-atom",
416cf0a9565SAndy Shevchenko 						       PLATFORM_DEVID_NONE,
417cf0a9565SAndy Shevchenko 						       NULL, 0);
418f58b082aSRafael J. Wysocki }
419f58b082aSRafael J. Wysocki 
register_device_clock(struct acpi_device * adev,struct lpss_private_data * pdata)420f58b082aSRafael J. Wysocki static int register_device_clock(struct acpi_device *adev,
421f58b082aSRafael J. Wysocki 				 struct lpss_private_data *pdata)
422f58b082aSRafael J. Wysocki {
423f58b082aSRafael J. Wysocki 	const struct lpss_device_desc *dev_desc = pdata->dev_desc;
424ed3a872eSHeikki Krogerus 	const char *devname = dev_name(&adev->dev);
42571c50dbeSColin Ian King 	struct clk *clk;
426b59cc200SRafael J. Wysocki 	struct lpss_clk_data *clk_data;
427ed3a872eSHeikki Krogerus 	const char *parent, *clk_name;
428ed3a872eSHeikki Krogerus 	void __iomem *prv_base;
429f58b082aSRafael J. Wysocki 
430f58b082aSRafael J. Wysocki 	if (!lpss_clk_dev)
431f58b082aSRafael J. Wysocki 		lpt_register_clock_device();
432f58b082aSRafael J. Wysocki 
433b4f1f61eShuhai 	if (IS_ERR(lpss_clk_dev))
434b4f1f61eShuhai 		return PTR_ERR(lpss_clk_dev);
435b4f1f61eShuhai 
436b59cc200SRafael J. Wysocki 	clk_data = platform_get_drvdata(lpss_clk_dev);
437b59cc200SRafael J. Wysocki 	if (!clk_data)
438b59cc200SRafael J. Wysocki 		return -ENODEV;
439b0d00f8bSHeikki Krogerus 	clk = clk_data->clk;
440b59cc200SRafael J. Wysocki 
441b59cc200SRafael J. Wysocki 	if (!pdata->mmio_base
4422e0f8822SRafael J. Wysocki 	    || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
443f58b082aSRafael J. Wysocki 		return -ENODATA;
444f58b082aSRafael J. Wysocki 
445f6272170SMika Westerberg 	parent = clk_data->name;
446ed3a872eSHeikki Krogerus 	prv_base = pdata->mmio_base + dev_desc->prv_offset;
447f6272170SMika Westerberg 
44803f09f73SHeikki Krogerus 	if (pdata->fixed_clk_rate) {
44903f09f73SHeikki Krogerus 		clk = clk_register_fixed_rate(NULL, devname, parent, 0,
45003f09f73SHeikki Krogerus 					      pdata->fixed_clk_rate);
45103f09f73SHeikki Krogerus 		goto out;
452f6272170SMika Westerberg 	}
453f6272170SMika Westerberg 
454ff8c1af5SHeikki Krogerus 	if (dev_desc->flags & LPSS_CLK_GATE) {
455ed3a872eSHeikki Krogerus 		clk = clk_register_gate(NULL, devname, parent, 0,
456ed3a872eSHeikki Krogerus 					prv_base, 0, 0, NULL);
457ed3a872eSHeikki Krogerus 		parent = devname;
458ed3a872eSHeikki Krogerus 	}
459ed3a872eSHeikki Krogerus 
460ff8c1af5SHeikki Krogerus 	if (dev_desc->flags & LPSS_CLK_DIVIDER) {
461ed3a872eSHeikki Krogerus 		/* Prevent division by zero */
462ed3a872eSHeikki Krogerus 		if (!readl(prv_base))
463ed3a872eSHeikki Krogerus 			writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
464ed3a872eSHeikki Krogerus 
465ed3a872eSHeikki Krogerus 		clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
466ed3a872eSHeikki Krogerus 		if (!clk_name)
467ed3a872eSHeikki Krogerus 			return -ENOMEM;
468ed3a872eSHeikki Krogerus 		clk = clk_register_fractional_divider(NULL, clk_name, parent,
4692920ac9dSAndy Shevchenko 						      0, prv_base, 1, 15, 16, 15,
47082f53f9eSAndy Shevchenko 						      CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
4712920ac9dSAndy Shevchenko 						      NULL);
472ed3a872eSHeikki Krogerus 		parent = clk_name;
473ed3a872eSHeikki Krogerus 
474ed3a872eSHeikki Krogerus 		clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
475ed3a872eSHeikki Krogerus 		if (!clk_name) {
476ed3a872eSHeikki Krogerus 			kfree(parent);
477ed3a872eSHeikki Krogerus 			return -ENOMEM;
478ed3a872eSHeikki Krogerus 		}
479ed3a872eSHeikki Krogerus 		clk = clk_register_gate(NULL, clk_name, parent,
480ed3a872eSHeikki Krogerus 					CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
481ed3a872eSHeikki Krogerus 					prv_base, 31, 0, NULL);
482ed3a872eSHeikki Krogerus 		kfree(parent);
483ed3a872eSHeikki Krogerus 		kfree(clk_name);
484f6272170SMika Westerberg 	}
48503f09f73SHeikki Krogerus out:
486f6272170SMika Westerberg 	if (IS_ERR(clk))
487f6272170SMika Westerberg 		return PTR_ERR(clk);
488f6272170SMika Westerberg 
489ed3a872eSHeikki Krogerus 	pdata->clk = clk;
490fcf0789aSHeikki Krogerus 	clk_register_clkdev(clk, dev_desc->clk_con_id, devname);
491f58b082aSRafael J. Wysocki 	return 0;
492f58b082aSRafael J. Wysocki }
493f58b082aSRafael J. Wysocki 
494e6ce0ce3SAdrian Hunter struct lpss_device_links {
495e6ce0ce3SAdrian Hunter 	const char *supplier_hid;
496e6ce0ce3SAdrian Hunter 	const char *supplier_uid;
497e6ce0ce3SAdrian Hunter 	const char *consumer_hid;
498e6ce0ce3SAdrian Hunter 	const char *consumer_uid;
499e6ce0ce3SAdrian Hunter 	u32 flags;
5006025e2faSHans de Goede 	const struct dmi_system_id *dep_missing_ids;
5016025e2faSHans de Goede };
5026025e2faSHans de Goede 
5036025e2faSHans de Goede /* Please keep this list sorted alphabetically by vendor and model */
5046025e2faSHans de Goede static const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = {
5056025e2faSHans de Goede 	{
5066025e2faSHans de Goede 		.matches = {
5076025e2faSHans de Goede 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
5086025e2faSHans de Goede 			DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"),
5096025e2faSHans de Goede 		},
5106025e2faSHans de Goede 	},
5116025e2faSHans de Goede 	{}
512e6ce0ce3SAdrian Hunter };
513e6ce0ce3SAdrian Hunter 
514e6ce0ce3SAdrian Hunter /*
515e6ce0ce3SAdrian Hunter  * The _DEP method is used to identify dependencies but instead of creating
516e6ce0ce3SAdrian Hunter  * device links for every handle in _DEP, only links in the following list are
517e6ce0ce3SAdrian Hunter  * created. That is necessary because, in the general case, _DEP can refer to
518e6ce0ce3SAdrian Hunter  * devices that might not have drivers, or that are on different buses, or where
519e6ce0ce3SAdrian Hunter  * the supplier is not enumerated until after the consumer is probed.
520e6ce0ce3SAdrian Hunter  */
521e6ce0ce3SAdrian Hunter static const struct lpss_device_links lpss_device_links[] = {
522cc18735fSHans de Goede 	/* CHT External sdcard slot controller depends on PMIC I2C ctrl */
523e6ce0ce3SAdrian Hunter 	{"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME},
524cc18735fSHans de Goede 	/* CHT iGPU depends on PMIC I2C controller */
525bd0f4e34SHans de Goede 	{"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
526b3b3519cSHans de Goede 	/* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */
5276025e2faSHans de Goede 	{"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME,
5286025e2faSHans de Goede 	 i2c1_dep_missing_dmi_ids},
529cc18735fSHans de Goede 	/* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */
5302d71ee0cSHans de Goede 	{"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
531cc18735fSHans de Goede 	/* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */
532cc18735fSHans de Goede 	{"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
533e6ce0ce3SAdrian Hunter };
534e6ce0ce3SAdrian Hunter 
acpi_lpss_is_supplier(struct acpi_device * adev,const struct lpss_device_links * link)535e6ce0ce3SAdrian Hunter static bool acpi_lpss_is_supplier(struct acpi_device *adev,
536e6ce0ce3SAdrian Hunter 				  const struct lpss_device_links *link)
537e6ce0ce3SAdrian Hunter {
5387e70c8acSAndy Shevchenko 	return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid);
539e6ce0ce3SAdrian Hunter }
540e6ce0ce3SAdrian Hunter 
acpi_lpss_is_consumer(struct acpi_device * adev,const struct lpss_device_links * link)541e6ce0ce3SAdrian Hunter static bool acpi_lpss_is_consumer(struct acpi_device *adev,
542e6ce0ce3SAdrian Hunter 				  const struct lpss_device_links *link)
543e6ce0ce3SAdrian Hunter {
5447e70c8acSAndy Shevchenko 	return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid);
545e6ce0ce3SAdrian Hunter }
546e6ce0ce3SAdrian Hunter 
547e6ce0ce3SAdrian Hunter struct hid_uid {
548e6ce0ce3SAdrian Hunter 	const char *hid;
549e6ce0ce3SAdrian Hunter 	const char *uid;
550e6ce0ce3SAdrian Hunter };
551e6ce0ce3SAdrian Hunter 
match_hid_uid(struct device * dev,const void * data)552418e3ea1SSuzuki K Poulose static int match_hid_uid(struct device *dev, const void *data)
553e6ce0ce3SAdrian Hunter {
554e6ce0ce3SAdrian Hunter 	struct acpi_device *adev = ACPI_COMPANION(dev);
555418e3ea1SSuzuki K Poulose 	const struct hid_uid *id = data;
556e6ce0ce3SAdrian Hunter 
557e6ce0ce3SAdrian Hunter 	if (!adev)
558e6ce0ce3SAdrian Hunter 		return 0;
559e6ce0ce3SAdrian Hunter 
5607e70c8acSAndy Shevchenko 	return acpi_dev_hid_uid_match(adev, id->hid, id->uid);
561e6ce0ce3SAdrian Hunter }
562e6ce0ce3SAdrian Hunter 
acpi_lpss_find_device(const char * hid,const char * uid)563e6ce0ce3SAdrian Hunter static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
564e6ce0ce3SAdrian Hunter {
5651e30124aSHans de Goede 	struct device *dev;
5661e30124aSHans de Goede 
567e6ce0ce3SAdrian Hunter 	struct hid_uid data = {
568e6ce0ce3SAdrian Hunter 		.hid = hid,
569e6ce0ce3SAdrian Hunter 		.uid = uid,
570e6ce0ce3SAdrian Hunter 	};
571e6ce0ce3SAdrian Hunter 
5721e30124aSHans de Goede 	dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
5731e30124aSHans de Goede 	if (dev)
5741e30124aSHans de Goede 		return dev;
5751e30124aSHans de Goede 
5761e30124aSHans de Goede 	return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
577e6ce0ce3SAdrian Hunter }
578e6ce0ce3SAdrian Hunter 
acpi_lpss_dep(struct acpi_device * adev,acpi_handle handle)579e6ce0ce3SAdrian Hunter static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
580e6ce0ce3SAdrian Hunter {
581e6ce0ce3SAdrian Hunter 	struct acpi_handle_list dep_devices;
582e6ce0ce3SAdrian Hunter 	acpi_status status;
583e6ce0ce3SAdrian Hunter 	int i;
584e6ce0ce3SAdrian Hunter 
585e6ce0ce3SAdrian Hunter 	if (!acpi_has_method(adev->handle, "_DEP"))
586e6ce0ce3SAdrian Hunter 		return false;
587e6ce0ce3SAdrian Hunter 
588e6ce0ce3SAdrian Hunter 	status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
589e6ce0ce3SAdrian Hunter 					 &dep_devices);
590e6ce0ce3SAdrian Hunter 	if (ACPI_FAILURE(status)) {
591e6ce0ce3SAdrian Hunter 		dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
592e6ce0ce3SAdrian Hunter 		return false;
593e6ce0ce3SAdrian Hunter 	}
594e6ce0ce3SAdrian Hunter 
595e6ce0ce3SAdrian Hunter 	for (i = 0; i < dep_devices.count; i++) {
596e6ce0ce3SAdrian Hunter 		if (dep_devices.handles[i] == handle)
597e6ce0ce3SAdrian Hunter 			return true;
598e6ce0ce3SAdrian Hunter 	}
599e6ce0ce3SAdrian Hunter 
600e6ce0ce3SAdrian Hunter 	return false;
601e6ce0ce3SAdrian Hunter }
602e6ce0ce3SAdrian Hunter 
acpi_lpss_link_consumer(struct device * dev1,const struct lpss_device_links * link)603e6ce0ce3SAdrian Hunter static void acpi_lpss_link_consumer(struct device *dev1,
604e6ce0ce3SAdrian Hunter 				    const struct lpss_device_links *link)
605e6ce0ce3SAdrian Hunter {
606e6ce0ce3SAdrian Hunter 	struct device *dev2;
607e6ce0ce3SAdrian Hunter 
608e6ce0ce3SAdrian Hunter 	dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
609e6ce0ce3SAdrian Hunter 	if (!dev2)
610e6ce0ce3SAdrian Hunter 		return;
611e6ce0ce3SAdrian Hunter 
6126025e2faSHans de Goede 	if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
6136025e2faSHans de Goede 	    || acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
614e6ce0ce3SAdrian Hunter 		device_link_add(dev2, dev1, link->flags);
615e6ce0ce3SAdrian Hunter 
616e6ce0ce3SAdrian Hunter 	put_device(dev2);
617e6ce0ce3SAdrian Hunter }
618e6ce0ce3SAdrian Hunter 
acpi_lpss_link_supplier(struct device * dev1,const struct lpss_device_links * link)619e6ce0ce3SAdrian Hunter static void acpi_lpss_link_supplier(struct device *dev1,
620e6ce0ce3SAdrian Hunter 				    const struct lpss_device_links *link)
621e6ce0ce3SAdrian Hunter {
622e6ce0ce3SAdrian Hunter 	struct device *dev2;
623e6ce0ce3SAdrian Hunter 
624e6ce0ce3SAdrian Hunter 	dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
625e6ce0ce3SAdrian Hunter 	if (!dev2)
626e6ce0ce3SAdrian Hunter 		return;
627e6ce0ce3SAdrian Hunter 
6286025e2faSHans de Goede 	if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
6296025e2faSHans de Goede 	    || acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
630e6ce0ce3SAdrian Hunter 		device_link_add(dev1, dev2, link->flags);
631e6ce0ce3SAdrian Hunter 
632e6ce0ce3SAdrian Hunter 	put_device(dev2);
633e6ce0ce3SAdrian Hunter }
634e6ce0ce3SAdrian Hunter 
acpi_lpss_create_device_links(struct acpi_device * adev,struct platform_device * pdev)635e6ce0ce3SAdrian Hunter static void acpi_lpss_create_device_links(struct acpi_device *adev,
636e6ce0ce3SAdrian Hunter 					  struct platform_device *pdev)
637e6ce0ce3SAdrian Hunter {
638e6ce0ce3SAdrian Hunter 	int i;
639e6ce0ce3SAdrian Hunter 
640e6ce0ce3SAdrian Hunter 	for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
641e6ce0ce3SAdrian Hunter 		const struct lpss_device_links *link = &lpss_device_links[i];
642e6ce0ce3SAdrian Hunter 
643e6ce0ce3SAdrian Hunter 		if (acpi_lpss_is_supplier(adev, link))
644e6ce0ce3SAdrian Hunter 			acpi_lpss_link_consumer(&pdev->dev, link);
645e6ce0ce3SAdrian Hunter 
646e6ce0ce3SAdrian Hunter 		if (acpi_lpss_is_consumer(adev, link))
647e6ce0ce3SAdrian Hunter 			acpi_lpss_link_supplier(&pdev->dev, link);
648e6ce0ce3SAdrian Hunter 	}
649e6ce0ce3SAdrian Hunter }
650e6ce0ce3SAdrian Hunter 
acpi_lpss_create_device(struct acpi_device * adev,const struct acpi_device_id * id)651f58b082aSRafael J. Wysocki static int acpi_lpss_create_device(struct acpi_device *adev,
652f58b082aSRafael J. Wysocki 				   const struct acpi_device_id *id)
653f58b082aSRafael J. Wysocki {
654b2687cd7SMathias Krause 	const struct lpss_device_desc *dev_desc;
655f58b082aSRafael J. Wysocki 	struct lpss_private_data *pdata;
65690e97820SJiang Liu 	struct resource_entry *rentry;
657f58b082aSRafael J. Wysocki 	struct list_head resource_list;
6588ce62f85SRafael J. Wysocki 	struct platform_device *pdev;
659f58b082aSRafael J. Wysocki 	int ret;
660f58b082aSRafael J. Wysocki 
661b2687cd7SMathias Krause 	dev_desc = (const struct lpss_device_desc *)id->driver_data;
6628ce62f85SRafael J. Wysocki 	if (!dev_desc) {
6631571875bSHeikki Krogerus 		pdev = acpi_create_platform_device(adev, NULL);
6648ce62f85SRafael J. Wysocki 		return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
6658ce62f85SRafael J. Wysocki 	}
666f58b082aSRafael J. Wysocki 	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
667f58b082aSRafael J. Wysocki 	if (!pdata)
668f58b082aSRafael J. Wysocki 		return -ENOMEM;
669f58b082aSRafael J. Wysocki 
670f58b082aSRafael J. Wysocki 	INIT_LIST_HEAD(&resource_list);
671840baca4SHeikki Krogerus 	ret = acpi_dev_get_memory_resources(adev, &resource_list);
672f58b082aSRafael J. Wysocki 	if (ret < 0)
673f58b082aSRafael J. Wysocki 		goto err_out;
674f58b082aSRafael J. Wysocki 
675da13b336SAndy Shevchenko 	rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node);
676da13b336SAndy Shevchenko 	if (rentry) {
677958c4eb2SMika Westerberg 		if (dev_desc->prv_size_override)
678958c4eb2SMika Westerberg 			pdata->mmio_size = dev_desc->prv_size_override;
679958c4eb2SMika Westerberg 		else
68090e97820SJiang Liu 			pdata->mmio_size = resource_size(rentry->res);
681da13b336SAndy Shevchenko 		pdata->mmio_base = ioremap(rentry->res->start, pdata->mmio_size);
682f58b082aSRafael J. Wysocki 	}
683f58b082aSRafael J. Wysocki 
684f58b082aSRafael J. Wysocki 	acpi_dev_free_resource_list(&resource_list);
685f58b082aSRafael J. Wysocki 
686d3e13ff3SRafael J. Wysocki 	if (!pdata->mmio_base) {
687e1681599SHans de Goede 		/* Avoid acpi_bus_attach() instantiating a pdev for this dev. */
688e1681599SHans de Goede 		adev->pnp.type.platform_id = 0;
6896cc401beSAndy Shevchenko 		goto out_free;
690d3e13ff3SRafael J. Wysocki 	}
691d3e13ff3SRafael J. Wysocki 
692dd242a08SHans de Goede 	pdata->adev = adev;
693af65cfe9SMika Westerberg 	pdata->dev_desc = dev_desc;
694af65cfe9SMika Westerberg 
69503f09f73SHeikki Krogerus 	if (dev_desc->setup)
69603f09f73SHeikki Krogerus 		dev_desc->setup(pdata);
69703f09f73SHeikki Krogerus 
698ff8c1af5SHeikki Krogerus 	if (dev_desc->flags & LPSS_CLK) {
699f58b082aSRafael J. Wysocki 		ret = register_device_clock(adev, pdata);
7006cc401beSAndy Shevchenko 		if (ret)
7016cc401beSAndy Shevchenko 			goto out_free;
702f58b082aSRafael J. Wysocki 	}
703f58b082aSRafael J. Wysocki 
704b9e95fc6SRafael J. Wysocki 	/*
705b9e95fc6SRafael J. Wysocki 	 * This works around a known issue in ACPI tables where LPSS devices
706b9e95fc6SRafael J. Wysocki 	 * have _PS0 and _PS3 without _PSC (and no power resources), so
707b9e95fc6SRafael J. Wysocki 	 * acpi_bus_init_power() will assume that the BIOS has put them into D0.
708b9e95fc6SRafael J. Wysocki 	 */
7091a2fa02fSHans de Goede 	acpi_device_fix_up_power(adev);
710b9e95fc6SRafael J. Wysocki 
711f58b082aSRafael J. Wysocki 	adev->driver_data = pdata;
7121571875bSHeikki Krogerus 	pdev = acpi_create_platform_device(adev, dev_desc->properties);
7136cc401beSAndy Shevchenko 	if (IS_ERR_OR_NULL(pdev)) {
7146cc401beSAndy Shevchenko 		adev->driver_data = NULL;
7156cc401beSAndy Shevchenko 		ret = PTR_ERR(pdev);
7166cc401beSAndy Shevchenko 		goto err_out;
7178ce62f85SRafael J. Wysocki 	}
718f58b082aSRafael J. Wysocki 
7196cc401beSAndy Shevchenko 	acpi_lpss_create_device_links(adev, pdev);
7206cc401beSAndy Shevchenko 	return 1;
721f58b082aSRafael J. Wysocki 
7226cc401beSAndy Shevchenko out_free:
7236cc401beSAndy Shevchenko 	/* Skip the device, but continue the namespace scan */
7246cc401beSAndy Shevchenko 	ret = 0;
725f58b082aSRafael J. Wysocki err_out:
726f58b082aSRafael J. Wysocki 	kfree(pdata);
727f58b082aSRafael J. Wysocki 	return ret;
728f58b082aSRafael J. Wysocki }
729f58b082aSRafael J. Wysocki 
__lpss_reg_read(struct lpss_private_data * pdata,unsigned int reg)7301a8f8351SRafael J. Wysocki static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
7311a8f8351SRafael J. Wysocki {
7321a8f8351SRafael J. Wysocki 	return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
7331a8f8351SRafael J. Wysocki }
7341a8f8351SRafael J. Wysocki 
__lpss_reg_write(u32 val,struct lpss_private_data * pdata,unsigned int reg)7351a8f8351SRafael J. Wysocki static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
7361a8f8351SRafael J. Wysocki 			     unsigned int reg)
7371a8f8351SRafael J. Wysocki {
7381a8f8351SRafael J. Wysocki 	writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
7391a8f8351SRafael J. Wysocki }
7401a8f8351SRafael J. Wysocki 
lpss_reg_read(struct device * dev,unsigned int reg,u32 * val)7412e0f8822SRafael J. Wysocki static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
7422e0f8822SRafael J. Wysocki {
74350861d43SRafael J. Wysocki 	struct acpi_device *adev = ACPI_COMPANION(dev);
7442e0f8822SRafael J. Wysocki 	struct lpss_private_data *pdata;
7452e0f8822SRafael J. Wysocki 	unsigned long flags;
7462e0f8822SRafael J. Wysocki 	int ret;
7472e0f8822SRafael J. Wysocki 
74850861d43SRafael J. Wysocki 	if (WARN_ON(!adev))
74950861d43SRafael J. Wysocki 		return -ENODEV;
7502e0f8822SRafael J. Wysocki 
7512e0f8822SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
7522e0f8822SRafael J. Wysocki 	if (pm_runtime_suspended(dev)) {
7532e0f8822SRafael J. Wysocki 		ret = -EAGAIN;
7542e0f8822SRafael J. Wysocki 		goto out;
7552e0f8822SRafael J. Wysocki 	}
7562e0f8822SRafael J. Wysocki 	pdata = acpi_driver_data(adev);
7572e0f8822SRafael J. Wysocki 	if (WARN_ON(!pdata || !pdata->mmio_base)) {
7582e0f8822SRafael J. Wysocki 		ret = -ENODEV;
7592e0f8822SRafael J. Wysocki 		goto out;
7602e0f8822SRafael J. Wysocki 	}
7611a8f8351SRafael J. Wysocki 	*val = __lpss_reg_read(pdata, reg);
76250861d43SRafael J. Wysocki 	ret = 0;
7632e0f8822SRafael J. Wysocki 
7642e0f8822SRafael J. Wysocki  out:
7652e0f8822SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
7662e0f8822SRafael J. Wysocki 	return ret;
7672e0f8822SRafael J. Wysocki }
7682e0f8822SRafael J. Wysocki 
lpss_ltr_show(struct device * dev,struct device_attribute * attr,char * buf)7692e0f8822SRafael J. Wysocki static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr,
7702e0f8822SRafael J. Wysocki 			     char *buf)
7712e0f8822SRafael J. Wysocki {
7722e0f8822SRafael J. Wysocki 	u32 ltr_value = 0;
7732e0f8822SRafael J. Wysocki 	unsigned int reg;
7742e0f8822SRafael J. Wysocki 	int ret;
7752e0f8822SRafael J. Wysocki 
7762e0f8822SRafael J. Wysocki 	reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR;
7772e0f8822SRafael J. Wysocki 	ret = lpss_reg_read(dev, reg, &ltr_value);
7782e0f8822SRafael J. Wysocki 	if (ret)
7792e0f8822SRafael J. Wysocki 		return ret;
7802e0f8822SRafael J. Wysocki 
781d47e983eSQing Wang 	return sysfs_emit(buf, "%08x\n", ltr_value);
7822e0f8822SRafael J. Wysocki }
7832e0f8822SRafael J. Wysocki 
lpss_ltr_mode_show(struct device * dev,struct device_attribute * attr,char * buf)7842e0f8822SRafael J. Wysocki static ssize_t lpss_ltr_mode_show(struct device *dev,
7852e0f8822SRafael J. Wysocki 				  struct device_attribute *attr, char *buf)
7862e0f8822SRafael J. Wysocki {
7872e0f8822SRafael J. Wysocki 	u32 ltr_mode = 0;
7882e0f8822SRafael J. Wysocki 	char *outstr;
7892e0f8822SRafael J. Wysocki 	int ret;
7902e0f8822SRafael J. Wysocki 
7912e0f8822SRafael J. Wysocki 	ret = lpss_reg_read(dev, LPSS_GENERAL, &ltr_mode);
7922e0f8822SRafael J. Wysocki 	if (ret)
7932e0f8822SRafael J. Wysocki 		return ret;
7942e0f8822SRafael J. Wysocki 
7952e0f8822SRafael J. Wysocki 	outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto";
7962e0f8822SRafael J. Wysocki 	return sprintf(buf, "%s\n", outstr);
7972e0f8822SRafael J. Wysocki }
7982e0f8822SRafael J. Wysocki 
7992e0f8822SRafael J. Wysocki static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL);
8002e0f8822SRafael J. Wysocki static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL);
8012e0f8822SRafael J. Wysocki static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL);
8022e0f8822SRafael J. Wysocki 
8032e0f8822SRafael J. Wysocki static struct attribute *lpss_attrs[] = {
8042e0f8822SRafael J. Wysocki 	&dev_attr_auto_ltr.attr,
8052e0f8822SRafael J. Wysocki 	&dev_attr_sw_ltr.attr,
8062e0f8822SRafael J. Wysocki 	&dev_attr_ltr_mode.attr,
8072e0f8822SRafael J. Wysocki 	NULL,
8082e0f8822SRafael J. Wysocki };
8092e0f8822SRafael J. Wysocki 
81031945d0eSArvind Yadav static const struct attribute_group lpss_attr_group = {
8112e0f8822SRafael J. Wysocki 	.attrs = lpss_attrs,
8122e0f8822SRafael J. Wysocki 	.name = "lpss_ltr",
8132e0f8822SRafael J. Wysocki };
8142e0f8822SRafael J. Wysocki 
acpi_lpss_set_ltr(struct device * dev,s32 val)8151a8f8351SRafael J. Wysocki static void acpi_lpss_set_ltr(struct device *dev, s32 val)
8161a8f8351SRafael J. Wysocki {
8171a8f8351SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
8181a8f8351SRafael J. Wysocki 	u32 ltr_mode, ltr_val;
8191a8f8351SRafael J. Wysocki 
8201a8f8351SRafael J. Wysocki 	ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
8211a8f8351SRafael J. Wysocki 	if (val < 0) {
8221a8f8351SRafael J. Wysocki 		if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
8231a8f8351SRafael J. Wysocki 			ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
8241a8f8351SRafael J. Wysocki 			__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
8251a8f8351SRafael J. Wysocki 		}
8261a8f8351SRafael J. Wysocki 		return;
8271a8f8351SRafael J. Wysocki 	}
8281a8f8351SRafael J. Wysocki 	ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
8291a8f8351SRafael J. Wysocki 	if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
8301a8f8351SRafael J. Wysocki 		ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
8311a8f8351SRafael J. Wysocki 		val = LPSS_LTR_MAX_VAL;
8321a8f8351SRafael J. Wysocki 	} else if (val > LPSS_LTR_MAX_VAL) {
8331a8f8351SRafael J. Wysocki 		ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
8341a8f8351SRafael J. Wysocki 		val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
8351a8f8351SRafael J. Wysocki 	} else {
8361a8f8351SRafael J. Wysocki 		ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
8371a8f8351SRafael J. Wysocki 	}
8381a8f8351SRafael J. Wysocki 	ltr_val |= val;
8391a8f8351SRafael J. Wysocki 	__lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
8401a8f8351SRafael J. Wysocki 	if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
8411a8f8351SRafael J. Wysocki 		ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
8421a8f8351SRafael J. Wysocki 		__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
8431a8f8351SRafael J. Wysocki 	}
8441a8f8351SRafael J. Wysocki }
8451a8f8351SRafael J. Wysocki 
846c78b0830SHeikki Krogerus #ifdef CONFIG_PM
847c78b0830SHeikki Krogerus /**
848c78b0830SHeikki Krogerus  * acpi_lpss_save_ctx() - Save the private registers of LPSS device
849c78b0830SHeikki Krogerus  * @dev: LPSS device
850cb39dcddSAndy Shevchenko  * @pdata: pointer to the private data of the LPSS device
851c78b0830SHeikki Krogerus  *
852c78b0830SHeikki Krogerus  * Most LPSS devices have private registers which may loose their context when
853c78b0830SHeikki Krogerus  * the device is powered down. acpi_lpss_save_ctx() saves those registers into
854c78b0830SHeikki Krogerus  * prv_reg_ctx array.
855c78b0830SHeikki Krogerus  */
acpi_lpss_save_ctx(struct device * dev,struct lpss_private_data * pdata)856cb39dcddSAndy Shevchenko static void acpi_lpss_save_ctx(struct device *dev,
857cb39dcddSAndy Shevchenko 			       struct lpss_private_data *pdata)
858c78b0830SHeikki Krogerus {
859c78b0830SHeikki Krogerus 	unsigned int i;
860c78b0830SHeikki Krogerus 
861c78b0830SHeikki Krogerus 	for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
862c78b0830SHeikki Krogerus 		unsigned long offset = i * sizeof(u32);
863c78b0830SHeikki Krogerus 
864c78b0830SHeikki Krogerus 		pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset);
865c78b0830SHeikki Krogerus 		dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n",
866c78b0830SHeikki Krogerus 			pdata->prv_reg_ctx[i], offset);
867c78b0830SHeikki Krogerus 	}
868c78b0830SHeikki Krogerus }
869c78b0830SHeikki Krogerus 
870c78b0830SHeikki Krogerus /**
871c78b0830SHeikki Krogerus  * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device
872c78b0830SHeikki Krogerus  * @dev: LPSS device
873cb39dcddSAndy Shevchenko  * @pdata: pointer to the private data of the LPSS device
874c78b0830SHeikki Krogerus  *
875c78b0830SHeikki Krogerus  * Restores the registers that were previously stored with acpi_lpss_save_ctx().
876c78b0830SHeikki Krogerus  */
acpi_lpss_restore_ctx(struct device * dev,struct lpss_private_data * pdata)877cb39dcddSAndy Shevchenko static void acpi_lpss_restore_ctx(struct device *dev,
878cb39dcddSAndy Shevchenko 				  struct lpss_private_data *pdata)
879c78b0830SHeikki Krogerus {
880c78b0830SHeikki Krogerus 	unsigned int i;
881c78b0830SHeikki Krogerus 
88202b98540SAndy Shevchenko 	for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
88302b98540SAndy Shevchenko 		unsigned long offset = i * sizeof(u32);
88402b98540SAndy Shevchenko 
88502b98540SAndy Shevchenko 		__lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
88602b98540SAndy Shevchenko 		dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
88702b98540SAndy Shevchenko 			pdata->prv_reg_ctx[i], offset);
88802b98540SAndy Shevchenko 	}
88902b98540SAndy Shevchenko }
89002b98540SAndy Shevchenko 
acpi_lpss_d3_to_d0_delay(struct lpss_private_data * pdata)89102b98540SAndy Shevchenko static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata)
89202b98540SAndy Shevchenko {
893c78b0830SHeikki Krogerus 	/*
894c78b0830SHeikki Krogerus 	 * The following delay is needed or the subsequent write operations may
895c78b0830SHeikki Krogerus 	 * fail. The LPSS devices are actually PCI devices and the PCI spec
896c78b0830SHeikki Krogerus 	 * expects 10ms delay before the device can be accessed after D3 to D0
897b00855aeSSrinidhi Kasagar 	 * transition. However some platforms like BSW does not need this delay.
898c78b0830SHeikki Krogerus 	 */
899b00855aeSSrinidhi Kasagar 	unsigned int delay = 10;	/* default 10ms delay */
900b00855aeSSrinidhi Kasagar 
901b00855aeSSrinidhi Kasagar 	if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY)
902b00855aeSSrinidhi Kasagar 		delay = 0;
903b00855aeSSrinidhi Kasagar 
904b00855aeSSrinidhi Kasagar 	msleep(delay);
905c78b0830SHeikki Krogerus }
906c78b0830SHeikki Krogerus 
acpi_lpss_activate(struct device * dev)907c3a49cf3SAndy Shevchenko static int acpi_lpss_activate(struct device *dev)
908c3a49cf3SAndy Shevchenko {
909c3a49cf3SAndy Shevchenko 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
910c3a49cf3SAndy Shevchenko 	int ret;
911c3a49cf3SAndy Shevchenko 
91263705c40SRafael J. Wysocki 	ret = acpi_dev_resume(dev);
913c3a49cf3SAndy Shevchenko 	if (ret)
914c3a49cf3SAndy Shevchenko 		return ret;
915c3a49cf3SAndy Shevchenko 
916c3a49cf3SAndy Shevchenko 	acpi_lpss_d3_to_d0_delay(pdata);
917c3a49cf3SAndy Shevchenko 
918c3a49cf3SAndy Shevchenko 	/*
919c3a49cf3SAndy Shevchenko 	 * This is called only on ->probe() stage where a device is either in
920c3a49cf3SAndy Shevchenko 	 * known state defined by BIOS or most likely powered off. Due to this
921c3a49cf3SAndy Shevchenko 	 * we have to deassert reset line to be sure that ->probe() will
922c3a49cf3SAndy Shevchenko 	 * recognize the device.
923c3a49cf3SAndy Shevchenko 	 */
92415aa5e4cSHans de Goede 	if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
925c3a49cf3SAndy Shevchenko 		lpss_deassert_reset(pdata);
926c3a49cf3SAndy Shevchenko 
92715aa5e4cSHans de Goede #ifdef CONFIG_PM
92815aa5e4cSHans de Goede 	if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE)
92915aa5e4cSHans de Goede 		acpi_lpss_save_ctx(dev, pdata);
93015aa5e4cSHans de Goede #endif
93115aa5e4cSHans de Goede 
932c3a49cf3SAndy Shevchenko 	return 0;
933c3a49cf3SAndy Shevchenko }
934c3a49cf3SAndy Shevchenko 
acpi_lpss_dismiss(struct device * dev)935c3a49cf3SAndy Shevchenko static void acpi_lpss_dismiss(struct device *dev)
936c3a49cf3SAndy Shevchenko {
937cbe25ce3SRafael J. Wysocki 	acpi_dev_suspend(dev, false);
938c3a49cf3SAndy Shevchenko }
939c3a49cf3SAndy Shevchenko 
940eebb3e8dSAndy Shevchenko /* IOSF SB for LPSS island */
941eebb3e8dSAndy Shevchenko #define LPSS_IOSF_UNIT_LPIOEP		0xA0
942eebb3e8dSAndy Shevchenko #define LPSS_IOSF_UNIT_LPIO1		0xAB
943eebb3e8dSAndy Shevchenko #define LPSS_IOSF_UNIT_LPIO2		0xAC
944eebb3e8dSAndy Shevchenko 
945eebb3e8dSAndy Shevchenko #define LPSS_IOSF_PMCSR			0x84
946eebb3e8dSAndy Shevchenko #define LPSS_PMCSR_D0			0
947eebb3e8dSAndy Shevchenko #define LPSS_PMCSR_D3hot		3
948eebb3e8dSAndy Shevchenko #define LPSS_PMCSR_Dx_MASK		GENMASK(1, 0)
949eebb3e8dSAndy Shevchenko 
950eebb3e8dSAndy Shevchenko #define LPSS_IOSF_GPIODEF0		0x154
951eebb3e8dSAndy Shevchenko #define LPSS_GPIODEF0_DMA1_D3		BIT(2)
952eebb3e8dSAndy Shevchenko #define LPSS_GPIODEF0_DMA2_D3		BIT(3)
953eebb3e8dSAndy Shevchenko #define LPSS_GPIODEF0_DMA_D3_MASK	GENMASK(3, 2)
954d132d6d5SAndy Shevchenko #define LPSS_GPIODEF0_DMA_LLP		BIT(13)
955eebb3e8dSAndy Shevchenko 
956eebb3e8dSAndy Shevchenko static DEFINE_MUTEX(lpss_iosf_mutex);
957f11fc4bcSZhang Rui static bool lpss_iosf_d3_entered = true;
958eebb3e8dSAndy Shevchenko 
lpss_iosf_enter_d3_state(void)959eebb3e8dSAndy Shevchenko static void lpss_iosf_enter_d3_state(void)
960eebb3e8dSAndy Shevchenko {
961eebb3e8dSAndy Shevchenko 	u32 value1 = 0;
962d132d6d5SAndy Shevchenko 	u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
963eebb3e8dSAndy Shevchenko 	u32 value2 = LPSS_PMCSR_D3hot;
964eebb3e8dSAndy Shevchenko 	u32 mask2 = LPSS_PMCSR_Dx_MASK;
965eebb3e8dSAndy Shevchenko 	/*
966eebb3e8dSAndy Shevchenko 	 * PMC provides an information about actual status of the LPSS devices.
967eebb3e8dSAndy Shevchenko 	 * Here we read the values related to LPSS power island, i.e. LPSS
968eebb3e8dSAndy Shevchenko 	 * devices, excluding both LPSS DMA controllers, along with SCC domain.
969eebb3e8dSAndy Shevchenko 	 */
97086b62e5cSHans de Goede 	u32 func_dis, d3_sts_0, pmc_status;
971eebb3e8dSAndy Shevchenko 	int ret;
972eebb3e8dSAndy Shevchenko 
973eebb3e8dSAndy Shevchenko 	ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
974eebb3e8dSAndy Shevchenko 	if (ret)
975eebb3e8dSAndy Shevchenko 		return;
976eebb3e8dSAndy Shevchenko 
977eebb3e8dSAndy Shevchenko 	mutex_lock(&lpss_iosf_mutex);
978eebb3e8dSAndy Shevchenko 
979eebb3e8dSAndy Shevchenko 	ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0);
980eebb3e8dSAndy Shevchenko 	if (ret)
981eebb3e8dSAndy Shevchenko 		goto exit;
982eebb3e8dSAndy Shevchenko 
983eebb3e8dSAndy Shevchenko 	/*
984eebb3e8dSAndy Shevchenko 	 * Get the status of entire LPSS power island per device basis.
985eebb3e8dSAndy Shevchenko 	 * Shutdown both LPSS DMA controllers if and only if all other devices
986eebb3e8dSAndy Shevchenko 	 * are already in D3hot.
987eebb3e8dSAndy Shevchenko 	 */
98886b62e5cSHans de Goede 	pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask;
989eebb3e8dSAndy Shevchenko 	if (pmc_status)
990eebb3e8dSAndy Shevchenko 		goto exit;
991eebb3e8dSAndy Shevchenko 
992eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
993eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
994eebb3e8dSAndy Shevchenko 
995eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
996eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
997eebb3e8dSAndy Shevchenko 
998eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
999eebb3e8dSAndy Shevchenko 			LPSS_IOSF_GPIODEF0, value1, mask1);
100012864ff8SRafael J. Wysocki 
100112864ff8SRafael J. Wysocki 	lpss_iosf_d3_entered = true;
100212864ff8SRafael J. Wysocki 
1003eebb3e8dSAndy Shevchenko exit:
1004eebb3e8dSAndy Shevchenko 	mutex_unlock(&lpss_iosf_mutex);
1005eebb3e8dSAndy Shevchenko }
1006eebb3e8dSAndy Shevchenko 
lpss_iosf_exit_d3_state(void)1007eebb3e8dSAndy Shevchenko static void lpss_iosf_exit_d3_state(void)
1008eebb3e8dSAndy Shevchenko {
1009d132d6d5SAndy Shevchenko 	u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 |
1010d132d6d5SAndy Shevchenko 		     LPSS_GPIODEF0_DMA_LLP;
1011d132d6d5SAndy Shevchenko 	u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
1012eebb3e8dSAndy Shevchenko 	u32 value2 = LPSS_PMCSR_D0;
1013eebb3e8dSAndy Shevchenko 	u32 mask2 = LPSS_PMCSR_Dx_MASK;
1014eebb3e8dSAndy Shevchenko 
1015eebb3e8dSAndy Shevchenko 	mutex_lock(&lpss_iosf_mutex);
1016eebb3e8dSAndy Shevchenko 
101712864ff8SRafael J. Wysocki 	if (!lpss_iosf_d3_entered)
101812864ff8SRafael J. Wysocki 		goto exit;
101912864ff8SRafael J. Wysocki 
102012864ff8SRafael J. Wysocki 	lpss_iosf_d3_entered = false;
102112864ff8SRafael J. Wysocki 
1022eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
1023eebb3e8dSAndy Shevchenko 			LPSS_IOSF_GPIODEF0, value1, mask1);
1024eebb3e8dSAndy Shevchenko 
1025eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
1026eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
1027eebb3e8dSAndy Shevchenko 
1028eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
1029eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
1030eebb3e8dSAndy Shevchenko 
103112864ff8SRafael J. Wysocki exit:
1032eebb3e8dSAndy Shevchenko 	mutex_unlock(&lpss_iosf_mutex);
1033eebb3e8dSAndy Shevchenko }
1034eebb3e8dSAndy Shevchenko 
acpi_lpss_suspend(struct device * dev,bool wakeup)103512864ff8SRafael J. Wysocki static int acpi_lpss_suspend(struct device *dev, bool wakeup)
1036c78b0830SHeikki Krogerus {
1037cb39dcddSAndy Shevchenko 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1038cb39dcddSAndy Shevchenko 	int ret;
1039c78b0830SHeikki Krogerus 
1040cb39dcddSAndy Shevchenko 	if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
1041cb39dcddSAndy Shevchenko 		acpi_lpss_save_ctx(dev, pdata);
1042cb39dcddSAndy Shevchenko 
1043a192aa92SRafael J. Wysocki 	ret = acpi_dev_suspend(dev, wakeup);
1044eebb3e8dSAndy Shevchenko 
1045eebb3e8dSAndy Shevchenko 	/*
1046eebb3e8dSAndy Shevchenko 	 * This call must be last in the sequence, otherwise PMC will return
1047eebb3e8dSAndy Shevchenko 	 * wrong status for devices being about to be powered off. See
1048eebb3e8dSAndy Shevchenko 	 * lpss_iosf_enter_d3_state() for further information.
1049eebb3e8dSAndy Shevchenko 	 */
105012864ff8SRafael J. Wysocki 	if (acpi_target_system_state() == ACPI_STATE_S0 &&
1051a09c5913SRafael J. Wysocki 	    lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
1052eebb3e8dSAndy Shevchenko 		lpss_iosf_enter_d3_state();
1053eebb3e8dSAndy Shevchenko 
1054eebb3e8dSAndy Shevchenko 	return ret;
1055c78b0830SHeikki Krogerus }
1056c78b0830SHeikki Krogerus 
acpi_lpss_resume(struct device * dev)105712864ff8SRafael J. Wysocki static int acpi_lpss_resume(struct device *dev)
1058c78b0830SHeikki Krogerus {
1059cb39dcddSAndy Shevchenko 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1060cb39dcddSAndy Shevchenko 	int ret;
1061c78b0830SHeikki Krogerus 
1062eebb3e8dSAndy Shevchenko 	/*
1063eebb3e8dSAndy Shevchenko 	 * This call is kept first to be in symmetry with
1064eebb3e8dSAndy Shevchenko 	 * acpi_lpss_runtime_suspend() one.
1065eebb3e8dSAndy Shevchenko 	 */
106612864ff8SRafael J. Wysocki 	if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
1067eebb3e8dSAndy Shevchenko 		lpss_iosf_exit_d3_state();
1068eebb3e8dSAndy Shevchenko 
106963705c40SRafael J. Wysocki 	ret = acpi_dev_resume(dev);
1070c78b0830SHeikki Krogerus 	if (ret)
1071c78b0830SHeikki Krogerus 		return ret;
1072c78b0830SHeikki Krogerus 
107302b98540SAndy Shevchenko 	acpi_lpss_d3_to_d0_delay(pdata);
107402b98540SAndy Shevchenko 
107515aa5e4cSHans de Goede 	if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
1076cb39dcddSAndy Shevchenko 		acpi_lpss_restore_ctx(dev, pdata);
1077cb39dcddSAndy Shevchenko 
1078a192aa92SRafael J. Wysocki 	return 0;
1079a192aa92SRafael J. Wysocki }
1080a192aa92SRafael J. Wysocki 
1081a192aa92SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
acpi_lpss_do_suspend_late(struct device * dev)108248402ceeSHans de Goede static int acpi_lpss_do_suspend_late(struct device *dev)
1083a192aa92SRafael J. Wysocki {
108405087360SRafael J. Wysocki 	int ret;
1085a192aa92SRafael J. Wysocki 
1086fa2bfeadSRafael J. Wysocki 	if (dev_pm_skip_suspend(dev))
108705087360SRafael J. Wysocki 		return 0;
108805087360SRafael J. Wysocki 
108905087360SRafael J. Wysocki 	ret = pm_generic_suspend_late(dev);
109012864ff8SRafael J. Wysocki 	return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
1091a192aa92SRafael J. Wysocki }
1092a192aa92SRafael J. Wysocki 
acpi_lpss_suspend_late(struct device * dev)109348402ceeSHans de Goede static int acpi_lpss_suspend_late(struct device *dev)
109448402ceeSHans de Goede {
109548402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
109648402ceeSHans de Goede 
109748402ceeSHans de Goede 	if (pdata->dev_desc->resume_from_noirq)
109848402ceeSHans de Goede 		return 0;
109948402ceeSHans de Goede 
110048402ceeSHans de Goede 	return acpi_lpss_do_suspend_late(dev);
110148402ceeSHans de Goede }
110248402ceeSHans de Goede 
acpi_lpss_suspend_noirq(struct device * dev)110348402ceeSHans de Goede static int acpi_lpss_suspend_noirq(struct device *dev)
110448402ceeSHans de Goede {
110548402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
110648402ceeSHans de Goede 	int ret;
110748402ceeSHans de Goede 
110848402ceeSHans de Goede 	if (pdata->dev_desc->resume_from_noirq) {
1109c95b7595SRafael J. Wysocki 		/*
1110c95b7595SRafael J. Wysocki 		 * The driver's ->suspend_late callback will be invoked by
1111c95b7595SRafael J. Wysocki 		 * acpi_lpss_do_suspend_late(), with the assumption that the
1112c95b7595SRafael J. Wysocki 		 * driver really wanted to run that code in ->suspend_noirq, but
1113c95b7595SRafael J. Wysocki 		 * it could not run after acpi_dev_suspend() and the driver
1114c95b7595SRafael J. Wysocki 		 * expected the latter to be called in the "late" phase.
1115c95b7595SRafael J. Wysocki 		 */
111648402ceeSHans de Goede 		ret = acpi_lpss_do_suspend_late(dev);
111748402ceeSHans de Goede 		if (ret)
111848402ceeSHans de Goede 			return ret;
111948402ceeSHans de Goede 	}
112048402ceeSHans de Goede 
112148402ceeSHans de Goede 	return acpi_subsys_suspend_noirq(dev);
112248402ceeSHans de Goede }
112348402ceeSHans de Goede 
acpi_lpss_do_resume_early(struct device * dev)112448402ceeSHans de Goede static int acpi_lpss_do_resume_early(struct device *dev)
1125a192aa92SRafael J. Wysocki {
112612864ff8SRafael J. Wysocki 	int ret = acpi_lpss_resume(dev);
1127a192aa92SRafael J. Wysocki 
1128a192aa92SRafael J. Wysocki 	return ret ? ret : pm_generic_resume_early(dev);
1129a192aa92SRafael J. Wysocki }
113048402ceeSHans de Goede 
acpi_lpss_resume_early(struct device * dev)113148402ceeSHans de Goede static int acpi_lpss_resume_early(struct device *dev)
113248402ceeSHans de Goede {
113348402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
113448402ceeSHans de Goede 
113548402ceeSHans de Goede 	if (pdata->dev_desc->resume_from_noirq)
113648402ceeSHans de Goede 		return 0;
113748402ceeSHans de Goede 
113876c70cb5SRafael J. Wysocki 	if (dev_pm_skip_resume(dev))
11396e176bf8SRafael J. Wysocki 		return 0;
11406e176bf8SRafael J. Wysocki 
114148402ceeSHans de Goede 	return acpi_lpss_do_resume_early(dev);
114248402ceeSHans de Goede }
114348402ceeSHans de Goede 
acpi_lpss_resume_noirq(struct device * dev)114448402ceeSHans de Goede static int acpi_lpss_resume_noirq(struct device *dev)
114548402ceeSHans de Goede {
114648402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
114748402ceeSHans de Goede 	int ret;
114848402ceeSHans de Goede 
11493cd7957eSRafael J. Wysocki 	/* Follow acpi_subsys_resume_noirq(). */
115076c70cb5SRafael J. Wysocki 	if (dev_pm_skip_resume(dev))
11513cd7957eSRafael J. Wysocki 		return 0;
11523cd7957eSRafael J. Wysocki 
11533cd7957eSRafael J. Wysocki 	ret = pm_generic_resume_noirq(dev);
115448402ceeSHans de Goede 	if (ret)
115548402ceeSHans de Goede 		return ret;
115648402ceeSHans de Goede 
11573cd7957eSRafael J. Wysocki 	if (!pdata->dev_desc->resume_from_noirq)
11583cd7957eSRafael J. Wysocki 		return 0;
115948402ceeSHans de Goede 
11603cd7957eSRafael J. Wysocki 	/*
11613cd7957eSRafael J. Wysocki 	 * The driver's ->resume_early callback will be invoked by
11623cd7957eSRafael J. Wysocki 	 * acpi_lpss_do_resume_early(), with the assumption that the driver
11633cd7957eSRafael J. Wysocki 	 * really wanted to run that code in ->resume_noirq, but it could not
11643cd7957eSRafael J. Wysocki 	 * run before acpi_dev_resume() and the driver expected the latter to be
11653cd7957eSRafael J. Wysocki 	 * called in the "early" phase.
11663cd7957eSRafael J. Wysocki 	 */
11673cd7957eSRafael J. Wysocki 	return acpi_lpss_do_resume_early(dev);
116848402ceeSHans de Goede }
116948402ceeSHans de Goede 
acpi_lpss_do_restore_early(struct device * dev)11703cd7957eSRafael J. Wysocki static int acpi_lpss_do_restore_early(struct device *dev)
11713cd7957eSRafael J. Wysocki {
11723cd7957eSRafael J. Wysocki 	int ret = acpi_lpss_resume(dev);
11733cd7957eSRafael J. Wysocki 
11743cd7957eSRafael J. Wysocki 	return ret ? ret : pm_generic_restore_early(dev);
11753cd7957eSRafael J. Wysocki }
11763cd7957eSRafael J. Wysocki 
acpi_lpss_restore_early(struct device * dev)11773cd7957eSRafael J. Wysocki static int acpi_lpss_restore_early(struct device *dev)
11783cd7957eSRafael J. Wysocki {
11793cd7957eSRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
11803cd7957eSRafael J. Wysocki 
11813cd7957eSRafael J. Wysocki 	if (pdata->dev_desc->resume_from_noirq)
11823cd7957eSRafael J. Wysocki 		return 0;
11833cd7957eSRafael J. Wysocki 
11843cd7957eSRafael J. Wysocki 	return acpi_lpss_do_restore_early(dev);
11853cd7957eSRafael J. Wysocki }
11863cd7957eSRafael J. Wysocki 
acpi_lpss_restore_noirq(struct device * dev)11873cd7957eSRafael J. Wysocki static int acpi_lpss_restore_noirq(struct device *dev)
11883cd7957eSRafael J. Wysocki {
11893cd7957eSRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
11903cd7957eSRafael J. Wysocki 	int ret;
11913cd7957eSRafael J. Wysocki 
11923cd7957eSRafael J. Wysocki 	ret = pm_generic_restore_noirq(dev);
11933cd7957eSRafael J. Wysocki 	if (ret)
11943cd7957eSRafael J. Wysocki 		return ret;
11953cd7957eSRafael J. Wysocki 
11963cd7957eSRafael J. Wysocki 	if (!pdata->dev_desc->resume_from_noirq)
11973cd7957eSRafael J. Wysocki 		return 0;
11983cd7957eSRafael J. Wysocki 
11993cd7957eSRafael J. Wysocki 	/* This is analogous to what happens in acpi_lpss_resume_noirq(). */
12003cd7957eSRafael J. Wysocki 	return acpi_lpss_do_restore_early(dev);
12013cd7957eSRafael J. Wysocki }
1202c95b7595SRafael J. Wysocki 
acpi_lpss_do_poweroff_late(struct device * dev)1203c95b7595SRafael J. Wysocki static int acpi_lpss_do_poweroff_late(struct device *dev)
1204c95b7595SRafael J. Wysocki {
1205c95b7595SRafael J. Wysocki 	int ret = pm_generic_poweroff_late(dev);
1206c95b7595SRafael J. Wysocki 
1207c95b7595SRafael J. Wysocki 	return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
1208c95b7595SRafael J. Wysocki }
1209c95b7595SRafael J. Wysocki 
acpi_lpss_poweroff_late(struct device * dev)1210c95b7595SRafael J. Wysocki static int acpi_lpss_poweroff_late(struct device *dev)
1211c95b7595SRafael J. Wysocki {
1212c95b7595SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1213c95b7595SRafael J. Wysocki 
1214fa2bfeadSRafael J. Wysocki 	if (dev_pm_skip_suspend(dev))
1215c95b7595SRafael J. Wysocki 		return 0;
1216c95b7595SRafael J. Wysocki 
1217c95b7595SRafael J. Wysocki 	if (pdata->dev_desc->resume_from_noirq)
1218c95b7595SRafael J. Wysocki 		return 0;
1219c95b7595SRafael J. Wysocki 
1220c95b7595SRafael J. Wysocki 	return acpi_lpss_do_poweroff_late(dev);
1221c95b7595SRafael J. Wysocki }
1222c95b7595SRafael J. Wysocki 
acpi_lpss_poweroff_noirq(struct device * dev)1223c95b7595SRafael J. Wysocki static int acpi_lpss_poweroff_noirq(struct device *dev)
1224c95b7595SRafael J. Wysocki {
1225c95b7595SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1226c95b7595SRafael J. Wysocki 
1227fa2bfeadSRafael J. Wysocki 	if (dev_pm_skip_suspend(dev))
1228c95b7595SRafael J. Wysocki 		return 0;
1229c95b7595SRafael J. Wysocki 
1230c95b7595SRafael J. Wysocki 	if (pdata->dev_desc->resume_from_noirq) {
1231c95b7595SRafael J. Wysocki 		/* This is analogous to the acpi_lpss_suspend_noirq() case. */
1232c95b7595SRafael J. Wysocki 		int ret = acpi_lpss_do_poweroff_late(dev);
1233bb415ed5SXiaofei Tan 
1234c95b7595SRafael J. Wysocki 		if (ret)
1235a192aa92SRafael J. Wysocki 			return ret;
1236a192aa92SRafael J. Wysocki 	}
1237a192aa92SRafael J. Wysocki 
1238c95b7595SRafael J. Wysocki 	return pm_generic_poweroff_noirq(dev);
1239c95b7595SRafael J. Wysocki }
1240a192aa92SRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1241a192aa92SRafael J. Wysocki 
acpi_lpss_runtime_suspend(struct device * dev)1242a192aa92SRafael J. Wysocki static int acpi_lpss_runtime_suspend(struct device *dev)
1243a192aa92SRafael J. Wysocki {
1244a192aa92SRafael J. Wysocki 	int ret = pm_generic_runtime_suspend(dev);
1245a192aa92SRafael J. Wysocki 
1246a192aa92SRafael J. Wysocki 	return ret ? ret : acpi_lpss_suspend(dev, true);
1247a192aa92SRafael J. Wysocki }
1248a192aa92SRafael J. Wysocki 
acpi_lpss_runtime_resume(struct device * dev)1249a192aa92SRafael J. Wysocki static int acpi_lpss_runtime_resume(struct device *dev)
1250a192aa92SRafael J. Wysocki {
125112864ff8SRafael J. Wysocki 	int ret = acpi_lpss_resume(dev);
1252a192aa92SRafael J. Wysocki 
1253a192aa92SRafael J. Wysocki 	return ret ? ret : pm_generic_runtime_resume(dev);
1254c78b0830SHeikki Krogerus }
1255c78b0830SHeikki Krogerus #endif /* CONFIG_PM */
1256c78b0830SHeikki Krogerus 
1257c78b0830SHeikki Krogerus static struct dev_pm_domain acpi_lpss_pm_domain = {
1258c3a49cf3SAndy Shevchenko #ifdef CONFIG_PM
1259c3a49cf3SAndy Shevchenko 	.activate = acpi_lpss_activate,
1260c3a49cf3SAndy Shevchenko 	.dismiss = acpi_lpss_dismiss,
1261c3a49cf3SAndy Shevchenko #endif
1262c78b0830SHeikki Krogerus 	.ops = {
12635de21bb9SRafael J. Wysocki #ifdef CONFIG_PM
1264c78b0830SHeikki Krogerus #ifdef CONFIG_PM_SLEEP
1265c78b0830SHeikki Krogerus 		.prepare = acpi_subsys_prepare,
1266e4da817dSUlf Hansson 		.complete = acpi_subsys_complete,
1267c78b0830SHeikki Krogerus 		.suspend = acpi_subsys_suspend,
1268f4168b61SFu Zhonghui 		.suspend_late = acpi_lpss_suspend_late,
126948402ceeSHans de Goede 		.suspend_noirq = acpi_lpss_suspend_noirq,
127048402ceeSHans de Goede 		.resume_noirq = acpi_lpss_resume_noirq,
1271f4168b61SFu Zhonghui 		.resume_early = acpi_lpss_resume_early,
1272c78b0830SHeikki Krogerus 		.freeze = acpi_subsys_freeze,
1273c95b7595SRafael J. Wysocki 		.poweroff = acpi_subsys_poweroff,
1274c95b7595SRafael J. Wysocki 		.poweroff_late = acpi_lpss_poweroff_late,
1275c95b7595SRafael J. Wysocki 		.poweroff_noirq = acpi_lpss_poweroff_noirq,
12763cd7957eSRafael J. Wysocki 		.restore_noirq = acpi_lpss_restore_noirq,
12773cd7957eSRafael J. Wysocki 		.restore_early = acpi_lpss_restore_early,
1278c78b0830SHeikki Krogerus #endif
1279c78b0830SHeikki Krogerus 		.runtime_suspend = acpi_lpss_runtime_suspend,
1280c78b0830SHeikki Krogerus 		.runtime_resume = acpi_lpss_runtime_resume,
1281c78b0830SHeikki Krogerus #endif
1282c78b0830SHeikki Krogerus 	},
1283c78b0830SHeikki Krogerus };
1284c78b0830SHeikki Krogerus 
acpi_lpss_platform_notify(struct notifier_block * nb,unsigned long action,void * data)12852e0f8822SRafael J. Wysocki static int acpi_lpss_platform_notify(struct notifier_block *nb,
12862e0f8822SRafael J. Wysocki 				     unsigned long action, void *data)
12872e0f8822SRafael J. Wysocki {
12882e0f8822SRafael J. Wysocki 	struct platform_device *pdev = to_platform_device(data);
12892e0f8822SRafael J. Wysocki 	struct lpss_private_data *pdata;
12902e0f8822SRafael J. Wysocki 	struct acpi_device *adev;
12912e0f8822SRafael J. Wysocki 	const struct acpi_device_id *id;
12922e0f8822SRafael J. Wysocki 
12932e0f8822SRafael J. Wysocki 	id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
12942e0f8822SRafael J. Wysocki 	if (!id || !id->driver_data)
12952e0f8822SRafael J. Wysocki 		return 0;
12962e0f8822SRafael J. Wysocki 
129750861d43SRafael J. Wysocki 	adev = ACPI_COMPANION(&pdev->dev);
129850861d43SRafael J. Wysocki 	if (!adev)
12992e0f8822SRafael J. Wysocki 		return 0;
13002e0f8822SRafael J. Wysocki 
13012e0f8822SRafael J. Wysocki 	pdata = acpi_driver_data(adev);
1302cb39dcddSAndy Shevchenko 	if (!pdata)
13032e0f8822SRafael J. Wysocki 		return 0;
13042e0f8822SRafael J. Wysocki 
1305cb39dcddSAndy Shevchenko 	if (pdata->mmio_base &&
1306cb39dcddSAndy Shevchenko 	    pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
13072e0f8822SRafael J. Wysocki 		dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n");
13082e0f8822SRafael J. Wysocki 		return 0;
13092e0f8822SRafael J. Wysocki 	}
13102e0f8822SRafael J. Wysocki 
1311c78b0830SHeikki Krogerus 	switch (action) {
1312de16d552SAndy Shevchenko 	case BUS_NOTIFY_BIND_DRIVER:
1313989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
1314b5f88dd1SAndy Shevchenko 		break;
1315de16d552SAndy Shevchenko 	case BUS_NOTIFY_DRIVER_NOT_BOUND:
1316b5f88dd1SAndy Shevchenko 	case BUS_NOTIFY_UNBOUND_DRIVER:
13175be6ada3SAndy Shevchenko 		dev_pm_domain_set(&pdev->dev, NULL);
1318b5f88dd1SAndy Shevchenko 		break;
1319b5f88dd1SAndy Shevchenko 	case BUS_NOTIFY_ADD_DEVICE:
1320989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
1321ff8c1af5SHeikki Krogerus 		if (pdata->dev_desc->flags & LPSS_LTR)
1322c78b0830SHeikki Krogerus 			return sysfs_create_group(&pdev->dev.kobj,
1323c78b0830SHeikki Krogerus 						  &lpss_attr_group);
132401ac170bSAndy Shevchenko 		break;
1325c78b0830SHeikki Krogerus 	case BUS_NOTIFY_DEL_DEVICE:
1326ff8c1af5SHeikki Krogerus 		if (pdata->dev_desc->flags & LPSS_LTR)
13272e0f8822SRafael J. Wysocki 			sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
1328989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, NULL);
132901ac170bSAndy Shevchenko 		break;
1330c78b0830SHeikki Krogerus 	default:
1331c78b0830SHeikki Krogerus 		break;
1332c78b0830SHeikki Krogerus 	}
13332e0f8822SRafael J. Wysocki 
1334c78b0830SHeikki Krogerus 	return 0;
13352e0f8822SRafael J. Wysocki }
13362e0f8822SRafael J. Wysocki 
13372e0f8822SRafael J. Wysocki static struct notifier_block acpi_lpss_nb = {
13382e0f8822SRafael J. Wysocki 	.notifier_call = acpi_lpss_platform_notify,
13392e0f8822SRafael J. Wysocki };
13402e0f8822SRafael J. Wysocki 
acpi_lpss_bind(struct device * dev)13411a8f8351SRafael J. Wysocki static void acpi_lpss_bind(struct device *dev)
13421a8f8351SRafael J. Wysocki {
13431a8f8351SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
13441a8f8351SRafael J. Wysocki 
1345ff8c1af5SHeikki Krogerus 	if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
13461a8f8351SRafael J. Wysocki 		return;
13471a8f8351SRafael J. Wysocki 
13481a8f8351SRafael J. Wysocki 	if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
13491a8f8351SRafael J. Wysocki 		dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
13501a8f8351SRafael J. Wysocki 	else
13511a8f8351SRafael J. Wysocki 		dev_err(dev, "MMIO size insufficient to access LTR\n");
13521a8f8351SRafael J. Wysocki }
13531a8f8351SRafael J. Wysocki 
acpi_lpss_unbind(struct device * dev)13541a8f8351SRafael J. Wysocki static void acpi_lpss_unbind(struct device *dev)
13551a8f8351SRafael J. Wysocki {
13561a8f8351SRafael J. Wysocki 	dev->power.set_latency_tolerance = NULL;
13571a8f8351SRafael J. Wysocki }
13581a8f8351SRafael J. Wysocki 
1359f58b082aSRafael J. Wysocki static struct acpi_scan_handler lpss_handler = {
1360f58b082aSRafael J. Wysocki 	.ids = acpi_lpss_device_ids,
1361f58b082aSRafael J. Wysocki 	.attach = acpi_lpss_create_device,
13621a8f8351SRafael J. Wysocki 	.bind = acpi_lpss_bind,
13631a8f8351SRafael J. Wysocki 	.unbind = acpi_lpss_unbind,
1364f58b082aSRafael J. Wysocki };
1365f58b082aSRafael J. Wysocki 
acpi_lpss_init(void)1366f58b082aSRafael J. Wysocki void __init acpi_lpss_init(void)
1367f58b082aSRafael J. Wysocki {
1368eebb3e8dSAndy Shevchenko 	const struct x86_cpu_id *id;
1369eebb3e8dSAndy Shevchenko 	int ret;
1370eebb3e8dSAndy Shevchenko 
1371cf0a9565SAndy Shevchenko 	ret = lpss_atom_clk_init();
1372eebb3e8dSAndy Shevchenko 	if (ret)
1373eebb3e8dSAndy Shevchenko 		return;
1374eebb3e8dSAndy Shevchenko 
1375eebb3e8dSAndy Shevchenko 	id = x86_match_cpu(lpss_cpu_ids);
1376eebb3e8dSAndy Shevchenko 	if (id)
1377eebb3e8dSAndy Shevchenko 		lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON;
1378eebb3e8dSAndy Shevchenko 
13792e0f8822SRafael J. Wysocki 	bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
1380f58b082aSRafael J. Wysocki 	acpi_scan_add_handler(&lpss_handler);
1381f58b082aSRafael J. Wysocki }
1382d6ddaaacSRafael J. Wysocki 
1383d6ddaaacSRafael J. Wysocki #else
1384d6ddaaacSRafael J. Wysocki 
1385d6ddaaacSRafael J. Wysocki static struct acpi_scan_handler lpss_handler = {
1386d6ddaaacSRafael J. Wysocki 	.ids = acpi_lpss_device_ids,
1387d6ddaaacSRafael J. Wysocki };
1388d6ddaaacSRafael J. Wysocki 
acpi_lpss_init(void)1389d6ddaaacSRafael J. Wysocki void __init acpi_lpss_init(void)
1390d6ddaaacSRafael J. Wysocki {
1391d6ddaaacSRafael J. Wysocki 	acpi_scan_add_handler(&lpss_handler);
1392d6ddaaacSRafael J. Wysocki }
1393d6ddaaacSRafael J. Wysocki 
1394d6ddaaacSRafael J. Wysocki #endif /* CONFIG_X86_INTEL_LPSS */
1395