xref: /openbmc/linux/drivers/acpi/acpi_lpss.c (revision 2920ac9d)
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),
336620c803fSAndy Shevchenko 	{ }
337620c803fSAndy Shevchenko };
338620c803fSAndy Shevchenko 
339eebb3e8dSAndy Shevchenko static const struct lpss_device_desc bsw_spi_dev_desc = {
340b00855aeSSrinidhi Kasagar 	.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
341b00855aeSSrinidhi Kasagar 			| LPSS_NO_D3_DELAY,
3423095794aSMika Westerberg 	.prv_offset = 0x400,
3433095794aSMika Westerberg 	.setup = lpss_deassert_reset,
344620c803fSAndy Shevchenko 	.properties = bsw_spi_properties,
3453095794aSMika Westerberg };
3463095794aSMika Westerberg 
347eebb3e8dSAndy Shevchenko static const struct x86_cpu_id lpss_cpu_ids[] = {
348e36cf2f7SThomas Gleixner 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT,	NULL),
349e36cf2f7SThomas Gleixner 	X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT,	NULL),
350eebb3e8dSAndy Shevchenko 	{}
351eebb3e8dSAndy Shevchenko };
352eebb3e8dSAndy Shevchenko 
353d6ddaaacSRafael J. Wysocki #else
354d6ddaaacSRafael J. Wysocki 
355d6ddaaacSRafael J. Wysocki #define LPSS_ADDR(desc) (0UL)
356d6ddaaacSRafael J. Wysocki 
357d6ddaaacSRafael J. Wysocki #endif /* CONFIG_X86_INTEL_LPSS */
358d6ddaaacSRafael J. Wysocki 
359f58b082aSRafael J. Wysocki static const struct acpi_device_id acpi_lpss_device_ids[] = {
360b59cc200SRafael J. Wysocki 	/* Generic LPSS devices */
361d6ddaaacSRafael J. Wysocki 	{ "INTL9C60", LPSS_ADDR(lpss_dma_desc) },
362b59cc200SRafael J. Wysocki 
363f58b082aSRafael J. Wysocki 	/* Lynxpoint LPSS devices */
364620c803fSAndy Shevchenko 	{ "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) },
365620c803fSAndy Shevchenko 	{ "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) },
366d6ddaaacSRafael J. Wysocki 	{ "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) },
367d6ddaaacSRafael J. Wysocki 	{ "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) },
368d6ddaaacSRafael J. Wysocki 	{ "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) },
369d6ddaaacSRafael J. Wysocki 	{ "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) },
370d6ddaaacSRafael J. Wysocki 	{ "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) },
371f58b082aSRafael J. Wysocki 	{ "INT33C7", },
372f58b082aSRafael J. Wysocki 
373f6272170SMika Westerberg 	/* BayTrail LPSS devices */
374d6ddaaacSRafael J. Wysocki 	{ "80860F09", LPSS_ADDR(byt_pwm_dev_desc) },
375d6ddaaacSRafael J. Wysocki 	{ "80860F0A", LPSS_ADDR(byt_uart_dev_desc) },
376d6ddaaacSRafael J. Wysocki 	{ "80860F0E", LPSS_ADDR(byt_spi_dev_desc) },
377d6ddaaacSRafael J. Wysocki 	{ "80860F14", LPSS_ADDR(byt_sdio_dev_desc) },
378d6ddaaacSRafael J. Wysocki 	{ "80860F41", LPSS_ADDR(byt_i2c_dev_desc) },
379f6272170SMika Westerberg 	{ "INT33B2", },
38020482d32SJin Yao 	{ "INT33FC", },
381f6272170SMika Westerberg 
3821bfbd8ebSAlan Cox 	/* Braswell LPSS devices */
38324071406SHans de Goede 	{ "80862286", LPSS_ADDR(lpss_dma_desc) },
384b00855aeSSrinidhi Kasagar 	{ "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
38503c57b01SHans de Goede 	{ "80862289", LPSS_ADDR(bsw_pwm2_dev_desc) },
386b00855aeSSrinidhi Kasagar 	{ "8086228A", LPSS_ADDR(bsw_uart_dev_desc) },
3873095794aSMika Westerberg 	{ "8086228E", LPSS_ADDR(bsw_spi_dev_desc) },
38824071406SHans de Goede 	{ "808622C0", LPSS_ADDR(lpss_dma_desc) },
389b00855aeSSrinidhi Kasagar 	{ "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) },
3901bfbd8ebSAlan Cox 
391b00855aeSSrinidhi Kasagar 	/* Broadwell LPSS devices */
392620c803fSAndy Shevchenko 	{ "INT3430", LPSS_ADDR(lpt_spi_dev_desc) },
393620c803fSAndy Shevchenko 	{ "INT3431", LPSS_ADDR(lpt_spi_dev_desc) },
394d6ddaaacSRafael J. Wysocki 	{ "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) },
395d6ddaaacSRafael J. Wysocki 	{ "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) },
396d6ddaaacSRafael J. Wysocki 	{ "INT3434", LPSS_ADDR(lpt_uart_dev_desc) },
397d6ddaaacSRafael J. Wysocki 	{ "INT3435", LPSS_ADDR(lpt_uart_dev_desc) },
398d6ddaaacSRafael J. Wysocki 	{ "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
399a4d97536SMika Westerberg 	{ "INT3437", },
400a4d97536SMika Westerberg 
401ff8c1af5SHeikki Krogerus 	/* Wildcat Point LPSS devices */
402620c803fSAndy Shevchenko 	{ "INT3438", LPSS_ADDR(lpt_spi_dev_desc) },
40343218a1bSJie Yang 
404f58b082aSRafael J. Wysocki 	{ }
405f58b082aSRafael J. Wysocki };
406f58b082aSRafael J. Wysocki 
407d6ddaaacSRafael J. Wysocki #ifdef CONFIG_X86_INTEL_LPSS
408d6ddaaacSRafael J. Wysocki 
409f58b082aSRafael J. Wysocki /* LPSS main clock device. */
410f58b082aSRafael J. Wysocki static struct platform_device *lpss_clk_dev;
411f58b082aSRafael J. Wysocki 
lpt_register_clock_device(void)412f58b082aSRafael J. Wysocki static inline void lpt_register_clock_device(void)
413f58b082aSRafael J. Wysocki {
414cf0a9565SAndy Shevchenko 	lpss_clk_dev = platform_device_register_simple("clk-lpss-atom",
415cf0a9565SAndy Shevchenko 						       PLATFORM_DEVID_NONE,
416cf0a9565SAndy Shevchenko 						       NULL, 0);
417f58b082aSRafael J. Wysocki }
418f58b082aSRafael J. Wysocki 
register_device_clock(struct acpi_device * adev,struct lpss_private_data * pdata)419f58b082aSRafael J. Wysocki static int register_device_clock(struct acpi_device *adev,
420f58b082aSRafael J. Wysocki 				 struct lpss_private_data *pdata)
421f58b082aSRafael J. Wysocki {
422f58b082aSRafael J. Wysocki 	const struct lpss_device_desc *dev_desc = pdata->dev_desc;
423ed3a872eSHeikki Krogerus 	const char *devname = dev_name(&adev->dev);
42471c50dbeSColin Ian King 	struct clk *clk;
425b59cc200SRafael J. Wysocki 	struct lpss_clk_data *clk_data;
426ed3a872eSHeikki Krogerus 	const char *parent, *clk_name;
427ed3a872eSHeikki Krogerus 	void __iomem *prv_base;
428f58b082aSRafael J. Wysocki 
429f58b082aSRafael J. Wysocki 	if (!lpss_clk_dev)
430f58b082aSRafael J. Wysocki 		lpt_register_clock_device();
431f58b082aSRafael J. Wysocki 
432b4f1f61eShuhai 	if (IS_ERR(lpss_clk_dev))
433b4f1f61eShuhai 		return PTR_ERR(lpss_clk_dev);
434b4f1f61eShuhai 
435b59cc200SRafael J. Wysocki 	clk_data = platform_get_drvdata(lpss_clk_dev);
436b59cc200SRafael J. Wysocki 	if (!clk_data)
437b59cc200SRafael J. Wysocki 		return -ENODEV;
438b0d00f8bSHeikki Krogerus 	clk = clk_data->clk;
439b59cc200SRafael J. Wysocki 
440b59cc200SRafael J. Wysocki 	if (!pdata->mmio_base
4412e0f8822SRafael J. Wysocki 	    || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
442f58b082aSRafael J. Wysocki 		return -ENODATA;
443f58b082aSRafael J. Wysocki 
444f6272170SMika Westerberg 	parent = clk_data->name;
445ed3a872eSHeikki Krogerus 	prv_base = pdata->mmio_base + dev_desc->prv_offset;
446f6272170SMika Westerberg 
44703f09f73SHeikki Krogerus 	if (pdata->fixed_clk_rate) {
44803f09f73SHeikki Krogerus 		clk = clk_register_fixed_rate(NULL, devname, parent, 0,
44903f09f73SHeikki Krogerus 					      pdata->fixed_clk_rate);
45003f09f73SHeikki Krogerus 		goto out;
451f6272170SMika Westerberg 	}
452f6272170SMika Westerberg 
453ff8c1af5SHeikki Krogerus 	if (dev_desc->flags & LPSS_CLK_GATE) {
454ed3a872eSHeikki Krogerus 		clk = clk_register_gate(NULL, devname, parent, 0,
455ed3a872eSHeikki Krogerus 					prv_base, 0, 0, NULL);
456ed3a872eSHeikki Krogerus 		parent = devname;
457ed3a872eSHeikki Krogerus 	}
458ed3a872eSHeikki Krogerus 
459ff8c1af5SHeikki Krogerus 	if (dev_desc->flags & LPSS_CLK_DIVIDER) {
460ed3a872eSHeikki Krogerus 		/* Prevent division by zero */
461ed3a872eSHeikki Krogerus 		if (!readl(prv_base))
462ed3a872eSHeikki Krogerus 			writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
463ed3a872eSHeikki Krogerus 
464ed3a872eSHeikki Krogerus 		clk_name = kasprintf(GFP_KERNEL, "%s-div", devname);
465ed3a872eSHeikki Krogerus 		if (!clk_name)
466ed3a872eSHeikki Krogerus 			return -ENOMEM;
467ed3a872eSHeikki Krogerus 		clk = clk_register_fractional_divider(NULL, clk_name, parent,
468*2920ac9dSAndy Shevchenko 						      0, prv_base, 1, 15, 16, 15,
46982f53f9eSAndy Shevchenko 						      CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
470*2920ac9dSAndy Shevchenko 						      NULL);
471ed3a872eSHeikki Krogerus 		parent = clk_name;
472ed3a872eSHeikki Krogerus 
473ed3a872eSHeikki Krogerus 		clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
474ed3a872eSHeikki Krogerus 		if (!clk_name) {
475ed3a872eSHeikki Krogerus 			kfree(parent);
476ed3a872eSHeikki Krogerus 			return -ENOMEM;
477ed3a872eSHeikki Krogerus 		}
478ed3a872eSHeikki Krogerus 		clk = clk_register_gate(NULL, clk_name, parent,
479ed3a872eSHeikki Krogerus 					CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
480ed3a872eSHeikki Krogerus 					prv_base, 31, 0, NULL);
481ed3a872eSHeikki Krogerus 		kfree(parent);
482ed3a872eSHeikki Krogerus 		kfree(clk_name);
483f6272170SMika Westerberg 	}
48403f09f73SHeikki Krogerus out:
485f6272170SMika Westerberg 	if (IS_ERR(clk))
486f6272170SMika Westerberg 		return PTR_ERR(clk);
487f6272170SMika Westerberg 
488ed3a872eSHeikki Krogerus 	pdata->clk = clk;
489fcf0789aSHeikki Krogerus 	clk_register_clkdev(clk, dev_desc->clk_con_id, devname);
490f58b082aSRafael J. Wysocki 	return 0;
491f58b082aSRafael J. Wysocki }
492f58b082aSRafael J. Wysocki 
493e6ce0ce3SAdrian Hunter struct lpss_device_links {
494e6ce0ce3SAdrian Hunter 	const char *supplier_hid;
495e6ce0ce3SAdrian Hunter 	const char *supplier_uid;
496e6ce0ce3SAdrian Hunter 	const char *consumer_hid;
497e6ce0ce3SAdrian Hunter 	const char *consumer_uid;
498e6ce0ce3SAdrian Hunter 	u32 flags;
4996025e2faSHans de Goede 	const struct dmi_system_id *dep_missing_ids;
5006025e2faSHans de Goede };
5016025e2faSHans de Goede 
5026025e2faSHans de Goede /* Please keep this list sorted alphabetically by vendor and model */
5036025e2faSHans de Goede static const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = {
5046025e2faSHans de Goede 	{
5056025e2faSHans de Goede 		.matches = {
5066025e2faSHans de Goede 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
5076025e2faSHans de Goede 			DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"),
5086025e2faSHans de Goede 		},
5096025e2faSHans de Goede 	},
5106025e2faSHans de Goede 	{}
511e6ce0ce3SAdrian Hunter };
512e6ce0ce3SAdrian Hunter 
513e6ce0ce3SAdrian Hunter /*
514e6ce0ce3SAdrian Hunter  * The _DEP method is used to identify dependencies but instead of creating
515e6ce0ce3SAdrian Hunter  * device links for every handle in _DEP, only links in the following list are
516e6ce0ce3SAdrian Hunter  * created. That is necessary because, in the general case, _DEP can refer to
517e6ce0ce3SAdrian Hunter  * devices that might not have drivers, or that are on different buses, or where
518e6ce0ce3SAdrian Hunter  * the supplier is not enumerated until after the consumer is probed.
519e6ce0ce3SAdrian Hunter  */
520e6ce0ce3SAdrian Hunter static const struct lpss_device_links lpss_device_links[] = {
521cc18735fSHans de Goede 	/* CHT External sdcard slot controller depends on PMIC I2C ctrl */
522e6ce0ce3SAdrian Hunter 	{"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME},
523cc18735fSHans de Goede 	/* CHT iGPU depends on PMIC I2C controller */
524bd0f4e34SHans de Goede 	{"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
525b3b3519cSHans de Goede 	/* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */
5266025e2faSHans de Goede 	{"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME,
5276025e2faSHans de Goede 	 i2c1_dep_missing_dmi_ids},
528cc18735fSHans de Goede 	/* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */
5292d71ee0cSHans de Goede 	{"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
530cc18735fSHans de Goede 	/* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */
531cc18735fSHans de Goede 	{"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME},
532e6ce0ce3SAdrian Hunter };
533e6ce0ce3SAdrian Hunter 
acpi_lpss_is_supplier(struct acpi_device * adev,const struct lpss_device_links * link)534e6ce0ce3SAdrian Hunter static bool acpi_lpss_is_supplier(struct acpi_device *adev,
535e6ce0ce3SAdrian Hunter 				  const struct lpss_device_links *link)
536e6ce0ce3SAdrian Hunter {
5377e70c8acSAndy Shevchenko 	return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid);
538e6ce0ce3SAdrian Hunter }
539e6ce0ce3SAdrian Hunter 
acpi_lpss_is_consumer(struct acpi_device * adev,const struct lpss_device_links * link)540e6ce0ce3SAdrian Hunter static bool acpi_lpss_is_consumer(struct acpi_device *adev,
541e6ce0ce3SAdrian Hunter 				  const struct lpss_device_links *link)
542e6ce0ce3SAdrian Hunter {
5437e70c8acSAndy Shevchenko 	return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid);
544e6ce0ce3SAdrian Hunter }
545e6ce0ce3SAdrian Hunter 
546e6ce0ce3SAdrian Hunter struct hid_uid {
547e6ce0ce3SAdrian Hunter 	const char *hid;
548e6ce0ce3SAdrian Hunter 	const char *uid;
549e6ce0ce3SAdrian Hunter };
550e6ce0ce3SAdrian Hunter 
match_hid_uid(struct device * dev,const void * data)551418e3ea1SSuzuki K Poulose static int match_hid_uid(struct device *dev, const void *data)
552e6ce0ce3SAdrian Hunter {
553e6ce0ce3SAdrian Hunter 	struct acpi_device *adev = ACPI_COMPANION(dev);
554418e3ea1SSuzuki K Poulose 	const struct hid_uid *id = data;
555e6ce0ce3SAdrian Hunter 
556e6ce0ce3SAdrian Hunter 	if (!adev)
557e6ce0ce3SAdrian Hunter 		return 0;
558e6ce0ce3SAdrian Hunter 
5597e70c8acSAndy Shevchenko 	return acpi_dev_hid_uid_match(adev, id->hid, id->uid);
560e6ce0ce3SAdrian Hunter }
561e6ce0ce3SAdrian Hunter 
acpi_lpss_find_device(const char * hid,const char * uid)562e6ce0ce3SAdrian Hunter static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
563e6ce0ce3SAdrian Hunter {
5641e30124aSHans de Goede 	struct device *dev;
5651e30124aSHans de Goede 
566e6ce0ce3SAdrian Hunter 	struct hid_uid data = {
567e6ce0ce3SAdrian Hunter 		.hid = hid,
568e6ce0ce3SAdrian Hunter 		.uid = uid,
569e6ce0ce3SAdrian Hunter 	};
570e6ce0ce3SAdrian Hunter 
5711e30124aSHans de Goede 	dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid);
5721e30124aSHans de Goede 	if (dev)
5731e30124aSHans de Goede 		return dev;
5741e30124aSHans de Goede 
5751e30124aSHans de Goede 	return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
576e6ce0ce3SAdrian Hunter }
577e6ce0ce3SAdrian Hunter 
acpi_lpss_dep(struct acpi_device * adev,acpi_handle handle)578e6ce0ce3SAdrian Hunter static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
579e6ce0ce3SAdrian Hunter {
580e6ce0ce3SAdrian Hunter 	struct acpi_handle_list dep_devices;
581e6ce0ce3SAdrian Hunter 	acpi_status status;
582e6ce0ce3SAdrian Hunter 	int i;
583e6ce0ce3SAdrian Hunter 
584e6ce0ce3SAdrian Hunter 	if (!acpi_has_method(adev->handle, "_DEP"))
585e6ce0ce3SAdrian Hunter 		return false;
586e6ce0ce3SAdrian Hunter 
587e6ce0ce3SAdrian Hunter 	status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
588e6ce0ce3SAdrian Hunter 					 &dep_devices);
589e6ce0ce3SAdrian Hunter 	if (ACPI_FAILURE(status)) {
590e6ce0ce3SAdrian Hunter 		dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
591e6ce0ce3SAdrian Hunter 		return false;
592e6ce0ce3SAdrian Hunter 	}
593e6ce0ce3SAdrian Hunter 
594e6ce0ce3SAdrian Hunter 	for (i = 0; i < dep_devices.count; i++) {
595e6ce0ce3SAdrian Hunter 		if (dep_devices.handles[i] == handle)
596e6ce0ce3SAdrian Hunter 			return true;
597e6ce0ce3SAdrian Hunter 	}
598e6ce0ce3SAdrian Hunter 
599e6ce0ce3SAdrian Hunter 	return false;
600e6ce0ce3SAdrian Hunter }
601e6ce0ce3SAdrian Hunter 
acpi_lpss_link_consumer(struct device * dev1,const struct lpss_device_links * link)602e6ce0ce3SAdrian Hunter static void acpi_lpss_link_consumer(struct device *dev1,
603e6ce0ce3SAdrian Hunter 				    const struct lpss_device_links *link)
604e6ce0ce3SAdrian Hunter {
605e6ce0ce3SAdrian Hunter 	struct device *dev2;
606e6ce0ce3SAdrian Hunter 
607e6ce0ce3SAdrian Hunter 	dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid);
608e6ce0ce3SAdrian Hunter 	if (!dev2)
609e6ce0ce3SAdrian Hunter 		return;
610e6ce0ce3SAdrian Hunter 
6116025e2faSHans de Goede 	if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
6126025e2faSHans de Goede 	    || acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
613e6ce0ce3SAdrian Hunter 		device_link_add(dev2, dev1, link->flags);
614e6ce0ce3SAdrian Hunter 
615e6ce0ce3SAdrian Hunter 	put_device(dev2);
616e6ce0ce3SAdrian Hunter }
617e6ce0ce3SAdrian Hunter 
acpi_lpss_link_supplier(struct device * dev1,const struct lpss_device_links * link)618e6ce0ce3SAdrian Hunter static void acpi_lpss_link_supplier(struct device *dev1,
619e6ce0ce3SAdrian Hunter 				    const struct lpss_device_links *link)
620e6ce0ce3SAdrian Hunter {
621e6ce0ce3SAdrian Hunter 	struct device *dev2;
622e6ce0ce3SAdrian Hunter 
623e6ce0ce3SAdrian Hunter 	dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid);
624e6ce0ce3SAdrian Hunter 	if (!dev2)
625e6ce0ce3SAdrian Hunter 		return;
626e6ce0ce3SAdrian Hunter 
6276025e2faSHans de Goede 	if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
6286025e2faSHans de Goede 	    || acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
629e6ce0ce3SAdrian Hunter 		device_link_add(dev1, dev2, link->flags);
630e6ce0ce3SAdrian Hunter 
631e6ce0ce3SAdrian Hunter 	put_device(dev2);
632e6ce0ce3SAdrian Hunter }
633e6ce0ce3SAdrian Hunter 
acpi_lpss_create_device_links(struct acpi_device * adev,struct platform_device * pdev)634e6ce0ce3SAdrian Hunter static void acpi_lpss_create_device_links(struct acpi_device *adev,
635e6ce0ce3SAdrian Hunter 					  struct platform_device *pdev)
636e6ce0ce3SAdrian Hunter {
637e6ce0ce3SAdrian Hunter 	int i;
638e6ce0ce3SAdrian Hunter 
639e6ce0ce3SAdrian Hunter 	for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) {
640e6ce0ce3SAdrian Hunter 		const struct lpss_device_links *link = &lpss_device_links[i];
641e6ce0ce3SAdrian Hunter 
642e6ce0ce3SAdrian Hunter 		if (acpi_lpss_is_supplier(adev, link))
643e6ce0ce3SAdrian Hunter 			acpi_lpss_link_consumer(&pdev->dev, link);
644e6ce0ce3SAdrian Hunter 
645e6ce0ce3SAdrian Hunter 		if (acpi_lpss_is_consumer(adev, link))
646e6ce0ce3SAdrian Hunter 			acpi_lpss_link_supplier(&pdev->dev, link);
647e6ce0ce3SAdrian Hunter 	}
648e6ce0ce3SAdrian Hunter }
649e6ce0ce3SAdrian Hunter 
acpi_lpss_create_device(struct acpi_device * adev,const struct acpi_device_id * id)650f58b082aSRafael J. Wysocki static int acpi_lpss_create_device(struct acpi_device *adev,
651f58b082aSRafael J. Wysocki 				   const struct acpi_device_id *id)
652f58b082aSRafael J. Wysocki {
653b2687cd7SMathias Krause 	const struct lpss_device_desc *dev_desc;
654f58b082aSRafael J. Wysocki 	struct lpss_private_data *pdata;
65590e97820SJiang Liu 	struct resource_entry *rentry;
656f58b082aSRafael J. Wysocki 	struct list_head resource_list;
6578ce62f85SRafael J. Wysocki 	struct platform_device *pdev;
658f58b082aSRafael J. Wysocki 	int ret;
659f58b082aSRafael J. Wysocki 
660b2687cd7SMathias Krause 	dev_desc = (const struct lpss_device_desc *)id->driver_data;
6618ce62f85SRafael J. Wysocki 	if (!dev_desc) {
6621571875bSHeikki Krogerus 		pdev = acpi_create_platform_device(adev, NULL);
6638ce62f85SRafael J. Wysocki 		return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
6648ce62f85SRafael J. Wysocki 	}
665f58b082aSRafael J. Wysocki 	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
666f58b082aSRafael J. Wysocki 	if (!pdata)
667f58b082aSRafael J. Wysocki 		return -ENOMEM;
668f58b082aSRafael J. Wysocki 
669f58b082aSRafael J. Wysocki 	INIT_LIST_HEAD(&resource_list);
670840baca4SHeikki Krogerus 	ret = acpi_dev_get_memory_resources(adev, &resource_list);
671f58b082aSRafael J. Wysocki 	if (ret < 0)
672f58b082aSRafael J. Wysocki 		goto err_out;
673f58b082aSRafael J. Wysocki 
674da13b336SAndy Shevchenko 	rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node);
675da13b336SAndy Shevchenko 	if (rentry) {
676958c4eb2SMika Westerberg 		if (dev_desc->prv_size_override)
677958c4eb2SMika Westerberg 			pdata->mmio_size = dev_desc->prv_size_override;
678958c4eb2SMika Westerberg 		else
67990e97820SJiang Liu 			pdata->mmio_size = resource_size(rentry->res);
680da13b336SAndy Shevchenko 		pdata->mmio_base = ioremap(rentry->res->start, pdata->mmio_size);
681f58b082aSRafael J. Wysocki 	}
682f58b082aSRafael J. Wysocki 
683f58b082aSRafael J. Wysocki 	acpi_dev_free_resource_list(&resource_list);
684f58b082aSRafael J. Wysocki 
685d3e13ff3SRafael J. Wysocki 	if (!pdata->mmio_base) {
686e1681599SHans de Goede 		/* Avoid acpi_bus_attach() instantiating a pdev for this dev. */
687e1681599SHans de Goede 		adev->pnp.type.platform_id = 0;
6886cc401beSAndy Shevchenko 		goto out_free;
689d3e13ff3SRafael J. Wysocki 	}
690d3e13ff3SRafael J. Wysocki 
691dd242a08SHans de Goede 	pdata->adev = adev;
692af65cfe9SMika Westerberg 	pdata->dev_desc = dev_desc;
693af65cfe9SMika Westerberg 
69403f09f73SHeikki Krogerus 	if (dev_desc->setup)
69503f09f73SHeikki Krogerus 		dev_desc->setup(pdata);
69603f09f73SHeikki Krogerus 
697ff8c1af5SHeikki Krogerus 	if (dev_desc->flags & LPSS_CLK) {
698f58b082aSRafael J. Wysocki 		ret = register_device_clock(adev, pdata);
6996cc401beSAndy Shevchenko 		if (ret)
7006cc401beSAndy Shevchenko 			goto out_free;
701f58b082aSRafael J. Wysocki 	}
702f58b082aSRafael J. Wysocki 
703b9e95fc6SRafael J. Wysocki 	/*
704b9e95fc6SRafael J. Wysocki 	 * This works around a known issue in ACPI tables where LPSS devices
705b9e95fc6SRafael J. Wysocki 	 * have _PS0 and _PS3 without _PSC (and no power resources), so
706b9e95fc6SRafael J. Wysocki 	 * acpi_bus_init_power() will assume that the BIOS has put them into D0.
707b9e95fc6SRafael J. Wysocki 	 */
7081a2fa02fSHans de Goede 	acpi_device_fix_up_power(adev);
709b9e95fc6SRafael J. Wysocki 
710f58b082aSRafael J. Wysocki 	adev->driver_data = pdata;
7111571875bSHeikki Krogerus 	pdev = acpi_create_platform_device(adev, dev_desc->properties);
7126cc401beSAndy Shevchenko 	if (IS_ERR_OR_NULL(pdev)) {
7136cc401beSAndy Shevchenko 		adev->driver_data = NULL;
7146cc401beSAndy Shevchenko 		ret = PTR_ERR(pdev);
7156cc401beSAndy Shevchenko 		goto err_out;
7168ce62f85SRafael J. Wysocki 	}
717f58b082aSRafael J. Wysocki 
7186cc401beSAndy Shevchenko 	acpi_lpss_create_device_links(adev, pdev);
7196cc401beSAndy Shevchenko 	return 1;
720f58b082aSRafael J. Wysocki 
7216cc401beSAndy Shevchenko out_free:
7226cc401beSAndy Shevchenko 	/* Skip the device, but continue the namespace scan */
7236cc401beSAndy Shevchenko 	ret = 0;
724f58b082aSRafael J. Wysocki err_out:
725f58b082aSRafael J. Wysocki 	kfree(pdata);
726f58b082aSRafael J. Wysocki 	return ret;
727f58b082aSRafael J. Wysocki }
728f58b082aSRafael J. Wysocki 
__lpss_reg_read(struct lpss_private_data * pdata,unsigned int reg)7291a8f8351SRafael J. Wysocki static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
7301a8f8351SRafael J. Wysocki {
7311a8f8351SRafael J. Wysocki 	return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
7321a8f8351SRafael J. Wysocki }
7331a8f8351SRafael J. Wysocki 
__lpss_reg_write(u32 val,struct lpss_private_data * pdata,unsigned int reg)7341a8f8351SRafael J. Wysocki static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
7351a8f8351SRafael J. Wysocki 			     unsigned int reg)
7361a8f8351SRafael J. Wysocki {
7371a8f8351SRafael J. Wysocki 	writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
7381a8f8351SRafael J. Wysocki }
7391a8f8351SRafael J. Wysocki 
lpss_reg_read(struct device * dev,unsigned int reg,u32 * val)7402e0f8822SRafael J. Wysocki static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
7412e0f8822SRafael J. Wysocki {
74250861d43SRafael J. Wysocki 	struct acpi_device *adev = ACPI_COMPANION(dev);
7432e0f8822SRafael J. Wysocki 	struct lpss_private_data *pdata;
7442e0f8822SRafael J. Wysocki 	unsigned long flags;
7452e0f8822SRafael J. Wysocki 	int ret;
7462e0f8822SRafael J. Wysocki 
74750861d43SRafael J. Wysocki 	if (WARN_ON(!adev))
74850861d43SRafael J. Wysocki 		return -ENODEV;
7492e0f8822SRafael J. Wysocki 
7502e0f8822SRafael J. Wysocki 	spin_lock_irqsave(&dev->power.lock, flags);
7512e0f8822SRafael J. Wysocki 	if (pm_runtime_suspended(dev)) {
7522e0f8822SRafael J. Wysocki 		ret = -EAGAIN;
7532e0f8822SRafael J. Wysocki 		goto out;
7542e0f8822SRafael J. Wysocki 	}
7552e0f8822SRafael J. Wysocki 	pdata = acpi_driver_data(adev);
7562e0f8822SRafael J. Wysocki 	if (WARN_ON(!pdata || !pdata->mmio_base)) {
7572e0f8822SRafael J. Wysocki 		ret = -ENODEV;
7582e0f8822SRafael J. Wysocki 		goto out;
7592e0f8822SRafael J. Wysocki 	}
7601a8f8351SRafael J. Wysocki 	*val = __lpss_reg_read(pdata, reg);
76150861d43SRafael J. Wysocki 	ret = 0;
7622e0f8822SRafael J. Wysocki 
7632e0f8822SRafael J. Wysocki  out:
7642e0f8822SRafael J. Wysocki 	spin_unlock_irqrestore(&dev->power.lock, flags);
7652e0f8822SRafael J. Wysocki 	return ret;
7662e0f8822SRafael J. Wysocki }
7672e0f8822SRafael J. Wysocki 
lpss_ltr_show(struct device * dev,struct device_attribute * attr,char * buf)7682e0f8822SRafael J. Wysocki static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr,
7692e0f8822SRafael J. Wysocki 			     char *buf)
7702e0f8822SRafael J. Wysocki {
7712e0f8822SRafael J. Wysocki 	u32 ltr_value = 0;
7722e0f8822SRafael J. Wysocki 	unsigned int reg;
7732e0f8822SRafael J. Wysocki 	int ret;
7742e0f8822SRafael J. Wysocki 
7752e0f8822SRafael J. Wysocki 	reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR;
7762e0f8822SRafael J. Wysocki 	ret = lpss_reg_read(dev, reg, &ltr_value);
7772e0f8822SRafael J. Wysocki 	if (ret)
7782e0f8822SRafael J. Wysocki 		return ret;
7792e0f8822SRafael J. Wysocki 
780d47e983eSQing Wang 	return sysfs_emit(buf, "%08x\n", ltr_value);
7812e0f8822SRafael J. Wysocki }
7822e0f8822SRafael J. Wysocki 
lpss_ltr_mode_show(struct device * dev,struct device_attribute * attr,char * buf)7832e0f8822SRafael J. Wysocki static ssize_t lpss_ltr_mode_show(struct device *dev,
7842e0f8822SRafael J. Wysocki 				  struct device_attribute *attr, char *buf)
7852e0f8822SRafael J. Wysocki {
7862e0f8822SRafael J. Wysocki 	u32 ltr_mode = 0;
7872e0f8822SRafael J. Wysocki 	char *outstr;
7882e0f8822SRafael J. Wysocki 	int ret;
7892e0f8822SRafael J. Wysocki 
7902e0f8822SRafael J. Wysocki 	ret = lpss_reg_read(dev, LPSS_GENERAL, &ltr_mode);
7912e0f8822SRafael J. Wysocki 	if (ret)
7922e0f8822SRafael J. Wysocki 		return ret;
7932e0f8822SRafael J. Wysocki 
7942e0f8822SRafael J. Wysocki 	outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto";
7952e0f8822SRafael J. Wysocki 	return sprintf(buf, "%s\n", outstr);
7962e0f8822SRafael J. Wysocki }
7972e0f8822SRafael J. Wysocki 
7982e0f8822SRafael J. Wysocki static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL);
7992e0f8822SRafael J. Wysocki static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL);
8002e0f8822SRafael J. Wysocki static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL);
8012e0f8822SRafael J. Wysocki 
8022e0f8822SRafael J. Wysocki static struct attribute *lpss_attrs[] = {
8032e0f8822SRafael J. Wysocki 	&dev_attr_auto_ltr.attr,
8042e0f8822SRafael J. Wysocki 	&dev_attr_sw_ltr.attr,
8052e0f8822SRafael J. Wysocki 	&dev_attr_ltr_mode.attr,
8062e0f8822SRafael J. Wysocki 	NULL,
8072e0f8822SRafael J. Wysocki };
8082e0f8822SRafael J. Wysocki 
80931945d0eSArvind Yadav static const struct attribute_group lpss_attr_group = {
8102e0f8822SRafael J. Wysocki 	.attrs = lpss_attrs,
8112e0f8822SRafael J. Wysocki 	.name = "lpss_ltr",
8122e0f8822SRafael J. Wysocki };
8132e0f8822SRafael J. Wysocki 
acpi_lpss_set_ltr(struct device * dev,s32 val)8141a8f8351SRafael J. Wysocki static void acpi_lpss_set_ltr(struct device *dev, s32 val)
8151a8f8351SRafael J. Wysocki {
8161a8f8351SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
8171a8f8351SRafael J. Wysocki 	u32 ltr_mode, ltr_val;
8181a8f8351SRafael J. Wysocki 
8191a8f8351SRafael J. Wysocki 	ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
8201a8f8351SRafael J. Wysocki 	if (val < 0) {
8211a8f8351SRafael J. Wysocki 		if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
8221a8f8351SRafael J. Wysocki 			ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
8231a8f8351SRafael J. Wysocki 			__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
8241a8f8351SRafael J. Wysocki 		}
8251a8f8351SRafael J. Wysocki 		return;
8261a8f8351SRafael J. Wysocki 	}
8271a8f8351SRafael J. Wysocki 	ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
8281a8f8351SRafael J. Wysocki 	if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
8291a8f8351SRafael J. Wysocki 		ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
8301a8f8351SRafael J. Wysocki 		val = LPSS_LTR_MAX_VAL;
8311a8f8351SRafael J. Wysocki 	} else if (val > LPSS_LTR_MAX_VAL) {
8321a8f8351SRafael J. Wysocki 		ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
8331a8f8351SRafael J. Wysocki 		val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
8341a8f8351SRafael J. Wysocki 	} else {
8351a8f8351SRafael J. Wysocki 		ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
8361a8f8351SRafael J. Wysocki 	}
8371a8f8351SRafael J. Wysocki 	ltr_val |= val;
8381a8f8351SRafael J. Wysocki 	__lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
8391a8f8351SRafael J. Wysocki 	if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
8401a8f8351SRafael J. Wysocki 		ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
8411a8f8351SRafael J. Wysocki 		__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
8421a8f8351SRafael J. Wysocki 	}
8431a8f8351SRafael J. Wysocki }
8441a8f8351SRafael J. Wysocki 
845c78b0830SHeikki Krogerus #ifdef CONFIG_PM
846c78b0830SHeikki Krogerus /**
847c78b0830SHeikki Krogerus  * acpi_lpss_save_ctx() - Save the private registers of LPSS device
848c78b0830SHeikki Krogerus  * @dev: LPSS device
849cb39dcddSAndy Shevchenko  * @pdata: pointer to the private data of the LPSS device
850c78b0830SHeikki Krogerus  *
851c78b0830SHeikki Krogerus  * Most LPSS devices have private registers which may loose their context when
852c78b0830SHeikki Krogerus  * the device is powered down. acpi_lpss_save_ctx() saves those registers into
853c78b0830SHeikki Krogerus  * prv_reg_ctx array.
854c78b0830SHeikki Krogerus  */
acpi_lpss_save_ctx(struct device * dev,struct lpss_private_data * pdata)855cb39dcddSAndy Shevchenko static void acpi_lpss_save_ctx(struct device *dev,
856cb39dcddSAndy Shevchenko 			       struct lpss_private_data *pdata)
857c78b0830SHeikki Krogerus {
858c78b0830SHeikki Krogerus 	unsigned int i;
859c78b0830SHeikki Krogerus 
860c78b0830SHeikki Krogerus 	for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
861c78b0830SHeikki Krogerus 		unsigned long offset = i * sizeof(u32);
862c78b0830SHeikki Krogerus 
863c78b0830SHeikki Krogerus 		pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset);
864c78b0830SHeikki Krogerus 		dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n",
865c78b0830SHeikki Krogerus 			pdata->prv_reg_ctx[i], offset);
866c78b0830SHeikki Krogerus 	}
867c78b0830SHeikki Krogerus }
868c78b0830SHeikki Krogerus 
869c78b0830SHeikki Krogerus /**
870c78b0830SHeikki Krogerus  * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device
871c78b0830SHeikki Krogerus  * @dev: LPSS device
872cb39dcddSAndy Shevchenko  * @pdata: pointer to the private data of the LPSS device
873c78b0830SHeikki Krogerus  *
874c78b0830SHeikki Krogerus  * Restores the registers that were previously stored with acpi_lpss_save_ctx().
875c78b0830SHeikki Krogerus  */
acpi_lpss_restore_ctx(struct device * dev,struct lpss_private_data * pdata)876cb39dcddSAndy Shevchenko static void acpi_lpss_restore_ctx(struct device *dev,
877cb39dcddSAndy Shevchenko 				  struct lpss_private_data *pdata)
878c78b0830SHeikki Krogerus {
879c78b0830SHeikki Krogerus 	unsigned int i;
880c78b0830SHeikki Krogerus 
88102b98540SAndy Shevchenko 	for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
88202b98540SAndy Shevchenko 		unsigned long offset = i * sizeof(u32);
88302b98540SAndy Shevchenko 
88402b98540SAndy Shevchenko 		__lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
88502b98540SAndy Shevchenko 		dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
88602b98540SAndy Shevchenko 			pdata->prv_reg_ctx[i], offset);
88702b98540SAndy Shevchenko 	}
88802b98540SAndy Shevchenko }
88902b98540SAndy Shevchenko 
acpi_lpss_d3_to_d0_delay(struct lpss_private_data * pdata)89002b98540SAndy Shevchenko static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata)
89102b98540SAndy Shevchenko {
892c78b0830SHeikki Krogerus 	/*
893c78b0830SHeikki Krogerus 	 * The following delay is needed or the subsequent write operations may
894c78b0830SHeikki Krogerus 	 * fail. The LPSS devices are actually PCI devices and the PCI spec
895c78b0830SHeikki Krogerus 	 * expects 10ms delay before the device can be accessed after D3 to D0
896b00855aeSSrinidhi Kasagar 	 * transition. However some platforms like BSW does not need this delay.
897c78b0830SHeikki Krogerus 	 */
898b00855aeSSrinidhi Kasagar 	unsigned int delay = 10;	/* default 10ms delay */
899b00855aeSSrinidhi Kasagar 
900b00855aeSSrinidhi Kasagar 	if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY)
901b00855aeSSrinidhi Kasagar 		delay = 0;
902b00855aeSSrinidhi Kasagar 
903b00855aeSSrinidhi Kasagar 	msleep(delay);
904c78b0830SHeikki Krogerus }
905c78b0830SHeikki Krogerus 
acpi_lpss_activate(struct device * dev)906c3a49cf3SAndy Shevchenko static int acpi_lpss_activate(struct device *dev)
907c3a49cf3SAndy Shevchenko {
908c3a49cf3SAndy Shevchenko 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
909c3a49cf3SAndy Shevchenko 	int ret;
910c3a49cf3SAndy Shevchenko 
91163705c40SRafael J. Wysocki 	ret = acpi_dev_resume(dev);
912c3a49cf3SAndy Shevchenko 	if (ret)
913c3a49cf3SAndy Shevchenko 		return ret;
914c3a49cf3SAndy Shevchenko 
915c3a49cf3SAndy Shevchenko 	acpi_lpss_d3_to_d0_delay(pdata);
916c3a49cf3SAndy Shevchenko 
917c3a49cf3SAndy Shevchenko 	/*
918c3a49cf3SAndy Shevchenko 	 * This is called only on ->probe() stage where a device is either in
919c3a49cf3SAndy Shevchenko 	 * known state defined by BIOS or most likely powered off. Due to this
920c3a49cf3SAndy Shevchenko 	 * we have to deassert reset line to be sure that ->probe() will
921c3a49cf3SAndy Shevchenko 	 * recognize the device.
922c3a49cf3SAndy Shevchenko 	 */
92315aa5e4cSHans de Goede 	if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
924c3a49cf3SAndy Shevchenko 		lpss_deassert_reset(pdata);
925c3a49cf3SAndy Shevchenko 
92615aa5e4cSHans de Goede #ifdef CONFIG_PM
92715aa5e4cSHans de Goede 	if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE)
92815aa5e4cSHans de Goede 		acpi_lpss_save_ctx(dev, pdata);
92915aa5e4cSHans de Goede #endif
93015aa5e4cSHans de Goede 
931c3a49cf3SAndy Shevchenko 	return 0;
932c3a49cf3SAndy Shevchenko }
933c3a49cf3SAndy Shevchenko 
acpi_lpss_dismiss(struct device * dev)934c3a49cf3SAndy Shevchenko static void acpi_lpss_dismiss(struct device *dev)
935c3a49cf3SAndy Shevchenko {
936cbe25ce3SRafael J. Wysocki 	acpi_dev_suspend(dev, false);
937c3a49cf3SAndy Shevchenko }
938c3a49cf3SAndy Shevchenko 
939eebb3e8dSAndy Shevchenko /* IOSF SB for LPSS island */
940eebb3e8dSAndy Shevchenko #define LPSS_IOSF_UNIT_LPIOEP		0xA0
941eebb3e8dSAndy Shevchenko #define LPSS_IOSF_UNIT_LPIO1		0xAB
942eebb3e8dSAndy Shevchenko #define LPSS_IOSF_UNIT_LPIO2		0xAC
943eebb3e8dSAndy Shevchenko 
944eebb3e8dSAndy Shevchenko #define LPSS_IOSF_PMCSR			0x84
945eebb3e8dSAndy Shevchenko #define LPSS_PMCSR_D0			0
946eebb3e8dSAndy Shevchenko #define LPSS_PMCSR_D3hot		3
947eebb3e8dSAndy Shevchenko #define LPSS_PMCSR_Dx_MASK		GENMASK(1, 0)
948eebb3e8dSAndy Shevchenko 
949eebb3e8dSAndy Shevchenko #define LPSS_IOSF_GPIODEF0		0x154
950eebb3e8dSAndy Shevchenko #define LPSS_GPIODEF0_DMA1_D3		BIT(2)
951eebb3e8dSAndy Shevchenko #define LPSS_GPIODEF0_DMA2_D3		BIT(3)
952eebb3e8dSAndy Shevchenko #define LPSS_GPIODEF0_DMA_D3_MASK	GENMASK(3, 2)
953d132d6d5SAndy Shevchenko #define LPSS_GPIODEF0_DMA_LLP		BIT(13)
954eebb3e8dSAndy Shevchenko 
955eebb3e8dSAndy Shevchenko static DEFINE_MUTEX(lpss_iosf_mutex);
956f11fc4bcSZhang Rui static bool lpss_iosf_d3_entered = true;
957eebb3e8dSAndy Shevchenko 
lpss_iosf_enter_d3_state(void)958eebb3e8dSAndy Shevchenko static void lpss_iosf_enter_d3_state(void)
959eebb3e8dSAndy Shevchenko {
960eebb3e8dSAndy Shevchenko 	u32 value1 = 0;
961d132d6d5SAndy Shevchenko 	u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
962eebb3e8dSAndy Shevchenko 	u32 value2 = LPSS_PMCSR_D3hot;
963eebb3e8dSAndy Shevchenko 	u32 mask2 = LPSS_PMCSR_Dx_MASK;
964eebb3e8dSAndy Shevchenko 	/*
965eebb3e8dSAndy Shevchenko 	 * PMC provides an information about actual status of the LPSS devices.
966eebb3e8dSAndy Shevchenko 	 * Here we read the values related to LPSS power island, i.e. LPSS
967eebb3e8dSAndy Shevchenko 	 * devices, excluding both LPSS DMA controllers, along with SCC domain.
968eebb3e8dSAndy Shevchenko 	 */
96986b62e5cSHans de Goede 	u32 func_dis, d3_sts_0, pmc_status;
970eebb3e8dSAndy Shevchenko 	int ret;
971eebb3e8dSAndy Shevchenko 
972eebb3e8dSAndy Shevchenko 	ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
973eebb3e8dSAndy Shevchenko 	if (ret)
974eebb3e8dSAndy Shevchenko 		return;
975eebb3e8dSAndy Shevchenko 
976eebb3e8dSAndy Shevchenko 	mutex_lock(&lpss_iosf_mutex);
977eebb3e8dSAndy Shevchenko 
978eebb3e8dSAndy Shevchenko 	ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0);
979eebb3e8dSAndy Shevchenko 	if (ret)
980eebb3e8dSAndy Shevchenko 		goto exit;
981eebb3e8dSAndy Shevchenko 
982eebb3e8dSAndy Shevchenko 	/*
983eebb3e8dSAndy Shevchenko 	 * Get the status of entire LPSS power island per device basis.
984eebb3e8dSAndy Shevchenko 	 * Shutdown both LPSS DMA controllers if and only if all other devices
985eebb3e8dSAndy Shevchenko 	 * are already in D3hot.
986eebb3e8dSAndy Shevchenko 	 */
98786b62e5cSHans de Goede 	pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask;
988eebb3e8dSAndy Shevchenko 	if (pmc_status)
989eebb3e8dSAndy Shevchenko 		goto exit;
990eebb3e8dSAndy Shevchenko 
991eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
992eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
993eebb3e8dSAndy Shevchenko 
994eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
995eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
996eebb3e8dSAndy Shevchenko 
997eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
998eebb3e8dSAndy Shevchenko 			LPSS_IOSF_GPIODEF0, value1, mask1);
99912864ff8SRafael J. Wysocki 
100012864ff8SRafael J. Wysocki 	lpss_iosf_d3_entered = true;
100112864ff8SRafael J. Wysocki 
1002eebb3e8dSAndy Shevchenko exit:
1003eebb3e8dSAndy Shevchenko 	mutex_unlock(&lpss_iosf_mutex);
1004eebb3e8dSAndy Shevchenko }
1005eebb3e8dSAndy Shevchenko 
lpss_iosf_exit_d3_state(void)1006eebb3e8dSAndy Shevchenko static void lpss_iosf_exit_d3_state(void)
1007eebb3e8dSAndy Shevchenko {
1008d132d6d5SAndy Shevchenko 	u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 |
1009d132d6d5SAndy Shevchenko 		     LPSS_GPIODEF0_DMA_LLP;
1010d132d6d5SAndy Shevchenko 	u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP;
1011eebb3e8dSAndy Shevchenko 	u32 value2 = LPSS_PMCSR_D0;
1012eebb3e8dSAndy Shevchenko 	u32 mask2 = LPSS_PMCSR_Dx_MASK;
1013eebb3e8dSAndy Shevchenko 
1014eebb3e8dSAndy Shevchenko 	mutex_lock(&lpss_iosf_mutex);
1015eebb3e8dSAndy Shevchenko 
101612864ff8SRafael J. Wysocki 	if (!lpss_iosf_d3_entered)
101712864ff8SRafael J. Wysocki 		goto exit;
101812864ff8SRafael J. Wysocki 
101912864ff8SRafael J. Wysocki 	lpss_iosf_d3_entered = false;
102012864ff8SRafael J. Wysocki 
1021eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
1022eebb3e8dSAndy Shevchenko 			LPSS_IOSF_GPIODEF0, value1, mask1);
1023eebb3e8dSAndy Shevchenko 
1024eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
1025eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
1026eebb3e8dSAndy Shevchenko 
1027eebb3e8dSAndy Shevchenko 	iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
1028eebb3e8dSAndy Shevchenko 			LPSS_IOSF_PMCSR, value2, mask2);
1029eebb3e8dSAndy Shevchenko 
103012864ff8SRafael J. Wysocki exit:
1031eebb3e8dSAndy Shevchenko 	mutex_unlock(&lpss_iosf_mutex);
1032eebb3e8dSAndy Shevchenko }
1033eebb3e8dSAndy Shevchenko 
acpi_lpss_suspend(struct device * dev,bool wakeup)103412864ff8SRafael J. Wysocki static int acpi_lpss_suspend(struct device *dev, bool wakeup)
1035c78b0830SHeikki Krogerus {
1036cb39dcddSAndy Shevchenko 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1037cb39dcddSAndy Shevchenko 	int ret;
1038c78b0830SHeikki Krogerus 
1039cb39dcddSAndy Shevchenko 	if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
1040cb39dcddSAndy Shevchenko 		acpi_lpss_save_ctx(dev, pdata);
1041cb39dcddSAndy Shevchenko 
1042a192aa92SRafael J. Wysocki 	ret = acpi_dev_suspend(dev, wakeup);
1043eebb3e8dSAndy Shevchenko 
1044eebb3e8dSAndy Shevchenko 	/*
1045eebb3e8dSAndy Shevchenko 	 * This call must be last in the sequence, otherwise PMC will return
1046eebb3e8dSAndy Shevchenko 	 * wrong status for devices being about to be powered off. See
1047eebb3e8dSAndy Shevchenko 	 * lpss_iosf_enter_d3_state() for further information.
1048eebb3e8dSAndy Shevchenko 	 */
104912864ff8SRafael J. Wysocki 	if (acpi_target_system_state() == ACPI_STATE_S0 &&
1050a09c5913SRafael J. Wysocki 	    lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
1051eebb3e8dSAndy Shevchenko 		lpss_iosf_enter_d3_state();
1052eebb3e8dSAndy Shevchenko 
1053eebb3e8dSAndy Shevchenko 	return ret;
1054c78b0830SHeikki Krogerus }
1055c78b0830SHeikki Krogerus 
acpi_lpss_resume(struct device * dev)105612864ff8SRafael J. Wysocki static int acpi_lpss_resume(struct device *dev)
1057c78b0830SHeikki Krogerus {
1058cb39dcddSAndy Shevchenko 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1059cb39dcddSAndy Shevchenko 	int ret;
1060c78b0830SHeikki Krogerus 
1061eebb3e8dSAndy Shevchenko 	/*
1062eebb3e8dSAndy Shevchenko 	 * This call is kept first to be in symmetry with
1063eebb3e8dSAndy Shevchenko 	 * acpi_lpss_runtime_suspend() one.
1064eebb3e8dSAndy Shevchenko 	 */
106512864ff8SRafael J. Wysocki 	if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
1066eebb3e8dSAndy Shevchenko 		lpss_iosf_exit_d3_state();
1067eebb3e8dSAndy Shevchenko 
106863705c40SRafael J. Wysocki 	ret = acpi_dev_resume(dev);
1069c78b0830SHeikki Krogerus 	if (ret)
1070c78b0830SHeikki Krogerus 		return ret;
1071c78b0830SHeikki Krogerus 
107202b98540SAndy Shevchenko 	acpi_lpss_d3_to_d0_delay(pdata);
107302b98540SAndy Shevchenko 
107415aa5e4cSHans de Goede 	if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE))
1075cb39dcddSAndy Shevchenko 		acpi_lpss_restore_ctx(dev, pdata);
1076cb39dcddSAndy Shevchenko 
1077a192aa92SRafael J. Wysocki 	return 0;
1078a192aa92SRafael J. Wysocki }
1079a192aa92SRafael J. Wysocki 
1080a192aa92SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
acpi_lpss_do_suspend_late(struct device * dev)108148402ceeSHans de Goede static int acpi_lpss_do_suspend_late(struct device *dev)
1082a192aa92SRafael J. Wysocki {
108305087360SRafael J. Wysocki 	int ret;
1084a192aa92SRafael J. Wysocki 
1085fa2bfeadSRafael J. Wysocki 	if (dev_pm_skip_suspend(dev))
108605087360SRafael J. Wysocki 		return 0;
108705087360SRafael J. Wysocki 
108805087360SRafael J. Wysocki 	ret = pm_generic_suspend_late(dev);
108912864ff8SRafael J. Wysocki 	return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
1090a192aa92SRafael J. Wysocki }
1091a192aa92SRafael J. Wysocki 
acpi_lpss_suspend_late(struct device * dev)109248402ceeSHans de Goede static int acpi_lpss_suspend_late(struct device *dev)
109348402ceeSHans de Goede {
109448402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
109548402ceeSHans de Goede 
109648402ceeSHans de Goede 	if (pdata->dev_desc->resume_from_noirq)
109748402ceeSHans de Goede 		return 0;
109848402ceeSHans de Goede 
109948402ceeSHans de Goede 	return acpi_lpss_do_suspend_late(dev);
110048402ceeSHans de Goede }
110148402ceeSHans de Goede 
acpi_lpss_suspend_noirq(struct device * dev)110248402ceeSHans de Goede static int acpi_lpss_suspend_noirq(struct device *dev)
110348402ceeSHans de Goede {
110448402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
110548402ceeSHans de Goede 	int ret;
110648402ceeSHans de Goede 
110748402ceeSHans de Goede 	if (pdata->dev_desc->resume_from_noirq) {
1108c95b7595SRafael J. Wysocki 		/*
1109c95b7595SRafael J. Wysocki 		 * The driver's ->suspend_late callback will be invoked by
1110c95b7595SRafael J. Wysocki 		 * acpi_lpss_do_suspend_late(), with the assumption that the
1111c95b7595SRafael J. Wysocki 		 * driver really wanted to run that code in ->suspend_noirq, but
1112c95b7595SRafael J. Wysocki 		 * it could not run after acpi_dev_suspend() and the driver
1113c95b7595SRafael J. Wysocki 		 * expected the latter to be called in the "late" phase.
1114c95b7595SRafael J. Wysocki 		 */
111548402ceeSHans de Goede 		ret = acpi_lpss_do_suspend_late(dev);
111648402ceeSHans de Goede 		if (ret)
111748402ceeSHans de Goede 			return ret;
111848402ceeSHans de Goede 	}
111948402ceeSHans de Goede 
112048402ceeSHans de Goede 	return acpi_subsys_suspend_noirq(dev);
112148402ceeSHans de Goede }
112248402ceeSHans de Goede 
acpi_lpss_do_resume_early(struct device * dev)112348402ceeSHans de Goede static int acpi_lpss_do_resume_early(struct device *dev)
1124a192aa92SRafael J. Wysocki {
112512864ff8SRafael J. Wysocki 	int ret = acpi_lpss_resume(dev);
1126a192aa92SRafael J. Wysocki 
1127a192aa92SRafael J. Wysocki 	return ret ? ret : pm_generic_resume_early(dev);
1128a192aa92SRafael J. Wysocki }
112948402ceeSHans de Goede 
acpi_lpss_resume_early(struct device * dev)113048402ceeSHans de Goede static int acpi_lpss_resume_early(struct device *dev)
113148402ceeSHans de Goede {
113248402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
113348402ceeSHans de Goede 
113448402ceeSHans de Goede 	if (pdata->dev_desc->resume_from_noirq)
113548402ceeSHans de Goede 		return 0;
113648402ceeSHans de Goede 
113776c70cb5SRafael J. Wysocki 	if (dev_pm_skip_resume(dev))
11386e176bf8SRafael J. Wysocki 		return 0;
11396e176bf8SRafael J. Wysocki 
114048402ceeSHans de Goede 	return acpi_lpss_do_resume_early(dev);
114148402ceeSHans de Goede }
114248402ceeSHans de Goede 
acpi_lpss_resume_noirq(struct device * dev)114348402ceeSHans de Goede static int acpi_lpss_resume_noirq(struct device *dev)
114448402ceeSHans de Goede {
114548402ceeSHans de Goede 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
114648402ceeSHans de Goede 	int ret;
114748402ceeSHans de Goede 
11483cd7957eSRafael J. Wysocki 	/* Follow acpi_subsys_resume_noirq(). */
114976c70cb5SRafael J. Wysocki 	if (dev_pm_skip_resume(dev))
11503cd7957eSRafael J. Wysocki 		return 0;
11513cd7957eSRafael J. Wysocki 
11523cd7957eSRafael J. Wysocki 	ret = pm_generic_resume_noirq(dev);
115348402ceeSHans de Goede 	if (ret)
115448402ceeSHans de Goede 		return ret;
115548402ceeSHans de Goede 
11563cd7957eSRafael J. Wysocki 	if (!pdata->dev_desc->resume_from_noirq)
11573cd7957eSRafael J. Wysocki 		return 0;
115848402ceeSHans de Goede 
11593cd7957eSRafael J. Wysocki 	/*
11603cd7957eSRafael J. Wysocki 	 * The driver's ->resume_early callback will be invoked by
11613cd7957eSRafael J. Wysocki 	 * acpi_lpss_do_resume_early(), with the assumption that the driver
11623cd7957eSRafael J. Wysocki 	 * really wanted to run that code in ->resume_noirq, but it could not
11633cd7957eSRafael J. Wysocki 	 * run before acpi_dev_resume() and the driver expected the latter to be
11643cd7957eSRafael J. Wysocki 	 * called in the "early" phase.
11653cd7957eSRafael J. Wysocki 	 */
11663cd7957eSRafael J. Wysocki 	return acpi_lpss_do_resume_early(dev);
116748402ceeSHans de Goede }
116848402ceeSHans de Goede 
acpi_lpss_do_restore_early(struct device * dev)11693cd7957eSRafael J. Wysocki static int acpi_lpss_do_restore_early(struct device *dev)
11703cd7957eSRafael J. Wysocki {
11713cd7957eSRafael J. Wysocki 	int ret = acpi_lpss_resume(dev);
11723cd7957eSRafael J. Wysocki 
11733cd7957eSRafael J. Wysocki 	return ret ? ret : pm_generic_restore_early(dev);
11743cd7957eSRafael J. Wysocki }
11753cd7957eSRafael J. Wysocki 
acpi_lpss_restore_early(struct device * dev)11763cd7957eSRafael J. Wysocki static int acpi_lpss_restore_early(struct device *dev)
11773cd7957eSRafael J. Wysocki {
11783cd7957eSRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
11793cd7957eSRafael J. Wysocki 
11803cd7957eSRafael J. Wysocki 	if (pdata->dev_desc->resume_from_noirq)
11813cd7957eSRafael J. Wysocki 		return 0;
11823cd7957eSRafael J. Wysocki 
11833cd7957eSRafael J. Wysocki 	return acpi_lpss_do_restore_early(dev);
11843cd7957eSRafael J. Wysocki }
11853cd7957eSRafael J. Wysocki 
acpi_lpss_restore_noirq(struct device * dev)11863cd7957eSRafael J. Wysocki static int acpi_lpss_restore_noirq(struct device *dev)
11873cd7957eSRafael J. Wysocki {
11883cd7957eSRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
11893cd7957eSRafael J. Wysocki 	int ret;
11903cd7957eSRafael J. Wysocki 
11913cd7957eSRafael J. Wysocki 	ret = pm_generic_restore_noirq(dev);
11923cd7957eSRafael J. Wysocki 	if (ret)
11933cd7957eSRafael J. Wysocki 		return ret;
11943cd7957eSRafael J. Wysocki 
11953cd7957eSRafael J. Wysocki 	if (!pdata->dev_desc->resume_from_noirq)
11963cd7957eSRafael J. Wysocki 		return 0;
11973cd7957eSRafael J. Wysocki 
11983cd7957eSRafael J. Wysocki 	/* This is analogous to what happens in acpi_lpss_resume_noirq(). */
11993cd7957eSRafael J. Wysocki 	return acpi_lpss_do_restore_early(dev);
12003cd7957eSRafael J. Wysocki }
1201c95b7595SRafael J. Wysocki 
acpi_lpss_do_poweroff_late(struct device * dev)1202c95b7595SRafael J. Wysocki static int acpi_lpss_do_poweroff_late(struct device *dev)
1203c95b7595SRafael J. Wysocki {
1204c95b7595SRafael J. Wysocki 	int ret = pm_generic_poweroff_late(dev);
1205c95b7595SRafael J. Wysocki 
1206c95b7595SRafael J. Wysocki 	return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
1207c95b7595SRafael J. Wysocki }
1208c95b7595SRafael J. Wysocki 
acpi_lpss_poweroff_late(struct device * dev)1209c95b7595SRafael J. Wysocki static int acpi_lpss_poweroff_late(struct device *dev)
1210c95b7595SRafael J. Wysocki {
1211c95b7595SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1212c95b7595SRafael J. Wysocki 
1213fa2bfeadSRafael J. Wysocki 	if (dev_pm_skip_suspend(dev))
1214c95b7595SRafael J. Wysocki 		return 0;
1215c95b7595SRafael J. Wysocki 
1216c95b7595SRafael J. Wysocki 	if (pdata->dev_desc->resume_from_noirq)
1217c95b7595SRafael J. Wysocki 		return 0;
1218c95b7595SRafael J. Wysocki 
1219c95b7595SRafael J. Wysocki 	return acpi_lpss_do_poweroff_late(dev);
1220c95b7595SRafael J. Wysocki }
1221c95b7595SRafael J. Wysocki 
acpi_lpss_poweroff_noirq(struct device * dev)1222c95b7595SRafael J. Wysocki static int acpi_lpss_poweroff_noirq(struct device *dev)
1223c95b7595SRafael J. Wysocki {
1224c95b7595SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
1225c95b7595SRafael J. Wysocki 
1226fa2bfeadSRafael J. Wysocki 	if (dev_pm_skip_suspend(dev))
1227c95b7595SRafael J. Wysocki 		return 0;
1228c95b7595SRafael J. Wysocki 
1229c95b7595SRafael J. Wysocki 	if (pdata->dev_desc->resume_from_noirq) {
1230c95b7595SRafael J. Wysocki 		/* This is analogous to the acpi_lpss_suspend_noirq() case. */
1231c95b7595SRafael J. Wysocki 		int ret = acpi_lpss_do_poweroff_late(dev);
1232bb415ed5SXiaofei Tan 
1233c95b7595SRafael J. Wysocki 		if (ret)
1234a192aa92SRafael J. Wysocki 			return ret;
1235a192aa92SRafael J. Wysocki 	}
1236a192aa92SRafael J. Wysocki 
1237c95b7595SRafael J. Wysocki 	return pm_generic_poweroff_noirq(dev);
1238c95b7595SRafael J. Wysocki }
1239a192aa92SRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
1240a192aa92SRafael J. Wysocki 
acpi_lpss_runtime_suspend(struct device * dev)1241a192aa92SRafael J. Wysocki static int acpi_lpss_runtime_suspend(struct device *dev)
1242a192aa92SRafael J. Wysocki {
1243a192aa92SRafael J. Wysocki 	int ret = pm_generic_runtime_suspend(dev);
1244a192aa92SRafael J. Wysocki 
1245a192aa92SRafael J. Wysocki 	return ret ? ret : acpi_lpss_suspend(dev, true);
1246a192aa92SRafael J. Wysocki }
1247a192aa92SRafael J. Wysocki 
acpi_lpss_runtime_resume(struct device * dev)1248a192aa92SRafael J. Wysocki static int acpi_lpss_runtime_resume(struct device *dev)
1249a192aa92SRafael J. Wysocki {
125012864ff8SRafael J. Wysocki 	int ret = acpi_lpss_resume(dev);
1251a192aa92SRafael J. Wysocki 
1252a192aa92SRafael J. Wysocki 	return ret ? ret : pm_generic_runtime_resume(dev);
1253c78b0830SHeikki Krogerus }
1254c78b0830SHeikki Krogerus #endif /* CONFIG_PM */
1255c78b0830SHeikki Krogerus 
1256c78b0830SHeikki Krogerus static struct dev_pm_domain acpi_lpss_pm_domain = {
1257c3a49cf3SAndy Shevchenko #ifdef CONFIG_PM
1258c3a49cf3SAndy Shevchenko 	.activate = acpi_lpss_activate,
1259c3a49cf3SAndy Shevchenko 	.dismiss = acpi_lpss_dismiss,
1260c3a49cf3SAndy Shevchenko #endif
1261c78b0830SHeikki Krogerus 	.ops = {
12625de21bb9SRafael J. Wysocki #ifdef CONFIG_PM
1263c78b0830SHeikki Krogerus #ifdef CONFIG_PM_SLEEP
1264c78b0830SHeikki Krogerus 		.prepare = acpi_subsys_prepare,
1265e4da817dSUlf Hansson 		.complete = acpi_subsys_complete,
1266c78b0830SHeikki Krogerus 		.suspend = acpi_subsys_suspend,
1267f4168b61SFu Zhonghui 		.suspend_late = acpi_lpss_suspend_late,
126848402ceeSHans de Goede 		.suspend_noirq = acpi_lpss_suspend_noirq,
126948402ceeSHans de Goede 		.resume_noirq = acpi_lpss_resume_noirq,
1270f4168b61SFu Zhonghui 		.resume_early = acpi_lpss_resume_early,
1271c78b0830SHeikki Krogerus 		.freeze = acpi_subsys_freeze,
1272c95b7595SRafael J. Wysocki 		.poweroff = acpi_subsys_poweroff,
1273c95b7595SRafael J. Wysocki 		.poweroff_late = acpi_lpss_poweroff_late,
1274c95b7595SRafael J. Wysocki 		.poweroff_noirq = acpi_lpss_poweroff_noirq,
12753cd7957eSRafael J. Wysocki 		.restore_noirq = acpi_lpss_restore_noirq,
12763cd7957eSRafael J. Wysocki 		.restore_early = acpi_lpss_restore_early,
1277c78b0830SHeikki Krogerus #endif
1278c78b0830SHeikki Krogerus 		.runtime_suspend = acpi_lpss_runtime_suspend,
1279c78b0830SHeikki Krogerus 		.runtime_resume = acpi_lpss_runtime_resume,
1280c78b0830SHeikki Krogerus #endif
1281c78b0830SHeikki Krogerus 	},
1282c78b0830SHeikki Krogerus };
1283c78b0830SHeikki Krogerus 
acpi_lpss_platform_notify(struct notifier_block * nb,unsigned long action,void * data)12842e0f8822SRafael J. Wysocki static int acpi_lpss_platform_notify(struct notifier_block *nb,
12852e0f8822SRafael J. Wysocki 				     unsigned long action, void *data)
12862e0f8822SRafael J. Wysocki {
12872e0f8822SRafael J. Wysocki 	struct platform_device *pdev = to_platform_device(data);
12882e0f8822SRafael J. Wysocki 	struct lpss_private_data *pdata;
12892e0f8822SRafael J. Wysocki 	struct acpi_device *adev;
12902e0f8822SRafael J. Wysocki 	const struct acpi_device_id *id;
12912e0f8822SRafael J. Wysocki 
12922e0f8822SRafael J. Wysocki 	id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
12932e0f8822SRafael J. Wysocki 	if (!id || !id->driver_data)
12942e0f8822SRafael J. Wysocki 		return 0;
12952e0f8822SRafael J. Wysocki 
129650861d43SRafael J. Wysocki 	adev = ACPI_COMPANION(&pdev->dev);
129750861d43SRafael J. Wysocki 	if (!adev)
12982e0f8822SRafael J. Wysocki 		return 0;
12992e0f8822SRafael J. Wysocki 
13002e0f8822SRafael J. Wysocki 	pdata = acpi_driver_data(adev);
1301cb39dcddSAndy Shevchenko 	if (!pdata)
13022e0f8822SRafael J. Wysocki 		return 0;
13032e0f8822SRafael J. Wysocki 
1304cb39dcddSAndy Shevchenko 	if (pdata->mmio_base &&
1305cb39dcddSAndy Shevchenko 	    pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
13062e0f8822SRafael J. Wysocki 		dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n");
13072e0f8822SRafael J. Wysocki 		return 0;
13082e0f8822SRafael J. Wysocki 	}
13092e0f8822SRafael J. Wysocki 
1310c78b0830SHeikki Krogerus 	switch (action) {
1311de16d552SAndy Shevchenko 	case BUS_NOTIFY_BIND_DRIVER:
1312989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
1313b5f88dd1SAndy Shevchenko 		break;
1314de16d552SAndy Shevchenko 	case BUS_NOTIFY_DRIVER_NOT_BOUND:
1315b5f88dd1SAndy Shevchenko 	case BUS_NOTIFY_UNBOUND_DRIVER:
13165be6ada3SAndy Shevchenko 		dev_pm_domain_set(&pdev->dev, NULL);
1317b5f88dd1SAndy Shevchenko 		break;
1318b5f88dd1SAndy Shevchenko 	case BUS_NOTIFY_ADD_DEVICE:
1319989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain);
1320ff8c1af5SHeikki Krogerus 		if (pdata->dev_desc->flags & LPSS_LTR)
1321c78b0830SHeikki Krogerus 			return sysfs_create_group(&pdev->dev.kobj,
1322c78b0830SHeikki Krogerus 						  &lpss_attr_group);
132301ac170bSAndy Shevchenko 		break;
1324c78b0830SHeikki Krogerus 	case BUS_NOTIFY_DEL_DEVICE:
1325ff8c1af5SHeikki Krogerus 		if (pdata->dev_desc->flags & LPSS_LTR)
13262e0f8822SRafael J. Wysocki 			sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
1327989561deSTomeu Vizoso 		dev_pm_domain_set(&pdev->dev, NULL);
132801ac170bSAndy Shevchenko 		break;
1329c78b0830SHeikki Krogerus 	default:
1330c78b0830SHeikki Krogerus 		break;
1331c78b0830SHeikki Krogerus 	}
13322e0f8822SRafael J. Wysocki 
1333c78b0830SHeikki Krogerus 	return 0;
13342e0f8822SRafael J. Wysocki }
13352e0f8822SRafael J. Wysocki 
13362e0f8822SRafael J. Wysocki static struct notifier_block acpi_lpss_nb = {
13372e0f8822SRafael J. Wysocki 	.notifier_call = acpi_lpss_platform_notify,
13382e0f8822SRafael J. Wysocki };
13392e0f8822SRafael J. Wysocki 
acpi_lpss_bind(struct device * dev)13401a8f8351SRafael J. Wysocki static void acpi_lpss_bind(struct device *dev)
13411a8f8351SRafael J. Wysocki {
13421a8f8351SRafael J. Wysocki 	struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
13431a8f8351SRafael J. Wysocki 
1344ff8c1af5SHeikki Krogerus 	if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
13451a8f8351SRafael J. Wysocki 		return;
13461a8f8351SRafael J. Wysocki 
13471a8f8351SRafael J. Wysocki 	if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
13481a8f8351SRafael J. Wysocki 		dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
13491a8f8351SRafael J. Wysocki 	else
13501a8f8351SRafael J. Wysocki 		dev_err(dev, "MMIO size insufficient to access LTR\n");
13511a8f8351SRafael J. Wysocki }
13521a8f8351SRafael J. Wysocki 
acpi_lpss_unbind(struct device * dev)13531a8f8351SRafael J. Wysocki static void acpi_lpss_unbind(struct device *dev)
13541a8f8351SRafael J. Wysocki {
13551a8f8351SRafael J. Wysocki 	dev->power.set_latency_tolerance = NULL;
13561a8f8351SRafael J. Wysocki }
13571a8f8351SRafael J. Wysocki 
1358f58b082aSRafael J. Wysocki static struct acpi_scan_handler lpss_handler = {
1359f58b082aSRafael J. Wysocki 	.ids = acpi_lpss_device_ids,
1360f58b082aSRafael J. Wysocki 	.attach = acpi_lpss_create_device,
13611a8f8351SRafael J. Wysocki 	.bind = acpi_lpss_bind,
13621a8f8351SRafael J. Wysocki 	.unbind = acpi_lpss_unbind,
1363f58b082aSRafael J. Wysocki };
1364f58b082aSRafael J. Wysocki 
acpi_lpss_init(void)1365f58b082aSRafael J. Wysocki void __init acpi_lpss_init(void)
1366f58b082aSRafael J. Wysocki {
1367eebb3e8dSAndy Shevchenko 	const struct x86_cpu_id *id;
1368eebb3e8dSAndy Shevchenko 	int ret;
1369eebb3e8dSAndy Shevchenko 
1370cf0a9565SAndy Shevchenko 	ret = lpss_atom_clk_init();
1371eebb3e8dSAndy Shevchenko 	if (ret)
1372eebb3e8dSAndy Shevchenko 		return;
1373eebb3e8dSAndy Shevchenko 
1374eebb3e8dSAndy Shevchenko 	id = x86_match_cpu(lpss_cpu_ids);
1375eebb3e8dSAndy Shevchenko 	if (id)
1376eebb3e8dSAndy Shevchenko 		lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON;
1377eebb3e8dSAndy Shevchenko 
13782e0f8822SRafael J. Wysocki 	bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
1379f58b082aSRafael J. Wysocki 	acpi_scan_add_handler(&lpss_handler);
1380f58b082aSRafael J. Wysocki }
1381d6ddaaacSRafael J. Wysocki 
1382d6ddaaacSRafael J. Wysocki #else
1383d6ddaaacSRafael J. Wysocki 
1384d6ddaaacSRafael J. Wysocki static struct acpi_scan_handler lpss_handler = {
1385d6ddaaacSRafael J. Wysocki 	.ids = acpi_lpss_device_ids,
1386d6ddaaacSRafael J. Wysocki };
1387d6ddaaacSRafael J. Wysocki 
acpi_lpss_init(void)1388d6ddaaacSRafael J. Wysocki void __init acpi_lpss_init(void)
1389d6ddaaacSRafael J. Wysocki {
1390d6ddaaacSRafael J. Wysocki 	acpi_scan_add_handler(&lpss_handler);
1391d6ddaaacSRafael J. Wysocki }
1392d6ddaaacSRafael J. Wysocki 
1393d6ddaaacSRafael J. Wysocki #endif /* CONFIG_X86_INTEL_LPSS */
1394