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, <r_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, <r_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