18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI support for Intel Lynxpoint LPSS. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013, Intel Corporation 68c2ecf20Sopenharmony_ci * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 78c2ecf20Sopenharmony_ci * Rafael J. Wysocki <rafael.j.wysocki@intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 138c2ecf20Sopenharmony_ci#include <linux/dmi.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_data/x86/clk-lpss.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_data/x86/pmc_atom.h> 218c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/pwm.h> 248c2ecf20Sopenharmony_ci#include <linux/suspend.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "internal.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_INTEL_LPSS 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 328c2ecf20Sopenharmony_ci#include <asm/intel-family.h> 338c2ecf20Sopenharmony_ci#include <asm/iosf_mbi.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define LPSS_ADDR(desc) ((unsigned long)&desc) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define LPSS_CLK_SIZE 0x04 388c2ecf20Sopenharmony_ci#define LPSS_LTR_SIZE 0x18 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Offsets relative to LPSS_PRIVATE_OFFSET */ 418c2ecf20Sopenharmony_ci#define LPSS_CLK_DIVIDER_DEF_MASK (BIT(1) | BIT(16)) 428c2ecf20Sopenharmony_ci#define LPSS_RESETS 0x04 438c2ecf20Sopenharmony_ci#define LPSS_RESETS_RESET_FUNC BIT(0) 448c2ecf20Sopenharmony_ci#define LPSS_RESETS_RESET_APB BIT(1) 458c2ecf20Sopenharmony_ci#define LPSS_GENERAL 0x08 468c2ecf20Sopenharmony_ci#define LPSS_GENERAL_LTR_MODE_SW BIT(2) 478c2ecf20Sopenharmony_ci#define LPSS_GENERAL_UART_RTS_OVRD BIT(3) 488c2ecf20Sopenharmony_ci#define LPSS_SW_LTR 0x10 498c2ecf20Sopenharmony_ci#define LPSS_AUTO_LTR 0x14 508c2ecf20Sopenharmony_ci#define LPSS_LTR_SNOOP_REQ BIT(15) 518c2ecf20Sopenharmony_ci#define LPSS_LTR_SNOOP_MASK 0x0000FFFF 528c2ecf20Sopenharmony_ci#define LPSS_LTR_SNOOP_LAT_1US 0x800 538c2ecf20Sopenharmony_ci#define LPSS_LTR_SNOOP_LAT_32US 0xC00 548c2ecf20Sopenharmony_ci#define LPSS_LTR_SNOOP_LAT_SHIFT 5 558c2ecf20Sopenharmony_ci#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000 568c2ecf20Sopenharmony_ci#define LPSS_LTR_MAX_VAL 0x3FF 578c2ecf20Sopenharmony_ci#define LPSS_TX_INT 0x20 588c2ecf20Sopenharmony_ci#define LPSS_TX_INT_MASK BIT(1) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define LPSS_PRV_REG_COUNT 9 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* LPSS Flags */ 638c2ecf20Sopenharmony_ci#define LPSS_CLK BIT(0) 648c2ecf20Sopenharmony_ci#define LPSS_CLK_GATE BIT(1) 658c2ecf20Sopenharmony_ci#define LPSS_CLK_DIVIDER BIT(2) 668c2ecf20Sopenharmony_ci#define LPSS_LTR BIT(3) 678c2ecf20Sopenharmony_ci#define LPSS_SAVE_CTX BIT(4) 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * For some devices the DSDT AML code for another device turns off the device 708c2ecf20Sopenharmony_ci * before our suspend handler runs, causing us to read/save all 1-s (0xffffffff) 718c2ecf20Sopenharmony_ci * as ctx register values. 728c2ecf20Sopenharmony_ci * Luckily these devices always use the same ctx register values, so we can 738c2ecf20Sopenharmony_ci * work around this by saving the ctx registers once on activation. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#define LPSS_SAVE_CTX_ONCE BIT(5) 768c2ecf20Sopenharmony_ci#define LPSS_NO_D3_DELAY BIT(6) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct lpss_private_data; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct lpss_device_desc { 818c2ecf20Sopenharmony_ci unsigned int flags; 828c2ecf20Sopenharmony_ci const char *clk_con_id; 838c2ecf20Sopenharmony_ci unsigned int prv_offset; 848c2ecf20Sopenharmony_ci size_t prv_size_override; 858c2ecf20Sopenharmony_ci struct property_entry *properties; 868c2ecf20Sopenharmony_ci void (*setup)(struct lpss_private_data *pdata); 878c2ecf20Sopenharmony_ci bool resume_from_noirq; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const struct lpss_device_desc lpss_dma_desc = { 918c2ecf20Sopenharmony_ci .flags = LPSS_CLK, 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistruct lpss_private_data { 958c2ecf20Sopenharmony_ci struct acpi_device *adev; 968c2ecf20Sopenharmony_ci void __iomem *mmio_base; 978c2ecf20Sopenharmony_ci resource_size_t mmio_size; 988c2ecf20Sopenharmony_ci unsigned int fixed_clk_rate; 998c2ecf20Sopenharmony_ci struct clk *clk; 1008c2ecf20Sopenharmony_ci const struct lpss_device_desc *dev_desc; 1018c2ecf20Sopenharmony_ci u32 prv_reg_ctx[LPSS_PRV_REG_COUNT]; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* Devices which need to be in D3 before lpss_iosf_enter_d3_state() proceeds */ 1058c2ecf20Sopenharmony_cistatic u32 pmc_atom_d3_mask = 0xfe000ffe; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* LPSS run time quirks */ 1088c2ecf20Sopenharmony_cistatic unsigned int lpss_quirks; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device. 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover 1148c2ecf20Sopenharmony_ci * it can be powered off automatically whenever the last LPSS device goes down. 1158c2ecf20Sopenharmony_ci * In case of no power any access to the DMA controller will hang the system. 1168c2ecf20Sopenharmony_ci * The behaviour is reproduced on some HP laptops based on Intel BayTrail as 1178c2ecf20Sopenharmony_ci * well as on ASuS T100TA transformer. 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * This quirk overrides power state of entire LPSS island to keep DMA powered 1208c2ecf20Sopenharmony_ci * on whenever we have at least one other device in use. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci#define LPSS_QUIRK_ALWAYS_POWER_ON BIT(0) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* UART Component Parameter Register */ 1258c2ecf20Sopenharmony_ci#define LPSS_UART_CPR 0xF4 1268c2ecf20Sopenharmony_ci#define LPSS_UART_CPR_AFCE BIT(4) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void lpss_uart_setup(struct lpss_private_data *pdata) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned int offset; 1318c2ecf20Sopenharmony_ci u32 val; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci offset = pdata->dev_desc->prv_offset + LPSS_TX_INT; 1348c2ecf20Sopenharmony_ci val = readl(pdata->mmio_base + offset); 1358c2ecf20Sopenharmony_ci writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci val = readl(pdata->mmio_base + LPSS_UART_CPR); 1388c2ecf20Sopenharmony_ci if (!(val & LPSS_UART_CPR_AFCE)) { 1398c2ecf20Sopenharmony_ci offset = pdata->dev_desc->prv_offset + LPSS_GENERAL; 1408c2ecf20Sopenharmony_ci val = readl(pdata->mmio_base + offset); 1418c2ecf20Sopenharmony_ci val |= LPSS_GENERAL_UART_RTS_OVRD; 1428c2ecf20Sopenharmony_ci writel(val, pdata->mmio_base + offset); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void lpss_deassert_reset(struct lpss_private_data *pdata) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci unsigned int offset; 1498c2ecf20Sopenharmony_ci u32 val; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci offset = pdata->dev_desc->prv_offset + LPSS_RESETS; 1528c2ecf20Sopenharmony_ci val = readl(pdata->mmio_base + offset); 1538c2ecf20Sopenharmony_ci val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC; 1548c2ecf20Sopenharmony_ci writel(val, pdata->mmio_base + offset); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * BYT PWM used for backlight control by the i915 driver on systems without 1598c2ecf20Sopenharmony_ci * the Crystal Cove PMIC. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic struct pwm_lookup byt_pwm_lookup[] = { 1628c2ecf20Sopenharmony_ci PWM_LOOKUP_WITH_MODULE("80860F09:00", 0, "0000:00:02.0", 1638c2ecf20Sopenharmony_ci "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL, 1648c2ecf20Sopenharmony_ci "pwm-lpss-platform"), 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void byt_pwm_setup(struct lpss_private_data *pdata) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci struct acpi_device *adev = pdata->adev; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Only call pwm_add_table for the first PWM controller */ 1728c2ecf20Sopenharmony_ci if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1")) 1738c2ecf20Sopenharmony_ci return; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#define LPSS_I2C_ENABLE 0x6c 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void byt_i2c_setup(struct lpss_private_data *pdata) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci const char *uid_str = acpi_device_uid(pdata->adev); 1838c2ecf20Sopenharmony_ci acpi_handle handle = pdata->adev->handle; 1848c2ecf20Sopenharmony_ci unsigned long long shared_host = 0; 1858c2ecf20Sopenharmony_ci acpi_status status; 1868c2ecf20Sopenharmony_ci long uid = 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Expected to always be true, but better safe then sorry */ 1898c2ecf20Sopenharmony_ci if (uid_str) 1908c2ecf20Sopenharmony_ci uid = simple_strtol(uid_str, NULL, 10); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Detect I2C bus shared with PUNIT and ignore its d3 status */ 1938c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); 1948c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status) && shared_host && uid) 1958c2ecf20Sopenharmony_ci pmc_atom_d3_mask &= ~(BIT_LPSS2_F1_I2C1 << (uid - 1)); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci lpss_deassert_reset(pdata); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset)) 2008c2ecf20Sopenharmony_ci pdata->fixed_clk_rate = 133000000; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* BSW PWM used for backlight control by the i915 driver */ 2068c2ecf20Sopenharmony_cistatic struct pwm_lookup bsw_pwm_lookup[] = { 2078c2ecf20Sopenharmony_ci PWM_LOOKUP_WITH_MODULE("80862288:00", 0, "0000:00:02.0", 2088c2ecf20Sopenharmony_ci "pwm_soc_backlight", 0, PWM_POLARITY_NORMAL, 2098c2ecf20Sopenharmony_ci "pwm-lpss-platform"), 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void bsw_pwm_setup(struct lpss_private_data *pdata) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct acpi_device *adev = pdata->adev; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Only call pwm_add_table for the first PWM controller */ 2178c2ecf20Sopenharmony_ci if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1")) 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic const struct lpss_device_desc lpt_dev_desc = { 2248c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR 2258c2ecf20Sopenharmony_ci | LPSS_SAVE_CTX, 2268c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic const struct lpss_device_desc lpt_i2c_dev_desc = { 2308c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR | LPSS_SAVE_CTX, 2318c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic struct property_entry uart_properties[] = { 2358c2ecf20Sopenharmony_ci PROPERTY_ENTRY_U32("reg-io-width", 4), 2368c2ecf20Sopenharmony_ci PROPERTY_ENTRY_U32("reg-shift", 2), 2378c2ecf20Sopenharmony_ci PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"), 2388c2ecf20Sopenharmony_ci { }, 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct lpss_device_desc lpt_uart_dev_desc = { 2428c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR 2438c2ecf20Sopenharmony_ci | LPSS_SAVE_CTX, 2448c2ecf20Sopenharmony_ci .clk_con_id = "baudclk", 2458c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2468c2ecf20Sopenharmony_ci .setup = lpss_uart_setup, 2478c2ecf20Sopenharmony_ci .properties = uart_properties, 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct lpss_device_desc lpt_sdio_dev_desc = { 2518c2ecf20Sopenharmony_ci .flags = LPSS_LTR, 2528c2ecf20Sopenharmony_ci .prv_offset = 0x1000, 2538c2ecf20Sopenharmony_ci .prv_size_override = 0x1018, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic const struct lpss_device_desc byt_pwm_dev_desc = { 2578c2ecf20Sopenharmony_ci .flags = LPSS_SAVE_CTX, 2588c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2598c2ecf20Sopenharmony_ci .setup = byt_pwm_setup, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic const struct lpss_device_desc bsw_pwm_dev_desc = { 2638c2ecf20Sopenharmony_ci .flags = LPSS_SAVE_CTX_ONCE | LPSS_NO_D3_DELAY, 2648c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2658c2ecf20Sopenharmony_ci .setup = bsw_pwm_setup, 2668c2ecf20Sopenharmony_ci .resume_from_noirq = true, 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const struct lpss_device_desc byt_uart_dev_desc = { 2708c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, 2718c2ecf20Sopenharmony_ci .clk_con_id = "baudclk", 2728c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2738c2ecf20Sopenharmony_ci .setup = lpss_uart_setup, 2748c2ecf20Sopenharmony_ci .properties = uart_properties, 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct lpss_device_desc bsw_uart_dev_desc = { 2788c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX 2798c2ecf20Sopenharmony_ci | LPSS_NO_D3_DELAY, 2808c2ecf20Sopenharmony_ci .clk_con_id = "baudclk", 2818c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2828c2ecf20Sopenharmony_ci .setup = lpss_uart_setup, 2838c2ecf20Sopenharmony_ci .properties = uart_properties, 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const struct lpss_device_desc byt_spi_dev_desc = { 2878c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, 2888c2ecf20Sopenharmony_ci .prv_offset = 0x400, 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic const struct lpss_device_desc byt_sdio_dev_desc = { 2928c2ecf20Sopenharmony_ci .flags = LPSS_CLK, 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic const struct lpss_device_desc byt_i2c_dev_desc = { 2968c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_SAVE_CTX, 2978c2ecf20Sopenharmony_ci .prv_offset = 0x800, 2988c2ecf20Sopenharmony_ci .setup = byt_i2c_setup, 2998c2ecf20Sopenharmony_ci .resume_from_noirq = true, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic const struct lpss_device_desc bsw_i2c_dev_desc = { 3038c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, 3048c2ecf20Sopenharmony_ci .prv_offset = 0x800, 3058c2ecf20Sopenharmony_ci .setup = byt_i2c_setup, 3068c2ecf20Sopenharmony_ci .resume_from_noirq = true, 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct lpss_device_desc bsw_spi_dev_desc = { 3108c2ecf20Sopenharmony_ci .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX 3118c2ecf20Sopenharmony_ci | LPSS_NO_D3_DELAY, 3128c2ecf20Sopenharmony_ci .prv_offset = 0x400, 3138c2ecf20Sopenharmony_ci .setup = lpss_deassert_reset, 3148c2ecf20Sopenharmony_ci}; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic const struct x86_cpu_id lpss_cpu_ids[] = { 3178c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, NULL), 3188c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL), 3198c2ecf20Sopenharmony_ci {} 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci#else 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci#define LPSS_ADDR(desc) (0UL) 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_INTEL_LPSS */ 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic const struct acpi_device_id acpi_lpss_device_ids[] = { 3298c2ecf20Sopenharmony_ci /* Generic LPSS devices */ 3308c2ecf20Sopenharmony_ci { "INTL9C60", LPSS_ADDR(lpss_dma_desc) }, 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Lynxpoint LPSS devices */ 3338c2ecf20Sopenharmony_ci { "INT33C0", LPSS_ADDR(lpt_dev_desc) }, 3348c2ecf20Sopenharmony_ci { "INT33C1", LPSS_ADDR(lpt_dev_desc) }, 3358c2ecf20Sopenharmony_ci { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) }, 3368c2ecf20Sopenharmony_ci { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) }, 3378c2ecf20Sopenharmony_ci { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) }, 3388c2ecf20Sopenharmony_ci { "INT33C5", LPSS_ADDR(lpt_uart_dev_desc) }, 3398c2ecf20Sopenharmony_ci { "INT33C6", LPSS_ADDR(lpt_sdio_dev_desc) }, 3408c2ecf20Sopenharmony_ci { "INT33C7", }, 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* BayTrail LPSS devices */ 3438c2ecf20Sopenharmony_ci { "80860F09", LPSS_ADDR(byt_pwm_dev_desc) }, 3448c2ecf20Sopenharmony_ci { "80860F0A", LPSS_ADDR(byt_uart_dev_desc) }, 3458c2ecf20Sopenharmony_ci { "80860F0E", LPSS_ADDR(byt_spi_dev_desc) }, 3468c2ecf20Sopenharmony_ci { "80860F14", LPSS_ADDR(byt_sdio_dev_desc) }, 3478c2ecf20Sopenharmony_ci { "80860F41", LPSS_ADDR(byt_i2c_dev_desc) }, 3488c2ecf20Sopenharmony_ci { "INT33B2", }, 3498c2ecf20Sopenharmony_ci { "INT33FC", }, 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Braswell LPSS devices */ 3528c2ecf20Sopenharmony_ci { "80862286", LPSS_ADDR(lpss_dma_desc) }, 3538c2ecf20Sopenharmony_ci { "80862288", LPSS_ADDR(bsw_pwm_dev_desc) }, 3548c2ecf20Sopenharmony_ci { "8086228A", LPSS_ADDR(bsw_uart_dev_desc) }, 3558c2ecf20Sopenharmony_ci { "8086228E", LPSS_ADDR(bsw_spi_dev_desc) }, 3568c2ecf20Sopenharmony_ci { "808622C0", LPSS_ADDR(lpss_dma_desc) }, 3578c2ecf20Sopenharmony_ci { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) }, 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* Broadwell LPSS devices */ 3608c2ecf20Sopenharmony_ci { "INT3430", LPSS_ADDR(lpt_dev_desc) }, 3618c2ecf20Sopenharmony_ci { "INT3431", LPSS_ADDR(lpt_dev_desc) }, 3628c2ecf20Sopenharmony_ci { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) }, 3638c2ecf20Sopenharmony_ci { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) }, 3648c2ecf20Sopenharmony_ci { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) }, 3658c2ecf20Sopenharmony_ci { "INT3435", LPSS_ADDR(lpt_uart_dev_desc) }, 3668c2ecf20Sopenharmony_ci { "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) }, 3678c2ecf20Sopenharmony_ci { "INT3437", }, 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Wildcat Point LPSS devices */ 3708c2ecf20Sopenharmony_ci { "INT3438", LPSS_ADDR(lpt_dev_desc) }, 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci { } 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_INTEL_LPSS 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int is_memory(struct acpi_resource *res, void *not_used) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct resource r; 3808c2ecf20Sopenharmony_ci return !acpi_dev_resource_memory(res, &r); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* LPSS main clock device. */ 3848c2ecf20Sopenharmony_cistatic struct platform_device *lpss_clk_dev; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic inline void lpt_register_clock_device(void) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int register_device_clock(struct acpi_device *adev, 3928c2ecf20Sopenharmony_ci struct lpss_private_data *pdata) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci const struct lpss_device_desc *dev_desc = pdata->dev_desc; 3958c2ecf20Sopenharmony_ci const char *devname = dev_name(&adev->dev); 3968c2ecf20Sopenharmony_ci struct clk *clk; 3978c2ecf20Sopenharmony_ci struct lpss_clk_data *clk_data; 3988c2ecf20Sopenharmony_ci const char *parent, *clk_name; 3998c2ecf20Sopenharmony_ci void __iomem *prv_base; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!lpss_clk_dev) 4028c2ecf20Sopenharmony_ci lpt_register_clock_device(); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (IS_ERR(lpss_clk_dev)) 4058c2ecf20Sopenharmony_ci return PTR_ERR(lpss_clk_dev); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci clk_data = platform_get_drvdata(lpss_clk_dev); 4088c2ecf20Sopenharmony_ci if (!clk_data) 4098c2ecf20Sopenharmony_ci return -ENODEV; 4108c2ecf20Sopenharmony_ci clk = clk_data->clk; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!pdata->mmio_base 4138c2ecf20Sopenharmony_ci || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) 4148c2ecf20Sopenharmony_ci return -ENODATA; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci parent = clk_data->name; 4178c2ecf20Sopenharmony_ci prv_base = pdata->mmio_base + dev_desc->prv_offset; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (pdata->fixed_clk_rate) { 4208c2ecf20Sopenharmony_ci clk = clk_register_fixed_rate(NULL, devname, parent, 0, 4218c2ecf20Sopenharmony_ci pdata->fixed_clk_rate); 4228c2ecf20Sopenharmony_ci goto out; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (dev_desc->flags & LPSS_CLK_GATE) { 4268c2ecf20Sopenharmony_ci clk = clk_register_gate(NULL, devname, parent, 0, 4278c2ecf20Sopenharmony_ci prv_base, 0, 0, NULL); 4288c2ecf20Sopenharmony_ci parent = devname; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (dev_desc->flags & LPSS_CLK_DIVIDER) { 4328c2ecf20Sopenharmony_ci /* Prevent division by zero */ 4338c2ecf20Sopenharmony_ci if (!readl(prv_base)) 4348c2ecf20Sopenharmony_ci writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci clk_name = kasprintf(GFP_KERNEL, "%s-div", devname); 4378c2ecf20Sopenharmony_ci if (!clk_name) 4388c2ecf20Sopenharmony_ci return -ENOMEM; 4398c2ecf20Sopenharmony_ci clk = clk_register_fractional_divider(NULL, clk_name, parent, 4408c2ecf20Sopenharmony_ci 0, prv_base, 4418c2ecf20Sopenharmony_ci 1, 15, 16, 15, 0, NULL); 4428c2ecf20Sopenharmony_ci parent = clk_name; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci clk_name = kasprintf(GFP_KERNEL, "%s-update", devname); 4458c2ecf20Sopenharmony_ci if (!clk_name) { 4468c2ecf20Sopenharmony_ci kfree(parent); 4478c2ecf20Sopenharmony_ci return -ENOMEM; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci clk = clk_register_gate(NULL, clk_name, parent, 4508c2ecf20Sopenharmony_ci CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, 4518c2ecf20Sopenharmony_ci prv_base, 31, 0, NULL); 4528c2ecf20Sopenharmony_ci kfree(parent); 4538c2ecf20Sopenharmony_ci kfree(clk_name); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ciout: 4568c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 4578c2ecf20Sopenharmony_ci return PTR_ERR(clk); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci pdata->clk = clk; 4608c2ecf20Sopenharmony_ci clk_register_clkdev(clk, dev_desc->clk_con_id, devname); 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistruct lpss_device_links { 4658c2ecf20Sopenharmony_ci const char *supplier_hid; 4668c2ecf20Sopenharmony_ci const char *supplier_uid; 4678c2ecf20Sopenharmony_ci const char *consumer_hid; 4688c2ecf20Sopenharmony_ci const char *consumer_uid; 4698c2ecf20Sopenharmony_ci u32 flags; 4708c2ecf20Sopenharmony_ci const struct dmi_system_id *dep_missing_ids; 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* Please keep this list sorted alphabetically by vendor and model */ 4748c2ecf20Sopenharmony_cistatic const struct dmi_system_id i2c1_dep_missing_dmi_ids[] = { 4758c2ecf20Sopenharmony_ci { 4768c2ecf20Sopenharmony_ci .matches = { 4778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 4788c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), 4798c2ecf20Sopenharmony_ci }, 4808c2ecf20Sopenharmony_ci }, 4818c2ecf20Sopenharmony_ci {} 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci/* 4858c2ecf20Sopenharmony_ci * The _DEP method is used to identify dependencies but instead of creating 4868c2ecf20Sopenharmony_ci * device links for every handle in _DEP, only links in the following list are 4878c2ecf20Sopenharmony_ci * created. That is necessary because, in the general case, _DEP can refer to 4888c2ecf20Sopenharmony_ci * devices that might not have drivers, or that are on different buses, or where 4898c2ecf20Sopenharmony_ci * the supplier is not enumerated until after the consumer is probed. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic const struct lpss_device_links lpss_device_links[] = { 4928c2ecf20Sopenharmony_ci /* CHT External sdcard slot controller depends on PMIC I2C ctrl */ 4938c2ecf20Sopenharmony_ci {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME}, 4948c2ecf20Sopenharmony_ci /* CHT iGPU depends on PMIC I2C controller */ 4958c2ecf20Sopenharmony_ci {"808622C1", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, 4968c2ecf20Sopenharmony_ci /* BYT iGPU depends on the Embedded Controller I2C controller (UID 1) */ 4978c2ecf20Sopenharmony_ci {"80860F41", "1", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME, 4988c2ecf20Sopenharmony_ci i2c1_dep_missing_dmi_ids}, 4998c2ecf20Sopenharmony_ci /* BYT CR iGPU depends on PMIC I2C controller (UID 5 on CR) */ 5008c2ecf20Sopenharmony_ci {"80860F41", "5", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, 5018c2ecf20Sopenharmony_ci /* BYT iGPU depends on PMIC I2C controller (UID 7 on non CR) */ 5028c2ecf20Sopenharmony_ci {"80860F41", "7", "LNXVIDEO", NULL, DL_FLAG_PM_RUNTIME}, 5038c2ecf20Sopenharmony_ci}; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic bool acpi_lpss_is_supplier(struct acpi_device *adev, 5068c2ecf20Sopenharmony_ci const struct lpss_device_links *link) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci return acpi_dev_hid_uid_match(adev, link->supplier_hid, link->supplier_uid); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic bool acpi_lpss_is_consumer(struct acpi_device *adev, 5128c2ecf20Sopenharmony_ci const struct lpss_device_links *link) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci return acpi_dev_hid_uid_match(adev, link->consumer_hid, link->consumer_uid); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistruct hid_uid { 5188c2ecf20Sopenharmony_ci const char *hid; 5198c2ecf20Sopenharmony_ci const char *uid; 5208c2ecf20Sopenharmony_ci}; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int match_hid_uid(struct device *dev, const void *data) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 5258c2ecf20Sopenharmony_ci const struct hid_uid *id = data; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (!adev) 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return acpi_dev_hid_uid_match(adev, id->hid, id->uid); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic struct device *acpi_lpss_find_device(const char *hid, const char *uid) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct device *dev; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci struct hid_uid data = { 5388c2ecf20Sopenharmony_ci .hid = hid, 5398c2ecf20Sopenharmony_ci .uid = uid, 5408c2ecf20Sopenharmony_ci }; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci dev = bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid); 5438c2ecf20Sopenharmony_ci if (dev) 5448c2ecf20Sopenharmony_ci return dev; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct acpi_handle_list dep_devices; 5528c2ecf20Sopenharmony_ci acpi_status status; 5538c2ecf20Sopenharmony_ci int i; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!acpi_has_method(adev->handle, "_DEP")) 5568c2ecf20Sopenharmony_ci return false; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci status = acpi_evaluate_reference(adev->handle, "_DEP", NULL, 5598c2ecf20Sopenharmony_ci &dep_devices); 5608c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5618c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n"); 5628c2ecf20Sopenharmony_ci return false; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci for (i = 0; i < dep_devices.count; i++) { 5668c2ecf20Sopenharmony_ci if (dep_devices.handles[i] == handle) 5678c2ecf20Sopenharmony_ci return true; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return false; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic void acpi_lpss_link_consumer(struct device *dev1, 5748c2ecf20Sopenharmony_ci const struct lpss_device_links *link) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct device *dev2; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid); 5798c2ecf20Sopenharmony_ci if (!dev2) 5808c2ecf20Sopenharmony_ci return; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) 5838c2ecf20Sopenharmony_ci || acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1))) 5848c2ecf20Sopenharmony_ci device_link_add(dev2, dev1, link->flags); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci put_device(dev2); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic void acpi_lpss_link_supplier(struct device *dev1, 5908c2ecf20Sopenharmony_ci const struct lpss_device_links *link) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct device *dev2; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid); 5958c2ecf20Sopenharmony_ci if (!dev2) 5968c2ecf20Sopenharmony_ci return; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids)) 5998c2ecf20Sopenharmony_ci || acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2))) 6008c2ecf20Sopenharmony_ci device_link_add(dev1, dev2, link->flags); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci put_device(dev2); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic void acpi_lpss_create_device_links(struct acpi_device *adev, 6068c2ecf20Sopenharmony_ci struct platform_device *pdev) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci int i; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) { 6118c2ecf20Sopenharmony_ci const struct lpss_device_links *link = &lpss_device_links[i]; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (acpi_lpss_is_supplier(adev, link)) 6148c2ecf20Sopenharmony_ci acpi_lpss_link_consumer(&pdev->dev, link); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (acpi_lpss_is_consumer(adev, link)) 6178c2ecf20Sopenharmony_ci acpi_lpss_link_supplier(&pdev->dev, link); 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int acpi_lpss_create_device(struct acpi_device *adev, 6228c2ecf20Sopenharmony_ci const struct acpi_device_id *id) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci const struct lpss_device_desc *dev_desc; 6258c2ecf20Sopenharmony_ci struct lpss_private_data *pdata; 6268c2ecf20Sopenharmony_ci struct resource_entry *rentry; 6278c2ecf20Sopenharmony_ci struct list_head resource_list; 6288c2ecf20Sopenharmony_ci struct platform_device *pdev; 6298c2ecf20Sopenharmony_ci int ret; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci dev_desc = (const struct lpss_device_desc *)id->driver_data; 6328c2ecf20Sopenharmony_ci if (!dev_desc) { 6338c2ecf20Sopenharmony_ci pdev = acpi_create_platform_device(adev, NULL); 6348c2ecf20Sopenharmony_ci return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); 6378c2ecf20Sopenharmony_ci if (!pdata) 6388c2ecf20Sopenharmony_ci return -ENOMEM; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 6418c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); 6428c2ecf20Sopenharmony_ci if (ret < 0) 6438c2ecf20Sopenharmony_ci goto err_out; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci list_for_each_entry(rentry, &resource_list, node) 6468c2ecf20Sopenharmony_ci if (resource_type(rentry->res) == IORESOURCE_MEM) { 6478c2ecf20Sopenharmony_ci if (dev_desc->prv_size_override) 6488c2ecf20Sopenharmony_ci pdata->mmio_size = dev_desc->prv_size_override; 6498c2ecf20Sopenharmony_ci else 6508c2ecf20Sopenharmony_ci pdata->mmio_size = resource_size(rentry->res); 6518c2ecf20Sopenharmony_ci pdata->mmio_base = ioremap(rentry->res->start, 6528c2ecf20Sopenharmony_ci pdata->mmio_size); 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!pdata->mmio_base) { 6598c2ecf20Sopenharmony_ci /* Avoid acpi_bus_attach() instantiating a pdev for this dev. */ 6608c2ecf20Sopenharmony_ci adev->pnp.type.platform_id = 0; 6618c2ecf20Sopenharmony_ci /* Skip the device, but continue the namespace scan. */ 6628c2ecf20Sopenharmony_ci ret = 0; 6638c2ecf20Sopenharmony_ci goto err_out; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci pdata->adev = adev; 6678c2ecf20Sopenharmony_ci pdata->dev_desc = dev_desc; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (dev_desc->setup) 6708c2ecf20Sopenharmony_ci dev_desc->setup(pdata); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (dev_desc->flags & LPSS_CLK) { 6738c2ecf20Sopenharmony_ci ret = register_device_clock(adev, pdata); 6748c2ecf20Sopenharmony_ci if (ret) { 6758c2ecf20Sopenharmony_ci /* Skip the device, but continue the namespace scan. */ 6768c2ecf20Sopenharmony_ci ret = 0; 6778c2ecf20Sopenharmony_ci goto err_out; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* 6828c2ecf20Sopenharmony_ci * This works around a known issue in ACPI tables where LPSS devices 6838c2ecf20Sopenharmony_ci * have _PS0 and _PS3 without _PSC (and no power resources), so 6848c2ecf20Sopenharmony_ci * acpi_bus_init_power() will assume that the BIOS has put them into D0. 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci acpi_device_fix_up_power(adev); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci adev->driver_data = pdata; 6898c2ecf20Sopenharmony_ci pdev = acpi_create_platform_device(adev, dev_desc->properties); 6908c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(pdev)) { 6918c2ecf20Sopenharmony_ci acpi_lpss_create_device_links(adev, pdev); 6928c2ecf20Sopenharmony_ci return 1; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci ret = PTR_ERR(pdev); 6968c2ecf20Sopenharmony_ci adev->driver_data = NULL; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci err_out: 6998c2ecf20Sopenharmony_ci kfree(pdata); 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic void __lpss_reg_write(u32 val, struct lpss_private_data *pdata, 7098c2ecf20Sopenharmony_ci unsigned int reg) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct acpi_device *adev; 7178c2ecf20Sopenharmony_ci struct lpss_private_data *pdata; 7188c2ecf20Sopenharmony_ci unsigned long flags; 7198c2ecf20Sopenharmony_ci int ret; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev); 7228c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 7238c2ecf20Sopenharmony_ci return ret; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 7268c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev)) { 7278c2ecf20Sopenharmony_ci ret = -EAGAIN; 7288c2ecf20Sopenharmony_ci goto out; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci pdata = acpi_driver_data(adev); 7318c2ecf20Sopenharmony_ci if (WARN_ON(!pdata || !pdata->mmio_base)) { 7328c2ecf20Sopenharmony_ci ret = -ENODEV; 7338c2ecf20Sopenharmony_ci goto out; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci *val = __lpss_reg_read(pdata, reg); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci out: 7388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 7398c2ecf20Sopenharmony_ci return ret; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr, 7438c2ecf20Sopenharmony_ci char *buf) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci u32 ltr_value = 0; 7468c2ecf20Sopenharmony_ci unsigned int reg; 7478c2ecf20Sopenharmony_ci int ret; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR; 7508c2ecf20Sopenharmony_ci ret = lpss_reg_read(dev, reg, <r_value); 7518c2ecf20Sopenharmony_ci if (ret) 7528c2ecf20Sopenharmony_ci return ret; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%08x\n", ltr_value); 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic ssize_t lpss_ltr_mode_show(struct device *dev, 7588c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci u32 ltr_mode = 0; 7618c2ecf20Sopenharmony_ci char *outstr; 7628c2ecf20Sopenharmony_ci int ret; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci ret = lpss_reg_read(dev, LPSS_GENERAL, <r_mode); 7658c2ecf20Sopenharmony_ci if (ret) 7668c2ecf20Sopenharmony_ci return ret; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto"; 7698c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", outstr); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL); 7738c2ecf20Sopenharmony_cistatic DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL); 7748c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic struct attribute *lpss_attrs[] = { 7778c2ecf20Sopenharmony_ci &dev_attr_auto_ltr.attr, 7788c2ecf20Sopenharmony_ci &dev_attr_sw_ltr.attr, 7798c2ecf20Sopenharmony_ci &dev_attr_ltr_mode.attr, 7808c2ecf20Sopenharmony_ci NULL, 7818c2ecf20Sopenharmony_ci}; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic const struct attribute_group lpss_attr_group = { 7848c2ecf20Sopenharmony_ci .attrs = lpss_attrs, 7858c2ecf20Sopenharmony_ci .name = "lpss_ltr", 7868c2ecf20Sopenharmony_ci}; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic void acpi_lpss_set_ltr(struct device *dev, s32 val) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 7918c2ecf20Sopenharmony_ci u32 ltr_mode, ltr_val; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL); 7948c2ecf20Sopenharmony_ci if (val < 0) { 7958c2ecf20Sopenharmony_ci if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) { 7968c2ecf20Sopenharmony_ci ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW; 7978c2ecf20Sopenharmony_ci __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci return; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK; 8028c2ecf20Sopenharmony_ci if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) { 8038c2ecf20Sopenharmony_ci ltr_val |= LPSS_LTR_SNOOP_LAT_32US; 8048c2ecf20Sopenharmony_ci val = LPSS_LTR_MAX_VAL; 8058c2ecf20Sopenharmony_ci } else if (val > LPSS_LTR_MAX_VAL) { 8068c2ecf20Sopenharmony_ci ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ; 8078c2ecf20Sopenharmony_ci val >>= LPSS_LTR_SNOOP_LAT_SHIFT; 8088c2ecf20Sopenharmony_ci } else { 8098c2ecf20Sopenharmony_ci ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci ltr_val |= val; 8128c2ecf20Sopenharmony_ci __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR); 8138c2ecf20Sopenharmony_ci if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) { 8148c2ecf20Sopenharmony_ci ltr_mode |= LPSS_GENERAL_LTR_MODE_SW; 8158c2ecf20Sopenharmony_ci __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 8208c2ecf20Sopenharmony_ci/** 8218c2ecf20Sopenharmony_ci * acpi_lpss_save_ctx() - Save the private registers of LPSS device 8228c2ecf20Sopenharmony_ci * @dev: LPSS device 8238c2ecf20Sopenharmony_ci * @pdata: pointer to the private data of the LPSS device 8248c2ecf20Sopenharmony_ci * 8258c2ecf20Sopenharmony_ci * Most LPSS devices have private registers which may loose their context when 8268c2ecf20Sopenharmony_ci * the device is powered down. acpi_lpss_save_ctx() saves those registers into 8278c2ecf20Sopenharmony_ci * prv_reg_ctx array. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_cistatic void acpi_lpss_save_ctx(struct device *dev, 8308c2ecf20Sopenharmony_ci struct lpss_private_data *pdata) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci unsigned int i; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { 8358c2ecf20Sopenharmony_ci unsigned long offset = i * sizeof(u32); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci pdata->prv_reg_ctx[i] = __lpss_reg_read(pdata, offset); 8388c2ecf20Sopenharmony_ci dev_dbg(dev, "saving 0x%08x from LPSS reg at offset 0x%02lx\n", 8398c2ecf20Sopenharmony_ci pdata->prv_reg_ctx[i], offset); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci/** 8448c2ecf20Sopenharmony_ci * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device 8458c2ecf20Sopenharmony_ci * @dev: LPSS device 8468c2ecf20Sopenharmony_ci * @pdata: pointer to the private data of the LPSS device 8478c2ecf20Sopenharmony_ci * 8488c2ecf20Sopenharmony_ci * Restores the registers that were previously stored with acpi_lpss_save_ctx(). 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_cistatic void acpi_lpss_restore_ctx(struct device *dev, 8518c2ecf20Sopenharmony_ci struct lpss_private_data *pdata) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci unsigned int i; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { 8568c2ecf20Sopenharmony_ci unsigned long offset = i * sizeof(u32); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset); 8598c2ecf20Sopenharmony_ci dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n", 8608c2ecf20Sopenharmony_ci pdata->prv_reg_ctx[i], offset); 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci /* 8678c2ecf20Sopenharmony_ci * The following delay is needed or the subsequent write operations may 8688c2ecf20Sopenharmony_ci * fail. The LPSS devices are actually PCI devices and the PCI spec 8698c2ecf20Sopenharmony_ci * expects 10ms delay before the device can be accessed after D3 to D0 8708c2ecf20Sopenharmony_ci * transition. However some platforms like BSW does not need this delay. 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_ci unsigned int delay = 10; /* default 10ms delay */ 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & LPSS_NO_D3_DELAY) 8758c2ecf20Sopenharmony_ci delay = 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci msleep(delay); 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic int acpi_lpss_activate(struct device *dev) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 8838c2ecf20Sopenharmony_ci int ret; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci ret = acpi_dev_resume(dev); 8868c2ecf20Sopenharmony_ci if (ret) 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci acpi_lpss_d3_to_d0_delay(pdata); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* 8928c2ecf20Sopenharmony_ci * This is called only on ->probe() stage where a device is either in 8938c2ecf20Sopenharmony_ci * known state defined by BIOS or most likely powered off. Due to this 8948c2ecf20Sopenharmony_ci * we have to deassert reset line to be sure that ->probe() will 8958c2ecf20Sopenharmony_ci * recognize the device. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) 8988c2ecf20Sopenharmony_ci lpss_deassert_reset(pdata); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9018c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & LPSS_SAVE_CTX_ONCE) 9028c2ecf20Sopenharmony_ci acpi_lpss_save_ctx(dev, pdata); 9038c2ecf20Sopenharmony_ci#endif 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci return 0; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic void acpi_lpss_dismiss(struct device *dev) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci acpi_dev_suspend(dev, false); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci/* IOSF SB for LPSS island */ 9148c2ecf20Sopenharmony_ci#define LPSS_IOSF_UNIT_LPIOEP 0xA0 9158c2ecf20Sopenharmony_ci#define LPSS_IOSF_UNIT_LPIO1 0xAB 9168c2ecf20Sopenharmony_ci#define LPSS_IOSF_UNIT_LPIO2 0xAC 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci#define LPSS_IOSF_PMCSR 0x84 9198c2ecf20Sopenharmony_ci#define LPSS_PMCSR_D0 0 9208c2ecf20Sopenharmony_ci#define LPSS_PMCSR_D3hot 3 9218c2ecf20Sopenharmony_ci#define LPSS_PMCSR_Dx_MASK GENMASK(1, 0) 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci#define LPSS_IOSF_GPIODEF0 0x154 9248c2ecf20Sopenharmony_ci#define LPSS_GPIODEF0_DMA1_D3 BIT(2) 9258c2ecf20Sopenharmony_ci#define LPSS_GPIODEF0_DMA2_D3 BIT(3) 9268c2ecf20Sopenharmony_ci#define LPSS_GPIODEF0_DMA_D3_MASK GENMASK(3, 2) 9278c2ecf20Sopenharmony_ci#define LPSS_GPIODEF0_DMA_LLP BIT(13) 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(lpss_iosf_mutex); 9308c2ecf20Sopenharmony_cistatic bool lpss_iosf_d3_entered = true; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void lpss_iosf_enter_d3_state(void) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci u32 value1 = 0; 9358c2ecf20Sopenharmony_ci u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP; 9368c2ecf20Sopenharmony_ci u32 value2 = LPSS_PMCSR_D3hot; 9378c2ecf20Sopenharmony_ci u32 mask2 = LPSS_PMCSR_Dx_MASK; 9388c2ecf20Sopenharmony_ci /* 9398c2ecf20Sopenharmony_ci * PMC provides an information about actual status of the LPSS devices. 9408c2ecf20Sopenharmony_ci * Here we read the values related to LPSS power island, i.e. LPSS 9418c2ecf20Sopenharmony_ci * devices, excluding both LPSS DMA controllers, along with SCC domain. 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_ci u32 func_dis, d3_sts_0, pmc_status; 9448c2ecf20Sopenharmony_ci int ret; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis); 9478c2ecf20Sopenharmony_ci if (ret) 9488c2ecf20Sopenharmony_ci return; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci mutex_lock(&lpss_iosf_mutex); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0); 9538c2ecf20Sopenharmony_ci if (ret) 9548c2ecf20Sopenharmony_ci goto exit; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* 9578c2ecf20Sopenharmony_ci * Get the status of entire LPSS power island per device basis. 9588c2ecf20Sopenharmony_ci * Shutdown both LPSS DMA controllers if and only if all other devices 9598c2ecf20Sopenharmony_ci * are already in D3hot. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_ci pmc_status = (~(d3_sts_0 | func_dis)) & pmc_atom_d3_mask; 9628c2ecf20Sopenharmony_ci if (pmc_status) 9638c2ecf20Sopenharmony_ci goto exit; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE, 9668c2ecf20Sopenharmony_ci LPSS_IOSF_PMCSR, value2, mask2); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE, 9698c2ecf20Sopenharmony_ci LPSS_IOSF_PMCSR, value2, mask2); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE, 9728c2ecf20Sopenharmony_ci LPSS_IOSF_GPIODEF0, value1, mask1); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci lpss_iosf_d3_entered = true; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ciexit: 9778c2ecf20Sopenharmony_ci mutex_unlock(&lpss_iosf_mutex); 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void lpss_iosf_exit_d3_state(void) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3 | 9838c2ecf20Sopenharmony_ci LPSS_GPIODEF0_DMA_LLP; 9848c2ecf20Sopenharmony_ci u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK | LPSS_GPIODEF0_DMA_LLP; 9858c2ecf20Sopenharmony_ci u32 value2 = LPSS_PMCSR_D0; 9868c2ecf20Sopenharmony_ci u32 mask2 = LPSS_PMCSR_Dx_MASK; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci mutex_lock(&lpss_iosf_mutex); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (!lpss_iosf_d3_entered) 9918c2ecf20Sopenharmony_ci goto exit; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci lpss_iosf_d3_entered = false; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE, 9968c2ecf20Sopenharmony_ci LPSS_IOSF_GPIODEF0, value1, mask1); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE, 9998c2ecf20Sopenharmony_ci LPSS_IOSF_PMCSR, value2, mask2); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE, 10028c2ecf20Sopenharmony_ci LPSS_IOSF_PMCSR, value2, mask2); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ciexit: 10058c2ecf20Sopenharmony_ci mutex_unlock(&lpss_iosf_mutex); 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic int acpi_lpss_suspend(struct device *dev, bool wakeup) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 10118c2ecf20Sopenharmony_ci int ret; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & LPSS_SAVE_CTX) 10148c2ecf20Sopenharmony_ci acpi_lpss_save_ctx(dev, pdata); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci ret = acpi_dev_suspend(dev, wakeup); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* 10198c2ecf20Sopenharmony_ci * This call must be last in the sequence, otherwise PMC will return 10208c2ecf20Sopenharmony_ci * wrong status for devices being about to be powered off. See 10218c2ecf20Sopenharmony_ci * lpss_iosf_enter_d3_state() for further information. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci if (acpi_target_system_state() == ACPI_STATE_S0 && 10248c2ecf20Sopenharmony_ci lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) 10258c2ecf20Sopenharmony_ci lpss_iosf_enter_d3_state(); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return ret; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic int acpi_lpss_resume(struct device *dev) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 10338c2ecf20Sopenharmony_ci int ret; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* 10368c2ecf20Sopenharmony_ci * This call is kept first to be in symmetry with 10378c2ecf20Sopenharmony_ci * acpi_lpss_runtime_suspend() one. 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) 10408c2ecf20Sopenharmony_ci lpss_iosf_exit_d3_state(); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci ret = acpi_dev_resume(dev); 10438c2ecf20Sopenharmony_ci if (ret) 10448c2ecf20Sopenharmony_ci return ret; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci acpi_lpss_d3_to_d0_delay(pdata); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & (LPSS_SAVE_CTX | LPSS_SAVE_CTX_ONCE)) 10498c2ecf20Sopenharmony_ci acpi_lpss_restore_ctx(dev, pdata); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci return 0; 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 10558c2ecf20Sopenharmony_cistatic int acpi_lpss_do_suspend_late(struct device *dev) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci int ret; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (dev_pm_skip_suspend(dev)) 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci ret = pm_generic_suspend_late(dev); 10638c2ecf20Sopenharmony_ci return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); 10648c2ecf20Sopenharmony_ci} 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic int acpi_lpss_suspend_late(struct device *dev) 10678c2ecf20Sopenharmony_ci{ 10688c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci if (pdata->dev_desc->resume_from_noirq) 10718c2ecf20Sopenharmony_ci return 0; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci return acpi_lpss_do_suspend_late(dev); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int acpi_lpss_suspend_noirq(struct device *dev) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 10798c2ecf20Sopenharmony_ci int ret; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (pdata->dev_desc->resume_from_noirq) { 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * The driver's ->suspend_late callback will be invoked by 10848c2ecf20Sopenharmony_ci * acpi_lpss_do_suspend_late(), with the assumption that the 10858c2ecf20Sopenharmony_ci * driver really wanted to run that code in ->suspend_noirq, but 10868c2ecf20Sopenharmony_ci * it could not run after acpi_dev_suspend() and the driver 10878c2ecf20Sopenharmony_ci * expected the latter to be called in the "late" phase. 10888c2ecf20Sopenharmony_ci */ 10898c2ecf20Sopenharmony_ci ret = acpi_lpss_do_suspend_late(dev); 10908c2ecf20Sopenharmony_ci if (ret) 10918c2ecf20Sopenharmony_ci return ret; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return acpi_subsys_suspend_noirq(dev); 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic int acpi_lpss_do_resume_early(struct device *dev) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci int ret = acpi_lpss_resume(dev); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci return ret ? ret : pm_generic_resume_early(dev); 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int acpi_lpss_resume_early(struct device *dev) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (pdata->dev_desc->resume_from_noirq) 11098c2ecf20Sopenharmony_ci return 0; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (dev_pm_skip_resume(dev)) 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci return acpi_lpss_do_resume_early(dev); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int acpi_lpss_resume_noirq(struct device *dev) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 11208c2ecf20Sopenharmony_ci int ret; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* Follow acpi_subsys_resume_noirq(). */ 11238c2ecf20Sopenharmony_ci if (dev_pm_skip_resume(dev)) 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci ret = pm_generic_resume_noirq(dev); 11278c2ecf20Sopenharmony_ci if (ret) 11288c2ecf20Sopenharmony_ci return ret; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (!pdata->dev_desc->resume_from_noirq) 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* 11348c2ecf20Sopenharmony_ci * The driver's ->resume_early callback will be invoked by 11358c2ecf20Sopenharmony_ci * acpi_lpss_do_resume_early(), with the assumption that the driver 11368c2ecf20Sopenharmony_ci * really wanted to run that code in ->resume_noirq, but it could not 11378c2ecf20Sopenharmony_ci * run before acpi_dev_resume() and the driver expected the latter to be 11388c2ecf20Sopenharmony_ci * called in the "early" phase. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci return acpi_lpss_do_resume_early(dev); 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int acpi_lpss_do_restore_early(struct device *dev) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci int ret = acpi_lpss_resume(dev); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return ret ? ret : pm_generic_restore_early(dev); 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic int acpi_lpss_restore_early(struct device *dev) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (pdata->dev_desc->resume_from_noirq) 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci return acpi_lpss_do_restore_early(dev); 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic int acpi_lpss_restore_noirq(struct device *dev) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 11638c2ecf20Sopenharmony_ci int ret; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci ret = pm_generic_restore_noirq(dev); 11668c2ecf20Sopenharmony_ci if (ret) 11678c2ecf20Sopenharmony_ci return ret; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (!pdata->dev_desc->resume_from_noirq) 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* This is analogous to what happens in acpi_lpss_resume_noirq(). */ 11738c2ecf20Sopenharmony_ci return acpi_lpss_do_restore_early(dev); 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic int acpi_lpss_do_poweroff_late(struct device *dev) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci int ret = pm_generic_poweroff_late(dev); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic int acpi_lpss_poweroff_late(struct device *dev) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (dev_pm_skip_suspend(dev)) 11888c2ecf20Sopenharmony_ci return 0; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (pdata->dev_desc->resume_from_noirq) 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci return acpi_lpss_do_poweroff_late(dev); 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic int acpi_lpss_poweroff_noirq(struct device *dev) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (dev_pm_skip_suspend(dev)) 12018c2ecf20Sopenharmony_ci return 0; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (pdata->dev_desc->resume_from_noirq) { 12048c2ecf20Sopenharmony_ci /* This is analogous to the acpi_lpss_suspend_noirq() case. */ 12058c2ecf20Sopenharmony_ci int ret = acpi_lpss_do_poweroff_late(dev); 12068c2ecf20Sopenharmony_ci if (ret) 12078c2ecf20Sopenharmony_ci return ret; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return pm_generic_poweroff_noirq(dev); 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic int acpi_lpss_runtime_suspend(struct device *dev) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci int ret = pm_generic_runtime_suspend(dev); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci return ret ? ret : acpi_lpss_suspend(dev, true); 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int acpi_lpss_runtime_resume(struct device *dev) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci int ret = acpi_lpss_resume(dev); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci return ret ? ret : pm_generic_runtime_resume(dev); 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cistatic struct dev_pm_domain acpi_lpss_pm_domain = { 12308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12318c2ecf20Sopenharmony_ci .activate = acpi_lpss_activate, 12328c2ecf20Sopenharmony_ci .dismiss = acpi_lpss_dismiss, 12338c2ecf20Sopenharmony_ci#endif 12348c2ecf20Sopenharmony_ci .ops = { 12358c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 12378c2ecf20Sopenharmony_ci .prepare = acpi_subsys_prepare, 12388c2ecf20Sopenharmony_ci .complete = acpi_subsys_complete, 12398c2ecf20Sopenharmony_ci .suspend = acpi_subsys_suspend, 12408c2ecf20Sopenharmony_ci .suspend_late = acpi_lpss_suspend_late, 12418c2ecf20Sopenharmony_ci .suspend_noirq = acpi_lpss_suspend_noirq, 12428c2ecf20Sopenharmony_ci .resume_noirq = acpi_lpss_resume_noirq, 12438c2ecf20Sopenharmony_ci .resume_early = acpi_lpss_resume_early, 12448c2ecf20Sopenharmony_ci .freeze = acpi_subsys_freeze, 12458c2ecf20Sopenharmony_ci .poweroff = acpi_subsys_poweroff, 12468c2ecf20Sopenharmony_ci .poweroff_late = acpi_lpss_poweroff_late, 12478c2ecf20Sopenharmony_ci .poweroff_noirq = acpi_lpss_poweroff_noirq, 12488c2ecf20Sopenharmony_ci .restore_noirq = acpi_lpss_restore_noirq, 12498c2ecf20Sopenharmony_ci .restore_early = acpi_lpss_restore_early, 12508c2ecf20Sopenharmony_ci#endif 12518c2ecf20Sopenharmony_ci .runtime_suspend = acpi_lpss_runtime_suspend, 12528c2ecf20Sopenharmony_ci .runtime_resume = acpi_lpss_runtime_resume, 12538c2ecf20Sopenharmony_ci#endif 12548c2ecf20Sopenharmony_ci }, 12558c2ecf20Sopenharmony_ci}; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic int acpi_lpss_platform_notify(struct notifier_block *nb, 12588c2ecf20Sopenharmony_ci unsigned long action, void *data) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(data); 12618c2ecf20Sopenharmony_ci struct lpss_private_data *pdata; 12628c2ecf20Sopenharmony_ci struct acpi_device *adev; 12638c2ecf20Sopenharmony_ci const struct acpi_device_id *id; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev); 12668c2ecf20Sopenharmony_ci if (!id || !id->driver_data) 12678c2ecf20Sopenharmony_ci return 0; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) 12708c2ecf20Sopenharmony_ci return 0; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci pdata = acpi_driver_data(adev); 12738c2ecf20Sopenharmony_ci if (!pdata) 12748c2ecf20Sopenharmony_ci return 0; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci if (pdata->mmio_base && 12778c2ecf20Sopenharmony_ci pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { 12788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); 12798c2ecf20Sopenharmony_ci return 0; 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci switch (action) { 12838c2ecf20Sopenharmony_ci case BUS_NOTIFY_BIND_DRIVER: 12848c2ecf20Sopenharmony_ci dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain); 12858c2ecf20Sopenharmony_ci break; 12868c2ecf20Sopenharmony_ci case BUS_NOTIFY_DRIVER_NOT_BOUND: 12878c2ecf20Sopenharmony_ci case BUS_NOTIFY_UNBOUND_DRIVER: 12888c2ecf20Sopenharmony_ci dev_pm_domain_set(&pdev->dev, NULL); 12898c2ecf20Sopenharmony_ci break; 12908c2ecf20Sopenharmony_ci case BUS_NOTIFY_ADD_DEVICE: 12918c2ecf20Sopenharmony_ci dev_pm_domain_set(&pdev->dev, &acpi_lpss_pm_domain); 12928c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & LPSS_LTR) 12938c2ecf20Sopenharmony_ci return sysfs_create_group(&pdev->dev.kobj, 12948c2ecf20Sopenharmony_ci &lpss_attr_group); 12958c2ecf20Sopenharmony_ci break; 12968c2ecf20Sopenharmony_ci case BUS_NOTIFY_DEL_DEVICE: 12978c2ecf20Sopenharmony_ci if (pdata->dev_desc->flags & LPSS_LTR) 12988c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); 12998c2ecf20Sopenharmony_ci dev_pm_domain_set(&pdev->dev, NULL); 13008c2ecf20Sopenharmony_ci break; 13018c2ecf20Sopenharmony_ci default: 13028c2ecf20Sopenharmony_ci break; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci return 0; 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic struct notifier_block acpi_lpss_nb = { 13098c2ecf20Sopenharmony_ci .notifier_call = acpi_lpss_platform_notify, 13108c2ecf20Sopenharmony_ci}; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic void acpi_lpss_bind(struct device *dev) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR)) 13178c2ecf20Sopenharmony_ci return; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) 13208c2ecf20Sopenharmony_ci dev->power.set_latency_tolerance = acpi_lpss_set_ltr; 13218c2ecf20Sopenharmony_ci else 13228c2ecf20Sopenharmony_ci dev_err(dev, "MMIO size insufficient to access LTR\n"); 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_cistatic void acpi_lpss_unbind(struct device *dev) 13268c2ecf20Sopenharmony_ci{ 13278c2ecf20Sopenharmony_ci dev->power.set_latency_tolerance = NULL; 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic struct acpi_scan_handler lpss_handler = { 13318c2ecf20Sopenharmony_ci .ids = acpi_lpss_device_ids, 13328c2ecf20Sopenharmony_ci .attach = acpi_lpss_create_device, 13338c2ecf20Sopenharmony_ci .bind = acpi_lpss_bind, 13348c2ecf20Sopenharmony_ci .unbind = acpi_lpss_unbind, 13358c2ecf20Sopenharmony_ci}; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_civoid __init acpi_lpss_init(void) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci const struct x86_cpu_id *id; 13408c2ecf20Sopenharmony_ci int ret; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci ret = lpt_clk_init(); 13438c2ecf20Sopenharmony_ci if (ret) 13448c2ecf20Sopenharmony_ci return; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci id = x86_match_cpu(lpss_cpu_ids); 13478c2ecf20Sopenharmony_ci if (id) 13488c2ecf20Sopenharmony_ci lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci bus_register_notifier(&platform_bus_type, &acpi_lpss_nb); 13518c2ecf20Sopenharmony_ci acpi_scan_add_handler(&lpss_handler); 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci#else 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic struct acpi_scan_handler lpss_handler = { 13578c2ecf20Sopenharmony_ci .ids = acpi_lpss_device_ids, 13588c2ecf20Sopenharmony_ci}; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_civoid __init acpi_lpss_init(void) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci acpi_scan_add_handler(&lpss_handler); 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_INTEL_LPSS */ 1366