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